diff options
| author | benjaminschlegel87 <[email protected]> | 2025-07-25 20:39:40 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-07-25 20:39:40 +0200 |
| commit | dbc1818acd69e2e15ac574356c9b07cb717df441 (patch) | |
| tree | 05e6360c1946183b524a1ce82268547fe4bbcfd0 | |
| parent | adb728009ceba095d2190038ff698aaee08907a9 (diff) | |
| parent | 996974e313fa5ec2c7c2d9dd0998fab244c0a180 (diff) | |
Merge branch 'embassy-rs:main' into stm32_adc_v3_hw_oversampling_support
321 files changed, 10994 insertions, 2220 deletions
diff --git a/.github/ci/book.sh b/.github/ci/book.sh index 285cdc8fa..2466f53f5 100755 --- a/.github/ci/book.sh +++ b/.github/ci/book.sh | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | #!/bin/bash | 1 | #!/bin/bash |
| 2 | ## on push branch=main | 2 | ## on push branch=main |
| 3 | ## priority -9 | ||
| 4 | ## dedup dequeue | ||
| 3 | 5 | ||
| 4 | set -euxo pipefail | 6 | set -euxo pipefail |
| 5 | 7 | ||
diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 58ffe5f2e..9162b37ae 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | #!/bin/bash | 1 | #!/bin/bash |
| 2 | ## on push branch=main | 2 | ## on push branch=main |
| 3 | ## priority -10 | ||
| 4 | ## dedup dequeue | ||
| 3 | 5 | ||
| 4 | set -euxo pipefail | 6 | set -euxo pipefail |
| 5 | 7 | ||
| @@ -26,6 +28,7 @@ docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup | |||
| 26 | docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup | 28 | docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup |
| 27 | docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup | 29 | docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup |
| 28 | docserver-builder -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup | 30 | docserver-builder -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup |
| 31 | docserver-builder -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup | ||
| 29 | docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup | 32 | docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup |
| 30 | docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup | 33 | docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup |
| 31 | docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup | 34 | docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup |
| @@ -42,6 +45,7 @@ docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/g | |||
| 42 | docserver-builder -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup | 45 | docserver-builder -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup |
| 43 | 46 | ||
| 44 | docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup | 47 | docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup |
| 48 | docserver-builder -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup | ||
| 45 | docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup | 49 | docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup |
| 46 | docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup | 50 | docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup |
| 47 | docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup | 51 | docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup |
diff --git a/.vscode/settings.json b/.vscode/settings.json index e4814ff27..6edd9312a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json | |||
| @@ -36,6 +36,8 @@ | |||
| 36 | // "examples/nrf52840-rtic/Cargo.toml", | 36 | // "examples/nrf52840-rtic/Cargo.toml", |
| 37 | // "examples/nrf5340/Cargo.toml", | 37 | // "examples/nrf5340/Cargo.toml", |
| 38 | // "examples/nrf-rtos-trace/Cargo.toml", | 38 | // "examples/nrf-rtos-trace/Cargo.toml", |
| 39 | // "examples/mimxrt1011/Cargo.toml", | ||
| 40 | // "examples/mimxrt1062-evk/Cargo.toml", | ||
| 39 | // "examples/rp/Cargo.toml", | 41 | // "examples/rp/Cargo.toml", |
| 40 | // "examples/std/Cargo.toml", | 42 | // "examples/std/Cargo.toml", |
| 41 | // "examples/stm32c0/Cargo.toml", | 43 | // "examples/stm32c0/Cargo.toml", |
diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 000000000..0ebcfd9fa --- /dev/null +++ b/RELEASE.md | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | # RELEASE.md | ||
| 2 | |||
| 3 | This document outlines the process for releasing Embassy crates using `cargo-release` and the `release/bump-dependency.sh` script. | ||
| 4 | |||
| 5 | When releasing a crate, keep in mind that you may need to recursively release dependencies as well. | ||
| 6 | |||
| 7 | ## Prerequisites | ||
| 8 | |||
| 9 | - Install [`cargo-release`](https://github.com/crate-ci/cargo-release): | ||
| 10 | |||
| 11 | ```sh | ||
| 12 | cargo binstall cargo-release | ||
| 13 | |||
| 14 | ## Crate release | ||
| 15 | |||
| 16 | Check if there are changes to the public API since the last release. If there is a breaking change, follow | ||
| 17 | the process for creating a minor release. Otherewise, follow the process for creating a new patch release. | ||
| 18 | |||
| 19 | Keep in mind that some crates may need the --features and --target flags passed to cargo release. For more information on that, | ||
| 20 | look at the `Cargo.toml` files. | ||
| 21 | |||
| 22 | ### Patch release (no breaking public API changes) | ||
| 23 | |||
| 24 | ``` | ||
| 25 | cd embassy-nrf/ | ||
| 26 | cargo release patch --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi | ||
| 27 | |||
| 28 | # If dry-run is OK (no missing dependencies on crates.io) | ||
| 29 | cargo release patch --execute --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi | ||
| 30 | ``` | ||
| 31 | |||
| 32 | ### Minor release | ||
| 33 | |||
| 34 | ``` | ||
| 35 | # Bump versions in crate files | ||
| 36 | ./release/bump-dependency.sh embassy-nrf 0.4.0 | ||
| 37 | |||
| 38 | # Commit version bump | ||
| 39 | git commit -am 'chore: update to `embassy-nrf` v0.4.0' | ||
| 40 | |||
| 41 | # Release crate | ||
| 42 | cd embassy-nrf/ | ||
| 43 | cargo release minor --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi | ||
| 44 | |||
| 45 | # If dry-run is OK (no missing dependencies on crates.io) | ||
| 46 | cargo release minor --execute --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi | ||
| 47 | ``` | ||
| 48 | |||
| 49 | ## Push tags | ||
| 50 | |||
| 51 | Push the git tags that `cargo release` created earlier: | ||
| 52 | |||
| 53 | ``` | ||
| 54 | git push --tags | ||
| 55 | ``` | ||
| 56 | ## Reference | ||
| 57 | |||
| 58 | * [PR introducing release automation](https://github.com/embassy-rs/embassy/pull/4289) | ||
| @@ -40,6 +40,8 @@ cargo batch \ | |||
| 40 | --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \ | 40 | --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \ |
| 41 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \ | 41 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \ |
| 42 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ | 42 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ |
| 43 | --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi \ | ||
| 44 | --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi --features time \ | ||
| 43 | --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ | 45 | --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ |
| 44 | --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \ | 46 | --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \ |
| 45 | --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target thumbv6m-none-eabi \ | 47 | --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target thumbv6m-none-eabi \ |
| @@ -102,6 +104,8 @@ cargo batch \ | |||
| 102 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti \ | 104 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti \ |
| 103 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt \ | 105 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt \ |
| 104 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,single-bank,defmt \ | 106 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,single-bank,defmt \ |
| 107 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb,defmt,exti,time-driver-any,time \ | ||
| 108 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c051f6,defmt,exti,time-driver-any,time \ | ||
| 105 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \ | 109 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \ |
| 106 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \ | 110 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \ |
| 107 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \ | 111 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \ |
| @@ -155,11 +159,13 @@ cargo batch \ | |||
| 155 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303c8,defmt,exti,time-driver-any,time \ | 159 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303c8,defmt,exti,time-driver-any,time \ |
| 156 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \ | 160 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \ |
| 157 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \ | 161 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \ |
| 162 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0b0ce,defmt,exti,time-driver-any,time \ | ||
| 158 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0c1ve,defmt,exti,time-driver-any,time \ | 163 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0c1ve,defmt,exti,time-driver-any,time \ |
| 159 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \ | 164 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \ |
| 160 | --- 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 \ | 165 | --- 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 \ |
| 161 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32wl54jc-cm0p,defmt,exti,time-driver-any,time \ | 166 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32wl54jc-cm0p,defmt,exti,time-driver-any,time \ |
| 162 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wle5jb,defmt,exti,time-driver-any,time \ | 167 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wle5jb,defmt,exti,time-driver-any,time \ |
| 168 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g431kb,defmt,exti,time-driver-any,time \ | ||
| 163 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,dual-bank,defmt,exti,time-driver-any,time \ | 169 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,dual-bank,defmt,exti,time-driver-any,time \ |
| 164 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc,defmt,exti,time-driver-any,time \ | 170 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc,defmt,exti,time-driver-any,time \ |
| 165 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103re,defmt,exti,time-driver-any,time \ | 171 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103re,defmt,exti,time-driver-any,time \ |
| @@ -169,6 +175,8 @@ cargo batch \ | |||
| 169 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \ | 175 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \ |
| 170 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba50ke,defmt,exti,time-driver-any,time \ | 176 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba50ke,defmt,exti,time-driver-any,time \ |
| 171 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba55ug,defmt,exti,time-driver-any,time \ | 177 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba55ug,defmt,exti,time-driver-any,time \ |
| 178 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba62cg,defmt,exti,time-driver-any,time \ | ||
| 179 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba65ri,defmt,exti,time-driver-any,low-power,time \ | ||
| 172 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5f9zj,defmt,exti,time-driver-any,time \ | 180 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5f9zj,defmt,exti,time-driver-any,time \ |
| 173 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5g9nj,defmt,exti,time-driver-any,time \ | 181 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5g9nj,defmt,exti,time-driver-any,time \ |
| 174 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \ | 182 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \ |
| @@ -176,7 +184,9 @@ cargo batch \ | |||
| 176 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \ | 184 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \ |
| 177 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ | 185 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ |
| 178 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ | 186 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ |
| 179 | --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf \ | 187 | --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf --features lpc55,defmt \ |
| 188 | --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1011,rt,defmt,time-driver-pit \ | ||
| 189 | --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1062,rt,defmt,time-driver-pit \ | ||
| 180 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \ | 190 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \ |
| 181 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \ | 191 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \ |
| 182 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \ | 192 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \ |
| @@ -244,6 +254,7 @@ cargo batch \ | |||
| 244 | --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32h5 \ | 254 | --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32h5 \ |
| 245 | --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7 \ | 255 | --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7 \ |
| 246 | --- build --release --manifest-path examples/stm32h7b0/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7b0 \ | 256 | --- build --release --manifest-path examples/stm32h7b0/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7b0 \ |
| 257 | --- build --release --manifest-path examples/stm32h723/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h723 \ | ||
| 247 | --- build --release --manifest-path examples/stm32h735/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h735 \ | 258 | --- build --release --manifest-path examples/stm32h735/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h735 \ |
| 248 | --- build --release --manifest-path examples/stm32h755cm4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm4 \ | 259 | --- build --release --manifest-path examples/stm32h755cm4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm4 \ |
| 249 | --- build --release --manifest-path examples/stm32h755cm7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm7 \ | 260 | --- build --release --manifest-path examples/stm32h755cm7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm7 \ |
| @@ -259,6 +270,8 @@ cargo batch \ | |||
| 259 | --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \ | 270 | --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \ |
| 260 | --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \ | 271 | --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \ |
| 261 | --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \ | 272 | --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \ |
| 273 | --- build --release --manifest-path examples/mimxrt1011/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1011 \ | ||
| 274 | --- build --release --manifest-path examples/mimxrt1062-evk/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1062-evk \ | ||
| 262 | --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \ | 275 | --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \ |
| 263 | --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \ | 276 | --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \ |
| 264 | --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \ | 277 | --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \ |
| @@ -294,6 +307,7 @@ cargo batch \ | |||
| 294 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --artifact-dir out/tests/stm32g491re \ | 307 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --artifact-dir out/tests/stm32g491re \ |
| 295 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --artifact-dir out/tests/stm32g071rb \ | 308 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --artifact-dir out/tests/stm32g071rb \ |
| 296 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --artifact-dir out/tests/stm32c031c6 \ | 309 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --artifact-dir out/tests/stm32c031c6 \ |
| 310 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb --artifact-dir out/tests/stm32c071rb \ | ||
| 297 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --artifact-dir out/tests/stm32h755zi \ | 311 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --artifact-dir out/tests/stm32h755zi \ |
| 298 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --artifact-dir out/tests/stm32h753zi \ | 312 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --artifact-dir out/tests/stm32h753zi \ |
| 299 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --artifact-dir out/tests/stm32h7a3zi \ | 313 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --artifact-dir out/tests/stm32h7a3zi \ |
| @@ -369,8 +383,12 @@ rm out/tests/pimoroni-pico-plus-2/adc | |||
| 369 | # temporarily disabled | 383 | # temporarily disabled |
| 370 | rm out/tests/pimoroni-pico-plus-2/pwm | 384 | rm out/tests/pimoroni-pico-plus-2/pwm |
| 371 | 385 | ||
| 372 | # temporarily disabled, bad hardware connection. | 386 | # flaky |
| 373 | rm -f out/tests/rpi-pico/* | 387 | rm out/tests/rpi-pico/pwm |
| 388 | rm out/tests/rpi-pico/cyw43-perf | ||
| 389 | |||
| 390 | # tests are implemented but the HIL test farm doesn't actually have this board yet | ||
| 391 | rm -rf out/tests/stm32c071rb | ||
| 374 | 392 | ||
| 375 | if [[ -z "${TELEPROBE_TOKEN-}" ]]; then | 393 | if [[ -z "${TELEPROBE_TOKEN-}" ]]; then |
| 376 | echo No teleprobe token found, skipping running HIL tests | 394 | echo No teleprobe token found, skipping running HIL tests |
diff --git a/cyw43-pio/CHANGELOG.md b/cyw43-pio/CHANGELOG.md index 4d56973df..218271e15 100644 --- a/cyw43-pio/CHANGELOG.md +++ b/cyw43-pio/CHANGELOG.md | |||
| @@ -5,9 +5,14 @@ All notable changes to this project will be documented in this file. | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 7 | 7 | ||
| 8 | ## Unreleased | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | ||
| 9 | 10 | ||
| 10 | - Update embassy-rp to 0.4.0 | 11 | ## 0.5.1 - 2025-07-16 |
| 12 | |||
| 13 | ## 0.5.0 - 2025-07-15 | ||
| 14 | |||
| 15 | - Update embassy-rp to 0.5.0 | ||
| 11 | 16 | ||
| 12 | ## 0.3.0 - 2025-01-05 | 17 | ## 0.3.0 - 2025-01-05 |
| 13 | 18 | ||
diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 93a2e7089..d60793bdc 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "cyw43-pio" | 2 | name = "cyw43-pio" |
| 3 | version = "0.4.0" | 3 | version = "0.5.1" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | description = "RP2040 PIO SPI implementation for cyw43" | 5 | description = "RP2040 PIO SPI implementation for cyw43" |
| 6 | keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] | 6 | keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] |
| @@ -10,8 +10,8 @@ repository = "https://github.com/embassy-rs/embassy" | |||
| 10 | documentation = "https://docs.embassy.dev/cyw43-pio" | 10 | documentation = "https://docs.embassy.dev/cyw43-pio" |
| 11 | 11 | ||
| 12 | [dependencies] | 12 | [dependencies] |
| 13 | cyw43 = { version = "0.3.0", path = "../cyw43" } | 13 | cyw43 = { version = "0.4.0", path = "../cyw43" } |
| 14 | embassy-rp = { version = "0.4.0", path = "../embassy-rp" } | 14 | embassy-rp = { version = "0.6.0", path = "../embassy-rp" } |
| 15 | fixed = "1.23.1" | 15 | fixed = "1.23.1" |
| 16 | defmt = { version = "1.0.1", optional = true } | 16 | defmt = { version = "1.0.1", optional = true } |
| 17 | 17 | ||
diff --git a/cyw43-pio/release.toml b/cyw43-pio/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/cyw43-pio/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/cyw43/CHANGELOG.md b/cyw43/CHANGELOG.md index 40a638388..c800e785b 100644 --- a/cyw43/CHANGELOG.md +++ b/cyw43/CHANGELOG.md | |||
| @@ -5,7 +5,14 @@ All notable changes to this project will be documented in this file. | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 7 | 7 | ||
| 8 | ## Unreleased | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | ||
| 10 | |||
| 11 | ## 0.4.0 - 2025-07-15 | ||
| 12 | |||
| 13 | - bump embassy-sync to 0.7.0 | ||
| 14 | - bump bt-hci to 0.3.0 | ||
| 15 | - make State::new const fn | ||
| 9 | 16 | ||
| 10 | ## 0.3.0 - 2025-01-05 | 17 | ## 0.3.0 - 2025-01-05 |
| 11 | 18 | ||
diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index c52a653bd..8aef8963c 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "cyw43" | 2 | name = "cyw43" |
| 3 | version = "0.3.0" | 3 | version = "0.4.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | description = "Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W." | 5 | description = "Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W." |
| 6 | keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] | 6 | keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] |
| @@ -21,7 +21,7 @@ firmware-logs = [] | |||
| 21 | embassy-time = { version = "0.4.0", path = "../embassy-time"} | 21 | embassy-time = { version = "0.4.0", path = "../embassy-time"} |
| 22 | embassy-sync = { version = "0.7.0", path = "../embassy-sync"} | 22 | embassy-sync = { version = "0.7.0", path = "../embassy-sync"} |
| 23 | embassy-futures = { version = "0.1.0", path = "../embassy-futures"} | 23 | embassy-futures = { version = "0.1.0", path = "../embassy-futures"} |
| 24 | embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"} | 24 | embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel"} |
| 25 | 25 | ||
| 26 | defmt = { version = "1.0.1", optional = true } | 26 | defmt = { version = "1.0.1", optional = true } |
| 27 | log = { version = "0.4.17", optional = true } | 27 | log = { version = "0.4.17", optional = true } |
diff --git a/cyw43/release.toml b/cyw43/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/cyw43/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/docs/examples/basic/Cargo.toml b/docs/examples/basic/Cargo.toml index f5cf2b231..c4b72d81a 100644 --- a/docs/examples/basic/Cargo.toml +++ b/docs/examples/basic/Cargo.toml | |||
| @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" | |||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["defmt", "arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["defmt", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt"] } | 10 | embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt"] } |
| 11 | embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } | 11 | embassy-nrf = { version = "0.5.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } |
| 12 | 12 | ||
| 13 | defmt = "1.0.1" | 13 | defmt = "1.0.1" |
| 14 | defmt-rtt = "1.0.0" | 14 | defmt-rtt = "1.0.0" |
diff --git a/docs/index.adoc b/docs/index.adoc index 9c6150196..80754d5a4 100644 --- a/docs/index.adoc +++ b/docs/index.adoc | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | :toc-placement: left | 5 | :toc-placement: left |
| 6 | :toclevels: 2 | 6 | :toclevels: 2 |
| 7 | :imagesdir: images | 7 | :imagesdir: images |
| 8 | :source-highlighter: rouge | ||
| 8 | 9 | ||
| 9 | # Embassy Book | 10 | # Embassy Book |
| 10 | 11 | ||
diff --git a/docs/pages/layer_by_layer.adoc b/docs/pages/layer_by_layer.adoc index 7dba11b5e..0692ee4fa 100644 --- a/docs/pages/layer_by_layer.adoc +++ b/docs/pages/layer_by_layer.adoc | |||
| @@ -8,7 +8,7 @@ The application we'll write is a simple 'push button, blink led' application, wh | |||
| 8 | 8 | ||
| 9 | == PAC version | 9 | == PAC version |
| 10 | 10 | ||
| 11 | The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code. | 11 | The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types to make accessing peripheral registers easier, but it does little to prevent you from configuring or coordinating those registers incorrectly. |
| 12 | 12 | ||
| 13 | Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use. | 13 | Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use. |
| 14 | 14 | ||
diff --git a/docs/pages/new_project.adoc b/docs/pages/new_project.adoc index cd943b4f6..906d89f36 100644 --- a/docs/pages/new_project.adoc +++ b/docs/pages/new_project.adoc | |||
| @@ -11,6 +11,8 @@ Once you’ve successfully xref:#_getting_started[run some example projects], th | |||
| 11 | - link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP) | 11 | - link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP) |
| 12 | - link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP) | 12 | - link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP) |
| 13 | 13 | ||
| 14 | === esp-generate | ||
| 15 | - link:https://github.com/esp-rs/esp-generate[esp-generate] (ESP32 using esp-hal) | ||
| 14 | 16 | ||
| 15 | == Starting a project from scratch | 17 | == Starting a project from scratch |
| 16 | 18 | ||
diff --git a/docs/pages/overview.adoc b/docs/pages/overview.adoc index acd757795..18eaaeb75 100644 --- a/docs/pages/overview.adoc +++ b/docs/pages/overview.adoc | |||
| @@ -30,7 +30,7 @@ The Embassy project maintains HALs for select hardware, but you can still use HA | |||
| 30 | * link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series. | 30 | * link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series. |
| 31 | * link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 as well as RP235x microcontroller. | 31 | * link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 as well as RP235x microcontroller. |
| 32 | * link:https://docs.embassy.dev/embassy-mspm0/[embassy-mspm0], for the Texas Instruments MSPM0 microcontrollers. | 32 | * link:https://docs.embassy.dev/embassy-mspm0/[embassy-mspm0], for the Texas Instruments MSPM0 microcontrollers. |
| 33 | * link:https://github.com/esp-rs[esp-rs], for the Espressif Systems ESP32 series of chips. | 33 | * link:https://github.com/esp-rs/esp-hal[esp-hal], for the Espressif Systems ESP32 series of chips. |
| 34 | * link:https://github.com/ch32-rs/ch32-hal[ch32-hal], for the WCH 32-bit RISC-V(CH32V) series of chips. | 34 | * link:https://github.com/ch32-rs/ch32-hal[ch32-hal], for the WCH 32-bit RISC-V(CH32V) series of chips. |
| 35 | * link:https://github.com/AlexCharlton/mpfs-hal[mpfs-hal], for the Microchip PolarFire SoC. | 35 | * link:https://github.com/AlexCharlton/mpfs-hal[mpfs-hal], for the Microchip PolarFire SoC. |
| 36 | * link:https://github.com/py32-rs/py32-hal[py32-hal], for the Puya Semiconductor PY32 series of chips. | 36 | * link:https://github.com/py32-rs/py32-hal[py32-hal], for the Puya Semiconductor PY32 series of chips. |
diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index 3cfaa5a80..81479759c 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot-nrf" | 3 | name = "embassy-boot-nrf" |
| 4 | version = "0.4.0" | 4 | version = "0.6.0" |
| 5 | description = "Bootloader lib for nRF chips" | 5 | description = "Bootloader lib for nRF chips" |
| 6 | license = "MIT OR Apache-2.0" | 6 | license = "MIT OR Apache-2.0" |
| 7 | repository = "https://github.com/embassy-rs/embassy" | 7 | repository = "https://github.com/embassy-rs/embassy" |
| @@ -25,8 +25,8 @@ defmt = { version = "1.0.1", optional = true } | |||
| 25 | log = { version = "0.4.17", optional = true } | 25 | log = { version = "0.4.17", optional = true } |
| 26 | 26 | ||
| 27 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 27 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 28 | embassy-nrf = { version = "0.3.1", path = "../embassy-nrf", default-features = false } | 28 | embassy-nrf = { version = "0.5.0", path = "../embassy-nrf", default-features = false } |
| 29 | embassy-boot = { version = "0.4.0", path = "../embassy-boot" } | 29 | embassy-boot = { version = "0.5.0", path = "../embassy-boot" } |
| 30 | cortex-m = { version = "0.7.6" } | 30 | cortex-m = { version = "0.7.6" } |
| 31 | cortex-m-rt = { version = "0.7" } | 31 | cortex-m-rt = { version = "0.7" } |
| 32 | embedded-storage = "0.3.1" | 32 | embedded-storage = "0.3.1" |
diff --git a/embassy-boot-rp/Cargo.toml b/embassy-boot-rp/Cargo.toml index afe5d6691..8ca999f67 100644 --- a/embassy-boot-rp/Cargo.toml +++ b/embassy-boot-rp/Cargo.toml | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot-rp" | 3 | name = "embassy-boot-rp" |
| 4 | version = "0.5.0" | 4 | version = "0.6.0" |
| 5 | description = "Bootloader lib for RP2040 chips" | 5 | description = "Bootloader lib for RP2040 chips" |
| 6 | license = "MIT OR Apache-2.0" | 6 | license = "MIT OR Apache-2.0" |
| 7 | repository = "https://github.com/embassy-rs/embassy" | 7 | repository = "https://github.com/embassy-rs/embassy" |
| @@ -25,8 +25,8 @@ defmt = { version = "1.0.1", optional = true } | |||
| 25 | log = { version = "0.4", optional = true } | 25 | log = { version = "0.4", optional = true } |
| 26 | 26 | ||
| 27 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 27 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 28 | embassy-rp = { version = "0.4.0", path = "../embassy-rp", default-features = false } | 28 | embassy-rp = { version = "0.6.0", path = "../embassy-rp", default-features = false } |
| 29 | embassy-boot = { version = "0.4.0", path = "../embassy-boot" } | 29 | embassy-boot = { version = "0.5.0", path = "../embassy-boot" } |
| 30 | embassy-time = { version = "0.4.0", path = "../embassy-time" } | 30 | embassy-time = { version = "0.4.0", path = "../embassy-time" } |
| 31 | 31 | ||
| 32 | cortex-m = { version = "0.7.6" } | 32 | cortex-m = { version = "0.7.6" } |
diff --git a/embassy-boot-stm32/Cargo.toml b/embassy-boot-stm32/Cargo.toml index 11ad453b8..b92d06c54 100644 --- a/embassy-boot-stm32/Cargo.toml +++ b/embassy-boot-stm32/Cargo.toml | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot-stm32" | 3 | name = "embassy-boot-stm32" |
| 4 | version = "0.3.0" | 4 | version = "0.4.0" |
| 5 | description = "Bootloader lib for STM32 chips" | 5 | description = "Bootloader lib for STM32 chips" |
| 6 | license = "MIT OR Apache-2.0" | 6 | license = "MIT OR Apache-2.0" |
| 7 | repository = "https://github.com/embassy-rs/embassy" | 7 | repository = "https://github.com/embassy-rs/embassy" |
| @@ -26,7 +26,7 @@ log = { version = "0.4", optional = true } | |||
| 26 | 26 | ||
| 27 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 27 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 28 | embassy-stm32 = { version = "0.2.0", path = "../embassy-stm32", default-features = false } | 28 | embassy-stm32 = { version = "0.2.0", path = "../embassy-stm32", default-features = false } |
| 29 | embassy-boot = { version = "0.4.0", path = "../embassy-boot" } | 29 | embassy-boot = { version = "0.5.0", path = "../embassy-boot" } |
| 30 | cortex-m = { version = "0.7.6" } | 30 | cortex-m = { version = "0.7.6" } |
| 31 | cortex-m-rt = { version = "0.7" } | 31 | cortex-m-rt = { version = "0.7" } |
| 32 | embedded-storage = "0.3.1" | 32 | embedded-storage = "0.3.1" |
diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml index f12e8e304..172330ef2 100644 --- a/embassy-boot/Cargo.toml +++ b/embassy-boot/Cargo.toml | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-boot" | 3 | name = "embassy-boot" |
| 4 | version = "0.4.0" | 4 | version = "0.5.0" |
| 5 | description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks." | 5 | description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks." |
| 6 | license = "MIT OR Apache-2.0" | 6 | license = "MIT OR Apache-2.0" |
| 7 | repository = "https://github.com/embassy-rs/embassy" | 7 | repository = "https://github.com/embassy-rs/embassy" |
| @@ -28,7 +28,7 @@ defmt = { version = "1.0.1", optional = true } | |||
| 28 | digest = "0.10" | 28 | digest = "0.10" |
| 29 | log = { version = "0.4", optional = true } | 29 | log = { version = "0.4", optional = true } |
| 30 | ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true } | 30 | ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true } |
| 31 | embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } | 31 | embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal" } |
| 32 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 32 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 33 | embedded-storage = "0.3.1" | 33 | embedded-storage = "0.3.1" |
| 34 | embedded-storage-async = { version = "0.4.1" } | 34 | embedded-storage-async = { version = "0.4.1" } |
diff --git a/embassy-embedded-hal/CHANGELOG.md b/embassy-embedded-hal/CHANGELOG.md index 224036af4..04d95415c 100644 --- a/embassy-embedded-hal/CHANGELOG.md +++ b/embassy-embedded-hal/CHANGELOG.md | |||
| @@ -5,7 +5,13 @@ All notable changes to this project will be documented in this file. | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 7 | 7 | ||
| 8 | ## Unreleased | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | ||
| 10 | |||
| 11 | ## 0.3.1 - 2025-07-16 | ||
| 12 | |||
| 13 | - `SpiDevice` cancel safety: always set CS pin to high on drop | ||
| 14 | - Update `embassy-sync` to v0.7.0 | ||
| 9 | 15 | ||
| 10 | ## 0.3.0 - 2025-01-05 | 16 | ## 0.3.0 - 2025-01-05 |
| 11 | 17 | ||
diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index efc3173d4..8277aa291 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-embedded-hal" | 2 | name = "embassy-embedded-hal" |
| 3 | version = "0.3.0" | 3 | version = "0.3.1" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "Collection of utilities to use `embedded-hal` and `embedded-storage` traits with Embassy." | 6 | description = "Collection of utilities to use `embedded-hal` and `embedded-storage` traits with Embassy." |
| @@ -19,10 +19,9 @@ target = "x86_64-unknown-linux-gnu" | |||
| 19 | 19 | ||
| 20 | [features] | 20 | [features] |
| 21 | time = ["dep:embassy-time"] | 21 | time = ["dep:embassy-time"] |
| 22 | default = ["time"] | ||
| 23 | 22 | ||
| 24 | [dependencies] | 23 | [dependencies] |
| 25 | embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal" } | 24 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } |
| 26 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 25 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 27 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 26 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 28 | embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } | 27 | embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } |
diff --git a/embassy-embedded-hal/release.toml b/embassy-embedded-hal/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-embedded-hal/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/embassy-embedded-hal/src/adapter/blocking_async.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs index bafc31583..3b6e0ec00 100644 --- a/embassy-embedded-hal/src/adapter/blocking_async.rs +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs | |||
| @@ -1,5 +1,3 @@ | |||
| 1 | use embedded_hal_02::blocking; | ||
| 2 | |||
| 3 | /// Wrapper that implements async traits using blocking implementations. | 1 | /// Wrapper that implements async traits using blocking implementations. |
| 4 | /// | 2 | /// |
| 5 | /// This allows driver writers to depend on the async traits while still supporting embedded-hal peripheral implementations. | 3 | /// This allows driver writers to depend on the async traits while still supporting embedded-hal peripheral implementations. |
| @@ -24,7 +22,7 @@ impl<T> BlockingAsync<T> { | |||
| 24 | impl<T, E> embedded_hal_1::i2c::ErrorType for BlockingAsync<T> | 22 | impl<T, E> embedded_hal_1::i2c::ErrorType for BlockingAsync<T> |
| 25 | where | 23 | where |
| 26 | E: embedded_hal_1::i2c::Error + 'static, | 24 | E: embedded_hal_1::i2c::Error + 'static, |
| 27 | T: blocking::i2c::WriteRead<Error = E> + blocking::i2c::Read<Error = E> + blocking::i2c::Write<Error = E>, | 25 | T: embedded_hal_1::i2c::I2c<Error = E>, |
| 28 | { | 26 | { |
| 29 | type Error = E; | 27 | type Error = E; |
| 30 | } | 28 | } |
| @@ -32,7 +30,7 @@ where | |||
| 32 | impl<T, E> embedded_hal_async::i2c::I2c for BlockingAsync<T> | 30 | impl<T, E> embedded_hal_async::i2c::I2c for BlockingAsync<T> |
| 33 | where | 31 | where |
| 34 | E: embedded_hal_1::i2c::Error + 'static, | 32 | E: embedded_hal_1::i2c::Error + 'static, |
| 35 | T: blocking::i2c::WriteRead<Error = E> + blocking::i2c::Read<Error = E> + blocking::i2c::Write<Error = E>, | 33 | T: embedded_hal_1::i2c::I2c<Error = E>, |
| 36 | { | 34 | { |
| 37 | async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { | 35 | async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { |
| 38 | self.wrapped.read(address, read) | 36 | self.wrapped.read(address, read) |
| @@ -51,9 +49,7 @@ where | |||
| 51 | address: u8, | 49 | address: u8, |
| 52 | operations: &mut [embedded_hal_1::i2c::Operation<'_>], | 50 | operations: &mut [embedded_hal_1::i2c::Operation<'_>], |
| 53 | ) -> Result<(), Self::Error> { | 51 | ) -> Result<(), Self::Error> { |
| 54 | let _ = address; | 52 | self.wrapped.transaction(address, operations) |
| 55 | let _ = operations; | ||
| 56 | todo!() | ||
| 57 | } | 53 | } |
| 58 | } | 54 | } |
| 59 | 55 | ||
| @@ -63,16 +59,16 @@ where | |||
| 63 | 59 | ||
| 64 | impl<T, E> embedded_hal_async::spi::ErrorType for BlockingAsync<T> | 60 | impl<T, E> embedded_hal_async::spi::ErrorType for BlockingAsync<T> |
| 65 | where | 61 | where |
| 66 | E: embedded_hal_1::spi::Error, | 62 | E: embedded_hal_async::spi::Error, |
| 67 | T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>, | 63 | T: embedded_hal_1::spi::SpiBus<Error = E>, |
| 68 | { | 64 | { |
| 69 | type Error = E; | 65 | type Error = E; |
| 70 | } | 66 | } |
| 71 | 67 | ||
| 72 | impl<T, E> embedded_hal_async::spi::SpiBus<u8> for BlockingAsync<T> | 68 | impl<T, E> embedded_hal_async::spi::SpiBus<u8> for BlockingAsync<T> |
| 73 | where | 69 | where |
| 74 | E: embedded_hal_1::spi::Error + 'static, | 70 | E: embedded_hal_async::spi::Error, |
| 75 | T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>, | 71 | T: embedded_hal_1::spi::SpiBus<Error = E>, |
| 76 | { | 72 | { |
| 77 | async fn flush(&mut self) -> Result<(), Self::Error> { | 73 | async fn flush(&mut self) -> Result<(), Self::Error> { |
| 78 | Ok(()) | 74 | Ok(()) |
| @@ -84,21 +80,17 @@ where | |||
| 84 | } | 80 | } |
| 85 | 81 | ||
| 86 | async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { | 82 | async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { |
| 87 | self.wrapped.transfer(data)?; | 83 | self.wrapped.read(data)?; |
| 88 | Ok(()) | 84 | Ok(()) |
| 89 | } | 85 | } |
| 90 | 86 | ||
| 91 | async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { | 87 | async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { |
| 92 | // Ensure we write the expected bytes | 88 | self.wrapped.transfer(read, write)?; |
| 93 | for i in 0..core::cmp::min(read.len(), write.len()) { | ||
| 94 | read[i] = write[i].clone(); | ||
| 95 | } | ||
| 96 | self.wrapped.transfer(read)?; | ||
| 97 | Ok(()) | 89 | Ok(()) |
| 98 | } | 90 | } |
| 99 | 91 | ||
| 100 | async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { | 92 | async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { |
| 101 | self.wrapped.transfer(data)?; | 93 | self.wrapped.transfer_in_place(data)?; |
| 102 | Ok(()) | 94 | Ok(()) |
| 103 | } | 95 | } |
| 104 | } | 96 | } |
diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 91d6beee8..1c5e3571d 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs | |||
| @@ -51,7 +51,11 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { | |||
| 51 | .embassy_executor | 51 | .embassy_executor |
| 52 | .unwrap_or(Expr::Verbatim(TokenStream::from_str("::embassy_executor").unwrap())); | 52 | .unwrap_or(Expr::Verbatim(TokenStream::from_str("::embassy_executor").unwrap())); |
| 53 | 53 | ||
| 54 | if f.sig.asyncness.is_none() { | 54 | let returns_impl_trait = match &f.sig.output { |
| 55 | ReturnType::Type(_, ty) => matches!(**ty, Type::ImplTrait(_)), | ||
| 56 | _ => false, | ||
| 57 | }; | ||
| 58 | if f.sig.asyncness.is_none() && !returns_impl_trait { | ||
| 55 | error(&mut errors, &f.sig, "task functions must be async"); | 59 | error(&mut errors, &f.sig, "task functions must be async"); |
| 56 | } | 60 | } |
| 57 | if !f.sig.generics.params.is_empty() { | 61 | if !f.sig.generics.params.is_empty() { |
| @@ -66,17 +70,19 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { | |||
| 66 | if !f.sig.variadic.is_none() { | 70 | if !f.sig.variadic.is_none() { |
| 67 | error(&mut errors, &f.sig, "task functions must not be variadic"); | 71 | error(&mut errors, &f.sig, "task functions must not be variadic"); |
| 68 | } | 72 | } |
| 69 | match &f.sig.output { | 73 | if f.sig.asyncness.is_some() { |
| 70 | ReturnType::Default => {} | 74 | match &f.sig.output { |
| 71 | ReturnType::Type(_, ty) => match &**ty { | 75 | ReturnType::Default => {} |
| 72 | Type::Tuple(tuple) if tuple.elems.is_empty() => {} | 76 | ReturnType::Type(_, ty) => match &**ty { |
| 73 | Type::Never(_) => {} | 77 | Type::Tuple(tuple) if tuple.elems.is_empty() => {} |
| 74 | _ => error( | 78 | Type::Never(_) => {} |
| 75 | &mut errors, | 79 | _ => error( |
| 76 | &f.sig, | 80 | &mut errors, |
| 77 | "task functions must either not return a value, return `()` or return `!`", | 81 | &f.sig, |
| 78 | ), | 82 | "task functions must either not return a value, return `()` or return `!`", |
| 79 | }, | 83 | ), |
| 84 | }, | ||
| 85 | } | ||
| 80 | } | 86 | } |
| 81 | 87 | ||
| 82 | let mut args = Vec::new(); | 88 | let mut args = Vec::new(); |
| @@ -125,15 +131,21 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { | |||
| 125 | )); | 131 | )); |
| 126 | } | 132 | } |
| 127 | 133 | ||
| 134 | let spawn = if returns_impl_trait { | ||
| 135 | quote!(spawn) | ||
| 136 | } else { | ||
| 137 | quote!(_spawn_async_fn) | ||
| 138 | }; | ||
| 139 | |||
| 128 | #[cfg(feature = "nightly")] | 140 | #[cfg(feature = "nightly")] |
| 129 | let mut task_outer_body = quote! { | 141 | let mut task_outer_body = quote! { |
| 130 | trait _EmbassyInternalTaskTrait { | 142 | trait _EmbassyInternalTaskTrait { |
| 131 | type Fut: ::core::future::Future + 'static; | 143 | type Fut: ::core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static; |
| 132 | fn construct(#fargs) -> Self::Fut; | 144 | fn construct(#fargs) -> Self::Fut; |
| 133 | } | 145 | } |
| 134 | 146 | ||
| 135 | impl _EmbassyInternalTaskTrait for () { | 147 | impl _EmbassyInternalTaskTrait for () { |
| 136 | type Fut = impl core::future::Future + 'static; | 148 | type Fut = impl core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static; |
| 137 | fn construct(#fargs) -> Self::Fut { | 149 | fn construct(#fargs) -> Self::Fut { |
| 138 | #task_inner_ident(#(#full_args,)*) | 150 | #task_inner_ident(#(#full_args,)*) |
| 139 | } | 151 | } |
| @@ -141,7 +153,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { | |||
| 141 | 153 | ||
| 142 | const POOL_SIZE: usize = #pool_size; | 154 | const POOL_SIZE: usize = #pool_size; |
| 143 | static POOL: #embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = #embassy_executor::raw::TaskPool::new(); | 155 | static POOL: #embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = #embassy_executor::raw::TaskPool::new(); |
| 144 | unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } | 156 | unsafe { POOL.#spawn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } |
| 145 | }; | 157 | }; |
| 146 | #[cfg(not(feature = "nightly"))] | 158 | #[cfg(not(feature = "nightly"))] |
| 147 | let mut task_outer_body = quote! { | 159 | let mut task_outer_body = quote! { |
| @@ -158,7 +170,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { | |||
| 158 | {#embassy_executor::_export::task_pool_size::<_, _, _, POOL_SIZE>(#task_inner_ident)}, | 170 | {#embassy_executor::_export::task_pool_size::<_, _, _, POOL_SIZE>(#task_inner_ident)}, |
| 159 | {#embassy_executor::_export::task_pool_align::<_, _, _, POOL_SIZE>(#task_inner_ident)}, | 171 | {#embassy_executor::_export::task_pool_align::<_, _, _, POOL_SIZE>(#task_inner_ident)}, |
| 160 | > = unsafe { ::core::mem::transmute(#embassy_executor::_export::task_pool_new::<_, _, _, POOL_SIZE>(#task_inner_ident)) }; | 172 | > = unsafe { ::core::mem::transmute(#embassy_executor::_export::task_pool_new::<_, _, _, POOL_SIZE>(#task_inner_ident)) }; |
| 161 | unsafe { __task_pool_get(#task_inner_ident)._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } | 173 | unsafe { __task_pool_get(#task_inner_ident).#spawn(move || #task_inner_ident(#(#full_args,)*)) } |
| 162 | }; | 174 | }; |
| 163 | 175 | ||
| 164 | let task_outer_attrs = task_inner.attrs.clone(); | 176 | let task_outer_attrs = task_inner.attrs.clone(); |
diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 608c67724..914863a83 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md | |||
| @@ -5,9 +5,22 @@ All notable changes to this project will be documented in this file. | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 7 | 7 | ||
| 8 | ## unreleased | 8 | <!-- next-header --> |
| 9 | 9 | ## Unreleased - ReleaseDate | |
| 10 | |||
| 11 | - Added `SpawnToken::id` | ||
| 12 | - Task pools are now statically allocated on stable rust. All `task-arena-size-*` features have been removed and are no longer necessary. | ||
| 13 | - New trace hooks: `_embassy_trace_poll_start` & `_embassy_trace_task_end` | ||
| 14 | - Added task naming capability to tracing infrastructure | ||
| 15 | - Added `Executor::id` & `Spawner::executor_id` | ||
| 16 | - Disable `critical-section/std` for arch-std | ||
| 17 | - Added possibility to select an executor in `#[embassy_executor::main]` | ||
| 18 | - Fix AVR executor | ||
| 19 | - executor: Make state implementations and their conditions match | ||
| 10 | - Added support for Cortex-A and Cortex-R | 20 | - Added support for Cortex-A and Cortex-R |
| 21 | - Added support for `-> impl Future<Output = ()>` in `#[task]` | ||
| 22 | - Fixed `Send` unsoundness with `-> impl Future` tasks | ||
| 23 | - Marked `Spawner::for_current_executor` as `unsafe` | ||
| 11 | 24 | ||
| 12 | ## 0.7.0 - 2025-01-02 | 25 | ## 0.7.0 - 2025-01-02 |
| 13 | 26 | ||
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index f014ccf30..2dbf2c29a 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml | |||
| @@ -59,6 +59,7 @@ avr-device = { version = "0.7.0", optional = true } | |||
| 59 | critical-section = { version = "1.1", features = ["std"] } | 59 | critical-section = { version = "1.1", features = ["std"] } |
| 60 | trybuild = "1.0" | 60 | trybuild = "1.0" |
| 61 | embassy-sync = { path = "../embassy-sync" } | 61 | embassy-sync = { path = "../embassy-sync" } |
| 62 | rustversion = "1.0.21" | ||
| 62 | 63 | ||
| 63 | [features] | 64 | [features] |
| 64 | 65 | ||
diff --git a/embassy-executor/build_common.rs b/embassy-executor/build_common.rs index b15a8369f..4f24e6d37 100644 --- a/embassy-executor/build_common.rs +++ b/embassy-executor/build_common.rs | |||
| @@ -92,35 +92,3 @@ pub fn set_target_cfgs(cfgs: &mut CfgSet) { | |||
| 92 | 92 | ||
| 93 | cfgs.set("has_fpu", target.ends_with("-eabihf")); | 93 | cfgs.set("has_fpu", target.ends_with("-eabihf")); |
| 94 | } | 94 | } |
| 95 | |||
| 96 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||
| 97 | pub struct CompilerDate { | ||
| 98 | year: u16, | ||
| 99 | month: u8, | ||
| 100 | day: u8, | ||
| 101 | } | ||
| 102 | |||
| 103 | impl CompilerDate { | ||
| 104 | fn parse(date: &str) -> Option<Self> { | ||
| 105 | let mut parts = date.split('-'); | ||
| 106 | let year = parts.next()?.parse().ok()?; | ||
| 107 | let month = parts.next()?.parse().ok()?; | ||
| 108 | let day = parts.next()?.parse().ok()?; | ||
| 109 | Some(Self { year, month, day }) | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | impl PartialEq<&str> for CompilerDate { | ||
| 114 | fn eq(&self, other: &&str) -> bool { | ||
| 115 | let Some(other) = Self::parse(other) else { | ||
| 116 | return false; | ||
| 117 | }; | ||
| 118 | self.eq(&other) | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | impl PartialOrd<&str> for CompilerDate { | ||
| 123 | fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> { | ||
| 124 | Self::parse(other).map(|other| self.cmp(&other)) | ||
| 125 | } | ||
| 126 | } | ||
diff --git a/embassy-executor/release.toml b/embassy-executor/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-executor/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index dfe420bab..e174a0594 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs | |||
| @@ -65,8 +65,17 @@ pub mod _export { | |||
| 65 | 65 | ||
| 66 | use crate::raw::TaskPool; | 66 | use crate::raw::TaskPool; |
| 67 | 67 | ||
| 68 | trait TaskReturnValue {} | ||
| 69 | impl TaskReturnValue for () {} | ||
| 70 | impl TaskReturnValue for Never {} | ||
| 71 | |||
| 72 | #[diagnostic::on_unimplemented( | ||
| 73 | message = "task futures must resolve to `()` or `!`", | ||
| 74 | note = "use `async fn` or change the return type to `impl Future<Output = ()>`" | ||
| 75 | )] | ||
| 76 | #[allow(private_bounds)] | ||
| 68 | pub trait TaskFn<Args>: Copy { | 77 | pub trait TaskFn<Args>: Copy { |
| 69 | type Fut: Future + 'static; | 78 | type Fut: Future<Output: TaskReturnValue> + 'static; |
| 70 | } | 79 | } |
| 71 | 80 | ||
| 72 | macro_rules! task_fn_impl { | 81 | macro_rules! task_fn_impl { |
| @@ -74,7 +83,7 @@ pub mod _export { | |||
| 74 | impl<F, Fut, $($Tn,)*> TaskFn<($($Tn,)*)> for F | 83 | impl<F, Fut, $($Tn,)*> TaskFn<($($Tn,)*)> for F |
| 75 | where | 84 | where |
| 76 | F: Copy + FnOnce($($Tn,)*) -> Fut, | 85 | F: Copy + FnOnce($($Tn,)*) -> Fut, |
| 77 | Fut: Future + 'static, | 86 | Fut: Future<Output: TaskReturnValue> + 'static, |
| 78 | { | 87 | { |
| 79 | type Fut = Fut; | 88 | type Fut = Fut; |
| 80 | } | 89 | } |
| @@ -205,4 +214,42 @@ pub mod _export { | |||
| 205 | Align268435456: 268435456, | 214 | Align268435456: 268435456, |
| 206 | Align536870912: 536870912, | 215 | Align536870912: 536870912, |
| 207 | ); | 216 | ); |
| 217 | |||
| 218 | #[allow(dead_code)] | ||
| 219 | trait HasOutput { | ||
| 220 | type Output; | ||
| 221 | } | ||
| 222 | |||
| 223 | impl<O> HasOutput for fn() -> O { | ||
| 224 | type Output = O; | ||
| 225 | } | ||
| 226 | |||
| 227 | #[allow(dead_code)] | ||
| 228 | type Never = <fn() -> ! as HasOutput>::Output; | ||
| 229 | } | ||
| 230 | |||
| 231 | /// Implementation details for embassy macros. | ||
| 232 | /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. | ||
| 233 | #[doc(hidden)] | ||
| 234 | #[cfg(feature = "nightly")] | ||
| 235 | pub mod _export { | ||
| 236 | #[diagnostic::on_unimplemented( | ||
| 237 | message = "task futures must resolve to `()` or `!`", | ||
| 238 | note = "use `async fn` or change the return type to `impl Future<Output = ()>`" | ||
| 239 | )] | ||
| 240 | pub trait TaskReturnValue {} | ||
| 241 | impl TaskReturnValue for () {} | ||
| 242 | impl TaskReturnValue for Never {} | ||
| 243 | |||
| 244 | #[allow(dead_code)] | ||
| 245 | trait HasOutput { | ||
| 246 | type Output; | ||
| 247 | } | ||
| 248 | |||
| 249 | impl<O> HasOutput for fn() -> O { | ||
| 250 | type Output = O; | ||
| 251 | } | ||
| 252 | |||
| 253 | #[allow(dead_code)] | ||
| 254 | type Never = <fn() -> ! as HasOutput>::Output; | ||
| 208 | } | 255 | } |
diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs index 6c9cfda25..aa27ab37e 100644 --- a/embassy-executor/src/raw/trace.rs +++ b/embassy-executor/src/raw/trace.rs | |||
| @@ -283,7 +283,17 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) { | |||
| 283 | } | 283 | } |
| 284 | 284 | ||
| 285 | #[cfg(feature = "rtos-trace")] | 285 | #[cfg(feature = "rtos-trace")] |
| 286 | rtos_trace::trace::task_new(task.as_ptr() as u32); | 286 | { |
| 287 | rtos_trace::trace::task_new(task.as_ptr() as u32); | ||
| 288 | let name = task.name().unwrap_or("unnamed task\0"); | ||
| 289 | let info = rtos_trace::TaskInfo { | ||
| 290 | name, | ||
| 291 | priority: 0, | ||
| 292 | stack_base: 0, | ||
| 293 | stack_size: 0, | ||
| 294 | }; | ||
| 295 | rtos_trace::trace::task_send_info(task.id(), info); | ||
| 296 | } | ||
| 287 | 297 | ||
| 288 | #[cfg(feature = "rtos-trace")] | 298 | #[cfg(feature = "rtos-trace")] |
| 289 | TASK_TRACKER.add(*task); | 299 | TASK_TRACKER.add(*task); |
diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 522d97db3..2909d19a0 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs | |||
| @@ -122,10 +122,26 @@ impl Spawner { | |||
| 122 | /// This function is `async` just to get access to the current async | 122 | /// This function is `async` just to get access to the current async |
| 123 | /// context. It returns instantly, it does not block/yield. | 123 | /// context. It returns instantly, it does not block/yield. |
| 124 | /// | 124 | /// |
| 125 | /// Using this method is discouraged due to it being unsafe. Consider the following | ||
| 126 | /// alternatives instead: | ||
| 127 | /// | ||
| 128 | /// - Pass the initial `Spawner` as an argument to tasks. Note that it's `Copy`, so you can | ||
| 129 | /// make as many copies of it as you want. | ||
| 130 | /// - Use `SendSpawner::for_current_executor()` instead, which is safe but can only be used | ||
| 131 | /// if task arguments are `Send`. | ||
| 132 | /// | ||
| 133 | /// The only case where using this method is absolutely required is obtaining the `Spawner` | ||
| 134 | /// for an `InterruptExecutor`. | ||
| 135 | /// | ||
| 136 | /// # Safety | ||
| 137 | /// | ||
| 138 | /// You must only execute this with an async `Context` created by the Embassy executor. | ||
| 139 | /// You must not execute it with manually-created `Context`s. | ||
| 140 | /// | ||
| 125 | /// # Panics | 141 | /// # Panics |
| 126 | /// | 142 | /// |
| 127 | /// Panics if the current executor is not an Embassy executor. | 143 | /// Panics if the current executor is not an Embassy executor. |
| 128 | pub fn for_current_executor() -> impl Future<Output = Self> { | 144 | pub unsafe fn for_current_executor() -> impl Future<Output = Self> { |
| 129 | poll_fn(|cx| { | 145 | poll_fn(|cx| { |
| 130 | let task = raw::task_from_waker(cx.waker()); | 146 | let task = raw::task_from_waker(cx.waker()); |
| 131 | let executor = unsafe { | 147 | let executor = unsafe { |
diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 78c49c071..c1e7ec5d7 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | #![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] | 1 | #![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] |
| 2 | #![cfg_attr(feature = "nightly", feature(never_type))] | ||
| 2 | 3 | ||
| 3 | use std::boxed::Box; | 4 | use std::boxed::Box; |
| 4 | use std::future::poll_fn; | 5 | use std::future::{poll_fn, Future}; |
| 5 | use std::sync::{Arc, Mutex}; | 6 | use std::sync::{Arc, Mutex}; |
| 6 | use std::task::Poll; | 7 | use std::task::Poll; |
| 7 | 8 | ||
| @@ -58,6 +59,39 @@ fn executor_task() { | |||
| 58 | trace.push("poll task1") | 59 | trace.push("poll task1") |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 62 | #[task] | ||
| 63 | async fn task2() -> ! { | ||
| 64 | panic!() | ||
| 65 | } | ||
| 66 | |||
| 67 | let (executor, trace) = setup(); | ||
| 68 | executor.spawner().spawn(task1(trace.clone())).unwrap(); | ||
| 69 | |||
| 70 | unsafe { executor.poll() }; | ||
| 71 | unsafe { executor.poll() }; | ||
| 72 | |||
| 73 | assert_eq!( | ||
| 74 | trace.get(), | ||
| 75 | &[ | ||
| 76 | "pend", // spawning a task pends the executor | ||
| 77 | "poll task1", // poll only once. | ||
| 78 | ] | ||
| 79 | ) | ||
| 80 | } | ||
| 81 | |||
| 82 | #[test] | ||
| 83 | fn executor_task_rpit() { | ||
| 84 | #[task] | ||
| 85 | fn task1(trace: Trace) -> impl Future<Output = ()> { | ||
| 86 | async move { trace.push("poll task1") } | ||
| 87 | } | ||
| 88 | |||
| 89 | #[cfg(feature = "nightly")] | ||
| 90 | #[task] | ||
| 91 | fn task2() -> impl Future<Output = !> { | ||
| 92 | async { panic!() } | ||
| 93 | } | ||
| 94 | |||
| 61 | let (executor, trace) = setup(); | 95 | let (executor, trace) = setup(); |
| 62 | executor.spawner().spawn(task1(trace.clone())).unwrap(); | 96 | executor.spawner().spawn(task1(trace.clone())).unwrap(); |
| 63 | 97 | ||
diff --git a/embassy-executor/tests/ui.rs b/embassy-executor/tests/ui.rs index 278a4b903..7757775ee 100644 --- a/embassy-executor/tests/ui.rs +++ b/embassy-executor/tests/ui.rs | |||
| @@ -17,6 +17,17 @@ fn ui() { | |||
| 17 | t.compile_fail("tests/ui/nonstatic_struct_elided.rs"); | 17 | t.compile_fail("tests/ui/nonstatic_struct_elided.rs"); |
| 18 | t.compile_fail("tests/ui/nonstatic_struct_generic.rs"); | 18 | t.compile_fail("tests/ui/nonstatic_struct_generic.rs"); |
| 19 | t.compile_fail("tests/ui/not_async.rs"); | 19 | t.compile_fail("tests/ui/not_async.rs"); |
| 20 | t.compile_fail("tests/ui/spawn_nonsend.rs"); | ||
| 21 | t.compile_fail("tests/ui/return_impl_future_nonsend.rs"); | ||
| 22 | if rustversion::cfg!(stable) { | ||
| 23 | // output is slightly different on nightly | ||
| 24 | t.compile_fail("tests/ui/bad_return_impl_future.rs"); | ||
| 25 | t.compile_fail("tests/ui/return_impl_send.rs"); | ||
| 26 | } | ||
| 27 | if cfg!(feature = "nightly") { | ||
| 28 | t.compile_fail("tests/ui/bad_return_impl_future_nightly.rs"); | ||
| 29 | t.compile_fail("tests/ui/return_impl_send_nightly.rs"); | ||
| 30 | } | ||
| 20 | t.compile_fail("tests/ui/self_ref.rs"); | 31 | t.compile_fail("tests/ui/self_ref.rs"); |
| 21 | t.compile_fail("tests/ui/self.rs"); | 32 | t.compile_fail("tests/ui/self.rs"); |
| 22 | t.compile_fail("tests/ui/type_error.rs"); | 33 | t.compile_fail("tests/ui/type_error.rs"); |
diff --git a/embassy-executor/tests/ui/bad_return_impl_future.rs b/embassy-executor/tests/ui/bad_return_impl_future.rs new file mode 100644 index 000000000..baaa7dc5a --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_future.rs | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] | ||
| 2 | use core::future::Future; | ||
| 3 | |||
| 4 | #[embassy_executor::task] | ||
| 5 | fn task() -> impl Future<Output = u32> { | ||
| 6 | async { 5 } | ||
| 7 | } | ||
| 8 | |||
| 9 | fn main() {} | ||
diff --git a/embassy-executor/tests/ui/bad_return_impl_future.stderr b/embassy-executor/tests/ui/bad_return_impl_future.stderr new file mode 100644 index 000000000..57f147714 --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_future.stderr | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 2 | --> tests/ui/bad_return_impl_future.rs:5:4 | ||
| 3 | | | ||
| 4 | 4 | #[embassy_executor::task] | ||
| 5 | | ------------------------- required by a bound introduced by this call | ||
| 6 | 5 | fn task() -> impl Future<Output = u32> { | ||
| 7 | | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}` | ||
| 8 | | | ||
| 9 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 10 | note: required by a bound in `task_pool_size` | ||
| 11 | --> src/lib.rs | ||
| 12 | | | ||
| 13 | | pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize | ||
| 14 | | -------------- required by a bound in this function | ||
| 15 | | where | ||
| 16 | | F: TaskFn<Args, Fut = Fut>, | ||
| 17 | | ^^^^^^^^^ required by this bound in `task_pool_size` | ||
| 18 | |||
| 19 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 20 | --> tests/ui/bad_return_impl_future.rs:4:1 | ||
| 21 | | | ||
| 22 | 4 | #[embassy_executor::task] | ||
| 23 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}` | ||
| 24 | | | ||
| 25 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 26 | note: required by a bound in `task_pool_size` | ||
| 27 | --> src/lib.rs | ||
| 28 | | | ||
| 29 | | pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize | ||
| 30 | | -------------- required by a bound in this function | ||
| 31 | | where | ||
| 32 | | F: TaskFn<Args, Fut = Fut>, | ||
| 33 | | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size` | ||
| 34 | = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
| 35 | |||
| 36 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 37 | --> tests/ui/bad_return_impl_future.rs:5:4 | ||
| 38 | | | ||
| 39 | 4 | #[embassy_executor::task] | ||
| 40 | | ------------------------- required by a bound introduced by this call | ||
| 41 | 5 | fn task() -> impl Future<Output = u32> { | ||
| 42 | | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}` | ||
| 43 | | | ||
| 44 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 45 | note: required by a bound in `task_pool_align` | ||
| 46 | --> src/lib.rs | ||
| 47 | | | ||
| 48 | | pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize | ||
| 49 | | --------------- required by a bound in this function | ||
| 50 | | where | ||
| 51 | | F: TaskFn<Args, Fut = Fut>, | ||
| 52 | | ^^^^^^^^^ required by this bound in `task_pool_align` | ||
| 53 | |||
| 54 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 55 | --> tests/ui/bad_return_impl_future.rs:4:1 | ||
| 56 | | | ||
| 57 | 4 | #[embassy_executor::task] | ||
| 58 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}` | ||
| 59 | | | ||
| 60 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 61 | note: required by a bound in `task_pool_align` | ||
| 62 | --> src/lib.rs | ||
| 63 | | | ||
| 64 | | pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize | ||
| 65 | | --------------- required by a bound in this function | ||
| 66 | | where | ||
| 67 | | F: TaskFn<Args, Fut = Fut>, | ||
| 68 | | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align` | ||
| 69 | = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
| 70 | |||
| 71 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 72 | --> tests/ui/bad_return_impl_future.rs:5:4 | ||
| 73 | | | ||
| 74 | 4 | #[embassy_executor::task] | ||
| 75 | | ------------------------- required by a bound introduced by this call | ||
| 76 | 5 | fn task() -> impl Future<Output = u32> { | ||
| 77 | | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}` | ||
| 78 | | | ||
| 79 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 80 | note: required by a bound in `__task_pool_get` | ||
| 81 | --> tests/ui/bad_return_impl_future.rs:4:1 | ||
| 82 | | | ||
| 83 | 4 | #[embassy_executor::task] | ||
| 84 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get` | ||
| 85 | = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
| 86 | |||
| 87 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 88 | --> tests/ui/bad_return_impl_future.rs:5:4 | ||
| 89 | | | ||
| 90 | 4 | #[embassy_executor::task] | ||
| 91 | | ------------------------- required by a bound introduced by this call | ||
| 92 | 5 | fn task() -> impl Future<Output = u32> { | ||
| 93 | | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}` | ||
| 94 | | | ||
| 95 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 96 | note: required by a bound in `task_pool_new` | ||
| 97 | --> src/lib.rs | ||
| 98 | | | ||
| 99 | | pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE> | ||
| 100 | | ------------- required by a bound in this function | ||
| 101 | | where | ||
| 102 | | F: TaskFn<Args, Fut = Fut>, | ||
| 103 | | ^^^^^^^^^ required by this bound in `task_pool_new` | ||
| 104 | |||
| 105 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 106 | --> tests/ui/bad_return_impl_future.rs:4:1 | ||
| 107 | | | ||
| 108 | 4 | #[embassy_executor::task] | ||
| 109 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}` | ||
| 110 | | | ||
| 111 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 112 | note: required by a bound in `task_pool_new` | ||
| 113 | --> src/lib.rs | ||
| 114 | | | ||
| 115 | | pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE> | ||
| 116 | | ------------- required by a bound in this function | ||
| 117 | | where | ||
| 118 | | F: TaskFn<Args, Fut = Fut>, | ||
| 119 | | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new` | ||
| 120 | = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
diff --git a/embassy-executor/tests/ui/bad_return_impl_future_nightly.rs b/embassy-executor/tests/ui/bad_return_impl_future_nightly.rs new file mode 100644 index 000000000..baaa7dc5a --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_future_nightly.rs | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] | ||
| 2 | use core::future::Future; | ||
| 3 | |||
| 4 | #[embassy_executor::task] | ||
| 5 | fn task() -> impl Future<Output = u32> { | ||
| 6 | async { 5 } | ||
| 7 | } | ||
| 8 | |||
| 9 | fn main() {} | ||
diff --git a/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr new file mode 100644 index 000000000..73ceb989d --- /dev/null +++ b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 2 | --> tests/ui/bad_return_impl_future_nightly.rs:4:1 | ||
| 3 | | | ||
| 4 | 4 | #[embassy_executor::task] | ||
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskReturnValue` is not implemented for `u32` | ||
| 6 | | | ||
| 7 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 8 | = help: the following other types implement trait `TaskReturnValue`: | ||
| 9 | () | ||
| 10 | <fn() -> ! as _export::HasOutput>::Output | ||
diff --git a/embassy-executor/tests/ui/return_impl_future_nonsend.rs b/embassy-executor/tests/ui/return_impl_future_nonsend.rs new file mode 100644 index 000000000..b8c184b21 --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_future_nonsend.rs | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] | ||
| 2 | |||
| 3 | use core::future::Future; | ||
| 4 | |||
| 5 | use embassy_executor::SendSpawner; | ||
| 6 | |||
| 7 | #[embassy_executor::task] | ||
| 8 | fn task() -> impl Future<Output = ()> { | ||
| 9 | // runs in spawning thread | ||
| 10 | let non_send: *mut () = core::ptr::null_mut(); | ||
| 11 | async move { | ||
| 12 | // runs in executor thread | ||
| 13 | println!("{}", non_send as usize); | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | fn send_spawn(s: SendSpawner) { | ||
| 18 | s.spawn(task()).unwrap(); | ||
| 19 | } | ||
| 20 | |||
| 21 | fn main() {} | ||
diff --git a/embassy-executor/tests/ui/return_impl_future_nonsend.stderr b/embassy-executor/tests/ui/return_impl_future_nonsend.stderr new file mode 100644 index 000000000..8aeb9738a --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_future_nonsend.stderr | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | error: future cannot be sent between threads safely | ||
| 2 | --> tests/ui/return_impl_future_nonsend.rs:18:13 | ||
| 3 | | | ||
| 4 | 18 | s.spawn(task()).unwrap(); | ||
| 5 | | ^^^^^^ future created by async block is not `Send` | ||
| 6 | | | ||
| 7 | = help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()` | ||
| 8 | note: captured value is not `Send` | ||
| 9 | --> tests/ui/return_impl_future_nonsend.rs:13:24 | ||
| 10 | | | ||
| 11 | 13 | println!("{}", non_send as usize); | ||
| 12 | | ^^^^^^^^ has type `*mut ()` which is not `Send` | ||
| 13 | note: required by a bound in `SendSpawner::spawn` | ||
| 14 | --> src/spawner.rs | ||
| 15 | | | ||
| 16 | | pub fn spawn<S: Send>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> { | ||
| 17 | | ^^^^ required by this bound in `SendSpawner::spawn` | ||
diff --git a/embassy-executor/tests/ui/return_impl_send.rs b/embassy-executor/tests/ui/return_impl_send.rs new file mode 100644 index 000000000..6ddb0e722 --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_send.rs | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] | ||
| 2 | |||
| 3 | #[embassy_executor::task] | ||
| 4 | fn task() -> impl Send {} | ||
| 5 | |||
| 6 | fn main() {} | ||
diff --git a/embassy-executor/tests/ui/return_impl_send.stderr b/embassy-executor/tests/ui/return_impl_send.stderr new file mode 100644 index 000000000..759be1cde --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_send.stderr | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 2 | --> tests/ui/return_impl_send.rs:4:4 | ||
| 3 | | | ||
| 4 | 3 | #[embassy_executor::task] | ||
| 5 | | ------------------------- required by a bound introduced by this call | ||
| 6 | 4 | fn task() -> impl Send {} | ||
| 7 | | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` | ||
| 8 | | | ||
| 9 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 10 | note: required by a bound in `task_pool_size` | ||
| 11 | --> src/lib.rs | ||
| 12 | | | ||
| 13 | | pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize | ||
| 14 | | -------------- required by a bound in this function | ||
| 15 | | where | ||
| 16 | | F: TaskFn<Args, Fut = Fut>, | ||
| 17 | | ^^^^^^^^^ required by this bound in `task_pool_size` | ||
| 18 | |||
| 19 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 20 | --> tests/ui/return_impl_send.rs:3:1 | ||
| 21 | | | ||
| 22 | 3 | #[embassy_executor::task] | ||
| 23 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` | ||
| 24 | | | ||
| 25 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 26 | note: required by a bound in `task_pool_size` | ||
| 27 | --> src/lib.rs | ||
| 28 | | | ||
| 29 | | pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize | ||
| 30 | | -------------- required by a bound in this function | ||
| 31 | | where | ||
| 32 | | F: TaskFn<Args, Fut = Fut>, | ||
| 33 | | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size` | ||
| 34 | = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
| 35 | |||
| 36 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 37 | --> tests/ui/return_impl_send.rs:4:4 | ||
| 38 | | | ||
| 39 | 3 | #[embassy_executor::task] | ||
| 40 | | ------------------------- required by a bound introduced by this call | ||
| 41 | 4 | fn task() -> impl Send {} | ||
| 42 | | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` | ||
| 43 | | | ||
| 44 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 45 | note: required by a bound in `task_pool_align` | ||
| 46 | --> src/lib.rs | ||
| 47 | | | ||
| 48 | | pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize | ||
| 49 | | --------------- required by a bound in this function | ||
| 50 | | where | ||
| 51 | | F: TaskFn<Args, Fut = Fut>, | ||
| 52 | | ^^^^^^^^^ required by this bound in `task_pool_align` | ||
| 53 | |||
| 54 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 55 | --> tests/ui/return_impl_send.rs:3:1 | ||
| 56 | | | ||
| 57 | 3 | #[embassy_executor::task] | ||
| 58 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` | ||
| 59 | | | ||
| 60 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 61 | note: required by a bound in `task_pool_align` | ||
| 62 | --> src/lib.rs | ||
| 63 | | | ||
| 64 | | pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize | ||
| 65 | | --------------- required by a bound in this function | ||
| 66 | | where | ||
| 67 | | F: TaskFn<Args, Fut = Fut>, | ||
| 68 | | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align` | ||
| 69 | = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
| 70 | |||
| 71 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 72 | --> tests/ui/return_impl_send.rs:4:4 | ||
| 73 | | | ||
| 74 | 3 | #[embassy_executor::task] | ||
| 75 | | ------------------------- required by a bound introduced by this call | ||
| 76 | 4 | fn task() -> impl Send {} | ||
| 77 | | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` | ||
| 78 | | | ||
| 79 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 80 | note: required by a bound in `__task_pool_get` | ||
| 81 | --> tests/ui/return_impl_send.rs:3:1 | ||
| 82 | | | ||
| 83 | 3 | #[embassy_executor::task] | ||
| 84 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get` | ||
| 85 | = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
| 86 | |||
| 87 | error[E0277]: `impl Send` is not a future | ||
| 88 | --> tests/ui/return_impl_send.rs:3:1 | ||
| 89 | | | ||
| 90 | 3 | #[embassy_executor::task] | ||
| 91 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ `impl Send` is not a future | ||
| 92 | | | ||
| 93 | = help: the trait `Future` is not implemented for `impl Send` | ||
| 94 | note: required by a bound in `TaskPool::<F, N>::spawn` | ||
| 95 | --> src/raw/mod.rs | ||
| 96 | | | ||
| 97 | | impl<F: Future + 'static, const N: usize> TaskPool<F, N> { | ||
| 98 | | ^^^^^^ required by this bound in `TaskPool::<F, N>::spawn` | ||
| 99 | ... | ||
| 100 | | pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> { | ||
| 101 | | ----- required by a bound in this associated function | ||
| 102 | = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
| 103 | |||
| 104 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 105 | --> tests/ui/return_impl_send.rs:4:4 | ||
| 106 | | | ||
| 107 | 3 | #[embassy_executor::task] | ||
| 108 | | ------------------------- required by a bound introduced by this call | ||
| 109 | 4 | fn task() -> impl Send {} | ||
| 110 | | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` | ||
| 111 | | | ||
| 112 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 113 | note: required by a bound in `task_pool_new` | ||
| 114 | --> src/lib.rs | ||
| 115 | | | ||
| 116 | | pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE> | ||
| 117 | | ------------- required by a bound in this function | ||
| 118 | | where | ||
| 119 | | F: TaskFn<Args, Fut = Fut>, | ||
| 120 | | ^^^^^^^^^ required by this bound in `task_pool_new` | ||
| 121 | |||
| 122 | error[E0277]: task futures must resolve to `()` or `!` | ||
| 123 | --> tests/ui/return_impl_send.rs:3:1 | ||
| 124 | | | ||
| 125 | 3 | #[embassy_executor::task] | ||
| 126 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}` | ||
| 127 | | | ||
| 128 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | ||
| 129 | note: required by a bound in `task_pool_new` | ||
| 130 | --> src/lib.rs | ||
| 131 | | | ||
| 132 | | pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE> | ||
| 133 | | ------------- required by a bound in this function | ||
| 134 | | where | ||
| 135 | | F: TaskFn<Args, Fut = Fut>, | ||
| 136 | | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new` | ||
| 137 | = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
diff --git a/embassy-executor/tests/ui/return_impl_send_nightly.rs b/embassy-executor/tests/ui/return_impl_send_nightly.rs new file mode 100644 index 000000000..6ddb0e722 --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_send_nightly.rs | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] | ||
| 2 | |||
| 3 | #[embassy_executor::task] | ||
| 4 | fn task() -> impl Send {} | ||
| 5 | |||
| 6 | fn main() {} | ||
diff --git a/embassy-executor/tests/ui/return_impl_send_nightly.stderr b/embassy-executor/tests/ui/return_impl_send_nightly.stderr new file mode 100644 index 000000000..de9ba6243 --- /dev/null +++ b/embassy-executor/tests/ui/return_impl_send_nightly.stderr | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | error[E0277]: `impl Send` is not a future | ||
| 2 | --> tests/ui/return_impl_send_nightly.rs:3:1 | ||
| 3 | | | ||
| 4 | 3 | #[embassy_executor::task] | ||
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 6 | | | | ||
| 7 | | `impl Send` is not a future | ||
| 8 | | return type was inferred to be `impl Send` here | ||
| 9 | | | ||
| 10 | = help: the trait `Future` is not implemented for `impl Send` | ||
diff --git a/embassy-executor/tests/ui/spawn_nonsend.rs b/embassy-executor/tests/ui/spawn_nonsend.rs new file mode 100644 index 000000000..4c4cc7697 --- /dev/null +++ b/embassy-executor/tests/ui/spawn_nonsend.rs | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | #![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] | ||
| 2 | |||
| 3 | use core::future::Future; | ||
| 4 | |||
| 5 | use embassy_executor::SendSpawner; | ||
| 6 | |||
| 7 | #[embassy_executor::task] | ||
| 8 | async fn task(non_send: *mut ()) { | ||
| 9 | println!("{}", non_send as usize); | ||
| 10 | } | ||
| 11 | |||
| 12 | fn send_spawn(s: SendSpawner) { | ||
| 13 | s.spawn(task(core::ptr::null_mut())).unwrap(); | ||
| 14 | } | ||
| 15 | |||
| 16 | fn main() {} | ||
diff --git a/embassy-executor/tests/ui/spawn_nonsend.stderr b/embassy-executor/tests/ui/spawn_nonsend.stderr new file mode 100644 index 000000000..2a06c8b94 --- /dev/null +++ b/embassy-executor/tests/ui/spawn_nonsend.stderr | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | warning: unused import: `core::future::Future` | ||
| 2 | --> tests/ui/spawn_nonsend.rs:3:5 | ||
| 3 | | | ||
| 4 | 3 | use core::future::Future; | ||
| 5 | | ^^^^^^^^^^^^^^^^^^^^ | ||
| 6 | | | ||
| 7 | = note: `#[warn(unused_imports)]` on by default | ||
| 8 | |||
| 9 | error[E0277]: `*mut ()` cannot be sent between threads safely | ||
| 10 | --> tests/ui/spawn_nonsend.rs:13:13 | ||
| 11 | | | ||
| 12 | 7 | #[embassy_executor::task] | ||
| 13 | | ------------------------- within this `impl Sized` | ||
| 14 | ... | ||
| 15 | 13 | s.spawn(task(core::ptr::null_mut())).unwrap(); | ||
| 16 | | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*mut ()` cannot be sent between threads safely | ||
| 17 | | | | ||
| 18 | | required by a bound introduced by this call | ||
| 19 | | | ||
| 20 | = help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()` | ||
| 21 | note: required because it's used within this closure | ||
| 22 | --> tests/ui/spawn_nonsend.rs:7:1 | ||
| 23 | | | ||
| 24 | 7 | #[embassy_executor::task] | ||
| 25 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 26 | note: required because it appears within the type `impl Sized` | ||
| 27 | --> src/raw/mod.rs | ||
| 28 | | | ||
| 29 | | pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized> | ||
| 30 | | ^^^^^^^^^^ | ||
| 31 | note: required because it appears within the type `impl Sized` | ||
| 32 | --> tests/ui/spawn_nonsend.rs:7:1 | ||
| 33 | | | ||
| 34 | 7 | #[embassy_executor::task] | ||
| 35 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 36 | note: required by a bound in `SendSpawner::spawn` | ||
| 37 | --> src/spawner.rs | ||
| 38 | | | ||
| 39 | | pub fn spawn<S: Send>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> { | ||
| 40 | | ^^^^ required by this bound in `SendSpawner::spawn` | ||
| 41 | = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
diff --git a/embassy-futures/CHANGELOG.md b/embassy-futures/CHANGELOG.md new file mode 100644 index 000000000..eb76cdc4a --- /dev/null +++ b/embassy-futures/CHANGELOG.md | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # Changelog for embassy-futures | ||
| 2 | |||
| 3 | All notable changes to this project will be documented in this file. | ||
| 4 | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
| 7 | |||
| 8 | <!-- next-header --> | ||
| 9 | ## Unreleased - ReleaseDate | ||
| 10 | |||
| 11 | - Preserve location information for `defmt` in `fmt` calls ([#3085](https://github.com/embassy-rs/embassy/pull/3085)) | ||
| 12 | - Fixed soundness issue in `select_slice` ([#3328](https://github.com/embassy-rs/embassy/pull/3328)) | ||
| 13 | - Added `select5` and `select6` ([#3430](https://github.com/embassy-rs/embassy/pull/3430)) | ||
| 14 | - Added `is_x` methods for all `EitherN` enum variants (#[3650](https://github.com/embassy-rs/embassy/pull/3650)) | ||
diff --git a/embassy-futures/release.toml b/embassy-futures/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-futures/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/embassy-hal-internal/Cargo.toml b/embassy-hal-internal/Cargo.toml index cc360682e..b4c52ccfa 100644 --- a/embassy-hal-internal/Cargo.toml +++ b/embassy-hal-internal/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-hal-internal" | 2 | name = "embassy-hal-internal" |
| 3 | version = "0.2.0" | 3 | version = "0.3.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY." | 6 | description = "Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY." |
diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml index 49dc8089c..df4687043 100644 --- a/embassy-imxrt/Cargo.toml +++ b/embassy-imxrt/Cargo.toml | |||
| @@ -67,8 +67,8 @@ embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | |||
| 67 | embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } | 67 | embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } |
| 68 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } | 68 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } |
| 69 | embassy-time = { version = "0.4", path = "../embassy-time", optional = true } | 69 | embassy-time = { version = "0.4", path = "../embassy-time", optional = true } |
| 70 | embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } | 70 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } |
| 71 | embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } | 71 | embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } |
| 72 | embassy-futures = { version = "0.1.1", path = "../embassy-futures" } | 72 | embassy-futures = { version = "0.1.1", path = "../embassy-futures" } |
| 73 | 73 | ||
| 74 | defmt = { version = "1.0.1", optional = true } | 74 | defmt = { version = "1.0.1", optional = true } |
diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index 5846afe5c..a3437c655 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs | |||
| @@ -76,9 +76,11 @@ macro_rules! bind_interrupts { | |||
| 76 | #[allow(non_snake_case)] | 76 | #[allow(non_snake_case)] |
| 77 | #[no_mangle] | 77 | #[no_mangle] |
| 78 | unsafe extern "C" fn $irq() { | 78 | unsafe extern "C" fn $irq() { |
| 79 | $( | 79 | unsafe { |
| 80 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); | 80 | $( |
| 81 | )* | 81 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); |
| 82 | )* | ||
| 83 | } | ||
| 82 | } | 84 | } |
| 83 | 85 | ||
| 84 | $( | 86 | $( |
diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index 456d302af..61afcd76d 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml | |||
| @@ -31,12 +31,15 @@ embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } | |||
| 31 | embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true, features = ["tick-hz-32_768"] } | 31 | embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true, features = ["tick-hz-32_768"] } |
| 32 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } | 32 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } |
| 33 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 33 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 34 | embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } | 34 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } |
| 35 | embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } | 35 | embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } |
| 36 | embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } | 36 | embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } |
| 37 | 37 | ||
| 38 | embedded-hal = { version = "1.0" } | 38 | embedded-hal = { version = "1.0" } |
| 39 | embedded-hal-nb = { version = "1.0" } | ||
| 39 | embedded-hal-async = { version = "1.0" } | 40 | embedded-hal-async = { version = "1.0" } |
| 41 | embedded-io = "0.6.1" | ||
| 42 | embedded-io-async = "0.6.1" | ||
| 40 | 43 | ||
| 41 | defmt = { version = "1.0.1", optional = true } | 44 | defmt = { version = "1.0.1", optional = true } |
| 42 | fixed = "1.29" | 45 | fixed = "1.29" |
| @@ -46,14 +49,15 @@ cortex-m = "0.7.6" | |||
| 46 | critical-section = "1.2.0" | 49 | critical-section = "1.2.0" |
| 47 | 50 | ||
| 48 | # mspm0-metapac = { version = "" } | 51 | # mspm0-metapac = { version = "" } |
| 49 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-26a6f681eda4ef120e8cb614a1631727c848590f" } | 52 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-235158ac2865d8aac3a1eceb2d62026eb12bf38f" } |
| 50 | 53 | ||
| 51 | [build-dependencies] | 54 | [build-dependencies] |
| 52 | proc-macro2 = "1.0.94" | 55 | proc-macro2 = "1.0.94" |
| 53 | quote = "1.0.40" | 56 | quote = "1.0.40" |
| 57 | cfg_aliases = "0.2.1" | ||
| 54 | 58 | ||
| 55 | # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } | 59 | # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } |
| 56 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-26a6f681eda4ef120e8cb614a1631727c848590f", default-features = false, features = ["metadata"] } | 60 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-235158ac2865d8aac3a1eceb2d62026eb12bf38f", default-features = false, features = ["metadata"] } |
| 57 | 61 | ||
| 58 | [features] | 62 | [features] |
| 59 | default = ["rt"] | 63 | default = ["rt"] |
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs index 6cd62895b..b9ba3aecf 100644 --- a/embassy-mspm0/build.rs +++ b/embassy-mspm0/build.rs | |||
| @@ -67,6 +67,7 @@ fn generate_code() { | |||
| 67 | g.extend(generate_peripheral_instances()); | 67 | g.extend(generate_peripheral_instances()); |
| 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 | 71 | ||
| 71 | let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | 72 | let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); |
| 72 | let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); | 73 | let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); |
| @@ -209,6 +210,12 @@ fn generate_groups() -> TokenStream { | |||
| 209 | } | 210 | } |
| 210 | } | 211 | } |
| 211 | 212 | ||
| 213 | fn generate_dma_channel_count() -> TokenStream { | ||
| 214 | let count = METADATA.dma_channels.len(); | ||
| 215 | |||
| 216 | quote! { pub const DMA_CHANNELS: usize = #count; } | ||
| 217 | } | ||
| 218 | |||
| 212 | #[derive(Debug, Clone)] | 219 | #[derive(Debug, Clone)] |
| 213 | struct Singleton { | 220 | struct Singleton { |
| 214 | name: String, | 221 | name: String, |
| @@ -543,8 +550,6 @@ fn generate_peripheral_instances() -> TokenStream { | |||
| 543 | for peripheral in METADATA.peripherals { | 550 | for peripheral in METADATA.peripherals { |
| 544 | let peri = format_ident!("{}", peripheral.name); | 551 | let peri = format_ident!("{}", peripheral.name); |
| 545 | 552 | ||
| 546 | // Will be filled in when uart implementation is finished | ||
| 547 | let _ = peri; | ||
| 548 | let tokens = match peripheral.kind { | 553 | let tokens = match peripheral.kind { |
| 549 | "uart" => Some(quote! { impl_uart_instance!(#peri); }), | 554 | "uart" => Some(quote! { impl_uart_instance!(#peri); }), |
| 550 | _ => None, | 555 | _ => None, |
| @@ -555,6 +560,18 @@ fn generate_peripheral_instances() -> TokenStream { | |||
| 555 | } | 560 | } |
| 556 | } | 561 | } |
| 557 | 562 | ||
| 563 | // DMA channels | ||
| 564 | for dma_channel in METADATA.dma_channels.iter() { | ||
| 565 | let peri = format_ident!("DMA_CH{}", dma_channel.number); | ||
| 566 | let num = dma_channel.number; | ||
| 567 | |||
| 568 | if dma_channel.full { | ||
| 569 | impls.push(quote! { impl_full_dma_channel!(#peri, #num); }); | ||
| 570 | } else { | ||
| 571 | impls.push(quote! { impl_dma_channel!(#peri, #num); }); | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 558 | quote! { | 575 | quote! { |
| 559 | #(#impls)* | 576 | #(#impls)* |
| 560 | } | 577 | } |
diff --git a/embassy-mspm0/src/dma.rs b/embassy-mspm0/src/dma.rs new file mode 100644 index 000000000..66b79709c --- /dev/null +++ b/embassy-mspm0/src/dma.rs | |||
| @@ -0,0 +1,626 @@ | |||
| 1 | //! Direct Memory Access (DMA) | ||
| 2 | |||
| 3 | #![macro_use] | ||
| 4 | |||
| 5 | use core::future::Future; | ||
| 6 | use core::mem; | ||
| 7 | use core::pin::Pin; | ||
| 8 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 9 | use core::task::{Context, Poll}; | ||
| 10 | |||
| 11 | use critical_section::CriticalSection; | ||
| 12 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 13 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | ||
| 14 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 15 | use mspm0_metapac::common::{Reg, RW}; | ||
| 16 | use mspm0_metapac::dma::regs; | ||
| 17 | use mspm0_metapac::dma::vals::{self, Autoen, Em, Incr, Preirq, Wdth}; | ||
| 18 | |||
| 19 | use crate::{interrupt, pac, Peri}; | ||
| 20 | |||
| 21 | /// The burst size of a DMA transfer. | ||
| 22 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 23 | pub enum BurstSize { | ||
| 24 | /// The whole block transfer is completed in one transfer without interruption. | ||
| 25 | Complete, | ||
| 26 | |||
| 27 | /// The burst size is 8, after 9 transfers the block transfer is interrupted and the priority | ||
| 28 | /// is reevaluated. | ||
| 29 | _8, | ||
| 30 | |||
| 31 | /// The burst size is 16, after 17 transfers the block transfer is interrupted and the priority | ||
| 32 | /// is reevaluated. | ||
| 33 | _16, | ||
| 34 | |||
| 35 | /// The burst size is 32, after 32 transfers the block transfer is interrupted and the priority | ||
| 36 | /// is reevaluated. | ||
| 37 | _32, | ||
| 38 | } | ||
| 39 | |||
| 40 | /// DMA channel. | ||
| 41 | #[allow(private_bounds)] | ||
| 42 | pub trait Channel: Into<AnyChannel> + PeripheralType {} | ||
| 43 | |||
| 44 | /// Full DMA channel. | ||
| 45 | #[allow(private_bounds)] | ||
| 46 | pub trait FullChannel: Channel + Into<AnyFullChannel> {} | ||
| 47 | |||
| 48 | /// Type-erased DMA channel. | ||
| 49 | pub struct AnyChannel { | ||
| 50 | pub(crate) id: u8, | ||
| 51 | } | ||
| 52 | impl_peripheral!(AnyChannel); | ||
| 53 | |||
| 54 | impl SealedChannel for AnyChannel { | ||
| 55 | fn id(&self) -> u8 { | ||
| 56 | self.id | ||
| 57 | } | ||
| 58 | } | ||
| 59 | impl Channel for AnyChannel {} | ||
| 60 | |||
| 61 | /// Type-erased full DMA channel. | ||
| 62 | pub struct AnyFullChannel { | ||
| 63 | pub(crate) id: u8, | ||
| 64 | } | ||
| 65 | impl_peripheral!(AnyFullChannel); | ||
| 66 | |||
| 67 | impl SealedChannel for AnyFullChannel { | ||
| 68 | fn id(&self) -> u8 { | ||
| 69 | self.id | ||
| 70 | } | ||
| 71 | } | ||
| 72 | impl Channel for AnyFullChannel {} | ||
| 73 | impl FullChannel for AnyFullChannel {} | ||
| 74 | |||
| 75 | impl From<AnyFullChannel> for AnyChannel { | ||
| 76 | fn from(value: AnyFullChannel) -> Self { | ||
| 77 | Self { id: value.id } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | #[allow(private_bounds)] | ||
| 82 | pub trait Word: SealedWord { | ||
| 83 | /// Size in bytes for the width. | ||
| 84 | fn size() -> isize; | ||
| 85 | } | ||
| 86 | |||
| 87 | impl SealedWord for u8 { | ||
| 88 | fn width() -> vals::Wdth { | ||
| 89 | vals::Wdth::BYTE | ||
| 90 | } | ||
| 91 | } | ||
| 92 | impl Word for u8 { | ||
| 93 | fn size() -> isize { | ||
| 94 | 1 | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | impl SealedWord for u16 { | ||
| 99 | fn width() -> vals::Wdth { | ||
| 100 | vals::Wdth::HALF | ||
| 101 | } | ||
| 102 | } | ||
| 103 | impl Word for u16 { | ||
| 104 | fn size() -> isize { | ||
| 105 | 2 | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | impl SealedWord for u32 { | ||
| 110 | fn width() -> vals::Wdth { | ||
| 111 | vals::Wdth::WORD | ||
| 112 | } | ||
| 113 | } | ||
| 114 | impl Word for u32 { | ||
| 115 | fn size() -> isize { | ||
| 116 | 4 | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | impl SealedWord for u64 { | ||
| 121 | fn width() -> vals::Wdth { | ||
| 122 | vals::Wdth::LONG | ||
| 123 | } | ||
| 124 | } | ||
| 125 | impl Word for u64 { | ||
| 126 | fn size() -> isize { | ||
| 127 | 8 | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | // TODO: u128 (LONGLONG) support. G350x does support it, but other parts do not such as C110x. More metadata is | ||
| 132 | // needed to properly enable this. | ||
| 133 | // impl SealedWord for u128 { | ||
| 134 | // fn width() -> vals::Wdth { | ||
| 135 | // vals::Wdth::LONGLONG | ||
| 136 | // } | ||
| 137 | // } | ||
| 138 | // impl Word for u128 { | ||
| 139 | // fn size() -> isize { | ||
| 140 | // 16 | ||
| 141 | // } | ||
| 142 | // } | ||
| 143 | |||
| 144 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 145 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 146 | pub enum Error { | ||
| 147 | /// The DMA transfer is too large. | ||
| 148 | /// | ||
| 149 | /// The hardware limits the DMA to 16384 transfers per channel at a time. This means that transferring | ||
| 150 | /// 16384 `u8` and 16384 `u64` are equivalent, since the DMA must copy 16384 values. | ||
| 151 | TooManyTransfers, | ||
| 152 | } | ||
| 153 | |||
| 154 | /// DMA transfer mode for basic channels. | ||
| 155 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 156 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 157 | pub enum TransferMode { | ||
| 158 | /// Each DMA trigger will transfer a single value. | ||
| 159 | Single, | ||
| 160 | |||
| 161 | /// Each DMA trigger will transfer the complete block with one trigger. | ||
| 162 | Block, | ||
| 163 | } | ||
| 164 | |||
| 165 | /// DMA transfer options. | ||
| 166 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 167 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 168 | #[non_exhaustive] | ||
| 169 | pub struct TransferOptions { | ||
| 170 | /// DMA transfer mode. | ||
| 171 | pub mode: TransferMode, | ||
| 172 | // TODO: Read and write stride. | ||
| 173 | } | ||
| 174 | |||
| 175 | impl Default for TransferOptions { | ||
| 176 | fn default() -> Self { | ||
| 177 | Self { | ||
| 178 | mode: TransferMode::Single, | ||
| 179 | } | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | /// DMA transfer. | ||
| 184 | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||
| 185 | pub struct Transfer<'a> { | ||
| 186 | channel: Peri<'a, AnyChannel>, | ||
| 187 | } | ||
| 188 | |||
| 189 | impl<'a> Transfer<'a> { | ||
| 190 | /// Software trigger source. | ||
| 191 | /// | ||
| 192 | /// Using this trigger source means that a transfer will start immediately rather than waiting for | ||
| 193 | /// a hardware event. This can be useful if you want to do a DMA accelerated memcpy. | ||
| 194 | pub const SOFTWARE_TRIGGER: u8 = 0; | ||
| 195 | |||
| 196 | /// Create a new read DMA transfer. | ||
| 197 | pub unsafe fn new_read<SW: Word, DW: Word>( | ||
| 198 | channel: Peri<'a, impl Channel>, | ||
| 199 | trigger_source: u8, | ||
| 200 | src: *mut SW, | ||
| 201 | dst: &'a mut [DW], | ||
| 202 | options: TransferOptions, | ||
| 203 | ) -> Result<Self, Error> { | ||
| 204 | Self::new_read_raw(channel, trigger_source, src, dst, options) | ||
| 205 | } | ||
| 206 | |||
| 207 | /// Create a new read DMA transfer, using raw pointers. | ||
| 208 | pub unsafe fn new_read_raw<SW: Word, DW: Word>( | ||
| 209 | channel: Peri<'a, impl Channel>, | ||
| 210 | trigger_source: u8, | ||
| 211 | src: *mut SW, | ||
| 212 | dst: *mut [DW], | ||
| 213 | options: TransferOptions, | ||
| 214 | ) -> Result<Self, Error> { | ||
| 215 | verify_transfer::<DW>(dst)?; | ||
| 216 | |||
| 217 | let channel = channel.into(); | ||
| 218 | channel.configure( | ||
| 219 | trigger_source, | ||
| 220 | src.cast(), | ||
| 221 | SW::width(), | ||
| 222 | dst.cast(), | ||
| 223 | DW::width(), | ||
| 224 | dst.len() as u16, | ||
| 225 | false, | ||
| 226 | true, | ||
| 227 | options, | ||
| 228 | ); | ||
| 229 | channel.start(); | ||
| 230 | |||
| 231 | Ok(Self { channel }) | ||
| 232 | } | ||
| 233 | |||
| 234 | /// Create a new write DMA transfer. | ||
| 235 | pub unsafe fn new_write<SW: Word, DW: Word>( | ||
| 236 | channel: Peri<'a, impl Channel>, | ||
| 237 | trigger_source: u8, | ||
| 238 | src: &'a [SW], | ||
| 239 | dst: *mut DW, | ||
| 240 | options: TransferOptions, | ||
| 241 | ) -> Result<Self, Error> { | ||
| 242 | Self::new_write_raw(channel, trigger_source, src, dst, options) | ||
| 243 | } | ||
| 244 | |||
| 245 | /// Create a new write DMA transfer, using raw pointers. | ||
| 246 | pub unsafe fn new_write_raw<SW: Word, DW: Word>( | ||
| 247 | channel: Peri<'a, impl Channel>, | ||
| 248 | trigger_source: u8, | ||
| 249 | src: *const [SW], | ||
| 250 | dst: *mut DW, | ||
| 251 | options: TransferOptions, | ||
| 252 | ) -> Result<Self, Error> { | ||
| 253 | verify_transfer::<SW>(src)?; | ||
| 254 | |||
| 255 | let channel = channel.into(); | ||
| 256 | channel.configure( | ||
| 257 | trigger_source, | ||
| 258 | src.cast(), | ||
| 259 | SW::width(), | ||
| 260 | dst.cast(), | ||
| 261 | DW::width(), | ||
| 262 | src.len() as u16, | ||
| 263 | true, | ||
| 264 | false, | ||
| 265 | options, | ||
| 266 | ); | ||
| 267 | channel.start(); | ||
| 268 | |||
| 269 | Ok(Self { channel }) | ||
| 270 | } | ||
| 271 | |||
| 272 | // TODO: Copy between slices. | ||
| 273 | |||
| 274 | /// Request the transfer to resume. | ||
| 275 | pub fn resume(&mut self) { | ||
| 276 | self.channel.resume(); | ||
| 277 | } | ||
| 278 | |||
| 279 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 280 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 281 | /// | ||
| 282 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 283 | pub fn request_pause(&mut self) { | ||
| 284 | self.channel.request_pause(); | ||
| 285 | } | ||
| 286 | |||
| 287 | /// Return whether this transfer is still running. | ||
| 288 | /// | ||
| 289 | /// If this returns [`false`], it can be because either the transfer finished, or | ||
| 290 | /// it was requested to stop early with [`request_stop`]. | ||
| 291 | pub fn is_running(&mut self) -> bool { | ||
| 292 | self.channel.is_running() | ||
| 293 | } | ||
| 294 | |||
| 295 | /// Blocking wait until the transfer finishes. | ||
| 296 | pub fn blocking_wait(mut self) { | ||
| 297 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 298 | compiler_fence(Ordering::SeqCst); | ||
| 299 | |||
| 300 | while self.is_running() {} | ||
| 301 | |||
| 302 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 303 | compiler_fence(Ordering::SeqCst); | ||
| 304 | |||
| 305 | // Prevent drop from being called since we ran to completion (drop will try to pause). | ||
| 306 | mem::forget(self); | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | impl<'a> Unpin for Transfer<'a> {} | ||
| 311 | impl<'a> Future for Transfer<'a> { | ||
| 312 | type Output = (); | ||
| 313 | |||
| 314 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 315 | let state: &ChannelState = &STATE[self.channel.id as usize]; | ||
| 316 | |||
| 317 | state.waker.register(cx.waker()); | ||
| 318 | |||
| 319 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 320 | compiler_fence(Ordering::SeqCst); | ||
| 321 | |||
| 322 | if self.channel.is_running() { | ||
| 323 | Poll::Pending | ||
| 324 | } else { | ||
| 325 | Poll::Ready(()) | ||
| 326 | } | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | impl<'a> Drop for Transfer<'a> { | ||
| 331 | fn drop(&mut self) { | ||
| 332 | self.channel.request_pause(); | ||
| 333 | while self.is_running() {} | ||
| 334 | |||
| 335 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 336 | compiler_fence(Ordering::SeqCst); | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | // impl details | ||
| 341 | |||
| 342 | fn verify_transfer<W: Word>(ptr: *const [W]) -> Result<(), Error> { | ||
| 343 | if ptr.len() > (u16::MAX as usize) { | ||
| 344 | return Err(Error::TooManyTransfers); | ||
| 345 | } | ||
| 346 | |||
| 347 | // TODO: Stride checks | ||
| 348 | |||
| 349 | Ok(()) | ||
| 350 | } | ||
| 351 | |||
| 352 | fn convert_burst_size(value: BurstSize) -> vals::Burstsz { | ||
| 353 | match value { | ||
| 354 | BurstSize::Complete => vals::Burstsz::INFINITI, | ||
| 355 | BurstSize::_8 => vals::Burstsz::BURST_8, | ||
| 356 | BurstSize::_16 => vals::Burstsz::BURST_16, | ||
| 357 | BurstSize::_32 => vals::Burstsz::BURST_32, | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | fn convert_mode(mode: TransferMode) -> vals::Tm { | ||
| 362 | match mode { | ||
| 363 | TransferMode::Single => vals::Tm::SINGLE, | ||
| 364 | TransferMode::Block => vals::Tm::BLOCK, | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | const CHANNEL_COUNT: usize = crate::_generated::DMA_CHANNELS; | ||
| 369 | static STATE: [ChannelState; CHANNEL_COUNT] = [const { ChannelState::new() }; CHANNEL_COUNT]; | ||
| 370 | |||
| 371 | struct ChannelState { | ||
| 372 | waker: AtomicWaker, | ||
| 373 | } | ||
| 374 | |||
| 375 | impl ChannelState { | ||
| 376 | const fn new() -> Self { | ||
| 377 | Self { | ||
| 378 | waker: AtomicWaker::new(), | ||
| 379 | } | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | /// SAFETY: Must only be called once. | ||
| 384 | /// | ||
| 385 | /// Changing the burst size mid transfer may have some odd behavior. | ||
| 386 | pub(crate) unsafe fn init(_cs: CriticalSection, burst_size: BurstSize, round_robin: bool) { | ||
| 387 | pac::DMA.prio().modify(|prio| { | ||
| 388 | prio.set_burstsz(convert_burst_size(burst_size)); | ||
| 389 | prio.set_roundrobin(round_robin); | ||
| 390 | }); | ||
| 391 | pac::DMA.int_event(0).imask().modify(|w| { | ||
| 392 | w.set_dataerr(true); | ||
| 393 | w.set_addrerr(true); | ||
| 394 | }); | ||
| 395 | |||
| 396 | interrupt::DMA.enable(); | ||
| 397 | } | ||
| 398 | |||
| 399 | pub(crate) trait SealedWord { | ||
| 400 | fn width() -> vals::Wdth; | ||
| 401 | } | ||
| 402 | |||
| 403 | pub(crate) trait SealedChannel { | ||
| 404 | fn id(&self) -> u8; | ||
| 405 | |||
| 406 | #[inline] | ||
| 407 | fn tctl(&self) -> Reg<regs::Tctl, RW> { | ||
| 408 | pac::DMA.trig(self.id() as usize).tctl() | ||
| 409 | } | ||
| 410 | |||
| 411 | #[inline] | ||
| 412 | fn ctl(&self) -> Reg<regs::Ctl, RW> { | ||
| 413 | pac::DMA.chan(self.id() as usize).ctl() | ||
| 414 | } | ||
| 415 | |||
| 416 | #[inline] | ||
| 417 | fn sa(&self) -> Reg<u32, RW> { | ||
| 418 | pac::DMA.chan(self.id() as usize).sa() | ||
| 419 | } | ||
| 420 | |||
| 421 | #[inline] | ||
| 422 | fn da(&self) -> Reg<u32, RW> { | ||
| 423 | pac::DMA.chan(self.id() as usize).da() | ||
| 424 | } | ||
| 425 | |||
| 426 | #[inline] | ||
| 427 | fn sz(&self) -> Reg<regs::Sz, RW> { | ||
| 428 | pac::DMA.chan(self.id() as usize).sz() | ||
| 429 | } | ||
| 430 | |||
| 431 | #[inline] | ||
| 432 | fn mask_interrupt(&self, enable: bool) { | ||
| 433 | // Enabling interrupts is an RMW operation. | ||
| 434 | critical_section::with(|_cs| { | ||
| 435 | pac::DMA.int_event(0).imask().modify(|w| { | ||
| 436 | w.set_ch(self.id() as usize, enable); | ||
| 437 | }); | ||
| 438 | }) | ||
| 439 | } | ||
| 440 | |||
| 441 | /// # Safety | ||
| 442 | /// | ||
| 443 | /// - `src` must be valid for the lifetime of the transfer. | ||
| 444 | /// - `dst` must be valid for the lifetime of the transfer. | ||
| 445 | unsafe fn configure( | ||
| 446 | &self, | ||
| 447 | trigger_sel: u8, | ||
| 448 | src: *const u32, | ||
| 449 | src_wdth: Wdth, | ||
| 450 | dst: *const u32, | ||
| 451 | dst_wdth: Wdth, | ||
| 452 | transfer_count: u16, | ||
| 453 | increment_src: bool, | ||
| 454 | increment_dst: bool, | ||
| 455 | options: TransferOptions, | ||
| 456 | ) { | ||
| 457 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 458 | compiler_fence(Ordering::SeqCst); | ||
| 459 | |||
| 460 | self.ctl().modify(|w| { | ||
| 461 | // SLAU 5.2.5: | ||
| 462 | // "The DMATSEL bits should be modified only when the DMACTLx.DMAEN bit is | ||
| 463 | // 0; otherwise, unpredictable DMA triggers can occur." | ||
| 464 | // | ||
| 465 | // We also want to stop any transfers before setup. | ||
| 466 | w.set_en(false); | ||
| 467 | w.set_req(false); | ||
| 468 | |||
| 469 | // Not every part supports auto enable, so force its value to 0. | ||
| 470 | w.set_autoen(Autoen::NONE); | ||
| 471 | w.set_preirq(Preirq::PREIRQ_DISABLE); | ||
| 472 | w.set_srcwdth(src_wdth); | ||
| 473 | w.set_dstwdth(dst_wdth); | ||
| 474 | w.set_srcincr(if increment_src { | ||
| 475 | Incr::INCREMENT | ||
| 476 | } else { | ||
| 477 | Incr::UNCHANGED | ||
| 478 | }); | ||
| 479 | w.set_dstincr(if increment_dst { | ||
| 480 | Incr::INCREMENT | ||
| 481 | } else { | ||
| 482 | Incr::UNCHANGED | ||
| 483 | }); | ||
| 484 | |||
| 485 | w.set_em(Em::NORMAL); | ||
| 486 | // Single and block will clear the enable bit when the transfers finish. | ||
| 487 | w.set_tm(convert_mode(options.mode)); | ||
| 488 | }); | ||
| 489 | |||
| 490 | self.tctl().write(|w| { | ||
| 491 | w.set_tsel(trigger_sel); | ||
| 492 | // Basic channels do not implement cross triggering. | ||
| 493 | w.set_tint(vals::Tint::EXTERNAL); | ||
| 494 | }); | ||
| 495 | |||
| 496 | self.sz().write(|w| { | ||
| 497 | w.set_size(transfer_count); | ||
| 498 | }); | ||
| 499 | |||
| 500 | self.sa().write_value(src as u32); | ||
| 501 | self.da().write_value(dst as u32); | ||
| 502 | |||
| 503 | // Enable the channel. | ||
| 504 | self.ctl().modify(|w| { | ||
| 505 | // FIXME: Why did putting set_req later fix some transfers | ||
| 506 | w.set_en(true); | ||
| 507 | w.set_req(true); | ||
| 508 | }); | ||
| 509 | } | ||
| 510 | |||
| 511 | fn start(&self) { | ||
| 512 | self.mask_interrupt(true); | ||
| 513 | |||
| 514 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 515 | compiler_fence(Ordering::SeqCst); | ||
| 516 | |||
| 517 | // Request the DMA transfer to start. | ||
| 518 | self.ctl().modify(|w| { | ||
| 519 | w.set_req(true); | ||
| 520 | }); | ||
| 521 | } | ||
| 522 | |||
| 523 | fn resume(&self) { | ||
| 524 | self.mask_interrupt(true); | ||
| 525 | |||
| 526 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 527 | compiler_fence(Ordering::SeqCst); | ||
| 528 | |||
| 529 | self.ctl().modify(|w| { | ||
| 530 | // w.set_en(true); | ||
| 531 | w.set_req(true); | ||
| 532 | }); | ||
| 533 | } | ||
| 534 | |||
| 535 | fn request_pause(&self) { | ||
| 536 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 537 | compiler_fence(Ordering::SeqCst); | ||
| 538 | |||
| 539 | // Stop the transfer. | ||
| 540 | // | ||
| 541 | // SLAU846 5.2.6: | ||
| 542 | // "A DMA block transfer in progress can be stopped by clearing the DMAEN bit" | ||
| 543 | self.ctl().modify(|w| { | ||
| 544 | // w.set_en(false); | ||
| 545 | w.set_req(false); | ||
| 546 | }); | ||
| 547 | } | ||
| 548 | |||
| 549 | fn is_running(&self) -> bool { | ||
| 550 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 551 | compiler_fence(Ordering::SeqCst); | ||
| 552 | |||
| 553 | let ctl = self.ctl().read(); | ||
| 554 | |||
| 555 | // Is the transfer requested? | ||
| 556 | ctl.req() | ||
| 557 | // Is the channel enabled? | ||
| 558 | && ctl.en() | ||
| 559 | } | ||
| 560 | } | ||
| 561 | |||
| 562 | macro_rules! impl_dma_channel { | ||
| 563 | ($instance: ident, $num: expr) => { | ||
| 564 | impl crate::dma::SealedChannel for crate::peripherals::$instance { | ||
| 565 | fn id(&self) -> u8 { | ||
| 566 | $num | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | impl From<crate::peripherals::$instance> for crate::dma::AnyChannel { | ||
| 571 | fn from(value: crate::peripherals::$instance) -> Self { | ||
| 572 | use crate::dma::SealedChannel; | ||
| 573 | |||
| 574 | Self { id: value.id() } | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | impl crate::dma::Channel for crate::peripherals::$instance {} | ||
| 579 | }; | ||
| 580 | } | ||
| 581 | |||
| 582 | // C1104 has no full DMA channels. | ||
| 583 | #[allow(unused_macros)] | ||
| 584 | macro_rules! impl_full_dma_channel { | ||
| 585 | ($instance: ident, $num: expr) => { | ||
| 586 | impl_dma_channel!($instance, $num); | ||
| 587 | |||
| 588 | impl From<crate::peripherals::$instance> for crate::dma::AnyFullChannel { | ||
| 589 | fn from(value: crate::peripherals::$instance) -> Self { | ||
| 590 | use crate::dma::SealedChannel; | ||
| 591 | |||
| 592 | Self { id: value.id() } | ||
| 593 | } | ||
| 594 | } | ||
| 595 | |||
| 596 | impl crate::dma::FullChannel for crate::peripherals::$instance {} | ||
| 597 | }; | ||
| 598 | } | ||
| 599 | |||
| 600 | #[cfg(feature = "rt")] | ||
| 601 | #[interrupt] | ||
| 602 | fn DMA() { | ||
| 603 | use crate::BitIter; | ||
| 604 | |||
| 605 | let events = pac::DMA.int_event(0); | ||
| 606 | let mis = events.mis().read(); | ||
| 607 | |||
| 608 | // TODO: Handle DATAERR and ADDRERR? However we do not know which channel causes an error. | ||
| 609 | if mis.dataerr() { | ||
| 610 | panic!("DMA data error"); | ||
| 611 | } else if mis.addrerr() { | ||
| 612 | panic!("DMA address error") | ||
| 613 | } | ||
| 614 | |||
| 615 | // Ignore preirq interrupts (values greater than 16). | ||
| 616 | for i in BitIter(mis.0 & 0x0000_FFFF) { | ||
| 617 | if let Some(state) = STATE.get(i as usize) { | ||
| 618 | state.waker.wake(); | ||
| 619 | |||
| 620 | // Notify the future that the counter size hit zero | ||
| 621 | events.imask().modify(|w| { | ||
| 622 | w.set_ch(i as usize, false); | ||
| 623 | }); | ||
| 624 | } | ||
| 625 | } | ||
| 626 | } | ||
diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs index 738d51928..f77848888 100644 --- a/embassy-mspm0/src/gpio.rs +++ b/embassy-mspm0/src/gpio.rs | |||
| @@ -223,13 +223,13 @@ impl<'d> Flex<'d> { | |||
| 223 | /// Get whether the pin input level is high. | 223 | /// Get whether the pin input level is high. |
| 224 | #[inline] | 224 | #[inline] |
| 225 | pub fn is_high(&self) -> bool { | 225 | pub fn is_high(&self) -> bool { |
| 226 | !self.is_low() | 226 | self.pin.block().din31_0().read().dio(self.pin.bit_index()) |
| 227 | } | 227 | } |
| 228 | 228 | ||
| 229 | /// Get whether the pin input level is low. | 229 | /// Get whether the pin input level is low. |
| 230 | #[inline] | 230 | #[inline] |
| 231 | pub fn is_low(&self) -> bool { | 231 | pub fn is_low(&self) -> bool { |
| 232 | self.pin.block().din31_0().read().dio(self.pin.bit_index()) | 232 | !self.is_high() |
| 233 | } | 233 | } |
| 234 | 234 | ||
| 235 | /// Returns current pin level | 235 | /// Returns current pin level |
| @@ -271,22 +271,22 @@ impl<'d> Flex<'d> { | |||
| 271 | } | 271 | } |
| 272 | } | 272 | } |
| 273 | 273 | ||
| 274 | /// Get the current pin input level. | 274 | /// Get the current pin output level. |
| 275 | #[inline] | 275 | #[inline] |
| 276 | pub fn get_output_level(&self) -> Level { | 276 | pub fn get_output_level(&self) -> Level { |
| 277 | self.is_high().into() | 277 | self.is_set_high().into() |
| 278 | } | 278 | } |
| 279 | 279 | ||
| 280 | /// Is the output level high? | 280 | /// Is the output level high? |
| 281 | #[inline] | 281 | #[inline] |
| 282 | pub fn is_set_high(&self) -> bool { | 282 | pub fn is_set_high(&self) -> bool { |
| 283 | !self.is_set_low() | 283 | self.pin.block().dout31_0().read().dio(self.pin.bit_index()) |
| 284 | } | 284 | } |
| 285 | 285 | ||
| 286 | /// Is the output level low? | 286 | /// Is the output level low? |
| 287 | #[inline] | 287 | #[inline] |
| 288 | pub fn is_set_low(&self) -> bool { | 288 | pub fn is_set_low(&self) -> bool { |
| 289 | (self.pin.block().dout31_0().read().0 & self.pin.bit_index() as u32) == 0 | 289 | !self.is_set_high() |
| 290 | } | 290 | } |
| 291 | 291 | ||
| 292 | /// Wait until the pin is high. If it is already high, return immediately. | 292 | /// Wait until the pin is high. If it is already high, return immediately. |
| @@ -1090,7 +1090,9 @@ pub(crate) fn init(gpio: gpio::Gpio) { | |||
| 1090 | 1090 | ||
| 1091 | #[cfg(feature = "rt")] | 1091 | #[cfg(feature = "rt")] |
| 1092 | fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) { | 1092 | fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) { |
| 1093 | use crate::BitIter; | ||
| 1093 | // Only consider pins which have interrupts unmasked. | 1094 | // Only consider pins which have interrupts unmasked. |
| 1095 | |||
| 1094 | let bits = gpio.cpu_int().mis().read().0; | 1096 | let bits = gpio.cpu_int().mis().read().0; |
| 1095 | 1097 | ||
| 1096 | for i in BitIter(bits) { | 1098 | for i in BitIter(bits) { |
| @@ -1103,22 +1105,6 @@ fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) { | |||
| 1103 | } | 1105 | } |
| 1104 | } | 1106 | } |
| 1105 | 1107 | ||
| 1106 | struct BitIter(u32); | ||
| 1107 | |||
| 1108 | impl Iterator for BitIter { | ||
| 1109 | type Item = u32; | ||
| 1110 | |||
| 1111 | fn next(&mut self) -> Option<Self::Item> { | ||
| 1112 | match self.0.trailing_zeros() { | ||
| 1113 | 32 => None, | ||
| 1114 | b => { | ||
| 1115 | self.0 &= !(1 << b); | ||
| 1116 | Some(b) | ||
| 1117 | } | ||
| 1118 | } | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | // C110x and L110x have a dedicated interrupts just for GPIOA. | 1108 | // C110x and L110x have a dedicated interrupts just for GPIOA. |
| 1123 | // | 1109 | // |
| 1124 | // These chips do not have a GROUP1 interrupt. | 1110 | // These chips do not have a GROUP1 interrupt. |
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index 7ff60e946..629ebfa1f 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 dma; | ||
| 16 | pub mod gpio; | 17 | pub mod gpio; |
| 17 | pub mod timer; | 18 | pub mod timer; |
| 18 | pub mod uart; | 19 | pub mod uart; |
| @@ -59,22 +60,108 @@ pub(crate) use mspm0_metapac as pac; | |||
| 59 | 60 | ||
| 60 | pub use crate::_generated::interrupt; | 61 | pub use crate::_generated::interrupt; |
| 61 | 62 | ||
| 63 | /// Macro to bind interrupts to handlers. | ||
| 64 | /// | ||
| 65 | /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) | ||
| 66 | /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to | ||
| 67 | /// prove at compile-time that the right interrupts have been bound. | ||
| 68 | /// | ||
| 69 | /// Example of how to bind one interrupt: | ||
| 70 | /// | ||
| 71 | /// ```rust,ignore | ||
| 72 | /// use embassy_nrf::{bind_interrupts, spim, peripherals}; | ||
| 73 | /// | ||
| 74 | /// bind_interrupts!( | ||
| 75 | /// /// Binds the SPIM3 interrupt. | ||
| 76 | /// struct Irqs { | ||
| 77 | /// SPIM3 => spim::InterruptHandler<peripherals::SPI3>; | ||
| 78 | /// } | ||
| 79 | /// ); | ||
| 80 | /// ``` | ||
| 81 | /// | ||
| 82 | /// Example of how to bind multiple interrupts in a single macro invocation: | ||
| 83 | /// | ||
| 84 | /// ```rust,ignore | ||
| 85 | /// use embassy_nrf::{bind_interrupts, spim, twim, peripherals}; | ||
| 86 | /// | ||
| 87 | /// bind_interrupts!(struct Irqs { | ||
| 88 | /// SPIM3 => spim::InterruptHandler<peripherals::SPI3>; | ||
| 89 | /// TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>; | ||
| 90 | /// }); | ||
| 91 | /// ``` | ||
| 92 | |||
| 93 | // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. | ||
| 94 | #[macro_export] | ||
| 95 | macro_rules! bind_interrupts { | ||
| 96 | ($(#[$attr:meta])* $vis:vis struct $name:ident { | ||
| 97 | $( | ||
| 98 | $(#[cfg($cond_irq:meta)])? | ||
| 99 | $irq:ident => $( | ||
| 100 | $(#[cfg($cond_handler:meta)])? | ||
| 101 | $handler:ty | ||
| 102 | ),*; | ||
| 103 | )* | ||
| 104 | }) => { | ||
| 105 | #[derive(Copy, Clone)] | ||
| 106 | $(#[$attr])* | ||
| 107 | $vis struct $name; | ||
| 108 | |||
| 109 | $( | ||
| 110 | #[allow(non_snake_case)] | ||
| 111 | #[no_mangle] | ||
| 112 | $(#[cfg($cond_irq)])? | ||
| 113 | unsafe extern "C" fn $irq() { | ||
| 114 | unsafe { | ||
| 115 | $( | ||
| 116 | $(#[cfg($cond_handler)])? | ||
| 117 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); | ||
| 118 | |||
| 119 | )* | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | $(#[cfg($cond_irq)])? | ||
| 124 | $crate::bind_interrupts!(@inner | ||
| 125 | $( | ||
| 126 | $(#[cfg($cond_handler)])? | ||
| 127 | unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} | ||
| 128 | )* | ||
| 129 | ); | ||
| 130 | )* | ||
| 131 | }; | ||
| 132 | (@inner $($t:tt)*) => { | ||
| 133 | $($t)* | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 62 | /// `embassy-mspm0` global configuration. | 137 | /// `embassy-mspm0` global configuration. |
| 63 | #[non_exhaustive] | 138 | #[non_exhaustive] |
| 64 | #[derive(Clone, Copy)] | 139 | #[derive(Clone, Copy)] |
| 65 | pub struct Config { | 140 | pub struct Config { |
| 66 | // TODO | 141 | // TODO: OSC configuration. |
| 142 | /// The size of DMA block transfer burst. | ||
| 143 | /// | ||
| 144 | /// If this is set to a value | ||
| 145 | pub dma_burst_size: dma::BurstSize, | ||
| 146 | |||
| 147 | /// Whether the DMA channels are used in a fixed priority or a round robin fashion. | ||
| 148 | /// | ||
| 149 | /// If [`false`], the DMA priorities are fixed. | ||
| 150 | /// | ||
| 151 | /// If [`true`], after a channel finishes a transfer it becomes the lowest priority. | ||
| 152 | pub dma_round_robin: bool, | ||
| 67 | } | 153 | } |
| 68 | 154 | ||
| 69 | impl Default for Config { | 155 | impl Default for Config { |
| 70 | fn default() -> Self { | 156 | fn default() -> Self { |
| 71 | Self { | 157 | Self { |
| 72 | // TODO | 158 | dma_burst_size: dma::BurstSize::Complete, |
| 159 | dma_round_robin: false, | ||
| 73 | } | 160 | } |
| 74 | } | 161 | } |
| 75 | } | 162 | } |
| 76 | 163 | ||
| 77 | pub fn init(_config: Config) -> Peripherals { | 164 | pub fn init(config: Config) -> Peripherals { |
| 78 | critical_section::with(|cs| { | 165 | critical_section::with(|cs| { |
| 79 | let peripherals = Peripherals::take_with_cs(cs); | 166 | let peripherals = Peripherals::take_with_cs(cs); |
| 80 | 167 | ||
| @@ -112,9 +199,33 @@ pub fn init(_config: Config) -> Peripherals { | |||
| 112 | crate::interrupt::typelevel::GPIOA::enable(); | 199 | crate::interrupt::typelevel::GPIOA::enable(); |
| 113 | } | 200 | } |
| 114 | 201 | ||
| 202 | // SAFETY: Peripherals::take_with_cs will only be run once or panic. | ||
| 203 | unsafe { dma::init(cs, config.dma_burst_size, config.dma_round_robin) }; | ||
| 204 | |||
| 115 | #[cfg(feature = "_time-driver")] | 205 | #[cfg(feature = "_time-driver")] |
| 116 | time_driver::init(cs); | 206 | time_driver::init(cs); |
| 117 | 207 | ||
| 118 | peripherals | 208 | peripherals |
| 119 | }) | 209 | }) |
| 120 | } | 210 | } |
| 211 | |||
| 212 | pub(crate) mod sealed { | ||
| 213 | #[allow(dead_code)] | ||
| 214 | pub trait Sealed {} | ||
| 215 | } | ||
| 216 | |||
| 217 | struct BitIter(u32); | ||
| 218 | |||
| 219 | impl Iterator for BitIter { | ||
| 220 | type Item = u32; | ||
| 221 | |||
| 222 | fn next(&mut self) -> Option<Self::Item> { | ||
| 223 | match self.0.trailing_zeros() { | ||
| 224 | 32 => None, | ||
| 225 | b => { | ||
| 226 | self.0 &= !(1 << b); | ||
| 227 | Some(b) | ||
| 228 | } | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | ||
diff --git a/embassy-mspm0/src/uart/buffered.rs b/embassy-mspm0/src/uart/buffered.rs new file mode 100644 index 000000000..cbc0b6c80 --- /dev/null +++ b/embassy-mspm0/src/uart/buffered.rs | |||
| @@ -0,0 +1,1060 @@ | |||
| 1 | use core::future::{poll_fn, Future}; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::slice; | ||
| 4 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 5 | use core::task::Poll; | ||
| 6 | |||
| 7 | use embassy_embedded_hal::SetConfig; | ||
| 8 | use embassy_hal_internal::atomic_ring_buffer::RingBuffer; | ||
| 9 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 10 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 11 | use embedded_hal_nb::nb; | ||
| 12 | |||
| 13 | use crate::gpio::{AnyPin, SealedPin}; | ||
| 14 | use crate::interrupt::typelevel::Binding; | ||
| 15 | use crate::pac::uart::Uart as Regs; | ||
| 16 | use crate::uart::{Config, ConfigError, CtsPin, Error, Info, Instance, RtsPin, RxPin, State, TxPin}; | ||
| 17 | use crate::{interrupt, Peri}; | ||
| 18 | |||
| 19 | /// Interrupt handler. | ||
| 20 | pub struct BufferedInterruptHandler<T: Instance> { | ||
| 21 | _uart: PhantomData<T>, | ||
| 22 | } | ||
| 23 | |||
| 24 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> { | ||
| 25 | unsafe fn on_interrupt() { | ||
| 26 | on_interrupt(T::info().regs, T::buffered_state()) | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | /// Bidirectional buffered UART which acts as a combination of [`BufferedUartTx`] and [`BufferedUartRx`]. | ||
| 31 | pub struct BufferedUart<'d> { | ||
| 32 | rx: BufferedUartRx<'d>, | ||
| 33 | tx: BufferedUartTx<'d>, | ||
| 34 | } | ||
| 35 | |||
| 36 | impl SetConfig for BufferedUart<'_> { | ||
| 37 | type Config = Config; | ||
| 38 | type ConfigError = ConfigError; | ||
| 39 | |||
| 40 | fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { | ||
| 41 | self.set_config(config) | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | impl<'d> BufferedUart<'d> { | ||
| 46 | /// Create a new bidirectional buffered UART. | ||
| 47 | pub fn new<T: Instance>( | ||
| 48 | uart: Peri<'d, T>, | ||
| 49 | tx: Peri<'d, impl TxPin<T>>, | ||
| 50 | rx: Peri<'d, impl RxPin<T>>, | ||
| 51 | _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>, | ||
| 52 | tx_buffer: &'d mut [u8], | ||
| 53 | rx_buffer: &'d mut [u8], | ||
| 54 | config: Config, | ||
| 55 | ) -> Result<Self, ConfigError> { | ||
| 56 | Self::new_inner( | ||
| 57 | uart, | ||
| 58 | new_pin!(rx, config.rx_pf()), | ||
| 59 | new_pin!(tx, config.tx_pf()), | ||
| 60 | None, | ||
| 61 | None, | ||
| 62 | tx_buffer, | ||
| 63 | rx_buffer, | ||
| 64 | config, | ||
| 65 | ) | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Create a new bidirectional buffered UART with request-to-send and clear-to-send pins | ||
| 69 | pub fn new_with_rtscts<T: Instance>( | ||
| 70 | uart: Peri<'d, T>, | ||
| 71 | tx: Peri<'d, impl TxPin<T>>, | ||
| 72 | rx: Peri<'d, impl RxPin<T>>, | ||
| 73 | rts: Peri<'d, impl RtsPin<T>>, | ||
| 74 | cts: Peri<'d, impl CtsPin<T>>, | ||
| 75 | _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>, | ||
| 76 | tx_buffer: &'d mut [u8], | ||
| 77 | rx_buffer: &'d mut [u8], | ||
| 78 | config: Config, | ||
| 79 | ) -> Result<Self, ConfigError> { | ||
| 80 | Self::new_inner( | ||
| 81 | uart, | ||
| 82 | new_pin!(rx, config.rx_pf()), | ||
| 83 | new_pin!(tx, config.tx_pf()), | ||
| 84 | new_pin!(rts, config.rts_pf()), | ||
| 85 | new_pin!(cts, config.cts_pf()), | ||
| 86 | tx_buffer, | ||
| 87 | rx_buffer, | ||
| 88 | config, | ||
| 89 | ) | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Reconfigure the driver | ||
| 93 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | ||
| 94 | self.tx.set_config(config)?; | ||
| 95 | self.rx.set_config(config) | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Set baudrate | ||
| 99 | pub fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 100 | self.rx.set_baudrate(baudrate) | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Write to UART TX buffer, blocking execution until done. | ||
| 104 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<usize, Error> { | ||
| 105 | self.tx.blocking_write(buffer) | ||
| 106 | } | ||
| 107 | |||
| 108 | /// Flush UART TX buffer, blocking execution until done. | ||
| 109 | pub fn blocking_flush(&mut self) -> Result<(), Error> { | ||
| 110 | self.tx.blocking_flush() | ||
| 111 | } | ||
| 112 | |||
| 113 | /// Check if UART is busy. | ||
| 114 | pub fn busy(&self) -> bool { | ||
| 115 | self.tx.busy() | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Read from UART RX buffer, blocking execution until done. | ||
| 119 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 120 | self.rx.blocking_read(buffer) | ||
| 121 | } | ||
| 122 | |||
| 123 | /// Send break character. | ||
| 124 | pub fn send_break(&mut self) { | ||
| 125 | self.tx.send_break() | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Split into separate RX and TX handles. | ||
| 129 | pub fn split(self) -> (BufferedUartTx<'d>, BufferedUartRx<'d>) { | ||
| 130 | (self.tx, self.rx) | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Split into separate RX and TX handles. | ||
| 134 | pub fn split_ref(&mut self) -> (BufferedUartTx<'_>, BufferedUartRx<'_>) { | ||
| 135 | ( | ||
| 136 | BufferedUartTx { | ||
| 137 | info: self.tx.info, | ||
| 138 | state: self.tx.state, | ||
| 139 | tx: self.tx.tx.as_mut().map(Peri::reborrow), | ||
| 140 | cts: self.tx.cts.as_mut().map(Peri::reborrow), | ||
| 141 | reborrowed: true, | ||
| 142 | }, | ||
| 143 | BufferedUartRx { | ||
| 144 | info: self.rx.info, | ||
| 145 | state: self.rx.state, | ||
| 146 | rx: self.rx.rx.as_mut().map(Peri::reborrow), | ||
| 147 | rts: self.rx.rts.as_mut().map(Peri::reborrow), | ||
| 148 | reborrowed: true, | ||
| 149 | }, | ||
| 150 | ) | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | /// Rx-only buffered UART. | ||
| 155 | /// | ||
| 156 | /// Can be obtained from [`BufferedUart::split`], or can be constructed independently, | ||
| 157 | /// if you do not need the transmitting half of the driver. | ||
| 158 | pub struct BufferedUartRx<'d> { | ||
| 159 | info: &'static Info, | ||
| 160 | state: &'static BufferedState, | ||
| 161 | rx: Option<Peri<'d, AnyPin>>, | ||
| 162 | rts: Option<Peri<'d, AnyPin>>, | ||
| 163 | reborrowed: bool, | ||
| 164 | } | ||
| 165 | |||
| 166 | impl SetConfig for BufferedUartRx<'_> { | ||
| 167 | type Config = Config; | ||
| 168 | type ConfigError = ConfigError; | ||
| 169 | |||
| 170 | fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { | ||
| 171 | self.set_config(config) | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | impl<'d> BufferedUartRx<'d> { | ||
| 176 | /// Create a new rx-only buffered UART with no hardware flow control. | ||
| 177 | /// | ||
| 178 | /// Useful if you only want Uart Rx. It saves 1 pin. | ||
| 179 | pub fn new<T: Instance>( | ||
| 180 | uart: Peri<'d, T>, | ||
| 181 | rx: Peri<'d, impl RxPin<T>>, | ||
| 182 | _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>, | ||
| 183 | rx_buffer: &'d mut [u8], | ||
| 184 | config: Config, | ||
| 185 | ) -> Result<Self, ConfigError> { | ||
| 186 | Self::new_inner(uart, new_pin!(rx, config.rx_pf()), None, rx_buffer, config) | ||
| 187 | } | ||
| 188 | |||
| 189 | /// Create a new rx-only buffered UART with a request-to-send pin | ||
| 190 | pub fn new_with_rts<T: Instance>( | ||
| 191 | uart: Peri<'d, T>, | ||
| 192 | rx: Peri<'d, impl RxPin<T>>, | ||
| 193 | rts: Peri<'d, impl RtsPin<T>>, | ||
| 194 | _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>, | ||
| 195 | rx_buffer: &'d mut [u8], | ||
| 196 | config: Config, | ||
| 197 | ) -> Result<Self, ConfigError> { | ||
| 198 | Self::new_inner( | ||
| 199 | uart, | ||
| 200 | new_pin!(rx, config.rx_pf()), | ||
| 201 | new_pin!(rts, config.rts_pf()), | ||
| 202 | rx_buffer, | ||
| 203 | config, | ||
| 204 | ) | ||
| 205 | } | ||
| 206 | |||
| 207 | /// Reconfigure the driver | ||
| 208 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | ||
| 209 | if let Some(ref rx) = self.rx { | ||
| 210 | rx.update_pf(config.rx_pf()); | ||
| 211 | } | ||
| 212 | |||
| 213 | if let Some(ref rts) = self.rts { | ||
| 214 | rts.update_pf(config.rts_pf()); | ||
| 215 | } | ||
| 216 | |||
| 217 | super::reconfigure(&self.info, &self.state.state, config) | ||
| 218 | } | ||
| 219 | |||
| 220 | /// Set baudrate | ||
| 221 | pub fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 222 | super::set_baudrate(&self.info, self.state.state.clock.load(Ordering::Relaxed), baudrate) | ||
| 223 | } | ||
| 224 | |||
| 225 | /// Read from UART RX buffer, blocking execution until done. | ||
| 226 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 227 | self.blocking_read_inner(buffer) | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | impl Drop for BufferedUartRx<'_> { | ||
| 232 | fn drop(&mut self) { | ||
| 233 | if !self.reborrowed { | ||
| 234 | let state = self.state; | ||
| 235 | |||
| 236 | // SAFETY: RX is being dropped (and is not reborrowed), so the ring buffer must be deinitialized | ||
| 237 | // in order to meet the requirements of init. | ||
| 238 | unsafe { | ||
| 239 | state.rx_buf.deinit(); | ||
| 240 | } | ||
| 241 | |||
| 242 | // TX is inactive if the buffer is not available. If this is true, then disable the | ||
| 243 | // interrupt handler since we are running in RX only mode. | ||
| 244 | if state.tx_buf.len() == 0 { | ||
| 245 | self.info.interrupt.disable(); | ||
| 246 | } | ||
| 247 | |||
| 248 | self.rx.as_ref().map(|x| x.set_as_disconnected()); | ||
| 249 | self.rts.as_ref().map(|x| x.set_as_disconnected()); | ||
| 250 | } | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | /// Tx-only buffered UART. | ||
| 255 | /// | ||
| 256 | /// Can be obtained from [`BufferedUart::split`], or can be constructed independently, | ||
| 257 | /// if you do not need the receiving half of the driver. | ||
| 258 | pub struct BufferedUartTx<'d> { | ||
| 259 | info: &'static Info, | ||
| 260 | state: &'static BufferedState, | ||
| 261 | tx: Option<Peri<'d, AnyPin>>, | ||
| 262 | cts: Option<Peri<'d, AnyPin>>, | ||
| 263 | reborrowed: bool, | ||
| 264 | } | ||
| 265 | |||
| 266 | impl SetConfig for BufferedUartTx<'_> { | ||
| 267 | type Config = Config; | ||
| 268 | type ConfigError = ConfigError; | ||
| 269 | |||
| 270 | fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { | ||
| 271 | self.set_config(config) | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | impl<'d> BufferedUartTx<'d> { | ||
| 276 | /// Create a new tx-only buffered UART with no hardware flow control. | ||
| 277 | /// | ||
| 278 | /// Useful if you only want Uart Tx. It saves 1 pin. | ||
| 279 | pub fn new<T: Instance>( | ||
| 280 | uart: Peri<'d, T>, | ||
| 281 | tx: Peri<'d, impl TxPin<T>>, | ||
| 282 | _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>, | ||
| 283 | tx_buffer: &'d mut [u8], | ||
| 284 | config: Config, | ||
| 285 | ) -> Result<Self, ConfigError> { | ||
| 286 | Self::new_inner(uart, new_pin!(tx, config.tx_pf()), None, tx_buffer, config) | ||
| 287 | } | ||
| 288 | |||
| 289 | /// Create a new tx-only buffered UART with a clear-to-send pin | ||
| 290 | pub fn new_with_rts<T: Instance>( | ||
| 291 | uart: Peri<'d, T>, | ||
| 292 | tx: Peri<'d, impl TxPin<T>>, | ||
| 293 | cts: Peri<'d, impl CtsPin<T>>, | ||
| 294 | _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>, | ||
| 295 | tx_buffer: &'d mut [u8], | ||
| 296 | config: Config, | ||
| 297 | ) -> Result<Self, ConfigError> { | ||
| 298 | Self::new_inner( | ||
| 299 | uart, | ||
| 300 | new_pin!(tx, config.tx_pf()), | ||
| 301 | new_pin!(cts, config.cts_pf()), | ||
| 302 | tx_buffer, | ||
| 303 | config, | ||
| 304 | ) | ||
| 305 | } | ||
| 306 | |||
| 307 | /// Reconfigure the driver | ||
| 308 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | ||
| 309 | if let Some(ref tx) = self.tx { | ||
| 310 | tx.update_pf(config.tx_pf()); | ||
| 311 | } | ||
| 312 | |||
| 313 | if let Some(ref cts) = self.cts { | ||
| 314 | cts.update_pf(config.cts_pf()); | ||
| 315 | } | ||
| 316 | |||
| 317 | super::reconfigure(self.info, &self.state.state, config) | ||
| 318 | } | ||
| 319 | |||
| 320 | /// Set baudrate | ||
| 321 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 322 | super::set_baudrate(&self.info, self.state.state.clock.load(Ordering::Relaxed), baudrate) | ||
| 323 | } | ||
| 324 | |||
| 325 | /// Write to UART TX buffer, blocking execution until done. | ||
| 326 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<usize, Error> { | ||
| 327 | self.blocking_write_inner(buffer) | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Flush UART TX buffer, blocking execution until done. | ||
| 331 | pub fn blocking_flush(&mut self) -> Result<(), Error> { | ||
| 332 | let state = self.state; | ||
| 333 | |||
| 334 | loop { | ||
| 335 | if state.tx_buf.is_empty() { | ||
| 336 | return Ok(()); | ||
| 337 | } | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | /// Check if UART is busy. | ||
| 342 | pub fn busy(&self) -> bool { | ||
| 343 | super::busy(self.info.regs) | ||
| 344 | } | ||
| 345 | |||
| 346 | /// Send break character | ||
| 347 | pub fn send_break(&mut self) { | ||
| 348 | let r = self.info.regs; | ||
| 349 | |||
| 350 | r.lcrh().modify(|w| { | ||
| 351 | w.set_brk(true); | ||
| 352 | }); | ||
| 353 | } | ||
| 354 | } | ||
| 355 | |||
| 356 | impl Drop for BufferedUartTx<'_> { | ||
| 357 | fn drop(&mut self) { | ||
| 358 | if !self.reborrowed { | ||
| 359 | let state = self.state; | ||
| 360 | |||
| 361 | // SAFETY: TX is being dropped (and is not reborrowed), so the ring buffer must be deinitialized | ||
| 362 | // in order to meet the requirements of init. | ||
| 363 | unsafe { | ||
| 364 | state.tx_buf.deinit(); | ||
| 365 | } | ||
| 366 | |||
| 367 | // RX is inactive if the buffer is not available. If this is true, then disable the | ||
| 368 | // interrupt handler since we are running in TX only mode. | ||
| 369 | if state.rx_buf.len() == 0 { | ||
| 370 | self.info.interrupt.disable(); | ||
| 371 | } | ||
| 372 | |||
| 373 | self.tx.as_ref().map(|x| x.set_as_disconnected()); | ||
| 374 | self.cts.as_ref().map(|x| x.set_as_disconnected()); | ||
| 375 | } | ||
| 376 | } | ||
| 377 | } | ||
| 378 | |||
| 379 | impl embedded_io_async::ErrorType for BufferedUart<'_> { | ||
| 380 | type Error = Error; | ||
| 381 | } | ||
| 382 | |||
| 383 | impl embedded_io_async::ErrorType for BufferedUartRx<'_> { | ||
| 384 | type Error = Error; | ||
| 385 | } | ||
| 386 | |||
| 387 | impl embedded_io_async::ErrorType for BufferedUartTx<'_> { | ||
| 388 | type Error = Error; | ||
| 389 | } | ||
| 390 | |||
| 391 | impl embedded_io_async::Read for BufferedUart<'_> { | ||
| 392 | async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 393 | self.rx.read(buf).await | ||
| 394 | } | ||
| 395 | } | ||
| 396 | |||
| 397 | impl embedded_io_async::Read for BufferedUartRx<'_> { | ||
| 398 | async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 399 | self.read_inner(buf).await | ||
| 400 | } | ||
| 401 | } | ||
| 402 | |||
| 403 | impl embedded_io_async::ReadReady for BufferedUart<'_> { | ||
| 404 | fn read_ready(&mut self) -> Result<bool, Self::Error> { | ||
| 405 | self.rx.read_ready() | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | impl embedded_io_async::ReadReady for BufferedUartRx<'_> { | ||
| 410 | fn read_ready(&mut self) -> Result<bool, Self::Error> { | ||
| 411 | self.read_ready_inner() | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | impl embedded_io_async::BufRead for BufferedUart<'_> { | ||
| 416 | async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { | ||
| 417 | self.rx.fill_buf().await | ||
| 418 | } | ||
| 419 | |||
| 420 | fn consume(&mut self, amt: usize) { | ||
| 421 | self.rx.consume(amt); | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | impl embedded_io_async::BufRead for BufferedUartRx<'_> { | ||
| 426 | async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { | ||
| 427 | self.fill_buf_inner().await | ||
| 428 | } | ||
| 429 | |||
| 430 | fn consume(&mut self, amt: usize) { | ||
| 431 | self.consume_inner(amt); | ||
| 432 | } | ||
| 433 | } | ||
| 434 | |||
| 435 | impl embedded_io_async::Write for BufferedUart<'_> { | ||
| 436 | async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 437 | self.tx.write_inner(buf).await | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | impl embedded_io_async::Write for BufferedUartTx<'_> { | ||
| 442 | async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 443 | self.write_inner(buf).await | ||
| 444 | } | ||
| 445 | |||
| 446 | async fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 447 | self.flush_inner().await | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | impl embedded_io::Read for BufferedUart<'_> { | ||
| 452 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 453 | self.rx.read(buf) | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 457 | impl embedded_io::Read for BufferedUartRx<'_> { | ||
| 458 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 459 | self.blocking_read_inner(buf) | ||
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | impl embedded_io::Write for BufferedUart<'_> { | ||
| 464 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 465 | self.tx.write(buf) | ||
| 466 | } | ||
| 467 | |||
| 468 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 469 | self.tx.flush() | ||
| 470 | } | ||
| 471 | } | ||
| 472 | |||
| 473 | impl embedded_io::Write for BufferedUartTx<'_> { | ||
| 474 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 475 | self.blocking_write_inner(buf) | ||
| 476 | } | ||
| 477 | |||
| 478 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 479 | self.blocking_flush() | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | impl embedded_hal_nb::serial::Error for Error { | ||
| 484 | fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { | ||
| 485 | match self { | ||
| 486 | Error::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, | ||
| 487 | Error::Noise => embedded_hal_nb::serial::ErrorKind::Noise, | ||
| 488 | Error::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, | ||
| 489 | Error::Parity => embedded_hal_nb::serial::ErrorKind::Parity, | ||
| 490 | Error::Break => embedded_hal_nb::serial::ErrorKind::Other, | ||
| 491 | } | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 495 | impl embedded_hal_nb::serial::ErrorType for BufferedUart<'_> { | ||
| 496 | type Error = Error; | ||
| 497 | } | ||
| 498 | |||
| 499 | impl embedded_hal_nb::serial::ErrorType for BufferedUartRx<'_> { | ||
| 500 | type Error = Error; | ||
| 501 | } | ||
| 502 | |||
| 503 | impl embedded_hal_nb::serial::ErrorType for BufferedUartTx<'_> { | ||
| 504 | type Error = Error; | ||
| 505 | } | ||
| 506 | |||
| 507 | impl embedded_hal_nb::serial::Read for BufferedUart<'_> { | ||
| 508 | fn read(&mut self) -> nb::Result<u8, Self::Error> { | ||
| 509 | self.rx.read() | ||
| 510 | } | ||
| 511 | } | ||
| 512 | |||
| 513 | impl embedded_hal_nb::serial::Read for BufferedUartRx<'_> { | ||
| 514 | fn read(&mut self) -> nb::Result<u8, Self::Error> { | ||
| 515 | if self.info.regs.stat().read().rxfe() { | ||
| 516 | return Err(nb::Error::WouldBlock); | ||
| 517 | } | ||
| 518 | |||
| 519 | super::read_with_error(self.info.regs).map_err(nb::Error::Other) | ||
| 520 | } | ||
| 521 | } | ||
| 522 | |||
| 523 | impl embedded_hal_nb::serial::Write for BufferedUart<'_> { | ||
| 524 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { | ||
| 525 | self.tx.write(word) | ||
| 526 | } | ||
| 527 | |||
| 528 | fn flush(&mut self) -> nb::Result<(), Self::Error> { | ||
| 529 | self.tx.flush() | ||
| 530 | } | ||
| 531 | } | ||
| 532 | |||
| 533 | impl embedded_hal_nb::serial::Write for BufferedUartTx<'_> { | ||
| 534 | fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { | ||
| 535 | self.blocking_write(&[word]).map(drop).map_err(nb::Error::Other) | ||
| 536 | } | ||
| 537 | |||
| 538 | fn flush(&mut self) -> nb::Result<(), Self::Error> { | ||
| 539 | self.blocking_flush().map_err(nb::Error::Other) | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | // Impl details | ||
| 544 | |||
| 545 | /// Buffered UART state. | ||
| 546 | pub(crate) struct BufferedState { | ||
| 547 | /// non-buffered UART state. This is inline in order to avoid [`BufferedUartRx`]/Tx | ||
| 548 | /// needing to carry around a 2nd static reference and waste another 4 bytes. | ||
| 549 | state: State, | ||
| 550 | rx_waker: AtomicWaker, | ||
| 551 | rx_buf: RingBuffer, | ||
| 552 | tx_waker: AtomicWaker, | ||
| 553 | tx_buf: RingBuffer, | ||
| 554 | rx_error: AtomicU8, | ||
| 555 | } | ||
| 556 | |||
| 557 | // these must match bits 8..12 in RXDATA, but shifted by 8 to the right | ||
| 558 | const RXE_NOISE: u8 = 16; | ||
| 559 | const RXE_OVERRUN: u8 = 8; | ||
| 560 | const RXE_BREAK: u8 = 4; | ||
| 561 | const RXE_PARITY: u8 = 2; | ||
| 562 | const RXE_FRAMING: u8 = 1; | ||
| 563 | |||
| 564 | impl BufferedState { | ||
| 565 | pub const fn new() -> Self { | ||
| 566 | Self { | ||
| 567 | state: State::new(), | ||
| 568 | rx_waker: AtomicWaker::new(), | ||
| 569 | rx_buf: RingBuffer::new(), | ||
| 570 | tx_waker: AtomicWaker::new(), | ||
| 571 | tx_buf: RingBuffer::new(), | ||
| 572 | rx_error: AtomicU8::new(0), | ||
| 573 | } | ||
| 574 | } | ||
| 575 | } | ||
| 576 | |||
| 577 | impl<'d> BufferedUart<'d> { | ||
| 578 | fn new_inner<T: Instance>( | ||
| 579 | _peri: Peri<'d, T>, | ||
| 580 | rx: Option<Peri<'d, AnyPin>>, | ||
| 581 | tx: Option<Peri<'d, AnyPin>>, | ||
| 582 | rts: Option<Peri<'d, AnyPin>>, | ||
| 583 | cts: Option<Peri<'d, AnyPin>>, | ||
| 584 | tx_buffer: &'d mut [u8], | ||
| 585 | rx_buffer: &'d mut [u8], | ||
| 586 | config: Config, | ||
| 587 | ) -> Result<Self, ConfigError> { | ||
| 588 | let info = T::info(); | ||
| 589 | let state = T::buffered_state(); | ||
| 590 | |||
| 591 | let mut this = Self { | ||
| 592 | tx: BufferedUartTx { | ||
| 593 | info, | ||
| 594 | state, | ||
| 595 | tx, | ||
| 596 | cts, | ||
| 597 | reborrowed: false, | ||
| 598 | }, | ||
| 599 | rx: BufferedUartRx { | ||
| 600 | info, | ||
| 601 | state, | ||
| 602 | rx, | ||
| 603 | rts, | ||
| 604 | reborrowed: false, | ||
| 605 | }, | ||
| 606 | }; | ||
| 607 | this.enable_and_configure(tx_buffer, rx_buffer, &config)?; | ||
| 608 | |||
| 609 | Ok(this) | ||
| 610 | } | ||
| 611 | |||
| 612 | fn enable_and_configure( | ||
| 613 | &mut self, | ||
| 614 | tx_buffer: &'d mut [u8], | ||
| 615 | rx_buffer: &'d mut [u8], | ||
| 616 | config: &Config, | ||
| 617 | ) -> Result<(), ConfigError> { | ||
| 618 | let info = self.rx.info; | ||
| 619 | let state = self.rx.state; | ||
| 620 | |||
| 621 | assert!(!tx_buffer.is_empty()); | ||
| 622 | assert!(!rx_buffer.is_empty()); | ||
| 623 | |||
| 624 | init_buffers(info, state, Some(tx_buffer), Some(rx_buffer)); | ||
| 625 | super::enable(info.regs); | ||
| 626 | super::configure( | ||
| 627 | info, | ||
| 628 | &state.state, | ||
| 629 | config, | ||
| 630 | true, | ||
| 631 | self.rx.rts.is_some(), | ||
| 632 | true, | ||
| 633 | self.tx.cts.is_some(), | ||
| 634 | )?; | ||
| 635 | |||
| 636 | info.interrupt.unpend(); | ||
| 637 | unsafe { info.interrupt.enable() }; | ||
| 638 | |||
| 639 | Ok(()) | ||
| 640 | } | ||
| 641 | } | ||
| 642 | |||
| 643 | impl<'d> BufferedUartRx<'d> { | ||
| 644 | fn new_inner<T: Instance>( | ||
| 645 | _peri: Peri<'d, T>, | ||
| 646 | rx: Option<Peri<'d, AnyPin>>, | ||
| 647 | rts: Option<Peri<'d, AnyPin>>, | ||
| 648 | rx_buffer: &'d mut [u8], | ||
| 649 | config: Config, | ||
| 650 | ) -> Result<Self, ConfigError> { | ||
| 651 | let mut this = Self { | ||
| 652 | info: T::info(), | ||
| 653 | state: T::buffered_state(), | ||
| 654 | rx, | ||
| 655 | rts, | ||
| 656 | reborrowed: false, | ||
| 657 | }; | ||
| 658 | this.enable_and_configure(rx_buffer, &config)?; | ||
| 659 | |||
| 660 | Ok(this) | ||
| 661 | } | ||
| 662 | |||
| 663 | fn enable_and_configure(&mut self, rx_buffer: &'d mut [u8], config: &Config) -> Result<(), ConfigError> { | ||
| 664 | let info = self.info; | ||
| 665 | let state = self.state; | ||
| 666 | |||
| 667 | init_buffers(info, state, None, Some(rx_buffer)); | ||
| 668 | super::enable(info.regs); | ||
| 669 | super::configure(info, &self.state.state, config, true, self.rts.is_some(), false, false)?; | ||
| 670 | |||
| 671 | info.interrupt.unpend(); | ||
| 672 | unsafe { info.interrupt.enable() }; | ||
| 673 | |||
| 674 | Ok(()) | ||
| 675 | } | ||
| 676 | |||
| 677 | async fn read_inner(&self, buf: &mut [u8]) -> Result<usize, Error> { | ||
| 678 | poll_fn(move |cx| { | ||
| 679 | let state = self.state; | ||
| 680 | |||
| 681 | if let Poll::Ready(r) = self.try_read(buf) { | ||
| 682 | return Poll::Ready(r); | ||
| 683 | } | ||
| 684 | |||
| 685 | state.rx_waker.register(cx.waker()); | ||
| 686 | Poll::Pending | ||
| 687 | }) | ||
| 688 | .await | ||
| 689 | } | ||
| 690 | |||
| 691 | fn blocking_read_inner(&self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 692 | loop { | ||
| 693 | match self.try_read(buffer) { | ||
| 694 | Poll::Ready(res) => return res, | ||
| 695 | Poll::Pending => continue, | ||
| 696 | } | ||
| 697 | } | ||
| 698 | } | ||
| 699 | |||
| 700 | fn fill_buf_inner(&self) -> impl Future<Output = Result<&'_ [u8], Error>> { | ||
| 701 | poll_fn(move |cx| { | ||
| 702 | let mut rx_reader = unsafe { self.state.rx_buf.reader() }; | ||
| 703 | let (p, n) = rx_reader.pop_buf(); | ||
| 704 | let result = if n == 0 { | ||
| 705 | match Self::get_rx_error(self.state) { | ||
| 706 | None => { | ||
| 707 | self.state.rx_waker.register(cx.waker()); | ||
| 708 | return Poll::Pending; | ||
| 709 | } | ||
| 710 | Some(e) => Err(e), | ||
| 711 | } | ||
| 712 | } else { | ||
| 713 | let buf = unsafe { slice::from_raw_parts(p, n) }; | ||
| 714 | Ok(buf) | ||
| 715 | }; | ||
| 716 | |||
| 717 | Poll::Ready(result) | ||
| 718 | }) | ||
| 719 | } | ||
| 720 | |||
| 721 | fn consume_inner(&self, amt: usize) { | ||
| 722 | let mut rx_reader = unsafe { self.state.rx_buf.reader() }; | ||
| 723 | rx_reader.pop_done(amt); | ||
| 724 | |||
| 725 | // (Re-)Enable the interrupt to receive more data in case it was | ||
| 726 | // disabled because the buffer was full or errors were detected. | ||
| 727 | self.info.regs.cpu_int(0).imask().modify(|w| { | ||
| 728 | w.set_rxint(true); | ||
| 729 | w.set_rtout(true); | ||
| 730 | }); | ||
| 731 | } | ||
| 732 | |||
| 733 | /// we are ready to read if there is data in the buffer | ||
| 734 | fn read_ready_inner(&self) -> Result<bool, Error> { | ||
| 735 | Ok(!self.state.rx_buf.is_empty()) | ||
| 736 | } | ||
| 737 | |||
| 738 | fn try_read(&self, buf: &mut [u8]) -> Poll<Result<usize, Error>> { | ||
| 739 | let state = self.state; | ||
| 740 | |||
| 741 | if buf.is_empty() { | ||
| 742 | return Poll::Ready(Ok(0)); | ||
| 743 | } | ||
| 744 | |||
| 745 | let mut rx_reader = unsafe { state.rx_buf.reader() }; | ||
| 746 | let n = rx_reader.pop(|data| { | ||
| 747 | let n = data.len().min(buf.len()); | ||
| 748 | buf[..n].copy_from_slice(&data[..n]); | ||
| 749 | n | ||
| 750 | }); | ||
| 751 | |||
| 752 | let result = if n == 0 { | ||
| 753 | match Self::get_rx_error(state) { | ||
| 754 | None => return Poll::Pending, | ||
| 755 | Some(e) => Err(e), | ||
| 756 | } | ||
| 757 | } else { | ||
| 758 | Ok(n) | ||
| 759 | }; | ||
| 760 | |||
| 761 | // (Re-)Enable the interrupt to receive more data in case it was | ||
| 762 | // disabled because the buffer was full or errors were detected. | ||
| 763 | self.info.regs.cpu_int(0).imask().modify(|w| { | ||
| 764 | w.set_rxint(true); | ||
| 765 | w.set_rtout(true); | ||
| 766 | }); | ||
| 767 | |||
| 768 | Poll::Ready(result) | ||
| 769 | } | ||
| 770 | |||
| 771 | fn get_rx_error(state: &BufferedState) -> Option<Error> { | ||
| 772 | // Cortex-M0 has does not support atomic swap, so we must do two operations. | ||
| 773 | let errs = critical_section::with(|_cs| { | ||
| 774 | let errs = state.rx_error.load(Ordering::Relaxed); | ||
| 775 | state.rx_error.store(0, Ordering::Relaxed); | ||
| 776 | |||
| 777 | errs | ||
| 778 | }); | ||
| 779 | |||
| 780 | if errs & RXE_NOISE != 0 { | ||
| 781 | Some(Error::Noise) | ||
| 782 | } else if errs & RXE_OVERRUN != 0 { | ||
| 783 | Some(Error::Overrun) | ||
| 784 | } else if errs & RXE_BREAK != 0 { | ||
| 785 | Some(Error::Break) | ||
| 786 | } else if errs & RXE_PARITY != 0 { | ||
| 787 | Some(Error::Parity) | ||
| 788 | } else if errs & RXE_FRAMING != 0 { | ||
| 789 | Some(Error::Framing) | ||
| 790 | } else { | ||
| 791 | None | ||
| 792 | } | ||
| 793 | } | ||
| 794 | } | ||
| 795 | |||
| 796 | impl<'d> BufferedUartTx<'d> { | ||
| 797 | fn new_inner<T: Instance>( | ||
| 798 | _peri: Peri<'d, T>, | ||
| 799 | tx: Option<Peri<'d, AnyPin>>, | ||
| 800 | cts: Option<Peri<'d, AnyPin>>, | ||
| 801 | tx_buffer: &'d mut [u8], | ||
| 802 | config: Config, | ||
| 803 | ) -> Result<Self, ConfigError> { | ||
| 804 | let mut this = Self { | ||
| 805 | info: T::info(), | ||
| 806 | state: T::buffered_state(), | ||
| 807 | tx, | ||
| 808 | cts, | ||
| 809 | reborrowed: false, | ||
| 810 | }; | ||
| 811 | |||
| 812 | this.enable_and_configure(tx_buffer, &config)?; | ||
| 813 | |||
| 814 | Ok(this) | ||
| 815 | } | ||
| 816 | |||
| 817 | async fn write_inner(&self, buf: &[u8]) -> Result<usize, Error> { | ||
| 818 | poll_fn(move |cx| { | ||
| 819 | let state = self.state; | ||
| 820 | |||
| 821 | if buf.is_empty() { | ||
| 822 | return Poll::Ready(Ok(0)); | ||
| 823 | } | ||
| 824 | |||
| 825 | let mut tx_writer = unsafe { state.tx_buf.writer() }; | ||
| 826 | let n = tx_writer.push(|data| { | ||
| 827 | let n = data.len().min(buf.len()); | ||
| 828 | data[..n].copy_from_slice(&buf[..n]); | ||
| 829 | n | ||
| 830 | }); | ||
| 831 | |||
| 832 | if n == 0 { | ||
| 833 | state.tx_waker.register(cx.waker()); | ||
| 834 | return Poll::Pending; | ||
| 835 | } | ||
| 836 | |||
| 837 | // The TX interrupt only triggers when the there was data in the | ||
| 838 | // FIFO and the number of bytes drops below a threshold. When the | ||
| 839 | // FIFO was empty we have to manually pend the interrupt to shovel | ||
| 840 | // TX data from the buffer into the FIFO. | ||
| 841 | self.info.interrupt.pend(); | ||
| 842 | Poll::Ready(Ok(n)) | ||
| 843 | }) | ||
| 844 | .await | ||
| 845 | } | ||
| 846 | |||
| 847 | fn blocking_write_inner(&self, buffer: &[u8]) -> Result<usize, Error> { | ||
| 848 | let state = self.state; | ||
| 849 | |||
| 850 | loop { | ||
| 851 | let empty = state.tx_buf.is_empty(); | ||
| 852 | |||
| 853 | // SAFETY: tx buf must be initialized if BufferedUartTx exists. | ||
| 854 | let mut tx_writer = unsafe { state.tx_buf.writer() }; | ||
| 855 | let data = tx_writer.push_slice(); | ||
| 856 | |||
| 857 | if !data.is_empty() { | ||
| 858 | let n = data.len().min(buffer.len()); | ||
| 859 | data[..n].copy_from_slice(&buffer[..n]); | ||
| 860 | tx_writer.push_done(n); | ||
| 861 | |||
| 862 | if empty { | ||
| 863 | self.info.interrupt.pend(); | ||
| 864 | } | ||
| 865 | |||
| 866 | return Ok(n); | ||
| 867 | } | ||
| 868 | } | ||
| 869 | } | ||
| 870 | |||
| 871 | async fn flush_inner(&self) -> Result<(), Error> { | ||
| 872 | poll_fn(move |cx| { | ||
| 873 | let state = self.state; | ||
| 874 | |||
| 875 | if !state.tx_buf.is_empty() { | ||
| 876 | state.tx_waker.register(cx.waker()); | ||
| 877 | return Poll::Pending; | ||
| 878 | } | ||
| 879 | |||
| 880 | Poll::Ready(Ok(())) | ||
| 881 | }) | ||
| 882 | .await | ||
| 883 | } | ||
| 884 | |||
| 885 | fn enable_and_configure(&mut self, tx_buffer: &'d mut [u8], config: &Config) -> Result<(), ConfigError> { | ||
| 886 | let info = self.info; | ||
| 887 | let state = self.state; | ||
| 888 | |||
| 889 | init_buffers(info, state, Some(tx_buffer), None); | ||
| 890 | super::enable(info.regs); | ||
| 891 | super::configure(info, &state.state, config, false, false, true, self.cts.is_some())?; | ||
| 892 | |||
| 893 | info.interrupt.unpend(); | ||
| 894 | unsafe { info.interrupt.enable() }; | ||
| 895 | |||
| 896 | Ok(()) | ||
| 897 | } | ||
| 898 | } | ||
| 899 | |||
| 900 | fn init_buffers<'d>( | ||
| 901 | info: &Info, | ||
| 902 | state: &BufferedState, | ||
| 903 | tx_buffer: Option<&'d mut [u8]>, | ||
| 904 | rx_buffer: Option<&'d mut [u8]>, | ||
| 905 | ) { | ||
| 906 | if let Some(tx_buffer) = tx_buffer { | ||
| 907 | let len = tx_buffer.len(); | ||
| 908 | unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; | ||
| 909 | } | ||
| 910 | |||
| 911 | if let Some(rx_buffer) = rx_buffer { | ||
| 912 | let len = rx_buffer.len(); | ||
| 913 | unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; | ||
| 914 | } | ||
| 915 | |||
| 916 | info.regs.cpu_int(0).imask().modify(|w| { | ||
| 917 | w.set_nerr(true); | ||
| 918 | w.set_frmerr(true); | ||
| 919 | w.set_parerr(true); | ||
| 920 | w.set_brkerr(true); | ||
| 921 | w.set_ovrerr(true); | ||
| 922 | }); | ||
| 923 | } | ||
| 924 | |||
| 925 | fn on_interrupt(r: Regs, state: &'static BufferedState) { | ||
| 926 | let int = r.cpu_int(0).mis().read(); | ||
| 927 | |||
| 928 | // Per https://github.com/embassy-rs/embassy/pull/1458, both buffered and unbuffered handlers may be bound. | ||
| 929 | if super::dma_enabled(r) { | ||
| 930 | return; | ||
| 931 | } | ||
| 932 | |||
| 933 | // RX | ||
| 934 | if state.rx_buf.is_available() { | ||
| 935 | // SAFETY: RX must have been initialized if RXE is set. | ||
| 936 | let mut rx_writer = unsafe { state.rx_buf.writer() }; | ||
| 937 | let rx_buf = rx_writer.push_slice(); | ||
| 938 | let mut n_read = 0; | ||
| 939 | let mut error = false; | ||
| 940 | |||
| 941 | for rx_byte in rx_buf { | ||
| 942 | let stat = r.stat().read(); | ||
| 943 | |||
| 944 | if stat.rxfe() { | ||
| 945 | break; | ||
| 946 | } | ||
| 947 | |||
| 948 | let data = r.rxdata().read(); | ||
| 949 | |||
| 950 | if (data.0 >> 8) != 0 { | ||
| 951 | // Cortex-M0 does not support atomic fetch_or, must do 2 operations. | ||
| 952 | critical_section::with(|_cs| { | ||
| 953 | let mut value = state.rx_error.load(Ordering::Relaxed); | ||
| 954 | value |= (data.0 >> 8) as u8; | ||
| 955 | state.rx_error.store(value, Ordering::Relaxed); | ||
| 956 | }); | ||
| 957 | error = true; | ||
| 958 | |||
| 959 | // only fill the buffer with valid characters. the current character is fine | ||
| 960 | // if the error is an overrun, but if we add it to the buffer we'll report | ||
| 961 | // the overrun one character too late. drop it instead and pretend we were | ||
| 962 | // a bit slower at draining the rx fifo than we actually were. | ||
| 963 | // this is consistent with blocking uart error reporting. | ||
| 964 | break; | ||
| 965 | } | ||
| 966 | |||
| 967 | *rx_byte = data.data(); | ||
| 968 | n_read += 1; | ||
| 969 | } | ||
| 970 | |||
| 971 | if n_read > 0 { | ||
| 972 | rx_writer.push_done(n_read); | ||
| 973 | state.rx_waker.wake(); | ||
| 974 | } else if error { | ||
| 975 | state.rx_waker.wake(); | ||
| 976 | } | ||
| 977 | |||
| 978 | // Disable any further RX interrupts when the buffer becomes full or | ||
| 979 | // errors have occurred. This lets us buffer additional errors in the | ||
| 980 | // fifo without needing more error storage locations, and most applications | ||
| 981 | // will want to do a full reset of their uart state anyway once an error | ||
| 982 | // has happened. | ||
| 983 | if state.rx_buf.is_full() || error { | ||
| 984 | r.cpu_int(0).imask().modify(|w| { | ||
| 985 | w.set_rxint(false); | ||
| 986 | w.set_rtout(false); | ||
| 987 | }); | ||
| 988 | } | ||
| 989 | } | ||
| 990 | |||
| 991 | if int.eot() { | ||
| 992 | r.cpu_int(0).imask().modify(|w| { | ||
| 993 | w.set_eot(false); | ||
| 994 | }); | ||
| 995 | |||
| 996 | r.cpu_int(0).iclr().write(|w| { | ||
| 997 | w.set_eot(true); | ||
| 998 | }); | ||
| 999 | |||
| 1000 | state.tx_waker.wake(); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | // TX | ||
| 1004 | if state.tx_buf.is_available() { | ||
| 1005 | // SAFETY: TX must have been initialized if TXE is set. | ||
| 1006 | let mut tx_reader = unsafe { state.tx_buf.reader() }; | ||
| 1007 | let buf = tx_reader.pop_slice(); | ||
| 1008 | let mut n_written = 0; | ||
| 1009 | |||
| 1010 | for tx_byte in buf.iter_mut() { | ||
| 1011 | let stat = r.stat().read(); | ||
| 1012 | |||
| 1013 | if stat.txff() { | ||
| 1014 | break; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | r.txdata().write(|w| { | ||
| 1018 | w.set_data(*tx_byte); | ||
| 1019 | }); | ||
| 1020 | n_written += 1; | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | if n_written > 0 { | ||
| 1024 | // EOT will wake. | ||
| 1025 | r.cpu_int(0).imask().modify(|w| { | ||
| 1026 | w.set_eot(true); | ||
| 1027 | }); | ||
| 1028 | |||
| 1029 | tx_reader.pop_done(n_written); | ||
| 1030 | } | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | // Clear TX and error interrupt flags | ||
| 1034 | // RX interrupt flags are cleared by writing to ICLR. | ||
| 1035 | let mis = r.cpu_int(0).mis().read(); | ||
| 1036 | r.cpu_int(0).iclr().write(|w| { | ||
| 1037 | w.set_nerr(mis.nerr()); | ||
| 1038 | w.set_frmerr(mis.frmerr()); | ||
| 1039 | w.set_parerr(mis.parerr()); | ||
| 1040 | w.set_brkerr(mis.brkerr()); | ||
| 1041 | w.set_ovrerr(mis.ovrerr()); | ||
| 1042 | }); | ||
| 1043 | |||
| 1044 | // Errors | ||
| 1045 | if mis.nerr() { | ||
| 1046 | warn!("Noise error"); | ||
| 1047 | } | ||
| 1048 | if mis.frmerr() { | ||
| 1049 | warn!("Framing error"); | ||
| 1050 | } | ||
| 1051 | if mis.parerr() { | ||
| 1052 | warn!("Parity error"); | ||
| 1053 | } | ||
| 1054 | if mis.brkerr() { | ||
| 1055 | warn!("Break error"); | ||
| 1056 | } | ||
| 1057 | if mis.ovrerr() { | ||
| 1058 | warn!("Overrun error"); | ||
| 1059 | } | ||
| 1060 | } | ||
diff --git a/embassy-mspm0/src/uart.rs b/embassy-mspm0/src/uart/mod.rs index bc1d2e343..6599cea06 100644 --- a/embassy-mspm0/src/uart.rs +++ b/embassy-mspm0/src/uart/mod.rs | |||
| @@ -1,8 +1,11 @@ | |||
| 1 | #![macro_use] | 1 | #![macro_use] |
| 2 | 2 | ||
| 3 | mod buffered; | ||
| 4 | |||
| 3 | use core::marker::PhantomData; | 5 | use core::marker::PhantomData; |
| 4 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; | 6 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; |
| 5 | 7 | ||
| 8 | pub use buffered::*; | ||
| 6 | use embassy_embedded_hal::SetConfig; | 9 | use embassy_embedded_hal::SetConfig; |
| 7 | use embassy_hal_internal::PeripheralType; | 10 | use embassy_hal_internal::PeripheralType; |
| 8 | 11 | ||
| @@ -128,8 +131,8 @@ pub struct Config { | |||
| 128 | // pub manchester: bool, | 131 | // pub manchester: bool, |
| 129 | 132 | ||
| 130 | // TODO: majority voting | 133 | // TODO: majority voting |
| 131 | 134 | /// If true: the built-in FIFO is enabled. | |
| 132 | // TODO: fifo level select - need power domain info in metapac | 135 | pub fifo_enable: bool, |
| 133 | 136 | ||
| 134 | // TODO: glitch suppression | 137 | // TODO: glitch suppression |
| 135 | /// If true: invert TX pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle). | 138 | /// If true: invert TX pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle). |
| @@ -169,6 +172,7 @@ impl Default for Config { | |||
| 169 | msb_order: BitOrder::LsbFirst, | 172 | msb_order: BitOrder::LsbFirst, |
| 170 | loop_back_enable: false, | 173 | loop_back_enable: false, |
| 171 | // manchester: false, | 174 | // manchester: false, |
| 175 | fifo_enable: false, | ||
| 172 | invert_tx: false, | 176 | invert_tx: false, |
| 173 | invert_rx: false, | 177 | invert_rx: false, |
| 174 | invert_rts: false, | 178 | invert_rts: false, |
| @@ -185,7 +189,7 @@ impl Default for Config { | |||
| 185 | /// | 189 | /// |
| 186 | /// ### Notes on [`embedded_io::Read`] | 190 | /// ### Notes on [`embedded_io::Read`] |
| 187 | /// | 191 | /// |
| 188 | /// `embedded_io::Read` requires guarantees that the base [`UartRx`] cannot provide. | 192 | /// [`embedded_io::Read`] requires guarantees that the base [`UartRx`] cannot provide. |
| 189 | /// | 193 | /// |
| 190 | /// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`] | 194 | /// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`] |
| 191 | /// as alternatives that do provide the necessary guarantees for `embedded_io::Read`. | 195 | /// as alternatives that do provide the necessary guarantees for `embedded_io::Read`. |
| @@ -199,8 +203,7 @@ impl<'d, M: Mode> SetConfig for Uart<'d, M> { | |||
| 199 | type ConfigError = ConfigError; | 203 | type ConfigError = ConfigError; |
| 200 | 204 | ||
| 201 | fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { | 205 | fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { |
| 202 | self.tx.set_config(config)?; | 206 | self.set_config(config) |
| 203 | self.rx.set_config(config) | ||
| 204 | } | 207 | } |
| 205 | } | 208 | } |
| 206 | 209 | ||
| @@ -236,6 +239,12 @@ impl core::fmt::Display for Error { | |||
| 236 | 239 | ||
| 237 | impl core::error::Error for Error {} | 240 | impl core::error::Error for Error {} |
| 238 | 241 | ||
| 242 | impl embedded_io::Error for Error { | ||
| 243 | fn kind(&self) -> embedded_io::ErrorKind { | ||
| 244 | embedded_io::ErrorKind::Other | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 239 | /// Rx-only UART Driver. | 248 | /// Rx-only UART Driver. |
| 240 | /// | 249 | /// |
| 241 | /// Can be obtained from [`Uart::split`], or can be constructed independently, | 250 | /// Can be obtained from [`Uart::split`], or can be constructed independently, |
| @@ -260,7 +269,7 @@ impl<'d, M: Mode> SetConfig for UartRx<'d, M> { | |||
| 260 | impl<'d> UartRx<'d, Blocking> { | 269 | impl<'d> UartRx<'d, Blocking> { |
| 261 | /// Create a new rx-only UART with no hardware flow control. | 270 | /// Create a new rx-only UART with no hardware flow control. |
| 262 | /// | 271 | /// |
| 263 | /// Useful if you only want Uart Rx. It saves 1 pin . | 272 | /// Useful if you only want Uart Rx. It saves 1 pin. |
| 264 | pub fn new_blocking<T: Instance>( | 273 | pub fn new_blocking<T: Instance>( |
| 265 | peri: Peri<'d, T>, | 274 | peri: Peri<'d, T>, |
| 266 | rx: Peri<'d, impl RxPin<T>>, | 275 | rx: Peri<'d, impl RxPin<T>>, |
| @@ -286,19 +295,6 @@ impl<'d> UartRx<'d, Blocking> { | |||
| 286 | } | 295 | } |
| 287 | 296 | ||
| 288 | impl<'d, M: Mode> UartRx<'d, M> { | 297 | impl<'d, M: Mode> UartRx<'d, M> { |
| 289 | /// Reconfigure the driver | ||
| 290 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | ||
| 291 | if let Some(ref rx) = self.rx { | ||
| 292 | rx.update_pf(config.rx_pf()); | ||
| 293 | } | ||
| 294 | |||
| 295 | if let Some(ref rts) = self.rts { | ||
| 296 | rts.update_pf(config.rts_pf()); | ||
| 297 | } | ||
| 298 | |||
| 299 | reconfigure(self.info, self.state, config) | ||
| 300 | } | ||
| 301 | |||
| 302 | /// Perform a blocking read into `buffer` | 298 | /// Perform a blocking read into `buffer` |
| 303 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 299 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 304 | let r = self.info.regs; | 300 | let r = self.info.regs; |
| @@ -315,6 +311,19 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 315 | Ok(()) | 311 | Ok(()) |
| 316 | } | 312 | } |
| 317 | 313 | ||
| 314 | /// Reconfigure the driver | ||
| 315 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | ||
| 316 | if let Some(ref rx) = self.rx { | ||
| 317 | rx.update_pf(config.rx_pf()); | ||
| 318 | } | ||
| 319 | |||
| 320 | if let Some(ref rts) = self.rts { | ||
| 321 | rts.update_pf(config.rts_pf()); | ||
| 322 | } | ||
| 323 | |||
| 324 | reconfigure(self.info, self.state, config) | ||
| 325 | } | ||
| 326 | |||
| 318 | /// Set baudrate | 327 | /// Set baudrate |
| 319 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | 328 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { |
| 320 | set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) | 329 | set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) |
| @@ -378,19 +387,6 @@ impl<'d> UartTx<'d, Blocking> { | |||
| 378 | } | 387 | } |
| 379 | 388 | ||
| 380 | impl<'d, M: Mode> UartTx<'d, M> { | 389 | impl<'d, M: Mode> UartTx<'d, M> { |
| 381 | /// Reconfigure the driver | ||
| 382 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | ||
| 383 | if let Some(ref tx) = self.tx { | ||
| 384 | tx.update_pf(config.tx_pf()); | ||
| 385 | } | ||
| 386 | |||
| 387 | if let Some(ref cts) = self.cts { | ||
| 388 | cts.update_pf(config.cts_pf()); | ||
| 389 | } | ||
| 390 | |||
| 391 | reconfigure(self.info, self.state, config) | ||
| 392 | } | ||
| 393 | |||
| 394 | /// Perform a blocking UART write | 390 | /// Perform a blocking UART write |
| 395 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 391 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 396 | let r = self.info.regs; | 392 | let r = self.info.regs; |
| @@ -427,6 +423,24 @@ impl<'d, M: Mode> UartTx<'d, M> { | |||
| 427 | }); | 423 | }); |
| 428 | } | 424 | } |
| 429 | 425 | ||
| 426 | /// Check if UART is busy. | ||
| 427 | pub fn busy(&self) -> bool { | ||
| 428 | busy(self.info.regs) | ||
| 429 | } | ||
| 430 | |||
| 431 | /// Reconfigure the driver | ||
| 432 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | ||
| 433 | if let Some(ref tx) = self.tx { | ||
| 434 | tx.update_pf(config.tx_pf()); | ||
| 435 | } | ||
| 436 | |||
| 437 | if let Some(ref cts) = self.cts { | ||
| 438 | cts.update_pf(config.cts_pf()); | ||
| 439 | } | ||
| 440 | |||
| 441 | reconfigure(self.info, self.state, config) | ||
| 442 | } | ||
| 443 | |||
| 430 | /// Set baudrate | 444 | /// Set baudrate |
| 431 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | 445 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { |
| 432 | set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) | 446 | set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) |
| @@ -489,6 +503,11 @@ impl<'d, M: Mode> Uart<'d, M> { | |||
| 489 | self.tx.blocking_flush() | 503 | self.tx.blocking_flush() |
| 490 | } | 504 | } |
| 491 | 505 | ||
| 506 | /// Check if UART is busy. | ||
| 507 | pub fn busy(&self) -> bool { | ||
| 508 | self.tx.busy() | ||
| 509 | } | ||
| 510 | |||
| 492 | /// Perform a blocking read into `buffer` | 511 | /// Perform a blocking read into `buffer` |
| 493 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 512 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 494 | self.rx.blocking_read(buffer) | 513 | self.rx.blocking_read(buffer) |
| @@ -508,6 +527,12 @@ impl<'d, M: Mode> Uart<'d, M> { | |||
| 508 | (&mut self.tx, &mut self.rx) | 527 | (&mut self.tx, &mut self.rx) |
| 509 | } | 528 | } |
| 510 | 529 | ||
| 530 | /// Reconfigure the driver | ||
| 531 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | ||
| 532 | self.tx.set_config(config)?; | ||
| 533 | self.rx.set_config(config) | ||
| 534 | } | ||
| 535 | |||
| 511 | /// Send break character | 536 | /// Send break character |
| 512 | pub fn send_break(&self) { | 537 | pub fn send_break(&self) { |
| 513 | self.tx.send_break(); | 538 | self.tx.send_break(); |
| @@ -557,8 +582,16 @@ pub(crate) struct Info { | |||
| 557 | } | 582 | } |
| 558 | 583 | ||
| 559 | pub(crate) struct State { | 584 | pub(crate) struct State { |
| 560 | /// The clock rate of the UART. This might be configured. | 585 | /// The clock rate of the UART in Hz. |
| 561 | pub(crate) clock: AtomicU32, | 586 | clock: AtomicU32, |
| 587 | } | ||
| 588 | |||
| 589 | impl State { | ||
| 590 | pub const fn new() -> Self { | ||
| 591 | Self { | ||
| 592 | clock: AtomicU32::new(0), | ||
| 593 | } | ||
| 594 | } | ||
| 562 | } | 595 | } |
| 563 | 596 | ||
| 564 | impl<'d, M: Mode> UartRx<'d, M> { | 597 | impl<'d, M: Mode> UartRx<'d, M> { |
| @@ -746,7 +779,8 @@ fn configure( | |||
| 746 | 779 | ||
| 747 | info.regs.ctl0().modify(|w| { | 780 | info.regs.ctl0().modify(|w| { |
| 748 | w.set_lbe(config.loop_back_enable); | 781 | w.set_lbe(config.loop_back_enable); |
| 749 | w.set_rxe(enable_rx); | 782 | // Errata UART_ERR_02, must set RXE to allow use of EOT. |
| 783 | w.set_rxe(enable_rx | enable_tx); | ||
| 750 | w.set_txe(enable_tx); | 784 | w.set_txe(enable_tx); |
| 751 | // RXD_OUT_EN and TXD_OUT_EN? | 785 | // RXD_OUT_EN and TXD_OUT_EN? |
| 752 | w.set_menc(false); | 786 | w.set_menc(false); |
| @@ -754,13 +788,18 @@ fn configure( | |||
| 754 | w.set_rtsen(enable_rts); | 788 | w.set_rtsen(enable_rts); |
| 755 | w.set_ctsen(enable_cts); | 789 | w.set_ctsen(enable_cts); |
| 756 | // oversampling is set later | 790 | // oversampling is set later |
| 757 | // TODO: config | 791 | w.set_fen(config.fifo_enable); |
| 758 | w.set_fen(false); | ||
| 759 | // TODO: config | 792 | // TODO: config |
| 760 | w.set_majvote(false); | 793 | w.set_majvote(false); |
| 761 | w.set_msbfirst(matches!(config.msb_order, BitOrder::MsbFirst)); | 794 | w.set_msbfirst(matches!(config.msb_order, BitOrder::MsbFirst)); |
| 762 | }); | 795 | }); |
| 763 | 796 | ||
| 797 | info.regs.ifls().modify(|w| { | ||
| 798 | // TODO: Need power domain info for other options. | ||
| 799 | w.set_txiflsel(vals::Iflssel::AT_LEAST_ONE); | ||
| 800 | w.set_rxiflsel(vals::Iflssel::AT_LEAST_ONE); | ||
| 801 | }); | ||
| 802 | |||
| 764 | info.regs.lcrh().modify(|w| { | 803 | info.regs.lcrh().modify(|w| { |
| 765 | let eps = if matches!(config.parity, Parity::ParityEven) { | 804 | let eps = if matches!(config.parity, Parity::ParityEven) { |
| 766 | vals::Eps::EVEN | 805 | vals::Eps::EVEN |
| @@ -1021,9 +1060,29 @@ fn read_with_error(r: Regs) -> Result<u8, Error> { | |||
| 1021 | Ok(rx.data()) | 1060 | Ok(rx.data()) |
| 1022 | } | 1061 | } |
| 1023 | 1062 | ||
| 1063 | /// This function assumes CTL0.ENABLE is set (for errata cases). | ||
| 1064 | fn busy(r: Regs) -> bool { | ||
| 1065 | // Errata UART_ERR_08 | ||
| 1066 | if cfg!(any( | ||
| 1067 | mspm0g151x, mspm0g351x, mspm0l110x, mspm0l130x, mspm0l134x, mspm0c110x, | ||
| 1068 | )) { | ||
| 1069 | let stat = r.stat().read(); | ||
| 1070 | // "Poll TXFIFO status and the CTL0.ENABLE register bit to identify BUSY status." | ||
| 1071 | !stat.txfe() | ||
| 1072 | } else { | ||
| 1073 | r.stat().read().busy() | ||
| 1074 | } | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | // TODO: Implement when dma uart is implemented. | ||
| 1078 | fn dma_enabled(_r: Regs) -> bool { | ||
| 1079 | false | ||
| 1080 | } | ||
| 1081 | |||
| 1024 | pub(crate) trait SealedInstance { | 1082 | pub(crate) trait SealedInstance { |
| 1025 | fn info() -> &'static Info; | 1083 | fn info() -> &'static Info; |
| 1026 | fn state() -> &'static State; | 1084 | fn state() -> &'static State; |
| 1085 | fn buffered_state() -> &'static BufferedState; | ||
| 1027 | } | 1086 | } |
| 1028 | 1087 | ||
| 1029 | macro_rules! impl_uart_instance { | 1088 | macro_rules! impl_uart_instance { |
| @@ -1041,12 +1100,16 @@ macro_rules! impl_uart_instance { | |||
| 1041 | } | 1100 | } |
| 1042 | 1101 | ||
| 1043 | fn state() -> &'static crate::uart::State { | 1102 | fn state() -> &'static crate::uart::State { |
| 1044 | use crate::interrupt::typelevel::Interrupt; | ||
| 1045 | use crate::uart::State; | 1103 | use crate::uart::State; |
| 1046 | 1104 | ||
| 1047 | static STATE: State = State { | 1105 | static STATE: State = State::new(); |
| 1048 | clock: core::sync::atomic::AtomicU32::new(0), | 1106 | &STATE |
| 1049 | }; | 1107 | } |
| 1108 | |||
| 1109 | fn buffered_state() -> &'static crate::uart::BufferedState { | ||
| 1110 | use crate::uart::BufferedState; | ||
| 1111 | |||
| 1112 | static STATE: BufferedState = BufferedState::new(); | ||
| 1050 | &STATE | 1113 | &STATE |
| 1051 | } | 1114 | } |
| 1052 | } | 1115 | } |
diff --git a/embassy-net-adin1110/Cargo.toml b/embassy-net-adin1110/Cargo.toml index a620928cb..9cda4dc5b 100644 --- a/embassy-net-adin1110/Cargo.toml +++ b/embassy-net-adin1110/Cargo.toml | |||
| @@ -16,7 +16,7 @@ log = { version = "0.4", default-features = false, optional = true } | |||
| 16 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 16 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 17 | embedded-hal-async = { version = "1.0" } | 17 | embedded-hal-async = { version = "1.0" } |
| 18 | embedded-hal-bus = { version = "0.1", features = ["async"] } | 18 | embedded-hal-bus = { version = "0.1", features = ["async"] } |
| 19 | embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } | 19 | embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" } |
| 20 | embassy-time = { version = "0.4.0", path = "../embassy-time" } | 20 | embassy-time = { version = "0.4.0", path = "../embassy-time" } |
| 21 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 21 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 22 | bitfield = "0.14.0" | 22 | bitfield = "0.14.0" |
diff --git a/embassy-net-driver-channel/CHANGELOG.md b/embassy-net-driver-channel/CHANGELOG.md index d7af7e55d..1189e50c3 100644 --- a/embassy-net-driver-channel/CHANGELOG.md +++ b/embassy-net-driver-channel/CHANGELOG.md | |||
| @@ -5,7 +5,12 @@ All notable changes to this project will be documented in this file. | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 7 | 7 | ||
| 8 | ## Unreleased | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | ||
| 10 | |||
| 11 | ## 0.3.1 - 2025-07-16 | ||
| 12 | |||
| 13 | - Update `embassy-sync` to v0.7.0 | ||
| 9 | 14 | ||
| 10 | ## 0.3.0 - 2024-08-05 | 15 | ## 0.3.0 - 2024-08-05 |
| 11 | 16 | ||
diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml index c16c4be74..386d492c6 100644 --- a/embassy-net-driver-channel/Cargo.toml +++ b/embassy-net-driver-channel/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-net-driver-channel" | 2 | name = "embassy-net-driver-channel" |
| 3 | version = "0.3.0" | 3 | version = "0.3.1" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack." | 6 | description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack." |
diff --git a/embassy-net-driver-channel/release.toml b/embassy-net-driver-channel/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-net-driver-channel/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index dea74ed65..7ccef84e8 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml | |||
| @@ -20,7 +20,7 @@ log = { version = "0.4.14", optional = true } | |||
| 20 | embassy-time = { version = "0.4.0", path = "../embassy-time" } | 20 | embassy-time = { version = "0.4.0", path = "../embassy-time" } |
| 21 | embassy-sync = { version = "0.7.0", path = "../embassy-sync"} | 21 | embassy-sync = { version = "0.7.0", path = "../embassy-sync"} |
| 22 | embassy-futures = { version = "0.1.0", path = "../embassy-futures"} | 22 | embassy-futures = { version = "0.1.0", path = "../embassy-futures"} |
| 23 | embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"} | 23 | embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel"} |
| 24 | 24 | ||
| 25 | embedded-hal = { version = "1.0" } | 25 | embedded-hal = { version = "1.0" } |
| 26 | embedded-hal-async = { version = "1.0" } | 26 | embedded-hal-async = { version = "1.0" } |
diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index 62813e546..f3c0a0078 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml | |||
| @@ -23,7 +23,7 @@ cortex-m = "0.7.7" | |||
| 23 | embassy-time = { version = "0.4.0", path = "../embassy-time" } | 23 | embassy-time = { version = "0.4.0", path = "../embassy-time" } |
| 24 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 24 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 25 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 25 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 26 | embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } | 26 | embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" } |
| 27 | 27 | ||
| 28 | heapless = "0.8" | 28 | heapless = "0.8" |
| 29 | embedded-io = "0.6.1" | 29 | embedded-io = "0.6.1" |
| @@ -33,7 +33,7 @@ at-commands = "0.5.4" | |||
| 33 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-nrf91-v$VERSION/embassy-net-nrf91/src/" | 33 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-nrf91-v$VERSION/embassy-net-nrf91/src/" |
| 34 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-nrf91/src/" | 34 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-nrf91/src/" |
| 35 | target = "thumbv7em-none-eabi" | 35 | target = "thumbv7em-none-eabi" |
| 36 | features = ["defmt"] | 36 | features = ["defmt", "nrf-pac/nrf9160"] |
| 37 | 37 | ||
| 38 | [package.metadata.docs.rs] | 38 | [package.metadata.docs.rs] |
| 39 | features = ["defmt"] | 39 | features = ["defmt"] |
diff --git a/embassy-net-nrf91/src/lib.rs b/embassy-net-nrf91/src/lib.rs index 61fcaea1f..0bd9be0d9 100644 --- a/embassy-net-nrf91/src/lib.rs +++ b/embassy-net-nrf91/src/lib.rs | |||
| @@ -119,14 +119,16 @@ async fn new_internal<'a>( | |||
| 119 | let shmem_ptr = shmem.as_mut_ptr() as *mut u8; | 119 | let shmem_ptr = shmem.as_mut_ptr() as *mut u8; |
| 120 | 120 | ||
| 121 | const SPU_REGION_SIZE: usize = 8192; // 8kb | 121 | const SPU_REGION_SIZE: usize = 8192; // 8kb |
| 122 | assert!(shmem_len != 0); | 122 | trace!(" shmem_ptr = {}, shmem_len = {}", shmem_ptr, shmem_len); |
| 123 | |||
| 124 | assert!(shmem_len != 0, "shmem length must not be zero"); | ||
| 123 | assert!( | 125 | assert!( |
| 124 | shmem_len % SPU_REGION_SIZE == 0, | 126 | shmem_len % SPU_REGION_SIZE == 0, |
| 125 | "shmem length must be a multiple of 8kb" | 127 | "shmem length must be a multiple of 8kb" |
| 126 | ); | 128 | ); |
| 127 | assert!( | 129 | assert!( |
| 128 | (shmem_ptr as usize) % SPU_REGION_SIZE == 0, | 130 | (shmem_ptr as usize) % SPU_REGION_SIZE == 0, |
| 129 | "shmem length must be a multiple of 8kb" | 131 | "shmem pointer must be 8kb-aligned" |
| 130 | ); | 132 | ); |
| 131 | assert!( | 133 | assert!( |
| 132 | (shmem_ptr as usize + shmem_len) < 0x2002_0000, | 134 | (shmem_ptr as usize + shmem_len) < 0x2002_0000, |
| @@ -135,8 +137,15 @@ async fn new_internal<'a>( | |||
| 135 | 137 | ||
| 136 | let spu = pac::SPU_S; | 138 | let spu = pac::SPU_S; |
| 137 | debug!("Setting IPC RAM as nonsecure..."); | 139 | debug!("Setting IPC RAM as nonsecure..."); |
| 140 | trace!( | ||
| 141 | " SPU_REGION_SIZE={}, shmem_ptr=0x{:08X}, shmem_len={}", | ||
| 142 | SPU_REGION_SIZE, | ||
| 143 | shmem_ptr as usize, | ||
| 144 | shmem_len | ||
| 145 | ); | ||
| 138 | let region_start = (shmem_ptr as usize - 0x2000_0000) / SPU_REGION_SIZE; | 146 | let region_start = (shmem_ptr as usize - 0x2000_0000) / SPU_REGION_SIZE; |
| 139 | let region_end = region_start + shmem_len / SPU_REGION_SIZE; | 147 | let region_end = region_start + shmem_len / SPU_REGION_SIZE; |
| 148 | trace!(" region_start={}, region_end={}", region_start, region_end); | ||
| 140 | for i in region_start..region_end { | 149 | for i in region_start..region_end { |
| 141 | spu.ramregion(i).perm().write(|w| { | 150 | spu.ramregion(i).perm().write(|w| { |
| 142 | w.set_execute(true); | 151 | w.set_execute(true); |
| @@ -154,13 +163,18 @@ async fn new_internal<'a>( | |||
| 154 | end: unsafe { shmem_ptr.add(shmem_len) }, | 163 | end: unsafe { shmem_ptr.add(shmem_len) }, |
| 155 | _phantom: PhantomData, | 164 | _phantom: PhantomData, |
| 156 | }; | 165 | }; |
| 157 | 166 | trace!( | |
| 158 | let ipc = pac::IPC_NS; | 167 | " Allocator: start=0x{:08X}, end=0x{:08X}", |
| 159 | let power = pac::POWER_S; | 168 | alloc.start as usize, |
| 169 | alloc.end as usize | ||
| 170 | ); | ||
| 160 | 171 | ||
| 161 | let cb: &mut ControlBlock = alloc.alloc().write(unsafe { mem::zeroed() }); | 172 | let cb: &mut ControlBlock = alloc.alloc().write(unsafe { mem::zeroed() }); |
| 173 | |||
| 162 | let rx = alloc.alloc_bytes(RX_SIZE); | 174 | let rx = alloc.alloc_bytes(RX_SIZE); |
| 175 | trace!(" RX buffer at {}, size={}", rx.as_ptr(), RX_SIZE); | ||
| 163 | let trace = alloc.alloc_bytes(TRACE_SIZE); | 176 | let trace = alloc.alloc_bytes(TRACE_SIZE); |
| 177 | trace!(" Trace buffer at {}, size={}", trace.as_ptr(), TRACE_SIZE); | ||
| 164 | 178 | ||
| 165 | cb.version = 0x00010000; | 179 | cb.version = 0x00010000; |
| 166 | cb.rx_base = rx.as_mut_ptr() as _; | 180 | cb.rx_base = rx.as_mut_ptr() as _; |
| @@ -174,8 +188,10 @@ async fn new_internal<'a>( | |||
| 174 | cb.trace.base = trace.as_mut_ptr() as _; | 188 | cb.trace.base = trace.as_mut_ptr() as _; |
| 175 | cb.trace.size = TRACE_SIZE; | 189 | cb.trace.size = TRACE_SIZE; |
| 176 | 190 | ||
| 191 | let ipc = pac::IPC_NS; | ||
| 177 | ipc.gpmem(0).write_value(cb as *mut _ as u32); | 192 | ipc.gpmem(0).write_value(cb as *mut _ as u32); |
| 178 | ipc.gpmem(1).write_value(0); | 193 | ipc.gpmem(1).write_value(0); |
| 194 | trace!(" GPMEM[0]={:#X}, GPMEM[1]={}", cb as *mut _ as u32, 0); | ||
| 179 | 195 | ||
| 180 | // connect task/event i to channel i | 196 | // connect task/event i to channel i |
| 181 | for i in 0..8 { | 197 | for i in 0..8 { |
| @@ -185,8 +201,9 @@ async fn new_internal<'a>( | |||
| 185 | 201 | ||
| 186 | compiler_fence(Ordering::SeqCst); | 202 | compiler_fence(Ordering::SeqCst); |
| 187 | 203 | ||
| 204 | let power = pac::POWER_S; | ||
| 188 | // POWER.LTEMODEM.STARTN = 0 | 205 | // POWER.LTEMODEM.STARTN = 0 |
| 189 | // The reg is missing in the PAC?? | 206 | // TODO: The reg is missing in the PAC?? |
| 190 | let startn = unsafe { (power.as_ptr() as *mut u32).add(0x610 / 4) }; | 207 | let startn = unsafe { (power.as_ptr() as *mut u32).add(0x610 / 4) }; |
| 191 | unsafe { startn.write_volatile(0) } | 208 | unsafe { startn.write_volatile(0) } |
| 192 | 209 | ||
| @@ -202,6 +219,8 @@ async fn new_internal<'a>( | |||
| 202 | 219 | ||
| 203 | rx_control_list: ptr::null_mut(), | 220 | rx_control_list: ptr::null_mut(), |
| 204 | rx_data_list: ptr::null_mut(), | 221 | rx_data_list: ptr::null_mut(), |
| 222 | rx_control_len: 0, | ||
| 223 | rx_data_len: 0, | ||
| 205 | rx_seq_no: 0, | 224 | rx_seq_no: 0, |
| 206 | rx_check: PointerChecker { | 225 | rx_check: PointerChecker { |
| 207 | start: rx.as_mut_ptr() as *mut u8, | 226 | start: rx.as_mut_ptr() as *mut u8, |
| @@ -310,6 +329,10 @@ struct StateInner { | |||
| 310 | 329 | ||
| 311 | rx_control_list: *mut List, | 330 | rx_control_list: *mut List, |
| 312 | rx_data_list: *mut List, | 331 | rx_data_list: *mut List, |
| 332 | /// Number of entries in the control list | ||
| 333 | rx_control_len: usize, | ||
| 334 | /// Number of entries in the data list | ||
| 335 | rx_data_len: usize, | ||
| 313 | rx_seq_no: u16, | 336 | rx_seq_no: u16, |
| 314 | rx_check: PointerChecker, | 337 | rx_check: PointerChecker, |
| 315 | 338 | ||
| @@ -346,8 +369,11 @@ impl StateInner { | |||
| 346 | self.rx_data_list = desc.data_list_ptr; | 369 | self.rx_data_list = desc.data_list_ptr; |
| 347 | let rx_control_len = unsafe { addr_of!((*self.rx_control_list).len).read_volatile() }; | 370 | let rx_control_len = unsafe { addr_of!((*self.rx_control_list).len).read_volatile() }; |
| 348 | let rx_data_len = unsafe { addr_of!((*self.rx_data_list).len).read_volatile() }; | 371 | let rx_data_len = unsafe { addr_of!((*self.rx_data_list).len).read_volatile() }; |
| 349 | assert_eq!(rx_control_len, LIST_LEN); | 372 | |
| 350 | assert_eq!(rx_data_len, LIST_LEN); | 373 | trace!("modem control list length: {}", rx_control_len); |
| 374 | trace!("modem data list length: {}", rx_data_len); | ||
| 375 | self.rx_control_len = rx_control_len; | ||
| 376 | self.rx_data_len = rx_data_len; | ||
| 351 | self.init = true; | 377 | self.init = true; |
| 352 | 378 | ||
| 353 | debug!("IPC initialized OK!"); | 379 | debug!("IPC initialized OK!"); |
| @@ -463,7 +489,12 @@ impl StateInner { | |||
| 463 | 489 | ||
| 464 | fn process(&mut self, list: *mut List, is_control: bool, ch: &mut ch::Runner<MTU>) -> bool { | 490 | fn process(&mut self, list: *mut List, is_control: bool, ch: &mut ch::Runner<MTU>) -> bool { |
| 465 | let mut did_work = false; | 491 | let mut did_work = false; |
| 466 | for i in 0..LIST_LEN { | 492 | let max = if is_control { |
| 493 | self.rx_control_len | ||
| 494 | } else { | ||
| 495 | self.rx_data_len | ||
| 496 | }; | ||
| 497 | for i in 0..max { | ||
| 467 | let item_ptr = unsafe { addr_of_mut!((*list).items[i]) }; | 498 | let item_ptr = unsafe { addr_of_mut!((*list).items[i]) }; |
| 468 | let preamble = unsafe { addr_of!((*item_ptr).state).read_volatile() }; | 499 | let preamble = unsafe { addr_of!((*item_ptr).state).read_volatile() }; |
| 469 | if preamble & 0xFF == 0x01 && preamble >> 16 == self.rx_seq_no as u32 { | 500 | if preamble & 0xFF == 0x01 && preamble >> 16 == self.rx_seq_no as u32 { |
| @@ -947,7 +978,7 @@ impl<'a> Runner<'a> { | |||
| 947 | } | 978 | } |
| 948 | } | 979 | } |
| 949 | 980 | ||
| 950 | const LIST_LEN: usize = 16; | 981 | const LIST_LEN: usize = 32; |
| 951 | 982 | ||
| 952 | #[repr(C)] | 983 | #[repr(C)] |
| 953 | struct ControlBlock { | 984 | struct ControlBlock { |
diff --git a/embassy-net-ppp/Cargo.toml b/embassy-net-ppp/Cargo.toml index 0936626eb..b724401c9 100644 --- a/embassy-net-ppp/Cargo.toml +++ b/embassy-net-ppp/Cargo.toml | |||
| @@ -18,7 +18,7 @@ defmt = { version = "1.0.1", optional = true } | |||
| 18 | log = { version = "0.4.14", optional = true } | 18 | log = { version = "0.4.14", optional = true } |
| 19 | 19 | ||
| 20 | embedded-io-async = { version = "0.6.1" } | 20 | embedded-io-async = { version = "0.6.1" } |
| 21 | embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } | 21 | embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" } |
| 22 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 22 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 23 | ppproto = { version = "0.2.1"} | 23 | ppproto = { version = "0.2.1"} |
| 24 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 24 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
diff --git a/embassy-net-wiznet/Cargo.toml b/embassy-net-wiznet/Cargo.toml index a06a09302..3ff01f72b 100644 --- a/embassy-net-wiznet/Cargo.toml +++ b/embassy-net-wiznet/Cargo.toml | |||
| @@ -12,7 +12,7 @@ documentation = "https://docs.embassy.dev/embassy-net-wiznet" | |||
| 12 | [dependencies] | 12 | [dependencies] |
| 13 | embedded-hal = { version = "1.0" } | 13 | embedded-hal = { version = "1.0" } |
| 14 | embedded-hal-async = { version = "1.0" } | 14 | embedded-hal-async = { version = "1.0" } |
| 15 | embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } | 15 | embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" } |
| 16 | embassy-time = { version = "0.4.0", path = "../embassy-time" } | 16 | embassy-time = { version = "0.4.0", path = "../embassy-time" } |
| 17 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 17 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 18 | defmt = { version = "1.0.1", optional = true } | 18 | defmt = { version = "1.0.1", optional = true } |
diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 63c2f4c75..482eb0e56 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs | |||
| @@ -172,7 +172,7 @@ impl<'a> UdpSocket<'a> { | |||
| 172 | /// register the current task to be notified when a datagram is received. | 172 | /// register the current task to be notified when a datagram is received. |
| 173 | /// | 173 | /// |
| 174 | /// When a datagram is received, this method will call the provided function | 174 | /// When a datagram is received, this method will call the provided function |
| 175 | /// with the number of bytes received and the remote endpoint and return | 175 | /// with a reference to the received bytes and the remote endpoint and return |
| 176 | /// `Poll::Ready` with the function's returned value. | 176 | /// `Poll::Ready` with the function's returned value. |
| 177 | pub async fn recv_from_with<F, R>(&mut self, f: F) -> R | 177 | pub async fn recv_from_with<F, R>(&mut self, f: F) -> R |
| 178 | where | 178 | where |
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index ffa7997f7..a4cb8ceaf 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md | |||
| @@ -5,7 +5,27 @@ All notable changes to this project will be documented in this file. | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 7 | 7 | ||
| 8 | ## Unreleased | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | ||
| 10 | |||
| 11 | ## 0.5.0 - 2025-07-16 | ||
| 12 | |||
| 13 | - changed: update to latest embassy-usb-driver | ||
| 14 | |||
| 15 | ## 0.4.1 - 2025-07-14 | ||
| 16 | |||
| 17 | - changed: nrf52833: configure internal LDO | ||
| 18 | - changed: nrf5340: add more options to clock config | ||
| 19 | - bugfix: clean the SAADC's register while dropping | ||
| 20 | - changed: Remove Peripheral trait, rename PeripheralRef->Peri. | ||
| 21 | - changed: take pins before interrupts in buffered uart init | ||
| 22 | - changed: nrf5340: add wdt support | ||
| 23 | - changed: remove nrf radio BLE | ||
| 24 | - changed: add Blocking/Async Mode param. | ||
| 25 | - bugfix: fix PWM loop count | ||
| 26 | - bugfix: fixing the nrf54l drive configuration bug | ||
| 27 | - changed: add temp driver for nrf5340 | ||
| 28 | - changed: add support for rand 0.9 | ||
| 9 | 29 | ||
| 10 | ## 0.3.1 - 2025-01-09 | 30 | ## 0.3.1 - 2025-01-09 |
| 11 | 31 | ||
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 47eb92697..8fa20580d 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-nrf" | 2 | name = "embassy-nrf" |
| 3 | version = "0.3.1" | 3 | version = "0.5.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "Embassy Hardware Abstraction Layer (HAL) for nRF series microcontrollers" | 6 | description = "Embassy Hardware Abstraction Layer (HAL) for nRF series microcontrollers" |
| @@ -144,9 +144,9 @@ embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", option | |||
| 144 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } | 144 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } |
| 145 | embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } | 145 | embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } |
| 146 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 146 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 147 | embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } | 147 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } |
| 148 | embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } | 148 | embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } |
| 149 | embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } | 149 | embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } |
| 150 | 150 | ||
| 151 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 151 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 152 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 152 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
diff --git a/embassy-nrf/release.toml b/embassy-nrf/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-nrf/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index d02da9ac5..65f2d99f7 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs | |||
| @@ -292,7 +292,7 @@ pub(crate) fn convert_drive(w: &mut pac::gpio::regs::PinCnf, drive: OutputDrive) | |||
| 292 | } | 292 | } |
| 293 | 293 | ||
| 294 | w.set_drive0(convert(drive.low)); | 294 | w.set_drive0(convert(drive.low)); |
| 295 | w.set_drive0(convert(drive.high)); | 295 | w.set_drive1(convert(drive.high)); |
| 296 | } | 296 | } |
| 297 | } | 297 | } |
| 298 | 298 | ||
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 9d44ae7e6..ba8206d13 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -240,11 +240,13 @@ macro_rules! bind_interrupts { | |||
| 240 | #[no_mangle] | 240 | #[no_mangle] |
| 241 | $(#[cfg($cond_irq)])? | 241 | $(#[cfg($cond_irq)])? |
| 242 | unsafe extern "C" fn $irq() { | 242 | unsafe extern "C" fn $irq() { |
| 243 | $( | 243 | unsafe { |
| 244 | $(#[cfg($cond_handler)])? | 244 | $( |
| 245 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); | 245 | $(#[cfg($cond_handler)])? |
| 246 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); | ||
| 246 | 247 | ||
| 247 | )* | 248 | )* |
| 249 | } | ||
| 248 | } | 250 | } |
| 249 | 251 | ||
| 250 | $(#[cfg($cond_irq)])? | 252 | $(#[cfg($cond_irq)])? |
| @@ -584,8 +586,12 @@ pub mod config { | |||
| 584 | #[allow(unused)] | 586 | #[allow(unused)] |
| 585 | mod consts { | 587 | mod consts { |
| 586 | pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32; | 588 | pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32; |
| 589 | pub const UICR_HFXOSRC: *mut u32 = 0x00FF801C as *mut u32; | ||
| 590 | pub const UICR_HFXOCNT: *mut u32 = 0x00FF8020 as *mut u32; | ||
| 587 | pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF802C as *mut u32; | 591 | pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF802C as *mut u32; |
| 588 | pub const APPROTECT_ENABLED: u32 = 0x0000_0000; | 592 | pub const APPROTECT_ENABLED: u32 = 0x0000_0000; |
| 593 | #[cfg(feature = "_nrf9120")] | ||
| 594 | pub const APPROTECT_DISABLED: u32 = 0x50FA50FA; | ||
| 589 | } | 595 | } |
| 590 | 596 | ||
| 591 | #[cfg(feature = "_nrf5340-app")] | 597 | #[cfg(feature = "_nrf5340-app")] |
| @@ -648,13 +654,18 @@ unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteRe | |||
| 648 | return WriteResult::Failed; | 654 | return WriteResult::Failed; |
| 649 | } | 655 | } |
| 650 | 656 | ||
| 651 | let nvmc = pac::NVMC; | 657 | // Nrf9151 errata 7, need to disable interrups + use DSB https://docs.nordicsemi.com/bundle/errata_nRF9151_Rev2/page/ERR/nRF9151/Rev2/latest/anomaly_151_7.html |
| 652 | nvmc.config().write(|w| w.set_wen(pac::nvmc::vals::Wen::WEN)); | 658 | cortex_m::interrupt::free(|_cs| { |
| 653 | while !nvmc.ready().read().ready() {} | 659 | let nvmc = pac::NVMC; |
| 654 | address.write_volatile(value | !mask); | 660 | |
| 655 | while !nvmc.ready().read().ready() {} | 661 | nvmc.config().write(|w| w.set_wen(pac::nvmc::vals::Wen::WEN)); |
| 656 | nvmc.config().write(|_| {}); | 662 | while !nvmc.ready().read().ready() {} |
| 657 | while !nvmc.ready().read().ready() {} | 663 | address.write_volatile(value | !mask); |
| 664 | cortex_m::asm::dsb(); | ||
| 665 | while !nvmc.ready().read().ready() {} | ||
| 666 | nvmc.config().write(|_| {}); | ||
| 667 | while !nvmc.ready().read().ready() {} | ||
| 668 | }); | ||
| 658 | 669 | ||
| 659 | WriteResult::Written | 670 | WriteResult::Written |
| 660 | } | 671 | } |
| @@ -672,6 +683,28 @@ pub fn init(config: config::Config) -> Peripherals { | |||
| 672 | #[allow(unused_mut)] | 683 | #[allow(unused_mut)] |
| 673 | let mut needs_reset = false; | 684 | let mut needs_reset = false; |
| 674 | 685 | ||
| 686 | // Workaround used in the nrf mdk: file system_nrf91.c , function SystemInit(), after `#if !defined(NRF_SKIP_UICR_HFXO_WORKAROUND)` | ||
| 687 | #[cfg(all(feature = "_nrf91", feature = "_s"))] | ||
| 688 | { | ||
| 689 | let uicr = pac::UICR_S; | ||
| 690 | let hfxocnt = uicr.hfxocnt().read().hfxocnt().to_bits(); | ||
| 691 | let hfxosrc = uicr.hfxosrc().read().hfxosrc().to_bits(); | ||
| 692 | |||
| 693 | if hfxosrc == 1 { | ||
| 694 | unsafe { | ||
| 695 | let _ = uicr_write(consts::UICR_HFXOSRC, 0); | ||
| 696 | } | ||
| 697 | needs_reset = true; | ||
| 698 | } | ||
| 699 | |||
| 700 | if hfxocnt == 255 { | ||
| 701 | unsafe { | ||
| 702 | let _ = uicr_write(consts::UICR_HFXOCNT, 32); | ||
| 703 | } | ||
| 704 | needs_reset = true; | ||
| 705 | } | ||
| 706 | } | ||
| 707 | |||
| 675 | // Setup debug protection. | 708 | // Setup debug protection. |
| 676 | #[cfg(not(feature = "_nrf51"))] | 709 | #[cfg(not(feature = "_nrf51"))] |
| 677 | match config.debug { | 710 | match config.debug { |
| @@ -768,6 +801,28 @@ pub fn init(config: config::Config) -> Peripherals { | |||
| 768 | } | 801 | } |
| 769 | 802 | ||
| 770 | // nothing to do on the nrf9160, debug is allowed by default. | 803 | // nothing to do on the nrf9160, debug is allowed by default. |
| 804 | |||
| 805 | // nrf9151, nrf9161 use the new-style approtect that requires writing a register. | ||
| 806 | #[cfg(feature = "nrf9120-s")] | ||
| 807 | unsafe { | ||
| 808 | let p = pac::APPROTECT_S; | ||
| 809 | |||
| 810 | let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED); | ||
| 811 | needs_reset |= res == WriteResult::Written; | ||
| 812 | p.approtect() | ||
| 813 | .disable() | ||
| 814 | .write(|w| w.set_disable(pac::approtect::vals::ApprotectDisableDisable::SW_UNPROTECTED)); | ||
| 815 | |||
| 816 | let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_DISABLED); | ||
| 817 | needs_reset |= res == WriteResult::Written; | ||
| 818 | p.secureapprotect() | ||
| 819 | .disable() | ||
| 820 | .write(|w| w.set_disable(pac::approtect::vals::SecureapprotectDisableDisable::SW_UNPROTECTED)); | ||
| 821 | |||
| 822 | // TODO: maybe add workaround for this errata | ||
| 823 | // It uses extra power, not sure how to let the user choose. | ||
| 824 | // https://docs.nordicsemi.com/bundle/errata_nRF9151_Rev1/page/ERR/nRF9151/Rev1/latest/anomaly_151_36.html#anomaly_151_36 | ||
| 825 | } | ||
| 771 | } | 826 | } |
| 772 | config::Debug::Disallowed => { | 827 | config::Debug::Disallowed => { |
| 773 | // TODO: Handle nRF54L | 828 | // TODO: Handle nRF54L |
| @@ -783,6 +838,13 @@ pub fn init(config: config::Config) -> Peripherals { | |||
| 783 | let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_ENABLED); | 838 | let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_ENABLED); |
| 784 | needs_reset |= res == WriteResult::Written; | 839 | needs_reset |= res == WriteResult::Written; |
| 785 | } | 840 | } |
| 841 | |||
| 842 | #[cfg(feature = "nrf9120-s")] | ||
| 843 | { | ||
| 844 | let p = pac::APPROTECT_S; | ||
| 845 | p.approtect().forceprotect().write(|w| w.set_forceprotect(true)); | ||
| 846 | p.secureapprotect().forceprotect().write(|w| w.set_forceprotect(true)); | ||
| 847 | } | ||
| 786 | } | 848 | } |
| 787 | } | 849 | } |
| 788 | config::Debug::NotConfigured => {} | 850 | config::Debug::NotConfigured => {} |
diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index a2e153e26..3d76272ac 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs | |||
| @@ -479,9 +479,8 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { | |||
| 479 | let seqstart_index = if start_seq == StartSequence::One { 1 } else { 0 }; | 479 | let seqstart_index = if start_seq == StartSequence::One { 1 } else { 0 }; |
| 480 | 480 | ||
| 481 | match times { | 481 | match times { |
| 482 | // just the one time, no loop count | 482 | SequenceMode::Loop(n) => { |
| 483 | SequenceMode::Loop(_) => { | 483 | r.loop_().write(|w| w.set_cnt(vals::LoopCnt(n))); |
| 484 | r.loop_().write(|w| w.set_cnt(vals::LoopCnt::DISABLED)); | ||
| 485 | } | 484 | } |
| 486 | // to play infinitely, repeat the sequence one time, then have loops done self trigger seq0 again | 485 | // to play infinitely, repeat the sequence one time, then have loops done self trigger seq0 again |
| 487 | SequenceMode::Infinite => { | 486 | SequenceMode::Infinite => { |
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index f377df49e..927a0ac08 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs | |||
| @@ -245,7 +245,7 @@ impl<'d, T: Instance> Uarte<'d, T> { | |||
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | /// Return the endtx event for use with PPI | 247 | /// Return the endtx event for use with PPI |
| 248 | pub fn event_endtx(&self) -> Event { | 248 | pub fn event_endtx(&self) -> Event<'_> { |
| 249 | let r = T::regs(); | 249 | let r = T::regs(); |
| 250 | Event::from_reg(r.events_endtx()) | 250 | Event::from_reg(r.events_endtx()) |
| 251 | } | 251 | } |
diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index 6cc1b0111..c6970fc0f 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs | |||
| @@ -121,10 +121,11 @@ impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V | |||
| 121 | fn alloc_endpoint_in( | 121 | fn alloc_endpoint_in( |
| 122 | &mut self, | 122 | &mut self, |
| 123 | ep_type: EndpointType, | 123 | ep_type: EndpointType, |
| 124 | ep_addr: Option<EndpointAddress>, | ||
| 124 | packet_size: u16, | 125 | packet_size: u16, |
| 125 | interval_ms: u8, | 126 | interval_ms: u8, |
| 126 | ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { | 127 | ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { |
| 127 | let index = self.alloc_in.allocate(ep_type)?; | 128 | let index = self.alloc_in.allocate(ep_type, ep_addr)?; |
| 128 | let ep_addr = EndpointAddress::from_parts(index, Direction::In); | 129 | let ep_addr = EndpointAddress::from_parts(index, Direction::In); |
| 129 | Ok(Endpoint::new(EndpointInfo { | 130 | Ok(Endpoint::new(EndpointInfo { |
| 130 | addr: ep_addr, | 131 | addr: ep_addr, |
| @@ -137,10 +138,11 @@ impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V | |||
| 137 | fn alloc_endpoint_out( | 138 | fn alloc_endpoint_out( |
| 138 | &mut self, | 139 | &mut self, |
| 139 | ep_type: EndpointType, | 140 | ep_type: EndpointType, |
| 141 | ep_addr: Option<EndpointAddress>, | ||
| 140 | packet_size: u16, | 142 | packet_size: u16, |
| 141 | interval_ms: u8, | 143 | interval_ms: u8, |
| 142 | ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { | 144 | ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { |
| 143 | let index = self.alloc_out.allocate(ep_type)?; | 145 | let index = self.alloc_out.allocate(ep_type, ep_addr)?; |
| 144 | let ep_addr = EndpointAddress::from_parts(index, Direction::Out); | 146 | let ep_addr = EndpointAddress::from_parts(index, Direction::Out); |
| 145 | Ok(Endpoint::new(EndpointInfo { | 147 | Ok(Endpoint::new(EndpointInfo { |
| 146 | addr: ep_addr, | 148 | addr: ep_addr, |
| @@ -734,7 +736,11 @@ impl Allocator { | |||
| 734 | Self { used: 0 } | 736 | Self { used: 0 } |
| 735 | } | 737 | } |
| 736 | 738 | ||
| 737 | fn allocate(&mut self, ep_type: EndpointType) -> Result<usize, driver::EndpointAllocError> { | 739 | fn allocate( |
| 740 | &mut self, | ||
| 741 | ep_type: EndpointType, | ||
| 742 | ep_addr: Option<EndpointAddress>, | ||
| 743 | ) -> Result<usize, driver::EndpointAllocError> { | ||
| 738 | // Endpoint addresses are fixed in hardware: | 744 | // Endpoint addresses are fixed in hardware: |
| 739 | // - 0x80 / 0x00 - Control EP0 | 745 | // - 0x80 / 0x00 - Control EP0 |
| 740 | // - 0x81 / 0x01 - Bulk/Interrupt EP1 | 746 | // - 0x81 / 0x01 - Bulk/Interrupt EP1 |
| @@ -748,16 +754,37 @@ impl Allocator { | |||
| 748 | 754 | ||
| 749 | // Endpoint directions are allocated individually. | 755 | // Endpoint directions are allocated individually. |
| 750 | 756 | ||
| 751 | let alloc_index = match ep_type { | 757 | let alloc_index = if let Some(addr) = ep_addr { |
| 752 | EndpointType::Isochronous => 8, | 758 | // Use the specified endpoint address |
| 753 | EndpointType::Control => return Err(driver::EndpointAllocError), | 759 | let requested_index = addr.index(); |
| 754 | EndpointType::Interrupt | EndpointType::Bulk => { | 760 | // Validate the requested index based on endpoint type |
| 755 | // Find rightmost zero bit in 1..=7 | 761 | match ep_type { |
| 756 | let ones = (self.used >> 1).trailing_ones() as usize; | 762 | EndpointType::Isochronous => { |
| 757 | if ones >= 7 { | 763 | if requested_index != 8 { |
| 758 | return Err(driver::EndpointAllocError); | 764 | return Err(driver::EndpointAllocError); |
| 765 | } | ||
| 766 | } | ||
| 767 | EndpointType::Control => return Err(driver::EndpointAllocError), | ||
| 768 | EndpointType::Interrupt | EndpointType::Bulk => { | ||
| 769 | if requested_index < 1 || requested_index > 7 { | ||
| 770 | return Err(driver::EndpointAllocError); | ||
| 771 | } | ||
| 772 | } | ||
| 773 | } | ||
| 774 | requested_index | ||
| 775 | } else { | ||
| 776 | // Allocate any available endpoint | ||
| 777 | match ep_type { | ||
| 778 | EndpointType::Isochronous => 8, | ||
| 779 | EndpointType::Control => return Err(driver::EndpointAllocError), | ||
| 780 | EndpointType::Interrupt | EndpointType::Bulk => { | ||
| 781 | // Find rightmost zero bit in 1..=7 | ||
| 782 | let ones = (self.used >> 1).trailing_ones() as usize; | ||
| 783 | if ones >= 7 { | ||
| 784 | return Err(driver::EndpointAllocError); | ||
| 785 | } | ||
| 786 | ones + 1 | ||
| 759 | } | 787 | } |
| 760 | ones + 1 | ||
| 761 | } | 788 | } |
| 762 | }; | 789 | }; |
| 763 | 790 | ||
diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 426af06a0..14fa7201d 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml | |||
| @@ -3,18 +3,69 @@ name = "embassy-nxp" | |||
| 3 | version = "0.1.0" | 3 | version = "0.1.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | 5 | ||
| 6 | [package.metadata.embassy_docs] | ||
| 7 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nxp-v$VERSION/embassy-nxp/src/" | ||
| 8 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nxp/src/" | ||
| 9 | features = ["defmt", "unstable-pac" ] # TODO: Add time-driver-any, as both lpc55 and mimxrt1xxx use different drivers. | ||
| 10 | |||
| 11 | flavors = [ | ||
| 12 | { regex_feature = "lpc55", target = "thumbv8m.main-none-eabihf" }, | ||
| 13 | { regex_feature = "mimxrt.*", target = "thumbv7em-none-eabihf" }, | ||
| 14 | ] | ||
| 15 | |||
| 6 | [dependencies] | 16 | [dependencies] |
| 7 | cortex-m = "0.7.7" | 17 | cortex-m = "0.7.7" |
| 8 | cortex-m-rt = "0.7.0" | 18 | cortex-m-rt = "0.7.0" |
| 9 | critical-section = "1.1.2" | 19 | critical-section = "1.1.2" |
| 10 | embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } | 20 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } |
| 11 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 21 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 12 | lpc55-pac = "0.5.0" | ||
| 13 | defmt = { version = "1", optional = true } | 22 | defmt = { version = "1", optional = true } |
| 23 | log = { version = "0.4.27", optional = true } | ||
| 24 | embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } | ||
| 25 | embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } | ||
| 26 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } | ||
| 27 | |||
| 28 | ## Chip dependencies | ||
| 29 | lpc55-pac = { version = "0.5.0", optional = true } | ||
| 30 | nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e" } | ||
| 31 | |||
| 32 | imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } | ||
| 33 | |||
| 34 | [build-dependencies] | ||
| 35 | cfg_aliases = "0.2.1" | ||
| 36 | nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e", features = ["metadata"], optional = true } | ||
| 37 | proc-macro2 = "1.0.95" | ||
| 38 | quote = "1.0.15" | ||
| 14 | 39 | ||
| 15 | [features] | 40 | [features] |
| 16 | default = ["rt"] | 41 | default = ["rt"] |
| 17 | rt = ["lpc55-pac/rt"] | 42 | # Enable PACs as optional dependencies, since some chip families will use different pac crates (temporarily). |
| 43 | rt = ["lpc55-pac?/rt", "nxp-pac?/rt"] | ||
| 18 | 44 | ||
| 19 | ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. | 45 | ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. |
| 20 | defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] | 46 | defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] |
| 47 | |||
| 48 | log = ["dep:log"] | ||
| 49 | |||
| 50 | ## Use Periodic Interrupt Timer (PIT) as the time driver for `embassy-time`, with a tick rate of 1 MHz | ||
| 51 | time-driver-pit = ["_time_driver", "embassy-time?/tick-hz-1_000_000"] | ||
| 52 | |||
| 53 | ## Reexport the PAC for the currently enabled chip at `embassy_nxp::pac` (unstable) | ||
| 54 | unstable-pac = [] | ||
| 55 | # This is unstable because semver-minor (non-breaking) releases of embassy-nxp may major-bump (breaking) the PAC version. | ||
| 56 | # If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. | ||
| 57 | # There are no plans to make this stable. | ||
| 58 | |||
| 59 | ## internal use only | ||
| 60 | # | ||
| 61 | # This feature is unfortunately a hack around the fact that cfg_aliases cannot apply to the buildscript | ||
| 62 | # that creates the aliases. | ||
| 63 | _rt1xxx = [] | ||
| 64 | |||
| 65 | # A timer driver is enabled. | ||
| 66 | _time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"] | ||
| 67 | |||
| 68 | #! ### Chip selection features | ||
| 69 | lpc55 = ["dep:lpc55-pac"] | ||
| 70 | mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"] | ||
| 71 | mimxrt1062 = ["nxp-pac/mimxrt1062", "_rt1xxx", "dep:imxrt-rt"] | ||
diff --git a/embassy-nxp/build.rs b/embassy-nxp/build.rs new file mode 100644 index 000000000..f3c062c87 --- /dev/null +++ b/embassy-nxp/build.rs | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | use std::io::Write; | ||
| 2 | use std::path::{Path, PathBuf}; | ||
| 3 | use std::process::Command; | ||
| 4 | use std::{env, fs}; | ||
| 5 | |||
| 6 | use cfg_aliases::cfg_aliases; | ||
| 7 | #[cfg(feature = "_rt1xxx")] | ||
| 8 | use nxp_pac::metadata; | ||
| 9 | #[allow(unused)] | ||
| 10 | use proc_macro2::TokenStream; | ||
| 11 | #[allow(unused)] | ||
| 12 | use quote::quote; | ||
| 13 | |||
| 14 | #[path = "./build_common.rs"] | ||
| 15 | mod common; | ||
| 16 | |||
| 17 | fn main() { | ||
| 18 | let mut cfgs = common::CfgSet::new(); | ||
| 19 | common::set_target_cfgs(&mut cfgs); | ||
| 20 | |||
| 21 | let chip_name = match env::vars() | ||
| 22 | .map(|(a, _)| a) | ||
| 23 | .filter(|x| x.starts_with("CARGO_FEATURE_MIMXRT") || x.starts_with("CARGO_FEATURE_LPC")) | ||
| 24 | .get_one() | ||
| 25 | { | ||
| 26 | Ok(x) => x, | ||
| 27 | Err(GetOneError::None) => panic!("No mimxrt/lpc Cargo feature enabled"), | ||
| 28 | Err(GetOneError::Multiple) => panic!("Multiple mimxrt/lpc Cargo features enabled"), | ||
| 29 | } | ||
| 30 | .strip_prefix("CARGO_FEATURE_") | ||
| 31 | .unwrap() | ||
| 32 | .to_ascii_lowercase(); | ||
| 33 | |||
| 34 | cfg_aliases! { | ||
| 35 | rt1xxx: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | ||
| 36 | gpio1: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | ||
| 37 | gpio2: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | ||
| 38 | gpio3: { feature = "mimxrt1062" }, | ||
| 39 | gpio4: { feature = "mimxrt1062" }, | ||
| 40 | gpio5: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, | ||
| 41 | } | ||
| 42 | |||
| 43 | eprintln!("chip: {chip_name}"); | ||
| 44 | |||
| 45 | generate_code(); | ||
| 46 | } | ||
| 47 | |||
| 48 | #[cfg(feature = "_rt1xxx")] | ||
| 49 | fn generate_iomuxc() -> TokenStream { | ||
| 50 | use proc_macro2::{Ident, Span}; | ||
| 51 | |||
| 52 | let pads = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { | ||
| 53 | let name = Ident::new(®isters.name, Span::call_site()); | ||
| 54 | let address = registers.pad_ctl; | ||
| 55 | |||
| 56 | quote! { | ||
| 57 | pub const #name: u32 = #address; | ||
| 58 | } | ||
| 59 | }); | ||
| 60 | |||
| 61 | let muxes = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { | ||
| 62 | let name = Ident::new(®isters.name, Span::call_site()); | ||
| 63 | let address = registers.mux_ctl; | ||
| 64 | |||
| 65 | quote! { | ||
| 66 | pub const #name: u32 = #address; | ||
| 67 | } | ||
| 68 | }); | ||
| 69 | |||
| 70 | quote! { | ||
| 71 | pub mod iomuxc { | ||
| 72 | pub mod pads { | ||
| 73 | #(#pads)* | ||
| 74 | } | ||
| 75 | |||
| 76 | pub mod muxes { | ||
| 77 | #(#muxes)* | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | fn generate_code() { | ||
| 84 | #[allow(unused)] | ||
| 85 | use std::fmt::Write; | ||
| 86 | |||
| 87 | let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 88 | #[allow(unused_mut)] | ||
| 89 | let mut output = String::new(); | ||
| 90 | |||
| 91 | #[cfg(feature = "_rt1xxx")] | ||
| 92 | writeln!(&mut output, "{}", generate_iomuxc()).unwrap(); | ||
| 93 | |||
| 94 | let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); | ||
| 95 | fs::write(&out_file, output).unwrap(); | ||
| 96 | rustfmt(&out_file); | ||
| 97 | } | ||
| 98 | |||
| 99 | /// rustfmt a given path. | ||
| 100 | /// Failures are logged to stderr and ignored. | ||
| 101 | fn rustfmt(path: impl AsRef<Path>) { | ||
| 102 | let path = path.as_ref(); | ||
| 103 | match Command::new("rustfmt").args([path]).output() { | ||
| 104 | Err(e) => { | ||
| 105 | eprintln!("failed to exec rustfmt {:?}: {:?}", path, e); | ||
| 106 | } | ||
| 107 | Ok(out) => { | ||
| 108 | if !out.status.success() { | ||
| 109 | eprintln!("rustfmt {:?} failed:", path); | ||
| 110 | eprintln!("=== STDOUT:"); | ||
| 111 | std::io::stderr().write_all(&out.stdout).unwrap(); | ||
| 112 | eprintln!("=== STDERR:"); | ||
| 113 | std::io::stderr().write_all(&out.stderr).unwrap(); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | enum GetOneError { | ||
| 120 | None, | ||
| 121 | Multiple, | ||
| 122 | } | ||
| 123 | |||
| 124 | trait IteratorExt: Iterator { | ||
| 125 | fn get_one(self) -> Result<Self::Item, GetOneError>; | ||
| 126 | } | ||
| 127 | |||
| 128 | impl<T: Iterator> IteratorExt for T { | ||
| 129 | fn get_one(mut self) -> Result<Self::Item, GetOneError> { | ||
| 130 | match self.next() { | ||
| 131 | None => Err(GetOneError::None), | ||
| 132 | Some(res) => match self.next() { | ||
| 133 | Some(_) => Err(GetOneError::Multiple), | ||
| 134 | None => Ok(res), | ||
| 135 | }, | ||
| 136 | } | ||
| 137 | } | ||
| 138 | } | ||
diff --git a/embassy-nxp/build_common.rs b/embassy-nxp/build_common.rs new file mode 100644 index 000000000..4f24e6d37 --- /dev/null +++ b/embassy-nxp/build_common.rs | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | // NOTE: this file is copy-pasted between several Embassy crates, because there is no | ||
| 2 | // straightforward way to share this code: | ||
| 3 | // - it cannot be placed into the root of the repo and linked from each build.rs using `#[path = | ||
| 4 | // "../build_common.rs"]`, because `cargo publish` requires that all files published with a crate | ||
| 5 | // reside in the crate's directory, | ||
| 6 | // - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because | ||
| 7 | // symlinks don't work on Windows. | ||
| 8 | |||
| 9 | use std::collections::HashSet; | ||
| 10 | use std::env; | ||
| 11 | |||
| 12 | /// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring | ||
| 13 | /// them (`cargo:rust-check-cfg=cfg(X)`). | ||
| 14 | #[derive(Debug)] | ||
| 15 | pub struct CfgSet { | ||
| 16 | enabled: HashSet<String>, | ||
| 17 | declared: HashSet<String>, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl CfgSet { | ||
| 21 | pub fn new() -> Self { | ||
| 22 | Self { | ||
| 23 | enabled: HashSet::new(), | ||
| 24 | declared: HashSet::new(), | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation. | ||
| 29 | /// | ||
| 30 | /// All configs that can potentially be enabled should be unconditionally declared using | ||
| 31 | /// [`Self::declare()`]. | ||
| 32 | pub fn enable(&mut self, cfg: impl AsRef<str>) { | ||
| 33 | if self.enabled.insert(cfg.as_ref().to_owned()) { | ||
| 34 | println!("cargo:rustc-cfg={}", cfg.as_ref()); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | pub fn enable_all(&mut self, cfgs: &[impl AsRef<str>]) { | ||
| 39 | for cfg in cfgs.iter() { | ||
| 40 | self.enable(cfg.as_ref()); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Declare a valid config for conditional compilation, without enabling it. | ||
| 45 | /// | ||
| 46 | /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid. | ||
| 47 | pub fn declare(&mut self, cfg: impl AsRef<str>) { | ||
| 48 | if self.declared.insert(cfg.as_ref().to_owned()) { | ||
| 49 | println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref()); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | pub fn declare_all(&mut self, cfgs: &[impl AsRef<str>]) { | ||
| 54 | for cfg in cfgs.iter() { | ||
| 55 | self.declare(cfg.as_ref()); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | pub fn set(&mut self, cfg: impl Into<String>, enable: bool) { | ||
| 60 | let cfg = cfg.into(); | ||
| 61 | if enable { | ||
| 62 | self.enable(cfg.clone()); | ||
| 63 | } | ||
| 64 | self.declare(cfg); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Sets configs that describe the target platform. | ||
| 69 | pub fn set_target_cfgs(cfgs: &mut CfgSet) { | ||
| 70 | let target = env::var("TARGET").unwrap(); | ||
| 71 | |||
| 72 | if target.starts_with("thumbv6m-") { | ||
| 73 | cfgs.enable_all(&["cortex_m", "armv6m"]); | ||
| 74 | } else if target.starts_with("thumbv7m-") { | ||
| 75 | cfgs.enable_all(&["cortex_m", "armv7m"]); | ||
| 76 | } else if target.starts_with("thumbv7em-") { | ||
| 77 | cfgs.enable_all(&["cortex_m", "armv7m", "armv7em"]); | ||
| 78 | } else if target.starts_with("thumbv8m.base") { | ||
| 79 | cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_base"]); | ||
| 80 | } else if target.starts_with("thumbv8m.main") { | ||
| 81 | cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_main"]); | ||
| 82 | } | ||
| 83 | cfgs.declare_all(&[ | ||
| 84 | "cortex_m", | ||
| 85 | "armv6m", | ||
| 86 | "armv7m", | ||
| 87 | "armv7em", | ||
| 88 | "armv8m", | ||
| 89 | "armv8m_base", | ||
| 90 | "armv8m_main", | ||
| 91 | ]); | ||
| 92 | |||
| 93 | cfgs.set("has_fpu", target.ends_with("-eabihf")); | ||
| 94 | } | ||
diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs new file mode 100644 index 000000000..c95218af0 --- /dev/null +++ b/embassy-nxp/src/chips/lpc55.rs | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | pub use lpc55_pac as pac; | ||
| 2 | |||
| 3 | embassy_hal_internal::peripherals! { | ||
| 4 | // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other | ||
| 5 | // peripheral types (e.g. I2C). | ||
| 6 | PIO0_0, | ||
| 7 | PIO0_1, | ||
| 8 | PIO0_2, | ||
| 9 | PIO0_3, | ||
| 10 | PIO0_4, | ||
| 11 | PIO0_5, | ||
| 12 | PIO0_6, | ||
| 13 | PIO0_7, | ||
| 14 | PIO0_8, | ||
| 15 | PIO0_9, | ||
| 16 | PIO0_10, | ||
| 17 | PIO0_11, | ||
| 18 | PIO0_12, | ||
| 19 | PIO0_13, | ||
| 20 | PIO0_14, | ||
| 21 | PIO0_15, | ||
| 22 | PIO0_16, | ||
| 23 | PIO0_17, | ||
| 24 | PIO0_18, | ||
| 25 | PIO0_19, | ||
| 26 | PIO0_20, | ||
| 27 | PIO0_21, | ||
| 28 | PIO0_22, | ||
| 29 | PIO0_23, | ||
| 30 | PIO0_24, | ||
| 31 | PIO0_25, | ||
| 32 | PIO0_26, | ||
| 33 | PIO0_27, | ||
| 34 | PIO0_28, | ||
| 35 | PIO0_29, | ||
| 36 | PIO0_30, | ||
| 37 | PIO0_31, | ||
| 38 | PIO1_0, | ||
| 39 | PIO1_1, | ||
| 40 | PIO1_2, | ||
| 41 | PIO1_3, | ||
| 42 | PIO1_4, | ||
| 43 | PIO1_5, | ||
| 44 | PIO1_6, | ||
| 45 | PIO1_7, | ||
| 46 | PIO1_8, | ||
| 47 | PIO1_9, | ||
| 48 | PIO1_10, | ||
| 49 | PIO1_11, | ||
| 50 | PIO1_12, | ||
| 51 | PIO1_13, | ||
| 52 | PIO1_14, | ||
| 53 | PIO1_15, | ||
| 54 | PIO1_16, | ||
| 55 | PIO1_17, | ||
| 56 | PIO1_18, | ||
| 57 | PIO1_19, | ||
| 58 | PIO1_20, | ||
| 59 | PIO1_21, | ||
| 60 | PIO1_22, | ||
| 61 | PIO1_23, | ||
| 62 | PIO1_24, | ||
| 63 | PIO1_25, | ||
| 64 | PIO1_26, | ||
| 65 | PIO1_27, | ||
| 66 | PIO1_28, | ||
| 67 | PIO1_29, | ||
| 68 | PIO1_30, | ||
| 69 | PIO1_31, | ||
| 70 | } | ||
diff --git a/embassy-nxp/src/chips/mimxrt1011.rs b/embassy-nxp/src/chips/mimxrt1011.rs new file mode 100644 index 000000000..a74d953fc --- /dev/null +++ b/embassy-nxp/src/chips/mimxrt1011.rs | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | // This must be imported so that __preinit is defined. | ||
| 2 | use imxrt_rt as _; | ||
| 3 | pub use nxp_pac as pac; | ||
| 4 | |||
| 5 | embassy_hal_internal::peripherals! { | ||
| 6 | // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other | ||
| 7 | // peripheral types (e.g. I2C). | ||
| 8 | GPIO_00, | ||
| 9 | GPIO_01, | ||
| 10 | GPIO_02, | ||
| 11 | GPIO_03, | ||
| 12 | GPIO_04, | ||
| 13 | GPIO_05, | ||
| 14 | GPIO_06, | ||
| 15 | GPIO_07, | ||
| 16 | GPIO_08, | ||
| 17 | GPIO_09, | ||
| 18 | GPIO_10, | ||
| 19 | GPIO_11, | ||
| 20 | GPIO_12, | ||
| 21 | GPIO_13, | ||
| 22 | GPIO_AD_00, | ||
| 23 | GPIO_AD_01, | ||
| 24 | GPIO_AD_02, | ||
| 25 | GPIO_AD_03, | ||
| 26 | GPIO_AD_04, | ||
| 27 | GPIO_AD_05, | ||
| 28 | GPIO_AD_06, | ||
| 29 | GPIO_AD_07, | ||
| 30 | GPIO_AD_08, | ||
| 31 | GPIO_AD_09, | ||
| 32 | GPIO_AD_10, | ||
| 33 | GPIO_AD_11, | ||
| 34 | GPIO_AD_12, | ||
| 35 | GPIO_AD_13, | ||
| 36 | GPIO_AD_14, | ||
| 37 | GPIO_SD_00, | ||
| 38 | GPIO_SD_01, | ||
| 39 | GPIO_SD_02, | ||
| 40 | GPIO_SD_03, | ||
| 41 | GPIO_SD_04, | ||
| 42 | GPIO_SD_05, | ||
| 43 | GPIO_SD_06, | ||
| 44 | GPIO_SD_07, | ||
| 45 | GPIO_SD_08, | ||
| 46 | GPIO_SD_09, | ||
| 47 | GPIO_SD_10, | ||
| 48 | GPIO_SD_11, | ||
| 49 | GPIO_SD_12, | ||
| 50 | GPIO_SD_13, | ||
| 51 | PMIC_ON_REQ, | ||
| 52 | } | ||
| 53 | |||
| 54 | impl_gpio! { | ||
| 55 | // GPIO Bank 1 | ||
| 56 | GPIO_00(Gpio1, 0); | ||
| 57 | GPIO_01(Gpio1, 1); | ||
| 58 | GPIO_02(Gpio1, 2); | ||
| 59 | GPIO_03(Gpio1, 3); | ||
| 60 | GPIO_04(Gpio1, 4); | ||
| 61 | GPIO_05(Gpio1, 5); | ||
| 62 | GPIO_06(Gpio1, 6); | ||
| 63 | GPIO_07(Gpio1, 7); | ||
| 64 | GPIO_08(Gpio1, 8); | ||
| 65 | GPIO_09(Gpio1, 9); | ||
| 66 | GPIO_10(Gpio1, 10); | ||
| 67 | GPIO_11(Gpio1, 11); | ||
| 68 | GPIO_12(Gpio1, 12); | ||
| 69 | GPIO_13(Gpio1, 13); | ||
| 70 | GPIO_AD_00(Gpio1, 14); | ||
| 71 | GPIO_AD_01(Gpio1, 15); | ||
| 72 | GPIO_AD_02(Gpio1, 16); | ||
| 73 | GPIO_AD_03(Gpio1, 17); | ||
| 74 | GPIO_AD_04(Gpio1, 18); | ||
| 75 | GPIO_AD_05(Gpio1, 19); | ||
| 76 | GPIO_AD_06(Gpio1, 20); | ||
| 77 | GPIO_AD_07(Gpio1, 21); | ||
| 78 | GPIO_AD_08(Gpio1, 22); | ||
| 79 | GPIO_AD_09(Gpio1, 23); | ||
| 80 | GPIO_AD_10(Gpio1, 24); | ||
| 81 | GPIO_AD_11(Gpio1, 25); | ||
| 82 | GPIO_AD_12(Gpio1, 26); | ||
| 83 | GPIO_AD_13(Gpio1, 27); | ||
| 84 | GPIO_AD_14(Gpio1, 28); | ||
| 85 | |||
| 86 | // GPIO Bank 2 | ||
| 87 | GPIO_SD_00(Gpio2, 0); | ||
| 88 | GPIO_SD_01(Gpio2, 1); | ||
| 89 | GPIO_SD_02(Gpio2, 2); | ||
| 90 | GPIO_SD_03(Gpio2, 3); | ||
| 91 | GPIO_SD_04(Gpio2, 4); | ||
| 92 | GPIO_SD_05(Gpio2, 5); | ||
| 93 | GPIO_SD_06(Gpio2, 6); | ||
| 94 | GPIO_SD_07(Gpio2, 7); | ||
| 95 | GPIO_SD_08(Gpio2, 8); | ||
| 96 | GPIO_SD_09(Gpio2, 9); | ||
| 97 | GPIO_SD_10(Gpio2, 10); | ||
| 98 | GPIO_SD_11(Gpio2, 11); | ||
| 99 | GPIO_SD_12(Gpio2, 12); | ||
| 100 | GPIO_SD_13(Gpio2, 13); | ||
| 101 | |||
| 102 | // GPIO Bank 5 | ||
| 103 | PMIC_ON_REQ(Gpio5, 0); | ||
| 104 | } | ||
| 105 | |||
| 106 | pub(crate) mod _generated { | ||
| 107 | #![allow(dead_code)] | ||
| 108 | #![allow(unused_imports)] | ||
| 109 | #![allow(non_snake_case)] | ||
| 110 | #![allow(missing_docs)] | ||
| 111 | |||
| 112 | include!(concat!(env!("OUT_DIR"), "/_generated.rs")); | ||
| 113 | } | ||
diff --git a/embassy-nxp/src/chips/mimxrt1062.rs b/embassy-nxp/src/chips/mimxrt1062.rs new file mode 100644 index 000000000..ef153bd66 --- /dev/null +++ b/embassy-nxp/src/chips/mimxrt1062.rs | |||
| @@ -0,0 +1,282 @@ | |||
| 1 | // This must be imported so that __preinit is defined. | ||
| 2 | use imxrt_rt as _; | ||
| 3 | pub use nxp_pac as pac; | ||
| 4 | |||
| 5 | embassy_hal_internal::peripherals! { | ||
| 6 | // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other | ||
| 7 | // peripheral types (e.g. I2C). | ||
| 8 | GPIO_AD_B0_00, | ||
| 9 | GPIO_AD_B0_01, | ||
| 10 | GPIO_AD_B0_02, | ||
| 11 | GPIO_AD_B0_03, | ||
| 12 | GPIO_AD_B0_04, | ||
| 13 | GPIO_AD_B0_05, | ||
| 14 | GPIO_AD_B0_06, | ||
| 15 | GPIO_AD_B0_07, | ||
| 16 | GPIO_AD_B0_08, | ||
| 17 | GPIO_AD_B0_09, | ||
| 18 | GPIO_AD_B0_10, | ||
| 19 | GPIO_AD_B0_11, | ||
| 20 | GPIO_AD_B0_12, | ||
| 21 | GPIO_AD_B0_13, | ||
| 22 | GPIO_AD_B0_14, | ||
| 23 | GPIO_AD_B0_15, | ||
| 24 | GPIO_AD_B1_00, | ||
| 25 | GPIO_AD_B1_01, | ||
| 26 | GPIO_AD_B1_02, | ||
| 27 | GPIO_AD_B1_03, | ||
| 28 | GPIO_AD_B1_04, | ||
| 29 | GPIO_AD_B1_05, | ||
| 30 | GPIO_AD_B1_06, | ||
| 31 | GPIO_AD_B1_07, | ||
| 32 | GPIO_AD_B1_08, | ||
| 33 | GPIO_AD_B1_09, | ||
| 34 | GPIO_AD_B1_10, | ||
| 35 | GPIO_AD_B1_11, | ||
| 36 | GPIO_AD_B1_12, | ||
| 37 | GPIO_AD_B1_13, | ||
| 38 | GPIO_AD_B1_14, | ||
| 39 | GPIO_AD_B1_15, | ||
| 40 | GPIO_B0_00, | ||
| 41 | GPIO_B0_01, | ||
| 42 | GPIO_B0_02, | ||
| 43 | GPIO_B0_03, | ||
| 44 | GPIO_B0_04, | ||
| 45 | GPIO_B0_05, | ||
| 46 | GPIO_B0_06, | ||
| 47 | GPIO_B0_07, | ||
| 48 | GPIO_B0_08, | ||
| 49 | GPIO_B0_09, | ||
| 50 | GPIO_B0_10, | ||
| 51 | GPIO_B0_11, | ||
| 52 | GPIO_B0_12, | ||
| 53 | GPIO_B0_13, | ||
| 54 | GPIO_B0_14, | ||
| 55 | GPIO_B0_15, | ||
| 56 | GPIO_B1_00, | ||
| 57 | GPIO_B1_01, | ||
| 58 | GPIO_B1_02, | ||
| 59 | GPIO_B1_03, | ||
| 60 | GPIO_B1_04, | ||
| 61 | GPIO_B1_05, | ||
| 62 | GPIO_B1_06, | ||
| 63 | GPIO_B1_07, | ||
| 64 | GPIO_B1_08, | ||
| 65 | GPIO_B1_09, | ||
| 66 | GPIO_B1_10, | ||
| 67 | GPIO_B1_11, | ||
| 68 | GPIO_B1_12, | ||
| 69 | GPIO_B1_13, | ||
| 70 | GPIO_B1_14, | ||
| 71 | GPIO_B1_15, | ||
| 72 | GPIO_EMC_00, | ||
| 73 | GPIO_EMC_01, | ||
| 74 | GPIO_EMC_02, | ||
| 75 | GPIO_EMC_03, | ||
| 76 | GPIO_EMC_04, | ||
| 77 | GPIO_EMC_05, | ||
| 78 | GPIO_EMC_06, | ||
| 79 | GPIO_EMC_07, | ||
| 80 | GPIO_EMC_08, | ||
| 81 | GPIO_EMC_09, | ||
| 82 | GPIO_EMC_10, | ||
| 83 | GPIO_EMC_11, | ||
| 84 | GPIO_EMC_12, | ||
| 85 | GPIO_EMC_13, | ||
| 86 | GPIO_EMC_14, | ||
| 87 | GPIO_EMC_15, | ||
| 88 | GPIO_EMC_16, | ||
| 89 | GPIO_EMC_17, | ||
| 90 | GPIO_EMC_18, | ||
| 91 | GPIO_EMC_19, | ||
| 92 | GPIO_EMC_20, | ||
| 93 | GPIO_EMC_21, | ||
| 94 | GPIO_EMC_22, | ||
| 95 | GPIO_EMC_23, | ||
| 96 | GPIO_EMC_24, | ||
| 97 | GPIO_EMC_25, | ||
| 98 | GPIO_EMC_26, | ||
| 99 | GPIO_EMC_27, | ||
| 100 | GPIO_EMC_28, | ||
| 101 | GPIO_EMC_29, | ||
| 102 | GPIO_EMC_30, | ||
| 103 | GPIO_EMC_31, | ||
| 104 | GPIO_EMC_32, | ||
| 105 | GPIO_EMC_33, | ||
| 106 | GPIO_EMC_34, | ||
| 107 | GPIO_EMC_35, | ||
| 108 | GPIO_EMC_36, | ||
| 109 | GPIO_EMC_37, | ||
| 110 | GPIO_EMC_38, | ||
| 111 | GPIO_EMC_39, | ||
| 112 | GPIO_EMC_40, | ||
| 113 | GPIO_EMC_41, | ||
| 114 | GPIO_SD_B0_00, | ||
| 115 | GPIO_SD_B0_01, | ||
| 116 | GPIO_SD_B0_02, | ||
| 117 | GPIO_SD_B0_03, | ||
| 118 | GPIO_SD_B0_04, | ||
| 119 | GPIO_SD_B0_05, | ||
| 120 | GPIO_SD_B1_00, | ||
| 121 | GPIO_SD_B1_01, | ||
| 122 | GPIO_SD_B1_02, | ||
| 123 | GPIO_SD_B1_03, | ||
| 124 | GPIO_SD_B1_04, | ||
| 125 | GPIO_SD_B1_05, | ||
| 126 | GPIO_SD_B1_06, | ||
| 127 | GPIO_SD_B1_07, | ||
| 128 | GPIO_SD_B1_08, | ||
| 129 | GPIO_SD_B1_09, | ||
| 130 | GPIO_SD_B1_10, | ||
| 131 | GPIO_SD_B1_11, | ||
| 132 | WAKEUP, | ||
| 133 | PMIC_ON_REQ, | ||
| 134 | PMIC_STBY_REQ, | ||
| 135 | } | ||
| 136 | |||
| 137 | impl_gpio! { | ||
| 138 | // GPIO Bank 1 | ||
| 139 | GPIO_AD_B0_00(Gpio1, 0); | ||
| 140 | GPIO_AD_B0_01(Gpio1, 1); | ||
| 141 | GPIO_AD_B0_02(Gpio1, 2); | ||
| 142 | GPIO_AD_B0_03(Gpio1, 3); | ||
| 143 | GPIO_AD_B0_04(Gpio1, 4); | ||
| 144 | GPIO_AD_B0_05(Gpio1, 5); | ||
| 145 | GPIO_AD_B0_06(Gpio1, 6); | ||
| 146 | GPIO_AD_B0_07(Gpio1, 7); | ||
| 147 | GPIO_AD_B0_08(Gpio1, 8); | ||
| 148 | GPIO_AD_B0_09(Gpio1, 9); | ||
| 149 | GPIO_AD_B0_10(Gpio1, 10); | ||
| 150 | GPIO_AD_B0_11(Gpio1, 11); | ||
| 151 | GPIO_AD_B0_12(Gpio1, 12); | ||
| 152 | GPIO_AD_B0_13(Gpio1, 13); | ||
| 153 | GPIO_AD_B0_14(Gpio1, 14); | ||
| 154 | GPIO_AD_B0_15(Gpio1, 15); | ||
| 155 | GPIO_AD_B1_00(Gpio1, 16); | ||
| 156 | GPIO_AD_B1_01(Gpio1, 17); | ||
| 157 | GPIO_AD_B1_02(Gpio1, 18); | ||
| 158 | GPIO_AD_B1_03(Gpio1, 19); | ||
| 159 | GPIO_AD_B1_04(Gpio1, 20); | ||
| 160 | GPIO_AD_B1_05(Gpio1, 21); | ||
| 161 | GPIO_AD_B1_06(Gpio1, 22); | ||
| 162 | GPIO_AD_B1_07(Gpio1, 23); | ||
| 163 | GPIO_AD_B1_08(Gpio1, 24); | ||
| 164 | GPIO_AD_B1_09(Gpio1, 25); | ||
| 165 | GPIO_AD_B1_10(Gpio1, 26); | ||
| 166 | GPIO_AD_B1_11(Gpio1, 27); | ||
| 167 | GPIO_AD_B1_12(Gpio1, 28); | ||
| 168 | GPIO_AD_B1_13(Gpio1, 29); | ||
| 169 | GPIO_AD_B1_14(Gpio1, 30); | ||
| 170 | GPIO_AD_B1_15(Gpio1, 31); | ||
| 171 | |||
| 172 | // GPIO Bank 2 | ||
| 173 | GPIO_B0_00(Gpio2, 0); | ||
| 174 | GPIO_B0_01(Gpio2, 1); | ||
| 175 | GPIO_B0_02(Gpio2, 2); | ||
| 176 | GPIO_B0_03(Gpio2, 3); | ||
| 177 | GPIO_B0_04(Gpio2, 4); | ||
| 178 | GPIO_B0_05(Gpio2, 5); | ||
| 179 | GPIO_B0_06(Gpio2, 6); | ||
| 180 | GPIO_B0_07(Gpio2, 7); | ||
| 181 | GPIO_B0_08(Gpio2, 8); | ||
| 182 | GPIO_B0_09(Gpio2, 9); | ||
| 183 | GPIO_B0_10(Gpio2, 10); | ||
| 184 | GPIO_B0_11(Gpio2, 11); | ||
| 185 | GPIO_B0_12(Gpio2, 12); | ||
| 186 | GPIO_B0_13(Gpio2, 13); | ||
| 187 | GPIO_B0_14(Gpio2, 14); | ||
| 188 | GPIO_B0_15(Gpio2, 15); | ||
| 189 | GPIO_B1_00(Gpio2, 16); | ||
| 190 | GPIO_B1_01(Gpio2, 17); | ||
| 191 | GPIO_B1_02(Gpio2, 18); | ||
| 192 | GPIO_B1_03(Gpio2, 19); | ||
| 193 | GPIO_B1_04(Gpio2, 20); | ||
| 194 | GPIO_B1_05(Gpio2, 21); | ||
| 195 | GPIO_B1_06(Gpio2, 22); | ||
| 196 | GPIO_B1_07(Gpio2, 23); | ||
| 197 | GPIO_B1_08(Gpio2, 24); | ||
| 198 | GPIO_B1_09(Gpio2, 25); | ||
| 199 | GPIO_B1_10(Gpio2, 26); | ||
| 200 | GPIO_B1_11(Gpio2, 27); | ||
| 201 | GPIO_B1_12(Gpio2, 28); | ||
| 202 | GPIO_B1_13(Gpio2, 29); | ||
| 203 | GPIO_B1_14(Gpio2, 30); | ||
| 204 | GPIO_B1_15(Gpio2, 31); | ||
| 205 | |||
| 206 | // GPIO Bank 4 (EMC is 4, then 3) | ||
| 207 | GPIO_EMC_00(Gpio4, 0); | ||
| 208 | GPIO_EMC_01(Gpio4, 1); | ||
| 209 | GPIO_EMC_02(Gpio4, 2); | ||
| 210 | GPIO_EMC_03(Gpio4, 3); | ||
| 211 | GPIO_EMC_04(Gpio4, 4); | ||
| 212 | GPIO_EMC_05(Gpio4, 5); | ||
| 213 | GPIO_EMC_06(Gpio4, 6); | ||
| 214 | GPIO_EMC_07(Gpio4, 7); | ||
| 215 | GPIO_EMC_08(Gpio4, 8); | ||
| 216 | GPIO_EMC_09(Gpio4, 9); | ||
| 217 | GPIO_EMC_10(Gpio4, 10); | ||
| 218 | GPIO_EMC_11(Gpio4, 11); | ||
| 219 | GPIO_EMC_12(Gpio4, 12); | ||
| 220 | GPIO_EMC_13(Gpio4, 13); | ||
| 221 | GPIO_EMC_14(Gpio4, 14); | ||
| 222 | GPIO_EMC_15(Gpio4, 15); | ||
| 223 | GPIO_EMC_16(Gpio4, 16); | ||
| 224 | GPIO_EMC_17(Gpio4, 17); | ||
| 225 | GPIO_EMC_18(Gpio4, 18); | ||
| 226 | GPIO_EMC_19(Gpio4, 19); | ||
| 227 | GPIO_EMC_20(Gpio4, 20); | ||
| 228 | GPIO_EMC_21(Gpio4, 21); | ||
| 229 | GPIO_EMC_22(Gpio4, 22); | ||
| 230 | GPIO_EMC_23(Gpio4, 23); | ||
| 231 | GPIO_EMC_24(Gpio4, 24); | ||
| 232 | GPIO_EMC_25(Gpio4, 25); | ||
| 233 | GPIO_EMC_26(Gpio4, 26); | ||
| 234 | GPIO_EMC_27(Gpio4, 27); | ||
| 235 | GPIO_EMC_28(Gpio4, 28); | ||
| 236 | GPIO_EMC_29(Gpio4, 29); | ||
| 237 | GPIO_EMC_30(Gpio4, 30); | ||
| 238 | GPIO_EMC_31(Gpio4, 31); | ||
| 239 | |||
| 240 | // GPIO Bank 3 | ||
| 241 | GPIO_EMC_32(Gpio3, 18); | ||
| 242 | GPIO_EMC_33(Gpio3, 19); | ||
| 243 | GPIO_EMC_34(Gpio3, 20); | ||
| 244 | GPIO_EMC_35(Gpio3, 21); | ||
| 245 | GPIO_EMC_36(Gpio3, 22); | ||
| 246 | GPIO_EMC_37(Gpio3, 23); | ||
| 247 | GPIO_EMC_38(Gpio3, 24); | ||
| 248 | GPIO_EMC_39(Gpio3, 25); | ||
| 249 | GPIO_EMC_40(Gpio3, 26); | ||
| 250 | GPIO_EMC_41(Gpio3, 27); | ||
| 251 | GPIO_SD_B0_00(Gpio3, 12); | ||
| 252 | GPIO_SD_B0_01(Gpio3, 13); | ||
| 253 | GPIO_SD_B0_02(Gpio3, 14); | ||
| 254 | GPIO_SD_B0_03(Gpio3, 15); | ||
| 255 | GPIO_SD_B0_04(Gpio3, 16); | ||
| 256 | GPIO_SD_B0_05(Gpio3, 17); | ||
| 257 | GPIO_SD_B1_00(Gpio3, 0); | ||
| 258 | GPIO_SD_B1_01(Gpio3, 1); | ||
| 259 | GPIO_SD_B1_02(Gpio3, 2); | ||
| 260 | GPIO_SD_B1_03(Gpio3, 3); | ||
| 261 | GPIO_SD_B1_04(Gpio3, 4); | ||
| 262 | GPIO_SD_B1_05(Gpio3, 5); | ||
| 263 | GPIO_SD_B1_06(Gpio3, 6); | ||
| 264 | GPIO_SD_B1_07(Gpio3, 7); | ||
| 265 | GPIO_SD_B1_08(Gpio3, 8); | ||
| 266 | GPIO_SD_B1_09(Gpio3, 9); | ||
| 267 | GPIO_SD_B1_10(Gpio3, 10); | ||
| 268 | GPIO_SD_B1_11(Gpio3, 11); | ||
| 269 | |||
| 270 | WAKEUP(Gpio5, 0); | ||
| 271 | PMIC_ON_REQ(Gpio5, 1); | ||
| 272 | PMIC_STBY_REQ(Gpio5, 2); | ||
| 273 | } | ||
| 274 | |||
| 275 | pub(crate) mod _generated { | ||
| 276 | #![allow(dead_code)] | ||
| 277 | #![allow(unused_imports)] | ||
| 278 | #![allow(non_snake_case)] | ||
| 279 | #![allow(missing_docs)] | ||
| 280 | |||
| 281 | include!(concat!(env!("OUT_DIR"), "/_generated.rs")); | ||
| 282 | } | ||
diff --git a/embassy-nxp/src/fmt.rs b/embassy-nxp/src/fmt.rs new file mode 100644 index 000000000..27d41ace6 --- /dev/null +++ b/embassy-nxp/src/fmt.rs | |||
| @@ -0,0 +1,284 @@ | |||
| 1 | //! Copied from embassy-rp | ||
| 2 | |||
| 3 | #![macro_use] | ||
| 4 | #![allow(unused)] | ||
| 5 | |||
| 6 | use core::fmt::{Debug, Display, LowerHex}; | ||
| 7 | |||
| 8 | #[cfg(all(feature = "defmt", feature = "log"))] | ||
| 9 | compile_error!("You may not enable both `defmt` and `log` features."); | ||
| 10 | |||
| 11 | #[collapse_debuginfo(yes)] | ||
| 12 | macro_rules! assert { | ||
| 13 | ($($x:tt)*) => { | ||
| 14 | { | ||
| 15 | #[cfg(not(feature = "defmt"))] | ||
| 16 | ::core::assert!($($x)*); | ||
| 17 | #[cfg(feature = "defmt")] | ||
| 18 | ::defmt::assert!($($x)*); | ||
| 19 | } | ||
| 20 | }; | ||
| 21 | } | ||
| 22 | |||
| 23 | #[collapse_debuginfo(yes)] | ||
| 24 | macro_rules! assert_eq { | ||
| 25 | ($($x:tt)*) => { | ||
| 26 | { | ||
| 27 | #[cfg(not(feature = "defmt"))] | ||
| 28 | ::core::assert_eq!($($x)*); | ||
| 29 | #[cfg(feature = "defmt")] | ||
| 30 | ::defmt::assert_eq!($($x)*); | ||
| 31 | } | ||
| 32 | }; | ||
| 33 | } | ||
| 34 | |||
| 35 | #[collapse_debuginfo(yes)] | ||
| 36 | macro_rules! assert_ne { | ||
| 37 | ($($x:tt)*) => { | ||
| 38 | { | ||
| 39 | #[cfg(not(feature = "defmt"))] | ||
| 40 | ::core::assert_ne!($($x)*); | ||
| 41 | #[cfg(feature = "defmt")] | ||
| 42 | ::defmt::assert_ne!($($x)*); | ||
| 43 | } | ||
| 44 | }; | ||
| 45 | } | ||
| 46 | |||
| 47 | #[collapse_debuginfo(yes)] | ||
| 48 | macro_rules! debug_assert { | ||
| 49 | ($($x:tt)*) => { | ||
| 50 | { | ||
| 51 | #[cfg(not(feature = "defmt"))] | ||
| 52 | ::core::debug_assert!($($x)*); | ||
| 53 | #[cfg(feature = "defmt")] | ||
| 54 | ::defmt::debug_assert!($($x)*); | ||
| 55 | } | ||
| 56 | }; | ||
| 57 | } | ||
| 58 | |||
| 59 | #[collapse_debuginfo(yes)] | ||
| 60 | macro_rules! debug_assert_eq { | ||
| 61 | ($($x:tt)*) => { | ||
| 62 | { | ||
| 63 | #[cfg(not(feature = "defmt"))] | ||
| 64 | ::core::debug_assert_eq!($($x)*); | ||
| 65 | #[cfg(feature = "defmt")] | ||
| 66 | ::defmt::debug_assert_eq!($($x)*); | ||
| 67 | } | ||
| 68 | }; | ||
| 69 | } | ||
| 70 | |||
| 71 | #[collapse_debuginfo(yes)] | ||
| 72 | macro_rules! debug_assert_ne { | ||
| 73 | ($($x:tt)*) => { | ||
| 74 | { | ||
| 75 | #[cfg(not(feature = "defmt"))] | ||
| 76 | ::core::debug_assert_ne!($($x)*); | ||
| 77 | #[cfg(feature = "defmt")] | ||
| 78 | ::defmt::debug_assert_ne!($($x)*); | ||
| 79 | } | ||
| 80 | }; | ||
| 81 | } | ||
| 82 | |||
| 83 | #[collapse_debuginfo(yes)] | ||
| 84 | macro_rules! todo { | ||
| 85 | ($($x:tt)*) => { | ||
| 86 | { | ||
| 87 | #[cfg(not(feature = "defmt"))] | ||
| 88 | ::core::todo!($($x)*); | ||
| 89 | #[cfg(feature = "defmt")] | ||
| 90 | ::defmt::todo!($($x)*); | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | |||
| 95 | #[collapse_debuginfo(yes)] | ||
| 96 | macro_rules! unreachable { | ||
| 97 | ($($x:tt)*) => { | ||
| 98 | { | ||
| 99 | #[cfg(not(feature = "defmt"))] | ||
| 100 | ::core::unreachable!($($x)*); | ||
| 101 | #[cfg(feature = "defmt")] | ||
| 102 | ::defmt::unreachable!($($x)*); | ||
| 103 | } | ||
| 104 | }; | ||
| 105 | } | ||
| 106 | |||
| 107 | #[collapse_debuginfo(yes)] | ||
| 108 | macro_rules! unimplemented { | ||
| 109 | ($($x:tt)*) => { | ||
| 110 | { | ||
| 111 | #[cfg(not(feature = "defmt"))] | ||
| 112 | ::core::unimplemented!($($x)*); | ||
| 113 | #[cfg(feature = "defmt")] | ||
| 114 | ::defmt::unimplemented!($($x)*); | ||
| 115 | } | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | |||
| 119 | #[collapse_debuginfo(yes)] | ||
| 120 | macro_rules! panic { | ||
| 121 | ($($x:tt)*) => { | ||
| 122 | { | ||
| 123 | #[cfg(not(feature = "defmt"))] | ||
| 124 | ::core::panic!($($x)*); | ||
| 125 | #[cfg(feature = "defmt")] | ||
| 126 | ::defmt::panic!($($x)*); | ||
| 127 | } | ||
| 128 | }; | ||
| 129 | } | ||
| 130 | |||
| 131 | #[collapse_debuginfo(yes)] | ||
| 132 | macro_rules! trace { | ||
| 133 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 134 | { | ||
| 135 | #[cfg(feature = "log")] | ||
| 136 | ::log::trace!($s $(, $x)*); | ||
| 137 | #[cfg(feature = "defmt")] | ||
| 138 | ::defmt::trace!($s $(, $x)*); | ||
| 139 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 140 | let _ = ($( & $x ),*); | ||
| 141 | } | ||
| 142 | }; | ||
| 143 | } | ||
| 144 | |||
| 145 | #[collapse_debuginfo(yes)] | ||
| 146 | macro_rules! debug { | ||
| 147 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 148 | { | ||
| 149 | #[cfg(feature = "log")] | ||
| 150 | ::log::debug!($s $(, $x)*); | ||
| 151 | #[cfg(feature = "defmt")] | ||
| 152 | ::defmt::debug!($s $(, $x)*); | ||
| 153 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 154 | let _ = ($( & $x ),*); | ||
| 155 | } | ||
| 156 | }; | ||
| 157 | } | ||
| 158 | |||
| 159 | #[collapse_debuginfo(yes)] | ||
| 160 | macro_rules! info { | ||
| 161 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 162 | { | ||
| 163 | #[cfg(feature = "log")] | ||
| 164 | ::log::info!($s $(, $x)*); | ||
| 165 | #[cfg(feature = "defmt")] | ||
| 166 | ::defmt::info!($s $(, $x)*); | ||
| 167 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 168 | let _ = ($( & $x ),*); | ||
| 169 | } | ||
| 170 | }; | ||
| 171 | } | ||
| 172 | |||
| 173 | #[collapse_debuginfo(yes)] | ||
| 174 | macro_rules! warn { | ||
| 175 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 176 | { | ||
| 177 | #[cfg(feature = "log")] | ||
| 178 | ::log::warn!($s $(, $x)*); | ||
| 179 | #[cfg(feature = "defmt")] | ||
| 180 | ::defmt::warn!($s $(, $x)*); | ||
| 181 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 182 | let _ = ($( & $x ),*); | ||
| 183 | } | ||
| 184 | }; | ||
| 185 | } | ||
| 186 | |||
| 187 | #[collapse_debuginfo(yes)] | ||
| 188 | macro_rules! error { | ||
| 189 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 190 | { | ||
| 191 | #[cfg(feature = "log")] | ||
| 192 | ::log::error!($s $(, $x)*); | ||
| 193 | #[cfg(feature = "defmt")] | ||
| 194 | ::defmt::error!($s $(, $x)*); | ||
| 195 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 196 | let _ = ($( & $x ),*); | ||
| 197 | } | ||
| 198 | }; | ||
| 199 | } | ||
| 200 | |||
| 201 | #[cfg(feature = "defmt")] | ||
| 202 | #[collapse_debuginfo(yes)] | ||
| 203 | macro_rules! unwrap { | ||
| 204 | ($($x:tt)*) => { | ||
| 205 | ::defmt::unwrap!($($x)*) | ||
| 206 | }; | ||
| 207 | } | ||
| 208 | |||
| 209 | #[cfg(not(feature = "defmt"))] | ||
| 210 | #[collapse_debuginfo(yes)] | ||
| 211 | macro_rules! unwrap { | ||
| 212 | ($arg:expr) => { | ||
| 213 | match $crate::fmt::Try::into_result($arg) { | ||
| 214 | ::core::result::Result::Ok(t) => t, | ||
| 215 | ::core::result::Result::Err(e) => { | ||
| 216 | ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); | ||
| 217 | } | ||
| 218 | } | ||
| 219 | }; | ||
| 220 | ($arg:expr, $($msg:expr),+ $(,)? ) => { | ||
| 221 | match $crate::fmt::Try::into_result($arg) { | ||
| 222 | ::core::result::Result::Ok(t) => t, | ||
| 223 | ::core::result::Result::Err(e) => { | ||
| 224 | ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 231 | pub struct NoneError; | ||
| 232 | |||
| 233 | pub trait Try { | ||
| 234 | type Ok; | ||
| 235 | type Error; | ||
| 236 | fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||
| 237 | } | ||
| 238 | |||
| 239 | impl<T> Try for Option<T> { | ||
| 240 | type Ok = T; | ||
| 241 | type Error = NoneError; | ||
| 242 | |||
| 243 | #[inline] | ||
| 244 | fn into_result(self) -> Result<T, NoneError> { | ||
| 245 | self.ok_or(NoneError) | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | impl<T, E> Try for Result<T, E> { | ||
| 250 | type Ok = T; | ||
| 251 | type Error = E; | ||
| 252 | |||
| 253 | #[inline] | ||
| 254 | fn into_result(self) -> Self { | ||
| 255 | self | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||
| 260 | |||
| 261 | impl<'a> Debug for Bytes<'a> { | ||
| 262 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 263 | write!(f, "{:#02x?}", self.0) | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | impl<'a> Display for Bytes<'a> { | ||
| 268 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 269 | write!(f, "{:#02x?}", self.0) | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | impl<'a> LowerHex for Bytes<'a> { | ||
| 274 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 275 | write!(f, "{:#02x?}", self.0) | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | #[cfg(feature = "defmt")] | ||
| 280 | impl<'a> defmt::Format for Bytes<'a> { | ||
| 281 | fn format(&self, fmt: defmt::Formatter) { | ||
| 282 | defmt::write!(fmt, "{:02x}", self.0) | ||
| 283 | } | ||
| 284 | } | ||
diff --git a/embassy-nxp/src/gpio.rs b/embassy-nxp/src/gpio.rs index c7c78ce61..3049cc12d 100644 --- a/embassy-nxp/src/gpio.rs +++ b/embassy-nxp/src/gpio.rs | |||
| @@ -1,354 +1,7 @@ | |||
| 1 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 1 | //! General purpose input/output (GPIO) driver. |
| 2 | #![macro_use] | ||
| 2 | 3 | ||
| 3 | use crate::pac_utils::*; | 4 | #[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")] |
| 4 | use crate::{peripherals, Peri}; | 5 | #[cfg_attr(rt1xxx, path = "./gpio/rt1xxx.rs")] |
| 5 | 6 | mod inner; | |
| 6 | pub(crate) fn init() { | 7 | pub use inner::*; |
| 7 | // Enable clocks for GPIO, PINT, and IOCON | ||
| 8 | syscon_reg() | ||
| 9 | .ahbclkctrl0 | ||
| 10 | .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable()); | ||
| 11 | } | ||
| 12 | |||
| 13 | /// The GPIO pin level for pins set on "Digital" mode. | ||
| 14 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 15 | pub enum Level { | ||
| 16 | /// Logical low. Corresponds to 0V. | ||
| 17 | Low, | ||
| 18 | /// Logical high. Corresponds to VDD. | ||
| 19 | High, | ||
| 20 | } | ||
| 21 | |||
| 22 | /// Pull setting for a GPIO input set on "Digital" mode. | ||
| 23 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
| 24 | pub enum Pull { | ||
| 25 | /// No pull. | ||
| 26 | None, | ||
| 27 | /// Internal pull-up resistor. | ||
| 28 | Up, | ||
| 29 | /// Internal pull-down resistor. | ||
| 30 | Down, | ||
| 31 | } | ||
| 32 | |||
| 33 | /// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks. | ||
| 34 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 35 | pub enum Bank { | ||
| 36 | Bank0 = 0, | ||
| 37 | Bank1 = 1, | ||
| 38 | } | ||
| 39 | |||
| 40 | /// GPIO output driver. Internally, this is a specialized [Flex] pin. | ||
| 41 | pub struct Output<'d> { | ||
| 42 | pub(crate) pin: Flex<'d>, | ||
| 43 | } | ||
| 44 | |||
| 45 | impl<'d> Output<'d> { | ||
| 46 | /// Create GPIO output driver for a [Pin] with the provided [initial output](Level). | ||
| 47 | #[inline] | ||
| 48 | pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self { | ||
| 49 | let mut pin = Flex::new(pin); | ||
| 50 | pin.set_as_output(); | ||
| 51 | let mut result = Self { pin }; | ||
| 52 | |||
| 53 | match initial_output { | ||
| 54 | Level::High => result.set_high(), | ||
| 55 | Level::Low => result.set_low(), | ||
| 56 | }; | ||
| 57 | |||
| 58 | result | ||
| 59 | } | ||
| 60 | |||
| 61 | pub fn set_high(&mut self) { | ||
| 62 | gpio_reg().set[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) | ||
| 63 | } | ||
| 64 | |||
| 65 | pub fn set_low(&mut self) { | ||
| 66 | gpio_reg().clr[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) | ||
| 67 | } | ||
| 68 | |||
| 69 | pub fn toggle(&mut self) { | ||
| 70 | gpio_reg().not[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) | ||
| 71 | } | ||
| 72 | |||
| 73 | /// 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. | ||
| 75 | pub fn level(&self) -> Level { | ||
| 76 | let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); | ||
| 77 | if bits & self.pin.bit() != 0 { | ||
| 78 | Level::High | ||
| 79 | } else { | ||
| 80 | Level::Low | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /// GPIO input driver. Internally, this is a specialized [Flex] pin. | ||
| 86 | pub struct Input<'d> { | ||
| 87 | pub(crate) pin: Flex<'d>, | ||
| 88 | } | ||
| 89 | |||
| 90 | impl<'d> Input<'d> { | ||
| 91 | /// Create GPIO output driver for a [Pin] with the provided [Pull]. | ||
| 92 | #[inline] | ||
| 93 | pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self { | ||
| 94 | let mut pin = Flex::new(pin); | ||
| 95 | pin.set_as_input(); | ||
| 96 | let mut result = Self { pin }; | ||
| 97 | result.set_pull(pull); | ||
| 98 | |||
| 99 | result | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Set the pull configuration for the pin. To disable the pull, use [Pull::None]. | ||
| 103 | pub fn set_pull(&mut self, pull: Pull) { | ||
| 104 | match_iocon!(register, iocon_reg(), self.pin.pin_bank(), self.pin.pin_number(), { | ||
| 105 | register.modify(|_, w| match pull { | ||
| 106 | Pull::None => w.mode().inactive(), | ||
| 107 | Pull::Up => w.mode().pull_up(), | ||
| 108 | Pull::Down => w.mode().pull_down(), | ||
| 109 | }); | ||
| 110 | }); | ||
| 111 | } | ||
| 112 | |||
| 113 | /// Get the current input level of the pin. | ||
| 114 | pub fn read(&self) -> Level { | ||
| 115 | let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); | ||
| 116 | if bits & self.pin.bit() != 0 { | ||
| 117 | Level::High | ||
| 118 | } else { | ||
| 119 | Level::Low | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | /// A flexible GPIO (digital mode) pin whose mode is not yet determined. Under the hood, this is a | ||
| 125 | /// reference to a type-erased pin called ["AnyPin"](AnyPin). | ||
| 126 | pub struct Flex<'d> { | ||
| 127 | pub(crate) pin: Peri<'d, AnyPin>, | ||
| 128 | } | ||
| 129 | |||
| 130 | impl<'d> Flex<'d> { | ||
| 131 | /// Wrap the pin in a `Flex`. | ||
| 132 | /// | ||
| 133 | /// Note: you cannot assume that the pin will be in Digital mode after this call. | ||
| 134 | #[inline] | ||
| 135 | pub fn new(pin: Peri<'d, impl Pin>) -> Self { | ||
| 136 | Self { pin: pin.into() } | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Get the bank of this pin. See also [Bank]. | ||
| 140 | /// | ||
| 141 | /// # Example | ||
| 142 | /// | ||
| 143 | /// ``` | ||
| 144 | /// use embassy_nxp::gpio::{Bank, Flex}; | ||
| 145 | /// | ||
| 146 | /// let p = embassy_nxp::init(Default::default()); | ||
| 147 | /// let pin = Flex::new(p.PIO1_15); | ||
| 148 | /// | ||
| 149 | /// assert_eq!(pin.pin_bank(), Bank::Bank1); | ||
| 150 | /// ``` | ||
| 151 | pub fn pin_bank(&self) -> Bank { | ||
| 152 | self.pin.pin_bank() | ||
| 153 | } | ||
| 154 | |||
| 155 | /// Get the number of this pin within its bank. See also [Bank]. | ||
| 156 | /// | ||
| 157 | /// # Example | ||
| 158 | /// | ||
| 159 | /// ``` | ||
| 160 | /// use embassy_nxp::gpio::Flex; | ||
| 161 | /// | ||
| 162 | /// let p = embassy_nxp::init(Default::default()); | ||
| 163 | /// let pin = Flex::new(p.PIO1_15); | ||
| 164 | /// | ||
| 165 | /// assert_eq!(pin.pin_number(), 15 as u8); | ||
| 166 | /// ``` | ||
| 167 | pub fn pin_number(&self) -> u8 { | ||
| 168 | self.pin.pin_number() | ||
| 169 | } | ||
| 170 | |||
| 171 | /// Get the bit mask for this pin. Useful for setting or clearing bits in a register. Note: | ||
| 172 | /// PIOx_0 is bit 0, PIOx_1 is bit 1, etc. | ||
| 173 | /// | ||
| 174 | /// # Example | ||
| 175 | /// | ||
| 176 | /// ``` | ||
| 177 | /// use embassy_nxp::gpio::Flex; | ||
| 178 | /// | ||
| 179 | /// let p = embassy_nxp::init(Default::default()); | ||
| 180 | /// let pin = Flex::new(p.PIO1_3); | ||
| 181 | /// | ||
| 182 | /// assert_eq!(pin.bit(), 0b0000_1000); | ||
| 183 | /// ``` | ||
| 184 | pub fn bit(&self) -> u32 { | ||
| 185 | 1 << self.pin.pin_number() | ||
| 186 | } | ||
| 187 | |||
| 188 | /// 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. | ||
| 190 | fn set_as_digital(&mut self) { | ||
| 191 | match_iocon!(register, iocon_reg(), self.pin_bank(), self.pin_number(), { | ||
| 192 | register.modify(|_, w| w.digimode().digital()); | ||
| 193 | }); | ||
| 194 | } | ||
| 195 | |||
| 196 | /// Set the pin in output mode. This implies setting the pin to digital mode, which this | ||
| 197 | /// function handles itself. | ||
| 198 | pub fn set_as_output(&mut self) { | ||
| 199 | self.set_as_digital(); | ||
| 200 | gpio_reg().dirset[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirsetp().bits(self.bit()) }) | ||
| 201 | } | ||
| 202 | |||
| 203 | pub fn set_as_input(&mut self) { | ||
| 204 | self.set_as_digital(); | ||
| 205 | gpio_reg().dirclr[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirclrp().bits(self.bit()) }) | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. | ||
| 210 | pub(crate) trait SealedPin: Sized { | ||
| 211 | fn pin_bank(&self) -> Bank; | ||
| 212 | fn pin_number(&self) -> u8; | ||
| 213 | } | ||
| 214 | |||
| 215 | /// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an | ||
| 216 | /// [AnyPin]. By default, this trait is sealed and cannot be implemented outside of the | ||
| 217 | /// `embassy-nxp` crate due to the [SealedPin] trait. | ||
| 218 | #[allow(private_bounds)] | ||
| 219 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | ||
| 220 | /// Returns the pin number within a bank | ||
| 221 | #[inline] | ||
| 222 | fn pin(&self) -> u8 { | ||
| 223 | self.pin_number() | ||
| 224 | } | ||
| 225 | |||
| 226 | /// Returns the bank of this pin | ||
| 227 | #[inline] | ||
| 228 | fn bank(&self) -> Bank { | ||
| 229 | self.pin_bank() | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | /// Type-erased GPIO pin. | ||
| 234 | pub struct AnyPin { | ||
| 235 | pin_bank: Bank, | ||
| 236 | pin_number: u8, | ||
| 237 | } | ||
| 238 | |||
| 239 | impl AnyPin { | ||
| 240 | /// Unsafely create a new type-erased pin. | ||
| 241 | /// | ||
| 242 | /// # Safety | ||
| 243 | /// | ||
| 244 | /// You must ensure that you’re only using one instance of this type at a time. | ||
| 245 | pub unsafe fn steal(pin_bank: Bank, pin_number: u8) -> Peri<'static, Self> { | ||
| 246 | Peri::new_unchecked(Self { pin_bank, pin_number }) | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | impl_peripheral!(AnyPin); | ||
| 251 | |||
| 252 | impl Pin for AnyPin {} | ||
| 253 | impl SealedPin for AnyPin { | ||
| 254 | #[inline] | ||
| 255 | fn pin_bank(&self) -> Bank { | ||
| 256 | self.pin_bank | ||
| 257 | } | ||
| 258 | |||
| 259 | #[inline] | ||
| 260 | fn pin_number(&self) -> u8 { | ||
| 261 | self.pin_number | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 265 | macro_rules! impl_pin { | ||
| 266 | ($name:ident, $bank:expr, $pin_num:expr) => { | ||
| 267 | impl Pin for peripherals::$name {} | ||
| 268 | impl SealedPin for peripherals::$name { | ||
| 269 | #[inline] | ||
| 270 | fn pin_bank(&self) -> Bank { | ||
| 271 | $bank | ||
| 272 | } | ||
| 273 | |||
| 274 | #[inline] | ||
| 275 | fn pin_number(&self) -> u8 { | ||
| 276 | $pin_num | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | impl From<peripherals::$name> for crate::gpio::AnyPin { | ||
| 281 | fn from(val: peripherals::$name) -> Self { | ||
| 282 | Self { | ||
| 283 | pin_bank: val.pin_bank(), | ||
| 284 | pin_number: val.pin_number(), | ||
| 285 | } | ||
| 286 | } | ||
| 287 | } | ||
| 288 | }; | ||
| 289 | } | ||
| 290 | |||
| 291 | impl_pin!(PIO0_0, Bank::Bank0, 0); | ||
| 292 | impl_pin!(PIO0_1, Bank::Bank0, 1); | ||
| 293 | impl_pin!(PIO0_2, Bank::Bank0, 2); | ||
| 294 | impl_pin!(PIO0_3, Bank::Bank0, 3); | ||
| 295 | impl_pin!(PIO0_4, Bank::Bank0, 4); | ||
| 296 | impl_pin!(PIO0_5, Bank::Bank0, 5); | ||
| 297 | impl_pin!(PIO0_6, Bank::Bank0, 6); | ||
| 298 | impl_pin!(PIO0_7, Bank::Bank0, 7); | ||
| 299 | impl_pin!(PIO0_8, Bank::Bank0, 8); | ||
| 300 | impl_pin!(PIO0_9, Bank::Bank0, 9); | ||
| 301 | impl_pin!(PIO0_10, Bank::Bank0, 10); | ||
| 302 | impl_pin!(PIO0_11, Bank::Bank0, 11); | ||
| 303 | impl_pin!(PIO0_12, Bank::Bank0, 12); | ||
| 304 | impl_pin!(PIO0_13, Bank::Bank0, 13); | ||
| 305 | impl_pin!(PIO0_14, Bank::Bank0, 14); | ||
| 306 | impl_pin!(PIO0_15, Bank::Bank0, 15); | ||
| 307 | impl_pin!(PIO0_16, Bank::Bank0, 16); | ||
| 308 | impl_pin!(PIO0_17, Bank::Bank0, 17); | ||
| 309 | impl_pin!(PIO0_18, Bank::Bank0, 18); | ||
| 310 | impl_pin!(PIO0_19, Bank::Bank0, 19); | ||
| 311 | impl_pin!(PIO0_20, Bank::Bank0, 20); | ||
| 312 | impl_pin!(PIO0_21, Bank::Bank0, 21); | ||
| 313 | impl_pin!(PIO0_22, Bank::Bank0, 22); | ||
| 314 | impl_pin!(PIO0_23, Bank::Bank0, 23); | ||
| 315 | impl_pin!(PIO0_24, Bank::Bank0, 24); | ||
| 316 | impl_pin!(PIO0_25, Bank::Bank0, 25); | ||
| 317 | impl_pin!(PIO0_26, Bank::Bank0, 26); | ||
| 318 | impl_pin!(PIO0_27, Bank::Bank0, 27); | ||
| 319 | impl_pin!(PIO0_28, Bank::Bank0, 28); | ||
| 320 | impl_pin!(PIO0_29, Bank::Bank0, 29); | ||
| 321 | impl_pin!(PIO0_30, Bank::Bank0, 30); | ||
| 322 | impl_pin!(PIO0_31, Bank::Bank0, 31); | ||
| 323 | impl_pin!(PIO1_0, Bank::Bank1, 0); | ||
| 324 | impl_pin!(PIO1_1, Bank::Bank1, 1); | ||
| 325 | impl_pin!(PIO1_2, Bank::Bank1, 2); | ||
| 326 | impl_pin!(PIO1_3, Bank::Bank1, 3); | ||
| 327 | impl_pin!(PIO1_4, Bank::Bank1, 4); | ||
| 328 | impl_pin!(PIO1_5, Bank::Bank1, 5); | ||
| 329 | impl_pin!(PIO1_6, Bank::Bank1, 6); | ||
| 330 | impl_pin!(PIO1_7, Bank::Bank1, 7); | ||
| 331 | impl_pin!(PIO1_8, Bank::Bank1, 8); | ||
| 332 | impl_pin!(PIO1_9, Bank::Bank1, 9); | ||
| 333 | impl_pin!(PIO1_10, Bank::Bank1, 10); | ||
| 334 | impl_pin!(PIO1_11, Bank::Bank1, 11); | ||
| 335 | impl_pin!(PIO1_12, Bank::Bank1, 12); | ||
| 336 | impl_pin!(PIO1_13, Bank::Bank1, 13); | ||
| 337 | impl_pin!(PIO1_14, Bank::Bank1, 14); | ||
| 338 | impl_pin!(PIO1_15, Bank::Bank1, 15); | ||
| 339 | impl_pin!(PIO1_16, Bank::Bank1, 16); | ||
| 340 | impl_pin!(PIO1_17, Bank::Bank1, 17); | ||
| 341 | impl_pin!(PIO1_18, Bank::Bank1, 18); | ||
| 342 | impl_pin!(PIO1_19, Bank::Bank1, 19); | ||
| 343 | impl_pin!(PIO1_20, Bank::Bank1, 20); | ||
| 344 | impl_pin!(PIO1_21, Bank::Bank1, 21); | ||
| 345 | impl_pin!(PIO1_22, Bank::Bank1, 22); | ||
| 346 | impl_pin!(PIO1_23, Bank::Bank1, 23); | ||
| 347 | impl_pin!(PIO1_24, Bank::Bank1, 24); | ||
| 348 | impl_pin!(PIO1_25, Bank::Bank1, 25); | ||
| 349 | impl_pin!(PIO1_26, Bank::Bank1, 26); | ||
| 350 | impl_pin!(PIO1_27, Bank::Bank1, 27); | ||
| 351 | impl_pin!(PIO1_28, Bank::Bank1, 28); | ||
| 352 | impl_pin!(PIO1_29, Bank::Bank1, 29); | ||
| 353 | impl_pin!(PIO1_30, Bank::Bank1, 30); | ||
| 354 | impl_pin!(PIO1_31, Bank::Bank1, 31); | ||
diff --git a/embassy-nxp/src/pac_utils.rs b/embassy-nxp/src/gpio/lpc55.rs index 86a807f6c..8f407bb3a 100644 --- a/embassy-nxp/src/pac_utils.rs +++ b/embassy-nxp/src/gpio/lpc55.rs | |||
| @@ -1,3 +1,267 @@ | |||
| 1 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | ||
| 2 | |||
| 3 | use crate::{peripherals, Peri}; | ||
| 4 | |||
| 5 | pub(crate) fn init() { | ||
| 6 | // Enable clocks for GPIO, PINT, and IOCON | ||
| 7 | syscon_reg() | ||
| 8 | .ahbclkctrl0 | ||
| 9 | .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable()); | ||
| 10 | info!("GPIO initialized"); | ||
| 11 | } | ||
| 12 | |||
| 13 | /// The GPIO pin level for pins set on "Digital" mode. | ||
| 14 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 15 | pub enum Level { | ||
| 16 | /// Logical low. Corresponds to 0V. | ||
| 17 | Low, | ||
| 18 | /// Logical high. Corresponds to VDD. | ||
| 19 | High, | ||
| 20 | } | ||
| 21 | |||
| 22 | /// Pull setting for a GPIO input set on "Digital" mode. | ||
| 23 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
| 24 | pub enum Pull { | ||
| 25 | /// No pull. | ||
| 26 | None, | ||
| 27 | /// Internal pull-up resistor. | ||
| 28 | Up, | ||
| 29 | /// Internal pull-down resistor. | ||
| 30 | Down, | ||
| 31 | } | ||
| 32 | |||
| 33 | /// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks. | ||
| 34 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 35 | pub enum Bank { | ||
| 36 | Bank0 = 0, | ||
| 37 | Bank1 = 1, | ||
| 38 | } | ||
| 39 | |||
| 40 | /// GPIO output driver. Internally, this is a specialized [Flex] pin. | ||
| 41 | pub struct Output<'d> { | ||
| 42 | pub(crate) pin: Flex<'d>, | ||
| 43 | } | ||
| 44 | |||
| 45 | impl<'d> Output<'d> { | ||
| 46 | /// Create GPIO output driver for a [Pin] with the provided [initial output](Level). | ||
| 47 | #[inline] | ||
| 48 | pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self { | ||
| 49 | let mut pin = Flex::new(pin); | ||
| 50 | pin.set_as_output(); | ||
| 51 | let mut result = Self { pin }; | ||
| 52 | |||
| 53 | match initial_output { | ||
| 54 | Level::High => result.set_high(), | ||
| 55 | Level::Low => result.set_low(), | ||
| 56 | }; | ||
| 57 | |||
| 58 | result | ||
| 59 | } | ||
| 60 | |||
| 61 | pub fn set_high(&mut self) { | ||
| 62 | gpio_reg().set[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) | ||
| 63 | } | ||
| 64 | |||
| 65 | pub fn set_low(&mut self) { | ||
| 66 | gpio_reg().clr[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) | ||
| 67 | } | ||
| 68 | |||
| 69 | pub fn toggle(&mut self) { | ||
| 70 | gpio_reg().not[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) | ||
| 71 | } | ||
| 72 | |||
| 73 | /// 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. | ||
| 75 | pub fn level(&self) -> Level { | ||
| 76 | let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); | ||
| 77 | if bits & self.pin.bit() != 0 { | ||
| 78 | Level::High | ||
| 79 | } else { | ||
| 80 | Level::Low | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /// GPIO input driver. Internally, this is a specialized [Flex] pin. | ||
| 86 | pub struct Input<'d> { | ||
| 87 | pub(crate) pin: Flex<'d>, | ||
| 88 | } | ||
| 89 | |||
| 90 | impl<'d> Input<'d> { | ||
| 91 | /// Create GPIO output driver for a [Pin] with the provided [Pull]. | ||
| 92 | #[inline] | ||
| 93 | pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self { | ||
| 94 | let mut pin = Flex::new(pin); | ||
| 95 | pin.set_as_input(); | ||
| 96 | let mut result = Self { pin }; | ||
| 97 | result.set_pull(pull); | ||
| 98 | |||
| 99 | result | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Set the pull configuration for the pin. To disable the pull, use [Pull::None]. | ||
| 103 | pub fn set_pull(&mut self, pull: Pull) { | ||
| 104 | match_iocon!(register, iocon_reg(), self.pin.pin_bank(), self.pin.pin_number(), { | ||
| 105 | register.modify(|_, w| match pull { | ||
| 106 | Pull::None => w.mode().inactive(), | ||
| 107 | Pull::Up => w.mode().pull_up(), | ||
| 108 | Pull::Down => w.mode().pull_down(), | ||
| 109 | }); | ||
| 110 | }); | ||
| 111 | } | ||
| 112 | |||
| 113 | /// Get the current input level of the pin. | ||
| 114 | pub fn read(&self) -> Level { | ||
| 115 | let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); | ||
| 116 | if bits & self.pin.bit() != 0 { | ||
| 117 | Level::High | ||
| 118 | } else { | ||
| 119 | Level::Low | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | /// A flexible GPIO (digital mode) pin whose mode is not yet determined. Under the hood, this is a | ||
| 125 | /// reference to a type-erased pin called ["AnyPin"](AnyPin). | ||
| 126 | pub struct Flex<'d> { | ||
| 127 | pub(crate) pin: Peri<'d, AnyPin>, | ||
| 128 | } | ||
| 129 | |||
| 130 | impl<'d> Flex<'d> { | ||
| 131 | /// Wrap the pin in a `Flex`. | ||
| 132 | /// | ||
| 133 | /// Note: you cannot assume that the pin will be in Digital mode after this call. | ||
| 134 | #[inline] | ||
| 135 | pub fn new(pin: Peri<'d, impl Pin>) -> Self { | ||
| 136 | Self { pin: pin.into() } | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Get the bank of this pin. See also [Bank]. | ||
| 140 | /// | ||
| 141 | /// # Example | ||
| 142 | /// | ||
| 143 | /// ``` | ||
| 144 | /// use embassy_nxp::gpio::{Bank, Flex}; | ||
| 145 | /// | ||
| 146 | /// let p = embassy_nxp::init(Default::default()); | ||
| 147 | /// let pin = Flex::new(p.PIO1_15); | ||
| 148 | /// | ||
| 149 | /// assert_eq!(pin.pin_bank(), Bank::Bank1); | ||
| 150 | /// ``` | ||
| 151 | pub fn pin_bank(&self) -> Bank { | ||
| 152 | self.pin.pin_bank() | ||
| 153 | } | ||
| 154 | |||
| 155 | /// Get the number of this pin within its bank. See also [Bank]. | ||
| 156 | /// | ||
| 157 | /// # Example | ||
| 158 | /// | ||
| 159 | /// ``` | ||
| 160 | /// use embassy_nxp::gpio::Flex; | ||
| 161 | /// | ||
| 162 | /// let p = embassy_nxp::init(Default::default()); | ||
| 163 | /// let pin = Flex::new(p.PIO1_15); | ||
| 164 | /// | ||
| 165 | /// assert_eq!(pin.pin_number(), 15 as u8); | ||
| 166 | /// ``` | ||
| 167 | pub fn pin_number(&self) -> u8 { | ||
| 168 | self.pin.pin_number() | ||
| 169 | } | ||
| 170 | |||
| 171 | /// Get the bit mask for this pin. Useful for setting or clearing bits in a register. Note: | ||
| 172 | /// PIOx_0 is bit 0, PIOx_1 is bit 1, etc. | ||
| 173 | /// | ||
| 174 | /// # Example | ||
| 175 | /// | ||
| 176 | /// ``` | ||
| 177 | /// use embassy_nxp::gpio::Flex; | ||
| 178 | /// | ||
| 179 | /// let p = embassy_nxp::init(Default::default()); | ||
| 180 | /// let pin = Flex::new(p.PIO1_3); | ||
| 181 | /// | ||
| 182 | /// assert_eq!(pin.bit(), 0b0000_1000); | ||
| 183 | /// ``` | ||
| 184 | pub fn bit(&self) -> u32 { | ||
| 185 | 1 << self.pin.pin_number() | ||
| 186 | } | ||
| 187 | |||
| 188 | /// 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. | ||
| 190 | fn set_as_digital(&mut self) { | ||
| 191 | match_iocon!(register, iocon_reg(), self.pin_bank(), self.pin_number(), { | ||
| 192 | register.modify(|_, w| w.digimode().digital()); | ||
| 193 | }); | ||
| 194 | } | ||
| 195 | |||
| 196 | /// Set the pin in output mode. This implies setting the pin to digital mode, which this | ||
| 197 | /// function handles itself. | ||
| 198 | pub fn set_as_output(&mut self) { | ||
| 199 | self.set_as_digital(); | ||
| 200 | gpio_reg().dirset[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirsetp().bits(self.bit()) }) | ||
| 201 | } | ||
| 202 | |||
| 203 | pub fn set_as_input(&mut self) { | ||
| 204 | self.set_as_digital(); | ||
| 205 | gpio_reg().dirclr[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirclrp().bits(self.bit()) }) | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. | ||
| 210 | pub(crate) trait SealedPin: Sized { | ||
| 211 | fn pin_bank(&self) -> Bank; | ||
| 212 | fn pin_number(&self) -> u8; | ||
| 213 | } | ||
| 214 | |||
| 215 | /// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an | ||
| 216 | /// [AnyPin]. By default, this trait is sealed and cannot be implemented outside of the | ||
| 217 | /// `embassy-nxp` crate due to the [SealedPin] trait. | ||
| 218 | #[allow(private_bounds)] | ||
| 219 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | ||
| 220 | /// Returns the pin number within a bank | ||
| 221 | #[inline] | ||
| 222 | fn pin(&self) -> u8 { | ||
| 223 | self.pin_number() | ||
| 224 | } | ||
| 225 | |||
| 226 | /// Returns the bank of this pin | ||
| 227 | #[inline] | ||
| 228 | fn bank(&self) -> Bank { | ||
| 229 | self.pin_bank() | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | /// Type-erased GPIO pin. | ||
| 234 | pub struct AnyPin { | ||
| 235 | pin_bank: Bank, | ||
| 236 | pin_number: u8, | ||
| 237 | } | ||
| 238 | |||
| 239 | impl AnyPin { | ||
| 240 | /// Unsafely create a new type-erased pin. | ||
| 241 | /// | ||
| 242 | /// # Safety | ||
| 243 | /// | ||
| 244 | /// You must ensure that you’re only using one instance of this type at a time. | ||
| 245 | pub unsafe fn steal(pin_bank: Bank, pin_number: u8) -> Peri<'static, Self> { | ||
| 246 | Peri::new_unchecked(Self { pin_bank, pin_number }) | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | impl_peripheral!(AnyPin); | ||
| 251 | |||
| 252 | impl Pin for AnyPin {} | ||
| 253 | impl SealedPin for AnyPin { | ||
| 254 | #[inline] | ||
| 255 | fn pin_bank(&self) -> Bank { | ||
| 256 | self.pin_bank | ||
| 257 | } | ||
| 258 | |||
| 259 | #[inline] | ||
| 260 | fn pin_number(&self) -> u8 { | ||
| 261 | self.pin_number | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 1 | /// Get the GPIO register block. This is used to configure all GPIO pins. | 265 | /// Get the GPIO register block. This is used to configure all GPIO pins. |
| 2 | /// | 266 | /// |
| 3 | /// # Safety | 267 | /// # Safety |
| @@ -321,3 +585,94 @@ macro_rules! match_iocon { | |||
| 321 | } | 585 | } |
| 322 | 586 | ||
| 323 | pub(crate) use match_iocon; | 587 | pub(crate) use match_iocon; |
| 588 | |||
| 589 | macro_rules! impl_pin { | ||
| 590 | ($name:ident, $bank:expr, $pin_num:expr) => { | ||
| 591 | impl Pin for peripherals::$name {} | ||
| 592 | impl SealedPin for peripherals::$name { | ||
| 593 | #[inline] | ||
| 594 | fn pin_bank(&self) -> Bank { | ||
| 595 | $bank | ||
| 596 | } | ||
| 597 | |||
| 598 | #[inline] | ||
| 599 | fn pin_number(&self) -> u8 { | ||
| 600 | $pin_num | ||
| 601 | } | ||
| 602 | } | ||
| 603 | |||
| 604 | impl From<peripherals::$name> for crate::gpio::AnyPin { | ||
| 605 | fn from(val: peripherals::$name) -> Self { | ||
| 606 | Self { | ||
| 607 | pin_bank: val.pin_bank(), | ||
| 608 | pin_number: val.pin_number(), | ||
| 609 | } | ||
| 610 | } | ||
| 611 | } | ||
| 612 | }; | ||
| 613 | } | ||
| 614 | |||
| 615 | impl_pin!(PIO0_0, Bank::Bank0, 0); | ||
| 616 | impl_pin!(PIO0_1, Bank::Bank0, 1); | ||
| 617 | impl_pin!(PIO0_2, Bank::Bank0, 2); | ||
| 618 | impl_pin!(PIO0_3, Bank::Bank0, 3); | ||
| 619 | impl_pin!(PIO0_4, Bank::Bank0, 4); | ||
| 620 | impl_pin!(PIO0_5, Bank::Bank0, 5); | ||
| 621 | impl_pin!(PIO0_6, Bank::Bank0, 6); | ||
| 622 | impl_pin!(PIO0_7, Bank::Bank0, 7); | ||
| 623 | impl_pin!(PIO0_8, Bank::Bank0, 8); | ||
| 624 | impl_pin!(PIO0_9, Bank::Bank0, 9); | ||
| 625 | impl_pin!(PIO0_10, Bank::Bank0, 10); | ||
| 626 | impl_pin!(PIO0_11, Bank::Bank0, 11); | ||
| 627 | impl_pin!(PIO0_12, Bank::Bank0, 12); | ||
| 628 | impl_pin!(PIO0_13, Bank::Bank0, 13); | ||
| 629 | impl_pin!(PIO0_14, Bank::Bank0, 14); | ||
| 630 | impl_pin!(PIO0_15, Bank::Bank0, 15); | ||
| 631 | impl_pin!(PIO0_16, Bank::Bank0, 16); | ||
| 632 | impl_pin!(PIO0_17, Bank::Bank0, 17); | ||
| 633 | impl_pin!(PIO0_18, Bank::Bank0, 18); | ||
| 634 | impl_pin!(PIO0_19, Bank::Bank0, 19); | ||
| 635 | impl_pin!(PIO0_20, Bank::Bank0, 20); | ||
| 636 | impl_pin!(PIO0_21, Bank::Bank0, 21); | ||
| 637 | impl_pin!(PIO0_22, Bank::Bank0, 22); | ||
| 638 | impl_pin!(PIO0_23, Bank::Bank0, 23); | ||
| 639 | impl_pin!(PIO0_24, Bank::Bank0, 24); | ||
| 640 | impl_pin!(PIO0_25, Bank::Bank0, 25); | ||
| 641 | impl_pin!(PIO0_26, Bank::Bank0, 26); | ||
| 642 | impl_pin!(PIO0_27, Bank::Bank0, 27); | ||
| 643 | impl_pin!(PIO0_28, Bank::Bank0, 28); | ||
| 644 | impl_pin!(PIO0_29, Bank::Bank0, 29); | ||
| 645 | impl_pin!(PIO0_30, Bank::Bank0, 30); | ||
| 646 | impl_pin!(PIO0_31, Bank::Bank0, 31); | ||
| 647 | impl_pin!(PIO1_0, Bank::Bank1, 0); | ||
| 648 | impl_pin!(PIO1_1, Bank::Bank1, 1); | ||
| 649 | impl_pin!(PIO1_2, Bank::Bank1, 2); | ||
| 650 | impl_pin!(PIO1_3, Bank::Bank1, 3); | ||
| 651 | impl_pin!(PIO1_4, Bank::Bank1, 4); | ||
| 652 | impl_pin!(PIO1_5, Bank::Bank1, 5); | ||
| 653 | impl_pin!(PIO1_6, Bank::Bank1, 6); | ||
| 654 | impl_pin!(PIO1_7, Bank::Bank1, 7); | ||
| 655 | impl_pin!(PIO1_8, Bank::Bank1, 8); | ||
| 656 | impl_pin!(PIO1_9, Bank::Bank1, 9); | ||
| 657 | impl_pin!(PIO1_10, Bank::Bank1, 10); | ||
| 658 | impl_pin!(PIO1_11, Bank::Bank1, 11); | ||
| 659 | impl_pin!(PIO1_12, Bank::Bank1, 12); | ||
| 660 | impl_pin!(PIO1_13, Bank::Bank1, 13); | ||
| 661 | impl_pin!(PIO1_14, Bank::Bank1, 14); | ||
| 662 | impl_pin!(PIO1_15, Bank::Bank1, 15); | ||
| 663 | impl_pin!(PIO1_16, Bank::Bank1, 16); | ||
| 664 | impl_pin!(PIO1_17, Bank::Bank1, 17); | ||
| 665 | impl_pin!(PIO1_18, Bank::Bank1, 18); | ||
| 666 | impl_pin!(PIO1_19, Bank::Bank1, 19); | ||
| 667 | impl_pin!(PIO1_20, Bank::Bank1, 20); | ||
| 668 | impl_pin!(PIO1_21, Bank::Bank1, 21); | ||
| 669 | impl_pin!(PIO1_22, Bank::Bank1, 22); | ||
| 670 | impl_pin!(PIO1_23, Bank::Bank1, 23); | ||
| 671 | impl_pin!(PIO1_24, Bank::Bank1, 24); | ||
| 672 | impl_pin!(PIO1_25, Bank::Bank1, 25); | ||
| 673 | impl_pin!(PIO1_26, Bank::Bank1, 26); | ||
| 674 | impl_pin!(PIO1_27, Bank::Bank1, 27); | ||
| 675 | impl_pin!(PIO1_28, Bank::Bank1, 28); | ||
| 676 | impl_pin!(PIO1_29, Bank::Bank1, 29); | ||
| 677 | impl_pin!(PIO1_30, Bank::Bank1, 30); | ||
| 678 | impl_pin!(PIO1_31, Bank::Bank1, 31); | ||
diff --git a/embassy-nxp/src/gpio/rt1xxx.rs b/embassy-nxp/src/gpio/rt1xxx.rs new file mode 100644 index 000000000..1d60a0d51 --- /dev/null +++ b/embassy-nxp/src/gpio/rt1xxx.rs | |||
| @@ -0,0 +1,945 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | use core::future::Future; | ||
| 4 | use core::ops::Not; | ||
| 5 | use core::pin::Pin as FuturePin; | ||
| 6 | use core::task::{Context, Poll}; | ||
| 7 | |||
| 8 | use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; | ||
| 9 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 10 | use nxp_pac::gpio::vals::Icr; | ||
| 11 | use nxp_pac::iomuxc::vals::Pus; | ||
| 12 | |||
| 13 | use crate::chip::{mux_address, pad_address}; | ||
| 14 | use crate::pac::common::{Reg, RW}; | ||
| 15 | use crate::pac::gpio::Gpio; | ||
| 16 | #[cfg(feature = "rt")] | ||
| 17 | use crate::pac::interrupt; | ||
| 18 | use crate::pac::iomuxc::regs::{Ctl, MuxCtl}; | ||
| 19 | use crate::pac::{self}; | ||
| 20 | |||
| 21 | /// The GPIO pin level for pins set on "Digital" mode. | ||
| 22 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | ||
| 23 | pub enum Level { | ||
| 24 | /// Logical low. Corresponds to 0V. | ||
| 25 | Low, | ||
| 26 | /// Logical high. Corresponds to VDD. | ||
| 27 | High, | ||
| 28 | } | ||
| 29 | |||
| 30 | impl From<bool> for Level { | ||
| 31 | fn from(val: bool) -> Self { | ||
| 32 | match val { | ||
| 33 | true => Self::High, | ||
| 34 | false => Self::Low, | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | impl From<Level> for bool { | ||
| 40 | fn from(level: Level) -> bool { | ||
| 41 | match level { | ||
| 42 | Level::Low => false, | ||
| 43 | Level::High => true, | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | impl Not for Level { | ||
| 49 | type Output = Self; | ||
| 50 | |||
| 51 | fn not(self) -> Self::Output { | ||
| 52 | match self { | ||
| 53 | Level::Low => Level::High, | ||
| 54 | Level::High => Level::Low, | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Pull setting for a GPIO input set on "Digital" mode. | ||
| 60 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
| 61 | pub enum Pull { | ||
| 62 | /// No pull. | ||
| 63 | None, | ||
| 64 | |||
| 65 | // TODO: What Does PUE::KEEPER mean here? | ||
| 66 | |||
| 67 | // 22 kOhm pull-up resistor. | ||
| 68 | Up22K, | ||
| 69 | |||
| 70 | // 47 kOhm pull-up resistor. | ||
| 71 | Up47K, | ||
| 72 | |||
| 73 | // 100 kOhm pull-up resistor. | ||
| 74 | Up100K, | ||
| 75 | |||
| 76 | // 100 kOhm pull-down resistor. | ||
| 77 | Down100K, | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Drive strength of an output | ||
| 81 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
| 82 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 83 | pub enum Drive { | ||
| 84 | Disabled, | ||
| 85 | _150R, | ||
| 86 | _75R, | ||
| 87 | _50R, | ||
| 88 | _37R, | ||
| 89 | _30R, | ||
| 90 | _25R, | ||
| 91 | _20R, | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Slew rate of an output | ||
| 95 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
| 96 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 97 | pub enum SlewRate { | ||
| 98 | Slow, | ||
| 99 | |||
| 100 | Fast, | ||
| 101 | } | ||
| 102 | |||
| 103 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 104 | pub enum Bank { | ||
| 105 | /// Bank 1 | ||
| 106 | #[cfg(gpio1)] | ||
| 107 | Gpio1, | ||
| 108 | |||
| 109 | /// Bank 2 | ||
| 110 | #[cfg(gpio2)] | ||
| 111 | Gpio2, | ||
| 112 | |||
| 113 | /// Bank 3 | ||
| 114 | #[cfg(gpio3)] | ||
| 115 | Gpio3, | ||
| 116 | |||
| 117 | /// Bank 4 | ||
| 118 | #[cfg(gpio4)] | ||
| 119 | Gpio4, | ||
| 120 | |||
| 121 | /// Bank 5 | ||
| 122 | #[cfg(gpio5)] | ||
| 123 | Gpio5, | ||
| 124 | } | ||
| 125 | |||
| 126 | /// GPIO flexible pin. | ||
| 127 | /// | ||
| 128 | /// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain | ||
| 129 | /// set while not in output mode, so the pin's level will be 'remembered' when it is not in output | ||
| 130 | /// mode. | ||
| 131 | pub struct Flex<'d> { | ||
| 132 | pub(crate) pin: Peri<'d, AnyPin>, | ||
| 133 | } | ||
| 134 | |||
| 135 | impl<'d> Flex<'d> { | ||
| 136 | /// Wrap the pin in a `Flex`. | ||
| 137 | /// | ||
| 138 | /// The pin remains disconnected. The initial output level is unspecified, but can be changed | ||
| 139 | /// before the pin is put into output mode. | ||
| 140 | #[inline] | ||
| 141 | pub fn new(pin: Peri<'d, impl Pin>) -> Self { | ||
| 142 | Self { pin: pin.into() } | ||
| 143 | } | ||
| 144 | |||
| 145 | /// Set the pin's pull. | ||
| 146 | #[inline] | ||
| 147 | pub fn set_pull(&mut self, pull: Pull) { | ||
| 148 | let (pke, pue, pus) = match pull { | ||
| 149 | Pull::None => (false, true, Pus::PUS_0_100K_OHM_PULL_DOWN), | ||
| 150 | Pull::Up22K => (true, true, Pus::PUS_3_22K_OHM_PULL_UP), | ||
| 151 | Pull::Up47K => (true, true, Pus::PUS_1_47K_OHM_PULL_UP), | ||
| 152 | Pull::Up100K => (true, true, Pus::PUS_2_100K_OHM_PULL_UP), | ||
| 153 | Pull::Down100K => (true, true, Pus::PUS_0_100K_OHM_PULL_DOWN), | ||
| 154 | }; | ||
| 155 | |||
| 156 | self.pin.pad().modify(|w| { | ||
| 157 | w.set_pke(pke); | ||
| 158 | w.set_pue(pue); | ||
| 159 | w.set_pus(pus); | ||
| 160 | }); | ||
| 161 | } | ||
| 162 | |||
| 163 | // Set the pin's slew rate. | ||
| 164 | #[inline] | ||
| 165 | pub fn set_slewrate(&mut self, rate: SlewRate) { | ||
| 166 | self.pin.pad().modify(|w| { | ||
| 167 | w.set_sre(match rate { | ||
| 168 | SlewRate::Slow => false, | ||
| 169 | SlewRate::Fast => true, | ||
| 170 | }); | ||
| 171 | }); | ||
| 172 | } | ||
| 173 | |||
| 174 | /// Set the pin's Schmitt trigger. | ||
| 175 | #[inline] | ||
| 176 | pub fn set_schmitt(&mut self, enable: bool) { | ||
| 177 | self.pin.pad().modify(|w| { | ||
| 178 | w.set_hys(enable); | ||
| 179 | }); | ||
| 180 | } | ||
| 181 | |||
| 182 | /// Put the pin into input mode. | ||
| 183 | /// | ||
| 184 | /// The pull setting is left unchanged. | ||
| 185 | #[inline] | ||
| 186 | pub fn set_as_input(&mut self) { | ||
| 187 | self.pin.mux().modify(|w| { | ||
| 188 | w.set_mux_mode(GPIO_MUX_MODE); | ||
| 189 | }); | ||
| 190 | |||
| 191 | // Setting direction is RMW | ||
| 192 | critical_section::with(|_cs| { | ||
| 193 | self.pin.block().gdir().modify(|w| { | ||
| 194 | w.set_gdir(self.pin.pin_number() as usize, false); | ||
| 195 | }); | ||
| 196 | }) | ||
| 197 | } | ||
| 198 | |||
| 199 | /// Put the pin into output mode. | ||
| 200 | /// | ||
| 201 | /// The pin level will be whatever was set before (or low by default). If you want it to begin | ||
| 202 | /// at a specific level, call `set_high`/`set_low` on the pin first. | ||
| 203 | #[inline] | ||
| 204 | pub fn set_as_output(&mut self) { | ||
| 205 | self.pin.mux().modify(|w| { | ||
| 206 | w.set_mux_mode(GPIO_MUX_MODE); | ||
| 207 | }); | ||
| 208 | |||
| 209 | // Setting direction is RMW | ||
| 210 | critical_section::with(|_cs| { | ||
| 211 | self.pin.block().gdir().modify(|w| { | ||
| 212 | w.set_gdir(self.pin.pin_number() as usize, true); | ||
| 213 | }); | ||
| 214 | }) | ||
| 215 | } | ||
| 216 | |||
| 217 | /// Put the pin into input + open-drain output mode. | ||
| 218 | /// | ||
| 219 | /// The hardware will drive the line low if you set it to low, and will leave it floating if you set | ||
| 220 | /// it to high, in which case you can read the input to figure out whether another device | ||
| 221 | /// is driving the line low. | ||
| 222 | /// | ||
| 223 | /// The pin level will be whatever was set before (or low by default). If you want it to begin | ||
| 224 | /// at a specific level, call `set_high`/`set_low` on the pin first. | ||
| 225 | /// | ||
| 226 | /// The internal weak pull-up and pull-down resistors will be disabled. | ||
| 227 | #[inline] | ||
| 228 | pub fn set_as_input_output(&mut self) { | ||
| 229 | self.pin.pad().modify(|w| { | ||
| 230 | w.set_ode(true); | ||
| 231 | }); | ||
| 232 | } | ||
| 233 | |||
| 234 | /// Set the pin as "disconnected", ie doing nothing and consuming the lowest | ||
| 235 | /// amount of power possible. | ||
| 236 | /// | ||
| 237 | /// This is currently the same as [`Self::set_as_analog()`] but is semantically different | ||
| 238 | /// really. Drivers should `set_as_disconnected()` pins when dropped. | ||
| 239 | /// | ||
| 240 | /// Note that this also disables the pull-up and pull-down resistors. | ||
| 241 | #[inline] | ||
| 242 | pub fn set_as_disconnected(&mut self) { | ||
| 243 | self.pin.pad().modify(|w| { | ||
| 244 | w.set_ode(false); | ||
| 245 | w.set_pke(false); | ||
| 246 | w.set_pue(false); | ||
| 247 | w.set_pus(Pus::PUS_0_100K_OHM_PULL_DOWN); | ||
| 248 | }); | ||
| 249 | } | ||
| 250 | |||
| 251 | /// Get whether the pin input level is high. | ||
| 252 | #[inline] | ||
| 253 | pub fn is_high(&self) -> bool { | ||
| 254 | self.pin.block().psr().read().psr(self.pin.pin_number() as usize) | ||
| 255 | } | ||
| 256 | |||
| 257 | /// Get whether the pin input level is low. | ||
| 258 | #[inline] | ||
| 259 | pub fn is_low(&self) -> bool { | ||
| 260 | !self.is_high() | ||
| 261 | } | ||
| 262 | |||
| 263 | /// Returns current pin level | ||
| 264 | #[inline] | ||
| 265 | pub fn get_level(&self) -> Level { | ||
| 266 | self.is_high().into() | ||
| 267 | } | ||
| 268 | |||
| 269 | /// Set the output as high. | ||
| 270 | #[inline] | ||
| 271 | pub fn set_high(&mut self) { | ||
| 272 | self.pin.block().dr_set().write(|w| { | ||
| 273 | w.set_dr_set(self.pin.pin_number() as usize, true); | ||
| 274 | }); | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Set the output as low. | ||
| 278 | #[inline] | ||
| 279 | pub fn set_low(&mut self) { | ||
| 280 | self.pin.block().dr_clear().write(|w| { | ||
| 281 | w.set_dr_clear(self.pin.pin_number() as usize, true); | ||
| 282 | }); | ||
| 283 | } | ||
| 284 | |||
| 285 | /// Toggle pin output | ||
| 286 | #[inline] | ||
| 287 | pub fn toggle(&mut self) { | ||
| 288 | self.pin.block().dr_toggle().write(|w| { | ||
| 289 | w.set_dr_toggle(self.pin.pin_number() as usize, true); | ||
| 290 | }); | ||
| 291 | } | ||
| 292 | |||
| 293 | /// Set the output level. | ||
| 294 | #[inline] | ||
| 295 | pub fn set_level(&mut self, level: Level) { | ||
| 296 | match level { | ||
| 297 | Level::Low => self.set_low(), | ||
| 298 | Level::High => self.set_high(), | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | /// Get the current pin output level. | ||
| 303 | #[inline] | ||
| 304 | pub fn get_output_level(&self) -> Level { | ||
| 305 | self.is_set_high().into() | ||
| 306 | } | ||
| 307 | |||
| 308 | /// Is the output level high? | ||
| 309 | /// | ||
| 310 | /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_high`]. | ||
| 311 | #[inline] | ||
| 312 | pub fn is_set_high(&self) -> bool { | ||
| 313 | self.pin.block().dr().read().dr(self.pin.pin_number() as usize) | ||
| 314 | } | ||
| 315 | |||
| 316 | /// Is the output level low? | ||
| 317 | /// | ||
| 318 | /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_low`]. | ||
| 319 | #[inline] | ||
| 320 | pub fn is_set_low(&self) -> bool { | ||
| 321 | !self.is_set_high() | ||
| 322 | } | ||
| 323 | |||
| 324 | /// Wait until the pin is high. If it is already high, return immediately. | ||
| 325 | #[inline] | ||
| 326 | pub async fn wait_for_high(&mut self) { | ||
| 327 | InputFuture::new(self.pin.reborrow(), InterruptConfiguration::High).await | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Wait until the pin is low. If it is already low, return immediately. | ||
| 331 | #[inline] | ||
| 332 | pub async fn wait_for_low(&mut self) { | ||
| 333 | InputFuture::new(self.pin.reborrow(), InterruptConfiguration::Low).await | ||
| 334 | } | ||
| 335 | |||
| 336 | /// Wait for the pin to undergo a transition from low to high. | ||
| 337 | #[inline] | ||
| 338 | pub async fn wait_for_rising_edge(&mut self) { | ||
| 339 | InputFuture::new(self.pin.reborrow(), InterruptConfiguration::RisingEdge).await | ||
| 340 | } | ||
| 341 | |||
| 342 | /// Wait for the pin to undergo a transition from high to low. | ||
| 343 | #[inline] | ||
| 344 | pub async fn wait_for_falling_edge(&mut self) { | ||
| 345 | InputFuture::new(self.pin.reborrow(), InterruptConfiguration::FallingEdge).await | ||
| 346 | } | ||
| 347 | |||
| 348 | /// Wait for the pin to undergo any transition, i.e low to high OR high to low. | ||
| 349 | #[inline] | ||
| 350 | pub async fn wait_for_any_edge(&mut self) { | ||
| 351 | InputFuture::new(self.pin.reborrow(), InterruptConfiguration::AnyEdge).await | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | impl<'d> Drop for Flex<'d> { | ||
| 356 | fn drop(&mut self) { | ||
| 357 | self.set_as_disconnected(); | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | /// GPIO input driver. | ||
| 362 | pub struct Input<'d> { | ||
| 363 | pin: Flex<'d>, | ||
| 364 | } | ||
| 365 | |||
| 366 | impl<'d> Input<'d> { | ||
| 367 | /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration. | ||
| 368 | #[inline] | ||
| 369 | pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self { | ||
| 370 | let mut pin = Flex::new(pin); | ||
| 371 | pin.set_as_input(); | ||
| 372 | pin.set_pull(pull); | ||
| 373 | Self { pin } | ||
| 374 | } | ||
| 375 | |||
| 376 | /// Get whether the pin input level is high. | ||
| 377 | #[inline] | ||
| 378 | pub fn is_high(&self) -> bool { | ||
| 379 | self.pin.is_high() | ||
| 380 | } | ||
| 381 | |||
| 382 | /// Get whether the pin input level is low. | ||
| 383 | #[inline] | ||
| 384 | pub fn is_low(&self) -> bool { | ||
| 385 | self.pin.is_low() | ||
| 386 | } | ||
| 387 | |||
| 388 | /// Get the current pin input level. | ||
| 389 | #[inline] | ||
| 390 | pub fn get_level(&self) -> Level { | ||
| 391 | self.pin.get_level() | ||
| 392 | } | ||
| 393 | |||
| 394 | /// Wait until the pin is high. If it is already high, return immediately. | ||
| 395 | #[inline] | ||
| 396 | pub async fn wait_for_high(&mut self) { | ||
| 397 | self.pin.wait_for_high().await | ||
| 398 | } | ||
| 399 | |||
| 400 | /// Wait until the pin is low. If it is already low, return immediately. | ||
| 401 | #[inline] | ||
| 402 | pub async fn wait_for_low(&mut self) { | ||
| 403 | self.pin.wait_for_low().await | ||
| 404 | } | ||
| 405 | |||
| 406 | /// Wait for the pin to undergo a transition from low to high. | ||
| 407 | #[inline] | ||
| 408 | pub async fn wait_for_rising_edge(&mut self) { | ||
| 409 | self.pin.wait_for_rising_edge().await | ||
| 410 | } | ||
| 411 | |||
| 412 | /// Wait for the pin to undergo a transition from high to low. | ||
| 413 | #[inline] | ||
| 414 | pub async fn wait_for_falling_edge(&mut self) { | ||
| 415 | self.pin.wait_for_falling_edge().await | ||
| 416 | } | ||
| 417 | |||
| 418 | /// Wait for the pin to undergo any transition, i.e low to high OR high to low. | ||
| 419 | #[inline] | ||
| 420 | pub async fn wait_for_any_edge(&mut self) { | ||
| 421 | self.pin.wait_for_any_edge().await | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | /// GPIO output driver. | ||
| 426 | /// | ||
| 427 | /// Note that pins will **return to their floating state** when `Output` is dropped. | ||
| 428 | /// If pins should retain their state indefinitely, either keep ownership of the | ||
| 429 | /// `Output`, or pass it to [`core::mem::forget`]. | ||
| 430 | pub struct Output<'d> { | ||
| 431 | pin: Flex<'d>, | ||
| 432 | } | ||
| 433 | |||
| 434 | impl<'d> Output<'d> { | ||
| 435 | /// Create GPIO output driver for a [Pin] with the provided [Level] configuration. | ||
| 436 | #[inline] | ||
| 437 | pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self { | ||
| 438 | let mut pin = Flex::new(pin); | ||
| 439 | pin.set_as_output(); | ||
| 440 | pin.set_level(initial_output); | ||
| 441 | Self { pin } | ||
| 442 | } | ||
| 443 | |||
| 444 | /// Set the output as high. | ||
| 445 | #[inline] | ||
| 446 | pub fn set_high(&mut self) { | ||
| 447 | self.pin.set_high(); | ||
| 448 | } | ||
| 449 | |||
| 450 | /// Set the output as low. | ||
| 451 | #[inline] | ||
| 452 | pub fn set_low(&mut self) { | ||
| 453 | self.pin.set_low(); | ||
| 454 | } | ||
| 455 | |||
| 456 | /// Set the output level. | ||
| 457 | #[inline] | ||
| 458 | pub fn set_level(&mut self, level: Level) { | ||
| 459 | self.pin.set_level(level) | ||
| 460 | } | ||
| 461 | |||
| 462 | /// Is the output pin set as high? | ||
| 463 | #[inline] | ||
| 464 | pub fn is_set_high(&self) -> bool { | ||
| 465 | self.pin.is_set_high() | ||
| 466 | } | ||
| 467 | |||
| 468 | /// Is the output pin set as low? | ||
| 469 | #[inline] | ||
| 470 | pub fn is_set_low(&self) -> bool { | ||
| 471 | self.pin.is_set_low() | ||
| 472 | } | ||
| 473 | |||
| 474 | /// What level output is set to | ||
| 475 | #[inline] | ||
| 476 | pub fn get_output_level(&self) -> Level { | ||
| 477 | self.pin.get_output_level() | ||
| 478 | } | ||
| 479 | |||
| 480 | /// Toggle pin output | ||
| 481 | #[inline] | ||
| 482 | pub fn toggle(&mut self) { | ||
| 483 | self.pin.toggle(); | ||
| 484 | } | ||
| 485 | } | ||
| 486 | |||
| 487 | /// GPIO output open-drain driver. | ||
| 488 | /// | ||
| 489 | /// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped. | ||
| 490 | /// If pins should retain their state indefinitely, either keep ownership of the | ||
| 491 | /// `OutputOpenDrain`, or pass it to [`core::mem::forget`]. | ||
| 492 | pub struct OutputOpenDrain<'d> { | ||
| 493 | pin: Flex<'d>, | ||
| 494 | } | ||
| 495 | |||
| 496 | impl<'d> OutputOpenDrain<'d> { | ||
| 497 | /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level]. | ||
| 498 | #[inline] | ||
| 499 | pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self { | ||
| 500 | let mut pin = Flex::new(pin); | ||
| 501 | pin.set_level(initial_output); | ||
| 502 | pin.set_as_input_output(); | ||
| 503 | Self { pin } | ||
| 504 | } | ||
| 505 | |||
| 506 | /// Get whether the pin input level is high. | ||
| 507 | #[inline] | ||
| 508 | pub fn is_high(&self) -> bool { | ||
| 509 | !self.pin.is_low() | ||
| 510 | } | ||
| 511 | |||
| 512 | /// Get whether the pin input level is low. | ||
| 513 | #[inline] | ||
| 514 | pub fn is_low(&self) -> bool { | ||
| 515 | self.pin.is_low() | ||
| 516 | } | ||
| 517 | |||
| 518 | /// Get the current pin input level. | ||
| 519 | #[inline] | ||
| 520 | pub fn get_level(&self) -> Level { | ||
| 521 | self.pin.get_level() | ||
| 522 | } | ||
| 523 | |||
| 524 | /// Set the output as high. | ||
| 525 | #[inline] | ||
| 526 | pub fn set_high(&mut self) { | ||
| 527 | self.pin.set_high(); | ||
| 528 | } | ||
| 529 | |||
| 530 | /// Set the output as low. | ||
| 531 | #[inline] | ||
| 532 | pub fn set_low(&mut self) { | ||
| 533 | self.pin.set_low(); | ||
| 534 | } | ||
| 535 | |||
| 536 | /// Set the output level. | ||
| 537 | #[inline] | ||
| 538 | pub fn set_level(&mut self, level: Level) { | ||
| 539 | self.pin.set_level(level); | ||
| 540 | } | ||
| 541 | |||
| 542 | /// Get whether the output level is set to high. | ||
| 543 | #[inline] | ||
| 544 | pub fn is_set_high(&self) -> bool { | ||
| 545 | self.pin.is_set_high() | ||
| 546 | } | ||
| 547 | |||
| 548 | /// Get whether the output level is set to low. | ||
| 549 | #[inline] | ||
| 550 | pub fn is_set_low(&self) -> bool { | ||
| 551 | self.pin.is_set_low() | ||
| 552 | } | ||
| 553 | |||
| 554 | /// Get the current output level. | ||
| 555 | #[inline] | ||
| 556 | pub fn get_output_level(&self) -> Level { | ||
| 557 | self.pin.get_output_level() | ||
| 558 | } | ||
| 559 | |||
| 560 | /// Toggle pin output | ||
| 561 | #[inline] | ||
| 562 | pub fn toggle(&mut self) { | ||
| 563 | self.pin.toggle() | ||
| 564 | } | ||
| 565 | |||
| 566 | /// Wait until the pin is high. If it is already high, return immediately. | ||
| 567 | #[inline] | ||
| 568 | pub async fn wait_for_high(&mut self) { | ||
| 569 | self.pin.wait_for_high().await | ||
| 570 | } | ||
| 571 | |||
| 572 | /// Wait until the pin is low. If it is already low, return immediately. | ||
| 573 | #[inline] | ||
| 574 | pub async fn wait_for_low(&mut self) { | ||
| 575 | self.pin.wait_for_low().await | ||
| 576 | } | ||
| 577 | |||
| 578 | /// Wait for the pin to undergo a transition from low to high. | ||
| 579 | #[inline] | ||
| 580 | pub async fn wait_for_rising_edge(&mut self) { | ||
| 581 | self.pin.wait_for_rising_edge().await | ||
| 582 | } | ||
| 583 | |||
| 584 | /// Wait for the pin to undergo a transition from high to low. | ||
| 585 | #[inline] | ||
| 586 | pub async fn wait_for_falling_edge(&mut self) { | ||
| 587 | self.pin.wait_for_falling_edge().await | ||
| 588 | } | ||
| 589 | |||
| 590 | /// Wait for the pin to undergo any transition, i.e low to high OR high to low. | ||
| 591 | #[inline] | ||
| 592 | pub async fn wait_for_any_edge(&mut self) { | ||
| 593 | self.pin.wait_for_any_edge().await | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | #[allow(private_bounds)] | ||
| 598 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { | ||
| 599 | /// Returns the pin number within a bank | ||
| 600 | #[inline] | ||
| 601 | fn pin(&self) -> u8 { | ||
| 602 | self.pin_number() | ||
| 603 | } | ||
| 604 | |||
| 605 | #[inline] | ||
| 606 | fn bank(&self) -> Bank { | ||
| 607 | self._bank() | ||
| 608 | } | ||
| 609 | } | ||
| 610 | |||
| 611 | /// Type-erased GPIO pin. | ||
| 612 | pub struct AnyPin { | ||
| 613 | pub(crate) pin_number: u8, | ||
| 614 | pub(crate) bank: Bank, | ||
| 615 | } | ||
| 616 | |||
| 617 | impl AnyPin { | ||
| 618 | /// Unsafely create a new type-erased pin. | ||
| 619 | /// | ||
| 620 | /// # Safety | ||
| 621 | /// | ||
| 622 | /// You must ensure that you’re only using one instance of this type at a time. | ||
| 623 | pub unsafe fn steal(bank: Bank, pin_number: u8) -> Peri<'static, Self> { | ||
| 624 | Peri::new_unchecked(Self { pin_number, bank }) | ||
| 625 | } | ||
| 626 | } | ||
| 627 | |||
| 628 | impl_peripheral!(AnyPin); | ||
| 629 | |||
| 630 | impl Pin for AnyPin {} | ||
| 631 | impl SealedPin for AnyPin { | ||
| 632 | #[inline] | ||
| 633 | fn pin_number(&self) -> u8 { | ||
| 634 | self.pin_number | ||
| 635 | } | ||
| 636 | |||
| 637 | #[inline] | ||
| 638 | fn _bank(&self) -> Bank { | ||
| 639 | self.bank | ||
| 640 | } | ||
| 641 | } | ||
| 642 | |||
| 643 | // Impl details | ||
| 644 | |||
| 645 | /// Mux mode for GPIO pins. This is constant across all RT1xxx parts. | ||
| 646 | const GPIO_MUX_MODE: u8 = 0b101; | ||
| 647 | |||
| 648 | // FIXME: These don't always need to be 32 entries. GPIO5 on RT1101 contains a single pin and GPIO2 only 14. | ||
| 649 | #[cfg(gpio1)] | ||
| 650 | static GPIO1_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | ||
| 651 | #[cfg(gpio2)] | ||
| 652 | static GPIO2_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | ||
| 653 | #[cfg(gpio3)] | ||
| 654 | static GPIO3_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | ||
| 655 | #[cfg(gpio4)] | ||
| 656 | static GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | ||
| 657 | #[cfg(gpio5)] | ||
| 658 | static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | ||
| 659 | |||
| 660 | /// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. | ||
| 661 | pub(crate) trait SealedPin: Sized { | ||
| 662 | fn pin_number(&self) -> u8; | ||
| 663 | |||
| 664 | fn _bank(&self) -> Bank; | ||
| 665 | |||
| 666 | #[inline] | ||
| 667 | fn block(&self) -> Gpio { | ||
| 668 | match self._bank() { | ||
| 669 | #[cfg(gpio1)] | ||
| 670 | Bank::Gpio1 => pac::GPIO1, | ||
| 671 | #[cfg(gpio2)] | ||
| 672 | Bank::Gpio2 => pac::GPIO2, | ||
| 673 | #[cfg(gpio3)] | ||
| 674 | Bank::Gpio3 => pac::GPIO3, | ||
| 675 | #[cfg(gpio4)] | ||
| 676 | Bank::Gpio4 => pac::GPIO4, | ||
| 677 | #[cfg(gpio5)] | ||
| 678 | Bank::Gpio5 => pac::GPIO5, | ||
| 679 | } | ||
| 680 | } | ||
| 681 | |||
| 682 | #[inline] | ||
| 683 | fn mux(&self) -> Reg<MuxCtl, RW> { | ||
| 684 | // SAFETY: The generated mux address table is valid since it is generated from the SVD files. | ||
| 685 | let address = unsafe { mux_address(self._bank(), self.pin_number()).unwrap_unchecked() }; | ||
| 686 | |||
| 687 | // SAFETY: The register at the address is an instance of MuxCtl. | ||
| 688 | unsafe { Reg::from_ptr(address as *mut _) } | ||
| 689 | } | ||
| 690 | |||
| 691 | #[inline] | ||
| 692 | fn pad(&self) -> Reg<Ctl, RW> { | ||
| 693 | // SAFETY: The generated pad address table is valid since it is generated from the SVD files. | ||
| 694 | let address = unsafe { pad_address(self._bank(), self.pin_number()).unwrap_unchecked() }; | ||
| 695 | |||
| 696 | // SAFETY: The register at the address is an instance of Ctl. | ||
| 697 | unsafe { Reg::from_ptr(address as *mut _) } | ||
| 698 | } | ||
| 699 | |||
| 700 | fn waker(&self) -> &AtomicWaker { | ||
| 701 | match self._bank() { | ||
| 702 | #[cfg(gpio1)] | ||
| 703 | Bank::Gpio1 => &GPIO1_WAKERS[self.pin_number() as usize], | ||
| 704 | #[cfg(gpio2)] | ||
| 705 | Bank::Gpio2 => &GPIO2_WAKERS[self.pin_number() as usize], | ||
| 706 | #[cfg(gpio3)] | ||
| 707 | Bank::Gpio3 => &GPIO3_WAKERS[self.pin_number() as usize], | ||
| 708 | #[cfg(gpio4)] | ||
| 709 | Bank::Gpio4 => &GPIO4_WAKERS[self.pin_number() as usize], | ||
| 710 | #[cfg(gpio5)] | ||
| 711 | Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize], | ||
| 712 | } | ||
| 713 | } | ||
| 714 | } | ||
| 715 | |||
| 716 | /// This enum matches the layout of Icr. | ||
| 717 | enum InterruptConfiguration { | ||
| 718 | Low, | ||
| 719 | High, | ||
| 720 | RisingEdge, | ||
| 721 | FallingEdge, | ||
| 722 | AnyEdge, | ||
| 723 | } | ||
| 724 | |||
| 725 | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||
| 726 | struct InputFuture<'d> { | ||
| 727 | pin: Peri<'d, AnyPin>, | ||
| 728 | } | ||
| 729 | |||
| 730 | impl<'d> InputFuture<'d> { | ||
| 731 | fn new(pin: Peri<'d, AnyPin>, config: InterruptConfiguration) -> Self { | ||
| 732 | let block = pin.block(); | ||
| 733 | |||
| 734 | let (icr, edge_sel) = match config { | ||
| 735 | InterruptConfiguration::Low => (Icr::LOW_LEVEL, false), | ||
| 736 | InterruptConfiguration::High => (Icr::HIGH_LEVEL, false), | ||
| 737 | InterruptConfiguration::RisingEdge => (Icr::RISING_EDGE, false), | ||
| 738 | InterruptConfiguration::FallingEdge => (Icr::FALLING_EDGE, false), | ||
| 739 | InterruptConfiguration::AnyEdge => (Icr::FALLING_EDGE, true), | ||
| 740 | }; | ||
| 741 | |||
| 742 | let index = if pin.pin_number() > 15 { 1 } else { 0 }; | ||
| 743 | |||
| 744 | // Interrupt configuration performs RMW | ||
| 745 | critical_section::with(|_cs| { | ||
| 746 | // Disable interrupt so a level/edge detection change does not cause ISR to be set. | ||
| 747 | block.imr().modify(|w| { | ||
| 748 | w.set_imr(pin.pin_number() as usize, false); | ||
| 749 | }); | ||
| 750 | |||
| 751 | block.icr(index).modify(|w| { | ||
| 752 | w.set_pin(pin.pin_number() as usize, icr); | ||
| 753 | }); | ||
| 754 | |||
| 755 | block.edge_sel().modify(|w| { | ||
| 756 | w.set_edge_sel(pin.pin_number() as usize, edge_sel); | ||
| 757 | }); | ||
| 758 | |||
| 759 | // Clear the previous interrupt. | ||
| 760 | block.isr().modify(|w| { | ||
| 761 | // "Status flags are cleared by writing a 1 to the corresponding bit position." | ||
| 762 | w.set_isr(pin.pin_number() as usize, true); | ||
| 763 | }); | ||
| 764 | }); | ||
| 765 | |||
| 766 | Self { pin } | ||
| 767 | } | ||
| 768 | } | ||
| 769 | |||
| 770 | impl<'d> Future for InputFuture<'d> { | ||
| 771 | type Output = (); | ||
| 772 | |||
| 773 | fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 774 | // We need to register/re-register the waker for each poll because any | ||
| 775 | // calls to wake will deregister the waker. | ||
| 776 | let waker = self.pin.waker(); | ||
| 777 | waker.register(cx.waker()); | ||
| 778 | |||
| 779 | // Enabling interrupt is RMW | ||
| 780 | critical_section::with(|_cs| { | ||
| 781 | self.pin.block().imr().modify(|w| { | ||
| 782 | w.set_imr(self.pin.pin_number() as usize, true); | ||
| 783 | }); | ||
| 784 | }); | ||
| 785 | |||
| 786 | let isr = self.pin.block().isr().read(); | ||
| 787 | |||
| 788 | if isr.isr(self.pin.pin_number() as usize) { | ||
| 789 | return Poll::Ready(()); | ||
| 790 | } | ||
| 791 | |||
| 792 | Poll::Pending | ||
| 793 | } | ||
| 794 | } | ||
| 795 | |||
| 796 | /// A macro to generate all GPIO pins. | ||
| 797 | /// | ||
| 798 | /// This generates a lookup table for IOMUX register addresses. | ||
| 799 | macro_rules! impl_gpio { | ||
| 800 | ( | ||
| 801 | $($name: ident($bank: ident, $pin_number: expr);)* | ||
| 802 | ) => { | ||
| 803 | #[inline] | ||
| 804 | pub(crate) const fn pad_address(bank: crate::gpio::Bank, pin: u8) -> Option<u32> { | ||
| 805 | match (bank, pin) { | ||
| 806 | $( | ||
| 807 | (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::pads::$name), | ||
| 808 | )* | ||
| 809 | _ => None | ||
| 810 | } | ||
| 811 | } | ||
| 812 | |||
| 813 | #[inline] | ||
| 814 | pub(crate) const fn mux_address(bank: crate::gpio::Bank, pin: u8) -> Option<u32> { | ||
| 815 | match (bank, pin) { | ||
| 816 | $( | ||
| 817 | (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::muxes::$name), | ||
| 818 | )* | ||
| 819 | _ => None | ||
| 820 | } | ||
| 821 | } | ||
| 822 | |||
| 823 | $( | ||
| 824 | impl_pin!($name, $bank, $pin_number); | ||
| 825 | )* | ||
| 826 | }; | ||
| 827 | } | ||
| 828 | |||
| 829 | macro_rules! impl_pin { | ||
| 830 | ($name: ident, $bank: ident, $pin_num: expr) => { | ||
| 831 | impl crate::gpio::Pin for crate::peripherals::$name {} | ||
| 832 | impl crate::gpio::SealedPin for crate::peripherals::$name { | ||
| 833 | #[inline] | ||
| 834 | fn pin_number(&self) -> u8 { | ||
| 835 | $pin_num | ||
| 836 | } | ||
| 837 | |||
| 838 | #[inline] | ||
| 839 | fn _bank(&self) -> crate::gpio::Bank { | ||
| 840 | crate::gpio::Bank::$bank | ||
| 841 | } | ||
| 842 | } | ||
| 843 | |||
| 844 | impl From<peripherals::$name> for crate::gpio::AnyPin { | ||
| 845 | fn from(val: peripherals::$name) -> Self { | ||
| 846 | use crate::gpio::SealedPin; | ||
| 847 | |||
| 848 | Self { | ||
| 849 | pin_number: val.pin_number(), | ||
| 850 | bank: val._bank(), | ||
| 851 | } | ||
| 852 | } | ||
| 853 | } | ||
| 854 | }; | ||
| 855 | } | ||
| 856 | |||
| 857 | pub(crate) fn init() { | ||
| 858 | #[cfg(feature = "rt")] | ||
| 859 | unsafe { | ||
| 860 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 861 | |||
| 862 | pac::Interrupt::GPIO1_COMBINED_0_15.enable(); | ||
| 863 | pac::Interrupt::GPIO1_COMBINED_16_31.enable(); | ||
| 864 | pac::Interrupt::GPIO2_COMBINED_0_15.enable(); | ||
| 865 | pac::Interrupt::GPIO5_COMBINED_0_15.enable(); | ||
| 866 | } | ||
| 867 | } | ||
| 868 | |||
| 869 | /// IRQ handler for GPIO pins. | ||
| 870 | /// | ||
| 871 | /// If `high_bits` is false, then the interrupt is for pins 0 through 15. If true, then the interrupt | ||
| 872 | /// is for pins 16 through 31 | ||
| 873 | #[cfg(feature = "rt")] | ||
| 874 | fn irq_handler(block: Gpio, wakers: &[AtomicWaker; 32], high_bits: bool) { | ||
| 875 | use crate::BitIter; | ||
| 876 | |||
| 877 | let isr = block.isr().read().0; | ||
| 878 | let imr = block.imr().read().0; | ||
| 879 | let mask = if high_bits { 0xFFFF_0000 } else { 0x0000_FFFF }; | ||
| 880 | let bits = isr & imr & mask; | ||
| 881 | |||
| 882 | for bit in BitIter(bits) { | ||
| 883 | wakers[bit as usize].wake(); | ||
| 884 | |||
| 885 | // Disable further interrupts for this pin. The input future will check ISR (which is kept | ||
| 886 | // until reset). | ||
| 887 | block.imr().modify(|w| { | ||
| 888 | w.set_imr(bit as usize, false); | ||
| 889 | }); | ||
| 890 | } | ||
| 891 | } | ||
| 892 | |||
| 893 | #[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] | ||
| 894 | #[interrupt] | ||
| 895 | fn GPIO1_COMBINED_0_15() { | ||
| 896 | irq_handler(pac::GPIO1, &GPIO1_WAKERS, false); | ||
| 897 | } | ||
| 898 | |||
| 899 | #[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] | ||
| 900 | #[interrupt] | ||
| 901 | fn GPIO1_COMBINED_16_31() { | ||
| 902 | irq_handler(pac::GPIO1, &GPIO1_WAKERS, true); | ||
| 903 | } | ||
| 904 | |||
| 905 | #[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] | ||
| 906 | #[interrupt] | ||
| 907 | fn GPIO2_COMBINED_0_15() { | ||
| 908 | irq_handler(pac::GPIO2, &GPIO2_WAKERS, false); | ||
| 909 | } | ||
| 910 | |||
| 911 | #[cfg(all(feature = "mimxrt1062", feature = "rt"))] | ||
| 912 | #[interrupt] | ||
| 913 | fn GPIO2_COMBINED_16_31() { | ||
| 914 | irq_handler(pac::GPIO2, &GPIO2_WAKERS, true); | ||
| 915 | } | ||
| 916 | |||
| 917 | #[cfg(all(feature = "mimxrt1062", feature = "rt"))] | ||
| 918 | #[interrupt] | ||
| 919 | fn GPIO3_COMBINED_0_15() { | ||
| 920 | irq_handler(pac::GPIO3, &GPIO3_WAKERS, false); | ||
| 921 | } | ||
| 922 | |||
| 923 | #[cfg(all(feature = "mimxrt1062", feature = "rt"))] | ||
| 924 | #[interrupt] | ||
| 925 | fn GPIO3_COMBINED_16_31() { | ||
| 926 | irq_handler(pac::GPIO3, &GPIO3_WAKERS, true); | ||
| 927 | } | ||
| 928 | |||
| 929 | #[cfg(all(feature = "mimxrt1062", feature = "rt"))] | ||
| 930 | #[interrupt] | ||
| 931 | fn GPIO4_COMBINED_0_15() { | ||
| 932 | irq_handler(pac::GPIO4, &GPIO4_WAKERS, false); | ||
| 933 | } | ||
| 934 | |||
| 935 | #[cfg(all(feature = "mimxrt1062", feature = "rt"))] | ||
| 936 | #[interrupt] | ||
| 937 | fn GPIO4_COMBINED_16_31() { | ||
| 938 | irq_handler(pac::GPIO4, &GPIO4_WAKERS, true); | ||
| 939 | } | ||
| 940 | |||
| 941 | #[cfg(all(any(feature = "mimxrt1011", feature = "mimxrt1062"), feature = "rt"))] | ||
| 942 | #[interrupt] | ||
| 943 | fn GPIO5_COMBINED_0_15() { | ||
| 944 | irq_handler(pac::GPIO5, &GPIO5_WAKERS, false); | ||
| 945 | } | ||
diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index ad2056c06..5e77fc0db 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs | |||
| @@ -1,11 +1,28 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | 2 | ||
| 3 | // This mod MUST go first, so that the others see its macros. | ||
| 4 | pub(crate) mod fmt; | ||
| 5 | |||
| 3 | pub mod gpio; | 6 | pub mod gpio; |
| 4 | mod pac_utils; | 7 | #[cfg(feature = "lpc55")] |
| 5 | pub mod pint; | 8 | pub mod pint; |
| 6 | 9 | ||
| 7 | pub use embassy_hal_internal::Peri; | 10 | #[cfg(feature = "_time_driver")] |
| 8 | pub use lpc55_pac as pac; | 11 | #[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")] |
| 12 | mod time_driver; | ||
| 13 | |||
| 14 | // This mod MUST go last, so that it sees all the `impl_foo!` macros | ||
| 15 | #[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] | ||
| 16 | #[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")] | ||
| 17 | #[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] | ||
| 18 | mod chip; | ||
| 19 | |||
| 20 | #[cfg(feature = "unstable-pac")] | ||
| 21 | pub use chip::pac; | ||
| 22 | #[cfg(not(feature = "unstable-pac"))] | ||
| 23 | pub(crate) use chip::pac; | ||
| 24 | pub use chip::{peripherals, Peripherals}; | ||
| 25 | pub use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 9 | 26 | ||
| 10 | /// Initialize the `embassy-nxp` HAL with the provided configuration. | 27 | /// Initialize the `embassy-nxp` HAL with the provided configuration. |
| 11 | /// | 28 | /// |
| @@ -13,79 +30,66 @@ pub use lpc55_pac as pac; | |||
| 13 | /// | 30 | /// |
| 14 | /// This should only be called once and at startup, otherwise it panics. | 31 | /// This should only be called once and at startup, otherwise it panics. |
| 15 | pub fn init(_config: config::Config) -> Peripherals { | 32 | pub fn init(_config: config::Config) -> Peripherals { |
| 33 | // Do this first, so that it panics if user is calling `init` a second time | ||
| 34 | // before doing anything important. | ||
| 35 | let peripherals = Peripherals::take(); | ||
| 36 | |||
| 37 | #[cfg(feature = "mimxrt1011")] | ||
| 38 | { | ||
| 39 | // The RT1010 Reference manual states that core clock root must be switched before | ||
| 40 | // reprogramming PLL2. | ||
| 41 | pac::CCM.cbcdr().modify(|w| { | ||
| 42 | w.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_1); | ||
| 43 | }); | ||
| 44 | |||
| 45 | while matches!( | ||
| 46 | pac::CCM.cdhipr().read().periph_clk_sel_busy(), | ||
| 47 | pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1 | ||
| 48 | ) {} | ||
| 49 | |||
| 50 | info!("Core clock root switched"); | ||
| 51 | |||
| 52 | // 480 * 18 / 24 = 360 | ||
| 53 | pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd2_frac(12)); | ||
| 54 | |||
| 55 | //480*18/24(pfd0)/4 | ||
| 56 | pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd0_frac(24)); | ||
| 57 | pac::CCM.cscmr1().modify(|x| x.set_flexspi_podf(3.into())); | ||
| 58 | |||
| 59 | // CPU Core | ||
| 60 | pac::CCM_ANALOG.pfd_528().modify(|x| x.set_pfd3_frac(18)); | ||
| 61 | cortex_m::asm::delay(500_000); | ||
| 62 | |||
| 63 | // Clock core clock with PLL 2. | ||
| 64 | pac::CCM | ||
| 65 | .cbcdr() | ||
| 66 | .modify(|x| x.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_0)); // false | ||
| 67 | |||
| 68 | while matches!( | ||
| 69 | pac::CCM.cdhipr().read().periph_clk_sel_busy(), | ||
| 70 | pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1 | ||
| 71 | ) {} | ||
| 72 | |||
| 73 | pac::CCM | ||
| 74 | .cbcmr() | ||
| 75 | .write(|v| v.set_pre_periph_clk_sel(pac::ccm::vals::PrePeriphClkSel::PRE_PERIPH_CLK_SEL_0)); | ||
| 76 | |||
| 77 | // TODO: Some for USB PLLs | ||
| 78 | |||
| 79 | // DCDC clock? | ||
| 80 | pac::CCM.ccgr6().modify(|v| v.set_cg0(1)); | ||
| 81 | } | ||
| 82 | |||
| 83 | #[cfg(any(feature = "lpc55", rt1xxx))] | ||
| 16 | gpio::init(); | 84 | gpio::init(); |
| 85 | |||
| 86 | #[cfg(feature = "lpc55")] | ||
| 17 | pint::init(); | 87 | pint::init(); |
| 18 | 88 | ||
| 19 | crate::Peripherals::take() | 89 | #[cfg(feature = "_time_driver")] |
| 20 | } | 90 | time_driver::init(); |
| 21 | 91 | ||
| 22 | embassy_hal_internal::peripherals! { | 92 | peripherals |
| 23 | // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other | ||
| 24 | // peripheral types (e.g. I2C). | ||
| 25 | PIO0_0, | ||
| 26 | PIO0_1, | ||
| 27 | PIO0_2, | ||
| 28 | PIO0_3, | ||
| 29 | PIO0_4, | ||
| 30 | PIO0_5, | ||
| 31 | PIO0_6, | ||
| 32 | PIO0_7, | ||
| 33 | PIO0_8, | ||
| 34 | PIO0_9, | ||
| 35 | PIO0_10, | ||
| 36 | PIO0_11, | ||
| 37 | PIO0_12, | ||
| 38 | PIO0_13, | ||
| 39 | PIO0_14, | ||
| 40 | PIO0_15, | ||
| 41 | PIO0_16, | ||
| 42 | PIO0_17, | ||
| 43 | PIO0_18, | ||
| 44 | PIO0_19, | ||
| 45 | PIO0_20, | ||
| 46 | PIO0_21, | ||
| 47 | PIO0_22, | ||
| 48 | PIO0_23, | ||
| 49 | PIO0_24, | ||
| 50 | PIO0_25, | ||
| 51 | PIO0_26, | ||
| 52 | PIO0_27, | ||
| 53 | PIO0_28, | ||
| 54 | PIO0_29, | ||
| 55 | PIO0_30, | ||
| 56 | PIO0_31, | ||
| 57 | PIO1_0, | ||
| 58 | PIO1_1, | ||
| 59 | PIO1_2, | ||
| 60 | PIO1_3, | ||
| 61 | PIO1_4, | ||
| 62 | PIO1_5, | ||
| 63 | PIO1_6, | ||
| 64 | PIO1_7, | ||
| 65 | PIO1_8, | ||
| 66 | PIO1_9, | ||
| 67 | PIO1_10, | ||
| 68 | PIO1_11, | ||
| 69 | PIO1_12, | ||
| 70 | PIO1_13, | ||
| 71 | PIO1_14, | ||
| 72 | PIO1_15, | ||
| 73 | PIO1_16, | ||
| 74 | PIO1_17, | ||
| 75 | PIO1_18, | ||
| 76 | PIO1_19, | ||
| 77 | PIO1_20, | ||
| 78 | PIO1_21, | ||
| 79 | PIO1_22, | ||
| 80 | PIO1_23, | ||
| 81 | PIO1_24, | ||
| 82 | PIO1_25, | ||
| 83 | PIO1_26, | ||
| 84 | PIO1_27, | ||
| 85 | PIO1_28, | ||
| 86 | PIO1_29, | ||
| 87 | PIO1_30, | ||
| 88 | PIO1_31, | ||
| 89 | } | 93 | } |
| 90 | 94 | ||
| 91 | /// HAL configuration for the NXP board. | 95 | /// HAL configuration for the NXP board. |
| @@ -93,3 +97,20 @@ pub mod config { | |||
| 93 | #[derive(Default)] | 97 | #[derive(Default)] |
| 94 | pub struct Config {} | 98 | pub struct Config {} |
| 95 | } | 99 | } |
| 100 | |||
| 101 | #[allow(unused)] | ||
| 102 | struct BitIter(u32); | ||
| 103 | |||
| 104 | impl Iterator for BitIter { | ||
| 105 | type Item = u32; | ||
| 106 | |||
| 107 | fn next(&mut self) -> Option<Self::Item> { | ||
| 108 | match self.0.trailing_zeros() { | ||
| 109 | 32 => None, | ||
| 110 | b => { | ||
| 111 | self.0 &= !(1 << b); | ||
| 112 | Some(b) | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
diff --git a/embassy-nxp/src/pint.rs b/embassy-nxp/src/pint.rs index 8d6dc1277..ff414b4e6 100644 --- a/embassy-nxp/src/pint.rs +++ b/embassy-nxp/src/pint.rs | |||
| @@ -7,9 +7,8 @@ use core::task::{Context, Poll}; | |||
| 7 | use critical_section::Mutex; | 7 | use critical_section::Mutex; |
| 8 | use embassy_sync::waitqueue::AtomicWaker; | 8 | use embassy_sync::waitqueue::AtomicWaker; |
| 9 | 9 | ||
| 10 | use crate::gpio::{self, AnyPin, Level, SealedPin}; | 10 | use crate::gpio::{self, inputmux_reg, pint_reg, syscon_reg, AnyPin, Level, SealedPin}; |
| 11 | use crate::pac::interrupt; | 11 | use crate::pac::interrupt; |
| 12 | use crate::pac_utils::*; | ||
| 13 | use crate::Peri; | 12 | use crate::Peri; |
| 14 | 13 | ||
| 15 | struct PinInterrupt { | 14 | struct PinInterrupt { |
| @@ -102,6 +101,8 @@ pub(crate) fn init() { | |||
| 102 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6); | 101 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6); |
| 103 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7); | 102 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7); |
| 104 | }; | 103 | }; |
| 104 | |||
| 105 | info!("Pin interrupts initialized"); | ||
| 105 | } | 106 | } |
| 106 | 107 | ||
| 107 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 108 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
diff --git a/embassy-nxp/src/time_driver/pit.rs b/embassy-nxp/src/time_driver/pit.rs new file mode 100644 index 000000000..985e5e815 --- /dev/null +++ b/embassy-nxp/src/time_driver/pit.rs | |||
| @@ -0,0 +1,187 @@ | |||
| 1 | //! Time driver using Periodic Interrupt Timer (PIT) | ||
| 2 | //! | ||
| 3 | //! This driver is used with the iMXRT1xxx parts. | ||
| 4 | //! | ||
| 5 | //! The PIT is run in lifetime mode. Timer 1 is chained to timer 0 to provide a free-running 64-bit timer. | ||
| 6 | //! The 64-bit timer is used to track how many ticks since boot. | ||
| 7 | //! | ||
| 8 | //! Timer 2 counts how many ticks there are within the current u32::MAX tick period. Timer 2 is restarted when | ||
| 9 | //! a new alarm is set (or every u32::MAX ticks). One caveat is that an alarm could be a few ticks late due to | ||
| 10 | //! restart. However the Cortex-M7 cores run at 500 MHz easily and the PIT will generally run at 1 MHz or lower. | ||
| 11 | //! Along with the fact that scheduling an alarm takes a critical section worst case an alarm may be a few | ||
| 12 | //! microseconds late. | ||
| 13 | //! | ||
| 14 | //! All PIT timers are clocked in lockstep, so the late start will not cause the now() count to drift. | ||
| 15 | |||
| 16 | use core::cell::{Cell, RefCell}; | ||
| 17 | use core::task::Waker; | ||
| 18 | |||
| 19 | use critical_section::{CriticalSection, Mutex}; | ||
| 20 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 21 | use embassy_time_driver::Driver as _; | ||
| 22 | use embassy_time_queue_utils::Queue; | ||
| 23 | |||
| 24 | use crate::pac::{self, interrupt}; | ||
| 25 | |||
| 26 | struct Driver { | ||
| 27 | alarm: Mutex<Cell<u64>>, | ||
| 28 | queue: Mutex<RefCell<Queue>>, | ||
| 29 | } | ||
| 30 | |||
| 31 | impl embassy_time_driver::Driver for Driver { | ||
| 32 | fn now(&self) -> u64 { | ||
| 33 | loop { | ||
| 34 | // Even though reading LTMR64H will latch LTMR64L if another thread preempts between any of the | ||
| 35 | // three reads and calls now() then the value in LTMR64L will be wrong when execution returns to | ||
| 36 | // thread which was preempted. | ||
| 37 | let hi = pac::PIT.ltmr64h().read().lth(); | ||
| 38 | let lo = pac::PIT.ltmr64l().read().ltl(); | ||
| 39 | let hi2 = pac::PIT.ltmr64h().read().lth(); | ||
| 40 | |||
| 41 | if hi == hi2 { | ||
| 42 | // PIT timers always count down. | ||
| 43 | return u64::MAX - ((hi as u64) << 32 | (lo as u64)); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | fn schedule_wake(&self, at: u64, waker: &Waker) { | ||
| 49 | critical_section::with(|cs| { | ||
| 50 | let mut queue = self.queue.borrow(cs).borrow_mut(); | ||
| 51 | |||
| 52 | if queue.schedule_wake(at, waker) { | ||
| 53 | let mut next = queue.next_expiration(self.now()); | ||
| 54 | |||
| 55 | while !self.set_alarm(cs, next) { | ||
| 56 | next = queue.next_expiration(self.now()); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | }) | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | impl Driver { | ||
| 64 | fn init(&'static self) { | ||
| 65 | // Disable PIT clock during mux configuration. | ||
| 66 | pac::CCM.ccgr1().modify(|r| r.set_cg6(0b00)); | ||
| 67 | |||
| 68 | // TODO: This forces the PIT to be driven by the oscillator. However that isn't the only option as you | ||
| 69 | // could divide the clock root by up to 64. | ||
| 70 | pac::CCM.cscmr1().modify(|r| { | ||
| 71 | // 1 MHz | ||
| 72 | r.set_perclk_podf(pac::ccm::vals::PerclkPodf::DIVIDE_24); | ||
| 73 | r.set_perclk_clk_sel(pac::ccm::vals::PerclkClkSel::PERCLK_CLK_SEL_1); | ||
| 74 | }); | ||
| 75 | |||
| 76 | pac::CCM.ccgr1().modify(|r| r.set_cg6(0b11)); | ||
| 77 | |||
| 78 | // Disable clock during init. | ||
| 79 | // | ||
| 80 | // It is important that the PIT clock is prepared to not exceed limit (50 MHz on RT1011), or else | ||
| 81 | // you will need to recover the device with boot mode switches when using any PIT registers. | ||
| 82 | pac::PIT.mcr().modify(|w| { | ||
| 83 | w.set_mdis(true); | ||
| 84 | }); | ||
| 85 | |||
| 86 | pac::PIT.timer(0).ldval().write_value(u32::MAX); | ||
| 87 | pac::PIT.timer(1).ldval().write_value(u32::MAX); | ||
| 88 | pac::PIT.timer(2).ldval().write_value(0); | ||
| 89 | pac::PIT.timer(3).ldval().write_value(0); | ||
| 90 | |||
| 91 | pac::PIT.timer(1).tctrl().write(|w| { | ||
| 92 | // In lifetime mode, timer 1 is chained to timer 0 to form a 64-bit timer. | ||
| 93 | w.set_chn(true); | ||
| 94 | w.set_ten(true); | ||
| 95 | w.set_tie(false); | ||
| 96 | }); | ||
| 97 | |||
| 98 | pac::PIT.timer(0).tctrl().write(|w| { | ||
| 99 | w.set_chn(false); | ||
| 100 | w.set_ten(true); | ||
| 101 | w.set_tie(false); | ||
| 102 | }); | ||
| 103 | |||
| 104 | pac::PIT.timer(2).tctrl().write(|w| { | ||
| 105 | w.set_tie(true); | ||
| 106 | }); | ||
| 107 | |||
| 108 | unsafe { interrupt::PIT.enable() }; | ||
| 109 | |||
| 110 | pac::PIT.mcr().write(|w| { | ||
| 111 | w.set_mdis(false); | ||
| 112 | }); | ||
| 113 | } | ||
| 114 | |||
| 115 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { | ||
| 116 | let alarm = self.alarm.borrow(cs); | ||
| 117 | alarm.set(timestamp); | ||
| 118 | |||
| 119 | let timer = pac::PIT.timer(2); | ||
| 120 | let now = self.now(); | ||
| 121 | |||
| 122 | if timestamp <= now { | ||
| 123 | alarm.set(u64::MAX); | ||
| 124 | |||
| 125 | return false; | ||
| 126 | } | ||
| 127 | |||
| 128 | timer.tctrl().modify(|x| x.set_ten(false)); | ||
| 129 | timer.tflg().modify(|x| x.set_tif(true)); | ||
| 130 | |||
| 131 | // If the next alarm happens in more than u32::MAX cycles then the alarm will be restarted later. | ||
| 132 | timer.ldval().write_value((timestamp - now) as u32); | ||
| 133 | timer.tctrl().modify(|x| x.set_ten(true)); | ||
| 134 | |||
| 135 | true | ||
| 136 | } | ||
| 137 | |||
| 138 | fn trigger_alarm(&self, cs: CriticalSection) { | ||
| 139 | let mut next = self.queue.borrow_ref_mut(cs).next_expiration(self.now()); | ||
| 140 | |||
| 141 | while !self.set_alarm(cs, next) { | ||
| 142 | next = self.queue.borrow_ref_mut(cs).next_expiration(self.now()); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | fn on_interrupt(&self) { | ||
| 147 | critical_section::with(|cs| { | ||
| 148 | let timer = pac::PIT.timer(2); | ||
| 149 | let alarm = self.alarm.borrow(cs); | ||
| 150 | let interrupted = timer.tflg().read().tif(); | ||
| 151 | timer.tflg().write(|r| r.set_tif(true)); | ||
| 152 | |||
| 153 | if interrupted { | ||
| 154 | // A new load value will not apply until the next timer expiration. | ||
| 155 | // | ||
| 156 | // The expiry may be up to u32::MAX cycles away, so the timer must be restarted. | ||
| 157 | timer.tctrl().modify(|r| r.set_ten(false)); | ||
| 158 | |||
| 159 | let now = self.now(); | ||
| 160 | let timestamp = alarm.get(); | ||
| 161 | |||
| 162 | if timestamp <= now { | ||
| 163 | self.trigger_alarm(cs); | ||
| 164 | } else { | ||
| 165 | // The alarm is not ready. Wait for u32::MAX cycles and check again or set the next alarm. | ||
| 166 | timer.ldval().write_value((timestamp - now) as u32); | ||
| 167 | timer.tctrl().modify(|r| r.set_ten(true)); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | }); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver { | ||
| 175 | alarm: Mutex::new(Cell::new(0)), | ||
| 176 | queue: Mutex::new(RefCell::new(Queue::new())) | ||
| 177 | }); | ||
| 178 | |||
| 179 | pub(crate) fn init() { | ||
| 180 | DRIVER.init(); | ||
| 181 | } | ||
| 182 | |||
| 183 | #[cfg(feature = "rt")] | ||
| 184 | #[interrupt] | ||
| 185 | fn PIT() { | ||
| 186 | DRIVER.on_interrupt(); | ||
| 187 | } | ||
diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index 7ac0a47cb..36e1ea9b4 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md | |||
| @@ -5,7 +5,40 @@ All notable changes to this project will be documented in this file. | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 7 | 7 | ||
| 8 | ## Unreleased | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | ||
| 10 | |||
| 11 | ## 0.6.0 - 2025-07-16 | ||
| 12 | |||
| 13 | - update to latest embassy-usb-driver | ||
| 14 | |||
| 15 | ## 0.5.0 - 2025-07-15 | ||
| 16 | |||
| 17 | - Fix wrong `funcsel` on RP2350 gpout/gpin ([#3975](https://github.com/embassy-rs/embassy/pull/3975)) | ||
| 18 | - Fix potential race condition in `ADC::wait_for_ready` ([#4012](https://github.com/embassy-rs/embassy/pull/4012)) | ||
| 19 | - `flash`: rename `BOOTROM_BASE` to `BOOTRAM_BASE` ([#4014](https://github.com/embassy-rs/embassy/pull/4014)) | ||
| 20 | - Remove `Peripheral` trait & rename `PeripheralRef` to `Peri` ([#3999](https://github.com/embassy-rs/embassy/pull/3999)) | ||
| 21 | - Fix watchdog count on RP235x ([#4021](https://github.com/embassy-rs/embassy/pull/4021)) | ||
| 22 | - I2C: ensure that wakers are registered before checking status of `wait_on` helpers ([#4043](https://github.com/embassy-rs/embassy/pull/4043)) | ||
| 23 | - Modify `Uarte` and `BufferedUarte` initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983)) | ||
| 24 | - `uart`: increase RX FIFO watermark from 1/8 to 7/8 ([#4055](https://github.com/embassy-rs/embassy/pull/4055)) | ||
| 25 | - Add `spinlock_mutex` ([#4017](https://github.com/embassy-rs/embassy/pull/4017)) | ||
| 26 | - Enable input mode for PWM pins on RP235x and disable it on drop ([#4093](https://github.com/embassy-rs/embassy/pull/4093)) | ||
| 27 | - Add `impl rand_core::CryptoRng for Trng` ([#4096](https://github.com/embassy-rs/embassy/pull/4096)) | ||
| 28 | - `pwm`: enable pull-down resistors for pins in `Drop` implementation ([#4115](https://github.com/embassy-rs/embassy/pull/4115)) | ||
| 29 | - Rewrite PIO onewire implementation ([#4128](https://github.com/embassy-rs/embassy/pull/4128)) | ||
| 30 | - Implement RP2040 overclocking ([#4150](https://github.com/embassy-rs/embassy/pull/4150)) | ||
| 31 | - Implement RP235x overclocking ([#4187](https://github.com/embassy-rs/embassy/pull/4187)) | ||
| 32 | - `trng`: improve error handling ([#4139](https://github.com/embassy-rs/embassy/pull/4139)) | ||
| 33 | - Remove `<T: Instance>` from `Uart` and `BufferedUart` ([#4155](https://github.com/embassy-rs/embassy/pull/4155)) | ||
| 34 | - Make bit-depth of I2S PIO program configurable ([#4193](https://github.com/embassy-rs/embassy/pull/4193)) | ||
| 35 | - Add the possibility to document `bind_interrupts` `struct`s ([#4206](https://github.com/embassy-rs/embassy/pull/4206)) | ||
| 36 | - Add missing `Debug` and `defmt::Format` `derive`s for ADC & `AnyPin` ([#4205](https://github.com/embassy-rs/embassy/pull/4205)) | ||
| 37 | - Add `rand-core` v0.9 support ([#4217](https://github.com/embassy-rs/embassy/pull/4217)) | ||
| 38 | - Update `embassy-sync` to v0.7.0 ([#4234](https://github.com/embassy-rs/embassy/pull/4234)) | ||
| 39 | - Add compatibility with ws2812 leds that have 4 addressable lights ([#4236](https://github.com/embassy-rs/embassy/pull/4236)) | ||
| 40 | - Implement input/output inversion ([#4237](https://github.com/embassy-rs/embassy/pull/4237)) | ||
| 41 | - Add `multicore::current_core` API ([#4362](https://github.com/embassy-rs/embassy/pull/4362)) | ||
| 9 | 42 | ||
| 10 | ## 0.4.0 - 2025-03-09 | 43 | ## 0.4.0 - 2025-03-09 |
| 11 | 44 | ||
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 5df9e154c..dcf4e7178 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-rp" | 2 | name = "embassy-rp" |
| 3 | version = "0.4.0" | 3 | version = "0.6.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller" | 6 | description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller" |
| @@ -141,9 +141,9 @@ embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", option | |||
| 141 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } | 141 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } |
| 142 | embassy-time = { version = "0.4.0", path = "../embassy-time" } | 142 | embassy-time = { version = "0.4.0", path = "../embassy-time" } |
| 143 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 143 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 144 | embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } | 144 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } |
| 145 | embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } | 145 | embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal" } |
| 146 | embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } | 146 | embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } |
| 147 | atomic-polyfill = "1.0.1" | 147 | atomic-polyfill = "1.0.1" |
| 148 | defmt = { version = "1.0.1", optional = true } | 148 | defmt = { version = "1.0.1", optional = true } |
| 149 | log = { version = "0.4.14", optional = true } | 149 | log = { version = "0.4.14", optional = true } |
diff --git a/embassy-rp/release.toml b/embassy-rp/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-rp/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index ef1cd9212..8c809090e 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs | |||
| @@ -482,7 +482,11 @@ mod ram_helpers { | |||
| 482 | /// # Safety | 482 | /// # Safety |
| 483 | /// | 483 | /// |
| 484 | /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode | 484 | /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode |
| 485 | unsafe fn flash_function_pointers_with_boot2(erase: bool, write: bool, boot2: &[u32; 64]) -> FlashFunctionPointers { | 485 | unsafe fn flash_function_pointers_with_boot2( |
| 486 | erase: bool, | ||
| 487 | write: bool, | ||
| 488 | boot2: &[u32; 64], | ||
| 489 | ) -> FlashFunctionPointers<'_> { | ||
| 486 | let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1); | 490 | let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1); |
| 487 | let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr); | 491 | let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr); |
| 488 | FlashFunctionPointers { | 492 | FlashFunctionPointers { |
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 9b5faac15..f79bf8948 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs | |||
| @@ -26,6 +26,7 @@ static QSPI_WAKERS: [AtomicWaker; QSPI_PIN_COUNT] = [const { AtomicWaker::new() | |||
| 26 | 26 | ||
| 27 | /// Represents a digital input or output level. | 27 | /// Represents a digital input or output level. |
| 28 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 28 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 29 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 29 | pub enum Level { | 30 | pub enum Level { |
| 30 | /// Logical low. | 31 | /// Logical low. |
| 31 | Low, | 32 | Low, |
| @@ -53,6 +54,7 @@ impl From<Level> for bool { | |||
| 53 | 54 | ||
| 54 | /// Represents a pull setting for an input. | 55 | /// Represents a pull setting for an input. |
| 55 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] | 56 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] |
| 57 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 56 | pub enum Pull { | 58 | pub enum Pull { |
| 57 | /// No pull. | 59 | /// No pull. |
| 58 | None, | 60 | None, |
| @@ -64,6 +66,7 @@ pub enum Pull { | |||
| 64 | 66 | ||
| 65 | /// Drive strength of an output | 67 | /// Drive strength of an output |
| 66 | #[derive(Debug, Eq, PartialEq)] | 68 | #[derive(Debug, Eq, PartialEq)] |
| 69 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 67 | pub enum Drive { | 70 | pub enum Drive { |
| 68 | /// 2 mA drive. | 71 | /// 2 mA drive. |
| 69 | _2mA, | 72 | _2mA, |
| @@ -76,6 +79,7 @@ pub enum Drive { | |||
| 76 | } | 79 | } |
| 77 | /// Slew rate of an output | 80 | /// Slew rate of an output |
| 78 | #[derive(Debug, Eq, PartialEq)] | 81 | #[derive(Debug, Eq, PartialEq)] |
| 82 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 79 | pub enum SlewRate { | 83 | pub enum SlewRate { |
| 80 | /// Fast slew rate. | 84 | /// Fast slew rate. |
| 81 | Fast, | 85 | Fast, |
| @@ -85,6 +89,7 @@ pub enum SlewRate { | |||
| 85 | 89 | ||
| 86 | /// A GPIO bank with up to 32 pins. | 90 | /// A GPIO bank with up to 32 pins. |
| 87 | #[derive(Debug, Eq, PartialEq)] | 91 | #[derive(Debug, Eq, PartialEq)] |
| 92 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 88 | pub enum Bank { | 93 | pub enum Bank { |
| 89 | /// Bank 0. | 94 | /// Bank 0. |
| 90 | Bank0 = 0, | 95 | Bank0 = 0, |
| @@ -108,6 +113,8 @@ pub struct DormantWakeConfig { | |||
| 108 | } | 113 | } |
| 109 | 114 | ||
| 110 | /// GPIO input driver. | 115 | /// GPIO input driver. |
| 116 | #[derive(Debug)] | ||
| 117 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 111 | pub struct Input<'d> { | 118 | pub struct Input<'d> { |
| 112 | pin: Flex<'d>, | 119 | pin: Flex<'d>, |
| 113 | } | 120 | } |
| @@ -358,6 +365,8 @@ impl<'d> Future for InputFuture<'d> { | |||
| 358 | } | 365 | } |
| 359 | 366 | ||
| 360 | /// GPIO output driver. | 367 | /// GPIO output driver. |
| 368 | #[derive(Debug)] | ||
| 369 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 361 | pub struct Output<'d> { | 370 | pub struct Output<'d> { |
| 362 | pin: Flex<'d>, | 371 | pin: Flex<'d>, |
| 363 | } | 372 | } |
| @@ -445,6 +454,8 @@ impl<'d> Output<'d> { | |||
| 445 | } | 454 | } |
| 446 | 455 | ||
| 447 | /// GPIO output open-drain. | 456 | /// GPIO output open-drain. |
| 457 | #[derive(Debug)] | ||
| 458 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 448 | pub struct OutputOpenDrain<'d> { | 459 | pub struct OutputOpenDrain<'d> { |
| 449 | pin: Flex<'d>, | 460 | pin: Flex<'d>, |
| 450 | } | 461 | } |
| @@ -592,6 +603,8 @@ impl<'d> OutputOpenDrain<'d> { | |||
| 592 | /// This pin can be either an input or output pin. The output level register bit will remain | 603 | /// This pin can be either an input or output pin. The output level register bit will remain |
| 593 | /// set while not in output mode, so the pin's level will be 'remembered' when it is not in output | 604 | /// set while not in output mode, so the pin's level will be 'remembered' when it is not in output |
| 594 | /// mode. | 605 | /// mode. |
| 606 | #[derive(Debug)] | ||
| 607 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 595 | pub struct Flex<'d> { | 608 | pub struct Flex<'d> { |
| 596 | pin: Peri<'d, AnyPin>, | 609 | pin: Peri<'d, AnyPin>, |
| 597 | } | 610 | } |
| @@ -864,6 +877,8 @@ impl<'d> Drop for Flex<'d> { | |||
| 864 | } | 877 | } |
| 865 | 878 | ||
| 866 | /// Dormant wake driver. | 879 | /// Dormant wake driver. |
| 880 | #[derive(Debug)] | ||
| 881 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 867 | pub struct DormantWake<'w> { | 882 | pub struct DormantWake<'w> { |
| 868 | pin: Peri<'w, AnyPin>, | 883 | pin: Peri<'w, AnyPin>, |
| 869 | cfg: DormantWakeConfig, | 884 | cfg: DormantWakeConfig, |
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index a983b7bc3..172193a07 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs | |||
| @@ -76,6 +76,7 @@ impl Default for Config { | |||
| 76 | pub const FIFO_SIZE: u8 = 16; | 76 | pub const FIFO_SIZE: u8 = 16; |
| 77 | 77 | ||
| 78 | /// I2C driver. | 78 | /// I2C driver. |
| 79 | #[derive(Debug)] | ||
| 79 | pub struct I2c<'d, T: Instance, M: Mode> { | 80 | pub struct I2c<'d, T: Instance, M: Mode> { |
| 80 | phantom: PhantomData<(&'d mut T, M)>, | 81 | phantom: PhantomData<(&'d mut T, M)>, |
| 81 | } | 82 | } |
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index f3c5a35bb..eb497de1a 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -189,11 +189,13 @@ macro_rules! bind_interrupts { | |||
| 189 | #[no_mangle] | 189 | #[no_mangle] |
| 190 | $(#[cfg($cond_irq)])? | 190 | $(#[cfg($cond_irq)])? |
| 191 | unsafe extern "C" fn $irq() { | 191 | unsafe extern "C" fn $irq() { |
| 192 | $( | 192 | unsafe { |
| 193 | $(#[cfg($cond_handler)])? | 193 | $( |
| 194 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); | 194 | $(#[cfg($cond_handler)])? |
| 195 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); | ||
| 195 | 196 | ||
| 196 | )* | 197 | )* |
| 198 | } | ||
| 197 | } | 199 | } |
| 198 | 200 | ||
| 199 | $(#[cfg($cond_irq)])? | 201 | $(#[cfg($cond_irq)])? |
| @@ -567,7 +569,7 @@ unsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { | |||
| 567 | unsafe { | 569 | unsafe { |
| 568 | core.MPU.ctrl.write(5); // enable mpu with background default map | 570 | core.MPU.ctrl.write(5); // enable mpu with background default map |
| 569 | core.MPU.rbar.write(stack_bottom as u32 & !0xff); // set address | 571 | core.MPU.rbar.write(stack_bottom as u32 & !0xff); // set address |
| 570 | core.MPU.rlar.write(1); // enable region | 572 | core.MPU.rlar.write(((stack_bottom as usize + 255) as u32) | 1); |
| 571 | } | 573 | } |
| 572 | Ok(()) | 574 | Ok(()) |
| 573 | } | 575 | } |
diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index d10b6837c..64065fcba 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs | |||
| @@ -57,6 +57,26 @@ const PAUSE_TOKEN: u32 = 0xDEADBEEF; | |||
| 57 | const RESUME_TOKEN: u32 = !0xDEADBEEF; | 57 | const RESUME_TOKEN: u32 = !0xDEADBEEF; |
| 58 | static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); | 58 | static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); |
| 59 | 59 | ||
| 60 | /// Represents a partiticular CPU core (SIO_CPUID) | ||
| 61 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] | ||
| 62 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 63 | #[repr(u8)] | ||
| 64 | pub enum CoreId { | ||
| 65 | /// Core 0 | ||
| 66 | Core0 = 0x0, | ||
| 67 | /// Core 1 | ||
| 68 | Core1 = 0x1, | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Gets which core we are currently executing from | ||
| 72 | pub fn current_core() -> CoreId { | ||
| 73 | if pac::SIO.cpuid().read() == 0 { | ||
| 74 | CoreId::Core0 | ||
| 75 | } else { | ||
| 76 | CoreId::Core1 | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 60 | #[inline(always)] | 80 | #[inline(always)] |
| 61 | unsafe fn core1_setup(stack_bottom: *mut usize) { | 81 | unsafe fn core1_setup(stack_bottom: *mut usize) { |
| 62 | if install_stack_guard(stack_bottom).is_err() { | 82 | if install_stack_guard(stack_bottom).is_err() { |
diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs index 34487819f..6ff40ddd7 100644 --- a/embassy-rp/src/relocate.rs +++ b/embassy-rp/src/relocate.rs | |||
| @@ -39,7 +39,7 @@ pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> { | |||
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { | 41 | impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { |
| 42 | pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<PROGRAM_SIZE> { | 42 | pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<'_, PROGRAM_SIZE> { |
| 43 | RelocatedProgram { program, origin } | 43 | RelocatedProgram { program, origin } |
| 44 | } | 44 | } |
| 45 | 45 | ||
diff --git a/embassy-rp/src/rom_data/mod.rs b/embassy-rp/src/rom_data/mod.rs index e5fcf8e3c..a4aba5737 100644 --- a/embassy-rp/src/rom_data/mod.rs +++ b/embassy-rp/src/rom_data/mod.rs | |||
| @@ -1,29 +1,29 @@ | |||
| 1 | #, Section 2.8.2.1: | 6 | From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1: |
| 7 | //! | 7 | |
| 8 | //! > The Bootrom contains a number of public functions that provide useful | 8 | > The Bootrom contains a number of public functions that provide useful |
| 9 | //! > RP2040 functionality that might be needed in the absence of any other code | 9 | > RP2040 functionality that might be needed in the absence of any other code |
| 10 | //! > on the device, as well as highly optimized versions of certain key | 10 | > on the device, as well as highly optimized versions of certain key |
| 11 | //! > functionality that would otherwise have to take up space in most user | 11 | > functionality that would otherwise have to take up space in most user |
| 12 | //! > binaries. | 12 | > binaries. |
| 13 | " | 13 | " |
| 14 | )] | 14 | )] |
| 15 | # of the | 20 | From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the |
| 21 | //! RP2350 datasheet: | 21 | RP2350 datasheet: |
| 22 | //! | 22 | |
| 23 | //! > Whilst some ROM space is dedicated to the implementation of the boot | 23 | > Whilst some ROM space is dedicated to the implementation of the boot |
| 24 | //! > sequence and USB/UART boot interfaces, the bootrom also contains public | 24 | > sequence and USB/UART boot interfaces, the bootrom also contains public |
| 25 | //! > functions that provide useful RP2350 functionality that may be useful for | 25 | > functions that provide useful RP2350 functionality that may be useful for |
| 26 | //! > any code or runtime running on the device | 26 | > any code or runtime running on the device |
| 27 | " | 27 | " |
| 28 | )] | 28 | )] |
| 29 | 29 | ||
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index c3a15fda5..6f4e2ee07 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs | |||
| @@ -495,52 +495,58 @@ impl<'d> UartRx<'d, Async> { | |||
| 495 | unreachable!("unrecognized rx error"); | 495 | unreachable!("unrecognized rx error"); |
| 496 | } | 496 | } |
| 497 | 497 | ||
| 498 | /// Read from the UART, waiting for a line break. | 498 | /// Read from the UART, waiting for a break. |
| 499 | /// | 499 | /// |
| 500 | /// We read until one of the following occurs: | 500 | /// We read until one of the following occurs: |
| 501 | /// | 501 | /// |
| 502 | /// * We read `buffer.len()` bytes without a line break | 502 | /// * We read `buffer.len()` bytes without a break |
| 503 | /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` | 503 | /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` |
| 504 | /// * We read `n` bytes then a line break occurs | 504 | /// * We read `n` bytes then a break occurs |
| 505 | /// * returns `Ok(n)` | 505 | /// * returns `Ok(n)` |
| 506 | /// * We encounter some error OTHER than a line break | 506 | /// * We encounter some error OTHER than a break |
| 507 | /// * returns `Err(ReadToBreakError::Other(error))` | 507 | /// * returns `Err(ReadToBreakError::Other(error))` |
| 508 | /// | 508 | /// |
| 509 | /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected | 509 | /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected |
| 510 | /// message to reliably detect the framing on one single call to `read_to_break()`. | 510 | /// message to reliably detect the framing on one single call to `read_to_break()`. |
| 511 | /// | 511 | /// |
| 512 | /// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer: | 512 | /// * If you expect a message of 20 bytes + break, and provide a 20-byte buffer: |
| 513 | /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` | 513 | /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` |
| 514 | /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break | 514 | /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" break |
| 515 | /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: | 515 | /// * If you expect a message of 20 bytes + break, and provide a 21-byte buffer: |
| 516 | /// * The first call to `read_to_break()` will return `Ok(20)`. | 516 | /// * The first call to `read_to_break()` will return `Ok(20)`. |
| 517 | /// * The next call to `read_to_break()` will work as expected | 517 | /// * The next call to `read_to_break()` will work as expected |
| 518 | /// | ||
| 519 | /// **NOTE**: In the UART context, a break refers to a break condition (the line being held low for | ||
| 520 | /// for longer than a single character), not an ASCII line break. | ||
| 518 | pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> { | 521 | pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> { |
| 519 | self.read_to_break_with_count(buffer, 0).await | 522 | self.read_to_break_with_count(buffer, 0).await |
| 520 | } | 523 | } |
| 521 | 524 | ||
| 522 | /// Read from the UART, waiting for a line break as soon as at least `min_count` bytes have been read. | 525 | /// Read from the UART, waiting for a break as soon as at least `min_count` bytes have been read. |
| 523 | /// | 526 | /// |
| 524 | /// We read until one of the following occurs: | 527 | /// We read until one of the following occurs: |
| 525 | /// | 528 | /// |
| 526 | /// * We read `buffer.len()` bytes without a line break | 529 | /// * We read `buffer.len()` bytes without a break |
| 527 | /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` | 530 | /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` |
| 528 | /// * We read `n > min_count` bytes then a line break occurs | 531 | /// * We read `n > min_count` bytes then a break occurs |
| 529 | /// * returns `Ok(n)` | 532 | /// * returns `Ok(n)` |
| 530 | /// * We encounter some error OTHER than a line break | 533 | /// * We encounter some error OTHER than a break |
| 531 | /// * returns `Err(ReadToBreakError::Other(error))` | 534 | /// * returns `Err(ReadToBreakError::Other(error))` |
| 532 | /// | 535 | /// |
| 533 | /// If a line break occurs before `min_count` bytes have been read, the break will be ignored and the read will continue | 536 | /// If a break occurs before `min_count` bytes have been read, the break will be ignored and the read will continue |
| 534 | /// | 537 | /// |
| 535 | /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected | 538 | /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected |
| 536 | /// message to reliably detect the framing on one single call to `read_to_break()`. | 539 | /// message to reliably detect the framing on one single call to `read_to_break()`. |
| 537 | /// | 540 | /// |
| 538 | /// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer: | 541 | /// * If you expect a message of 20 bytes + break, and provide a 20-byte buffer: |
| 539 | /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` | 542 | /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` |
| 540 | /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break | 543 | /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break |
| 541 | /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: | 544 | /// * If you expect a message of 20 bytes + break, and provide a 21-byte buffer: |
| 542 | /// * The first call to `read_to_break()` will return `Ok(20)`. | 545 | /// * The first call to `read_to_break()` will return `Ok(20)`. |
| 543 | /// * The next call to `read_to_break()` will work as expected | 546 | /// * The next call to `read_to_break()` will work as expected |
| 547 | /// | ||
| 548 | /// **NOTE**: In the UART context, a break refers to a break condition (the line being held low for | ||
| 549 | /// for longer than a single character), not an ASCII line break. | ||
| 544 | pub async fn read_to_break_with_count( | 550 | pub async fn read_to_break_with_count( |
| 545 | &mut self, | 551 | &mut self, |
| 546 | buffer: &mut [u8], | 552 | buffer: &mut [u8], |
diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 96541ade6..671ecbd32 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs | |||
| @@ -153,6 +153,7 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 153 | fn alloc_endpoint<D: Dir>( | 153 | fn alloc_endpoint<D: Dir>( |
| 154 | &mut self, | 154 | &mut self, |
| 155 | ep_type: EndpointType, | 155 | ep_type: EndpointType, |
| 156 | ep_addr: Option<EndpointAddress>, | ||
| 156 | max_packet_size: u16, | 157 | max_packet_size: u16, |
| 157 | interval_ms: u8, | 158 | interval_ms: u8, |
| 158 | ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { | 159 | ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { |
| @@ -169,12 +170,25 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 169 | Direction::In => &mut self.ep_in, | 170 | Direction::In => &mut self.ep_in, |
| 170 | }; | 171 | }; |
| 171 | 172 | ||
| 172 | let index = alloc.iter_mut().enumerate().find(|(i, ep)| { | 173 | let index = if let Some(addr) = ep_addr { |
| 173 | if *i == 0 { | 174 | // Use the specified endpoint address |
| 174 | return false; // reserved for control pipe | 175 | let requested_index = addr.index(); |
| 176 | if requested_index == 0 || requested_index >= EP_COUNT { | ||
| 177 | return Err(EndpointAllocError); | ||
| 175 | } | 178 | } |
| 176 | !ep.used | 179 | if alloc[requested_index].used { |
| 177 | }); | 180 | return Err(EndpointAllocError); |
| 181 | } | ||
| 182 | Some((requested_index, &mut alloc[requested_index])) | ||
| 183 | } else { | ||
| 184 | // Find any available endpoint | ||
| 185 | alloc.iter_mut().enumerate().find(|(i, ep)| { | ||
| 186 | if *i == 0 { | ||
| 187 | return false; // reserved for control pipe | ||
| 188 | } | ||
| 189 | !ep.used | ||
| 190 | }) | ||
| 191 | }; | ||
| 178 | 192 | ||
| 179 | let (index, ep) = index.ok_or(EndpointAllocError)?; | 193 | let (index, ep) = index.ok_or(EndpointAllocError)?; |
| 180 | assert!(!ep.used); | 194 | assert!(!ep.used); |
| @@ -299,19 +313,21 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { | |||
| 299 | fn alloc_endpoint_in( | 313 | fn alloc_endpoint_in( |
| 300 | &mut self, | 314 | &mut self, |
| 301 | ep_type: EndpointType, | 315 | ep_type: EndpointType, |
| 316 | ep_addr: Option<EndpointAddress>, | ||
| 302 | max_packet_size: u16, | 317 | max_packet_size: u16, |
| 303 | interval_ms: u8, | 318 | interval_ms: u8, |
| 304 | ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { | 319 | ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { |
| 305 | self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | 320 | self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) |
| 306 | } | 321 | } |
| 307 | 322 | ||
| 308 | fn alloc_endpoint_out( | 323 | fn alloc_endpoint_out( |
| 309 | &mut self, | 324 | &mut self, |
| 310 | ep_type: EndpointType, | 325 | ep_type: EndpointType, |
| 326 | ep_addr: Option<EndpointAddress>, | ||
| 311 | max_packet_size: u16, | 327 | max_packet_size: u16, |
| 312 | interval_ms: u8, | 328 | interval_ms: u8, |
| 313 | ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { | 329 | ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { |
| 314 | self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | 330 | self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) |
| 315 | } | 331 | } |
| 316 | 332 | ||
| 317 | fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { | 333 | fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { |
diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index b749625aa..a16aa4b54 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml | |||
| @@ -23,8 +23,8 @@ embassy-stm32 = { version = "0.2.0", path = "../embassy-stm32" } | |||
| 23 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 23 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 24 | embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } | 24 | embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } |
| 25 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 25 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 26 | embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal" } | 26 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } |
| 27 | embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" } | 27 | embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal" } |
| 28 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } | 28 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } |
| 29 | 29 | ||
| 30 | defmt = { version = "1.0.1", optional = true } | 30 | defmt = { version = "1.0.1", optional = true } |
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index b6781905e..c4c2cd013 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 8 | ## Unreleased | 8 | ## Unreleased |
| 9 | - Modify BufferedUart initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983)) | 9 | - Modify BufferedUart initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983)) |
| 10 | - Added a 'single-bank' and a 'dual-bank' feature so chips with configurable flash bank setups are be supported in embassy ([#4125](https://github.com/embassy-rs/embassy/pull/4125)) | 10 | - Added a 'single-bank' and a 'dual-bank' feature so chips with configurable flash bank setups are be supported in embassy ([#4125](https://github.com/embassy-rs/embassy/pull/4125)) |
| 11 | - Add automatic setting of remap bits when using alternate DMA channels on STM32F0 and STM32F3 devices ([#3653](https://github.com/embassy-rs/embassy/pull/3653)) | ||
| 11 | 12 | ||
| 12 | ## 0.2.0 - 2025-01-10 | 13 | ## 0.2.0 - 2025-01-10 |
| 13 | 14 | ||
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 034f51df9..02e75733e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -53,11 +53,11 @@ embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } | |||
| 53 | embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } | 53 | embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } |
| 54 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } | 54 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } |
| 55 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 55 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 56 | embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } | 56 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } |
| 57 | embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } | 57 | embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } |
| 58 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } | 58 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } |
| 59 | embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } | 59 | embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } |
| 60 | embassy-usb-synopsys-otg = { version = "0.2.0", path = "../embassy-usb-synopsys-otg" } | 60 | embassy-usb-synopsys-otg = { version = "0.3.0", path = "../embassy-usb-synopsys-otg" } |
| 61 | embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } | 61 | embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } |
| 62 | 62 | ||
| 63 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 63 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } | |||
| 81 | sdio-host = "0.9.0" | 81 | sdio-host = "0.9.0" |
| 82 | critical-section = "1.1" | 82 | critical-section = "1.1" |
| 83 | #stm32-metapac = { version = "16" } | 83 | #stm32-metapac = { version = "16" } |
| 84 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec" } | 84 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3" } |
| 85 | 85 | ||
| 86 | vcell = "0.1.3" | 86 | vcell = "0.1.3" |
| 87 | nb = "1.0.0" | 87 | nb = "1.0.0" |
| @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" | |||
| 110 | quote = "1.0.15" | 110 | quote = "1.0.15" |
| 111 | 111 | ||
| 112 | #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} | 112 | #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} |
| 113 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec", default-features = false, features = ["metadata"] } | 113 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3", default-features = false, features = ["metadata"] } |
| 114 | 114 | ||
| 115 | [features] | 115 | [features] |
| 116 | default = ["rt"] | 116 | default = ["rt"] |
| @@ -129,6 +129,7 @@ defmt = [ | |||
| 129 | "embassy-net-driver/defmt", | 129 | "embassy-net-driver/defmt", |
| 130 | "embassy-time?/defmt", | 130 | "embassy-time?/defmt", |
| 131 | "embassy-usb-synopsys-otg/defmt", | 131 | "embassy-usb-synopsys-otg/defmt", |
| 132 | "stm32-metapac/defmt" | ||
| 132 | ] | 133 | ] |
| 133 | 134 | ||
| 134 | exti = [] | 135 | exti = [] |
| @@ -237,6 +238,47 @@ stm32c031g4 = [ "stm32-metapac/stm32c031g4" ] | |||
| 237 | stm32c031g6 = [ "stm32-metapac/stm32c031g6" ] | 238 | stm32c031g6 = [ "stm32-metapac/stm32c031g6" ] |
| 238 | stm32c031k4 = [ "stm32-metapac/stm32c031k4" ] | 239 | stm32c031k4 = [ "stm32-metapac/stm32c031k4" ] |
| 239 | stm32c031k6 = [ "stm32-metapac/stm32c031k6" ] | 240 | stm32c031k6 = [ "stm32-metapac/stm32c031k6" ] |
| 241 | stm32c051c6 = [ "stm32-metapac/stm32c051c6" ] | ||
| 242 | stm32c051c8 = [ "stm32-metapac/stm32c051c8" ] | ||
| 243 | stm32c051d8 = [ "stm32-metapac/stm32c051d8" ] | ||
| 244 | stm32c051f6 = [ "stm32-metapac/stm32c051f6" ] | ||
| 245 | stm32c051f8 = [ "stm32-metapac/stm32c051f8" ] | ||
| 246 | stm32c051g6 = [ "stm32-metapac/stm32c051g6" ] | ||
| 247 | stm32c051g8 = [ "stm32-metapac/stm32c051g8" ] | ||
| 248 | stm32c051k6 = [ "stm32-metapac/stm32c051k6" ] | ||
| 249 | stm32c051k8 = [ "stm32-metapac/stm32c051k8" ] | ||
| 250 | stm32c071c8 = [ "stm32-metapac/stm32c071c8" ] | ||
| 251 | stm32c071cb = [ "stm32-metapac/stm32c071cb" ] | ||
| 252 | stm32c071f8 = [ "stm32-metapac/stm32c071f8" ] | ||
| 253 | stm32c071fb = [ "stm32-metapac/stm32c071fb" ] | ||
| 254 | stm32c071g8 = [ "stm32-metapac/stm32c071g8" ] | ||
| 255 | stm32c071gb = [ "stm32-metapac/stm32c071gb" ] | ||
| 256 | stm32c071k8 = [ "stm32-metapac/stm32c071k8" ] | ||
| 257 | stm32c071kb = [ "stm32-metapac/stm32c071kb" ] | ||
| 258 | stm32c071r8 = [ "stm32-metapac/stm32c071r8" ] | ||
| 259 | stm32c071rb = [ "stm32-metapac/stm32c071rb" ] | ||
| 260 | stm32c091cb = [ "stm32-metapac/stm32c091cb" ] | ||
| 261 | stm32c091cc = [ "stm32-metapac/stm32c091cc" ] | ||
| 262 | stm32c091ec = [ "stm32-metapac/stm32c091ec" ] | ||
| 263 | stm32c091fb = [ "stm32-metapac/stm32c091fb" ] | ||
| 264 | stm32c091fc = [ "stm32-metapac/stm32c091fc" ] | ||
| 265 | stm32c091gb = [ "stm32-metapac/stm32c091gb" ] | ||
| 266 | stm32c091gc = [ "stm32-metapac/stm32c091gc" ] | ||
| 267 | stm32c091kb = [ "stm32-metapac/stm32c091kb" ] | ||
| 268 | stm32c091kc = [ "stm32-metapac/stm32c091kc" ] | ||
| 269 | stm32c091rb = [ "stm32-metapac/stm32c091rb" ] | ||
| 270 | stm32c091rc = [ "stm32-metapac/stm32c091rc" ] | ||
| 271 | stm32c092cb = [ "stm32-metapac/stm32c092cb" ] | ||
| 272 | stm32c092cc = [ "stm32-metapac/stm32c092cc" ] | ||
| 273 | stm32c092ec = [ "stm32-metapac/stm32c092ec" ] | ||
| 274 | stm32c092fb = [ "stm32-metapac/stm32c092fb" ] | ||
| 275 | stm32c092fc = [ "stm32-metapac/stm32c092fc" ] | ||
| 276 | stm32c092gb = [ "stm32-metapac/stm32c092gb" ] | ||
| 277 | stm32c092gc = [ "stm32-metapac/stm32c092gc" ] | ||
| 278 | stm32c092kb = [ "stm32-metapac/stm32c092kb" ] | ||
| 279 | stm32c092kc = [ "stm32-metapac/stm32c092kc" ] | ||
| 280 | stm32c092rb = [ "stm32-metapac/stm32c092rb" ] | ||
| 281 | stm32c092rc = [ "stm32-metapac/stm32c092rc" ] | ||
| 240 | stm32f030c6 = [ "stm32-metapac/stm32f030c6" ] | 282 | stm32f030c6 = [ "stm32-metapac/stm32f030c6" ] |
| 241 | stm32f030c8 = [ "stm32-metapac/stm32f030c8" ] | 283 | stm32f030c8 = [ "stm32-metapac/stm32f030c8" ] |
| 242 | stm32f030cc = [ "stm32-metapac/stm32f030cc" ] | 284 | stm32f030cc = [ "stm32-metapac/stm32f030cc" ] |
| @@ -1634,6 +1676,24 @@ stm32wba55he = [ "stm32-metapac/stm32wba55he" ] | |||
| 1634 | stm32wba55hg = [ "stm32-metapac/stm32wba55hg" ] | 1676 | stm32wba55hg = [ "stm32-metapac/stm32wba55hg" ] |
| 1635 | stm32wba55ue = [ "stm32-metapac/stm32wba55ue" ] | 1677 | stm32wba55ue = [ "stm32-metapac/stm32wba55ue" ] |
| 1636 | stm32wba55ug = [ "stm32-metapac/stm32wba55ug" ] | 1678 | stm32wba55ug = [ "stm32-metapac/stm32wba55ug" ] |
| 1679 | stm32wba62cg = [ "stm32-metapac/stm32wba62cg" ] | ||
| 1680 | stm32wba62ci = [ "stm32-metapac/stm32wba62ci" ] | ||
| 1681 | stm32wba62mg = [ "stm32-metapac/stm32wba62mg" ] | ||
| 1682 | stm32wba62mi = [ "stm32-metapac/stm32wba62mi" ] | ||
| 1683 | stm32wba62pg = [ "stm32-metapac/stm32wba62pg" ] | ||
| 1684 | stm32wba62pi = [ "stm32-metapac/stm32wba62pi" ] | ||
| 1685 | stm32wba63cg = [ "stm32-metapac/stm32wba63cg" ] | ||
| 1686 | stm32wba63ci = [ "stm32-metapac/stm32wba63ci" ] | ||
| 1687 | stm32wba64cg = [ "stm32-metapac/stm32wba64cg" ] | ||
| 1688 | stm32wba64ci = [ "stm32-metapac/stm32wba64ci" ] | ||
| 1689 | stm32wba65cg = [ "stm32-metapac/stm32wba65cg" ] | ||
| 1690 | stm32wba65ci = [ "stm32-metapac/stm32wba65ci" ] | ||
| 1691 | stm32wba65mg = [ "stm32-metapac/stm32wba65mg" ] | ||
| 1692 | stm32wba65mi = [ "stm32-metapac/stm32wba65mi" ] | ||
| 1693 | stm32wba65pg = [ "stm32-metapac/stm32wba65pg" ] | ||
| 1694 | stm32wba65pi = [ "stm32-metapac/stm32wba65pi" ] | ||
| 1695 | stm32wba65rg = [ "stm32-metapac/stm32wba65rg" ] | ||
| 1696 | stm32wba65ri = [ "stm32-metapac/stm32wba65ri" ] | ||
| 1637 | stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4", "_dual-core", "_core-cm4" ] | 1697 | stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4", "_dual-core", "_core-cm4" ] |
| 1638 | stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p", "_dual-core", "_core-cm0p" ] | 1698 | stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p", "_dual-core", "_core-cm0p" ] |
| 1639 | stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4", "_dual-core", "_core-cm4" ] | 1699 | stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4", "_dual-core", "_core-cm4" ] |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index bb5ef53d7..73860c64a 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -505,6 +505,13 @@ fn main() { | |||
| 505 | field: "CLK48SEL", | 505 | field: "CLK48SEL", |
| 506 | }, | 506 | }, |
| 507 | ); | 507 | ); |
| 508 | clock_gen.chained_muxes.insert( | ||
| 509 | "RFWKP", | ||
| 510 | &PeripheralRccRegister { | ||
| 511 | register: "CSR", | ||
| 512 | field: "RFWKPSEL", | ||
| 513 | }, | ||
| 514 | ); | ||
| 508 | } | 515 | } |
| 509 | if chip_name.starts_with("stm32f7") { | 516 | if chip_name.starts_with("stm32f7") { |
| 510 | clock_gen.chained_muxes.insert( | 517 | clock_gen.chained_muxes.insert( |
| @@ -1090,21 +1097,21 @@ fn main() { | |||
| 1090 | (("fmc", "CLK"), quote!(crate::fmc::ClkPin)), | 1097 | (("fmc", "CLK"), quote!(crate::fmc::ClkPin)), |
| 1091 | (("fmc", "BA0"), quote!(crate::fmc::BA0Pin)), | 1098 | (("fmc", "BA0"), quote!(crate::fmc::BA0Pin)), |
| 1092 | (("fmc", "BA1"), quote!(crate::fmc::BA1Pin)), | 1099 | (("fmc", "BA1"), quote!(crate::fmc::BA1Pin)), |
| 1093 | (("timer", "CH1"), quote!(crate::timer::Channel1Pin)), | 1100 | (("timer", "CH1"), quote!(crate::timer::TimerPin<Ch1>)), |
| 1094 | (("timer", "CH1N"), quote!(crate::timer::Channel1ComplementaryPin)), | 1101 | (("timer", "CH1N"), quote!(crate::timer::TimerComplementaryPin<Ch1>)), |
| 1095 | (("timer", "CH2"), quote!(crate::timer::Channel2Pin)), | 1102 | (("timer", "CH2"), quote!(crate::timer::TimerPin<Ch2>)), |
| 1096 | (("timer", "CH2N"), quote!(crate::timer::Channel2ComplementaryPin)), | 1103 | (("timer", "CH2N"), quote!(crate::timer::TimerComplementaryPin<Ch2>)), |
| 1097 | (("timer", "CH3"), quote!(crate::timer::Channel3Pin)), | 1104 | (("timer", "CH3"), quote!(crate::timer::TimerPin<Ch3>)), |
| 1098 | (("timer", "CH3N"), quote!(crate::timer::Channel3ComplementaryPin)), | 1105 | (("timer", "CH3N"), quote!(crate::timer::TimerComplementaryPin<Ch3>)), |
| 1099 | (("timer", "CH4"), quote!(crate::timer::Channel4Pin)), | 1106 | (("timer", "CH4"), quote!(crate::timer::TimerPin<Ch4>)), |
| 1100 | (("timer", "CH4N"), quote!(crate::timer::Channel4ComplementaryPin)), | 1107 | (("timer", "CH4N"), quote!(crate::timer::TimerComplementaryPin<Ch4>)), |
| 1101 | (("timer", "ETR"), quote!(crate::timer::ExternalTriggerPin)), | 1108 | (("timer", "ETR"), quote!(crate::timer::ExternalTriggerPin)), |
| 1102 | (("timer", "BKIN"), quote!(crate::timer::BreakInputPin)), | 1109 | (("timer", "BKIN"), quote!(crate::timer::BreakInputPin<BkIn1>)), |
| 1103 | (("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin)), | 1110 | (("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin<BkIn1>)), |
| 1104 | (("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin)), | 1111 | (("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin<BkIn1>)), |
| 1105 | (("timer", "BKIN2"), quote!(crate::timer::BreakInput2Pin)), | 1112 | (("timer", "BKIN2"), quote!(crate::timer::BreakInputPin<BkIn2>)), |
| 1106 | (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)), | 1113 | (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInputComparator1Pin<BkIn2>)), |
| 1107 | (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)), | 1114 | (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInputComparator2Pin<BkIn2>)), |
| 1108 | (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)), | 1115 | (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)), |
| 1109 | (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)), | 1116 | (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)), |
| 1110 | (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)), | 1117 | (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)), |
| @@ -1402,31 +1409,24 @@ fn main() { | |||
| 1402 | } | 1409 | } |
| 1403 | 1410 | ||
| 1404 | if regs.kind == "opamp" { | 1411 | if regs.kind == "opamp" { |
| 1405 | if pin.signal.starts_with("VP") { | 1412 | let peri = format_ident!("{}", p.name); |
| 1406 | // Impl NonInvertingPin for the VP* signals (VP0, VP1, VP2, etc) | 1413 | let pin_name = format_ident!("{}", pin.pin); |
| 1407 | let peri = format_ident!("{}", p.name); | 1414 | if let Some(ch_str) = pin.signal.strip_prefix("VINP") { |
| 1408 | let pin_name = format_ident!("{}", pin.pin); | 1415 | // Impl NonInvertingPin for VINP0, VINP1 etc. |
| 1409 | let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap(); | 1416 | if let Ok(ch) = ch_str.parse::<u8>() { |
| 1410 | 1417 | g.extend(quote! { | |
| 1411 | g.extend(quote! { | 1418 | impl_opamp_vp_pin!( #peri, #pin_name, #ch ); |
| 1412 | impl_opamp_vp_pin!( #peri, #pin_name, #ch); | 1419 | }); |
| 1413 | }) | 1420 | } |
| 1414 | } else if pin.signal.starts_with("VINM") { | 1421 | } else if let Some(ch_str) = pin.signal.strip_prefix("VINM") { |
| 1415 | // Impl NonInvertingPin for the VINM* signals ( VINM0, VINM1, etc) | 1422 | // Impl InvertingPin for VINM0, VINM1 etc. |
| 1416 | // STM32G4 | 1423 | if let Ok(ch) = ch_str.parse::<u8>() { |
| 1417 | let peri = format_ident!("{}", p.name); | ||
| 1418 | let pin_name = format_ident!("{}", pin.pin); | ||
| 1419 | let ch: Result<u8, _> = pin.signal.strip_prefix("VINM").unwrap().parse(); | ||
| 1420 | |||
| 1421 | if let Ok(ch) = ch { | ||
| 1422 | g.extend(quote! { | 1424 | g.extend(quote! { |
| 1423 | impl_opamp_vn_pin!( #peri, #pin_name, #ch); | 1425 | impl_opamp_vn_pin!( #peri, #pin_name, #ch); |
| 1424 | }) | 1426 | }); |
| 1425 | } | 1427 | } |
| 1426 | } else if pin.signal == "VOUT" { | 1428 | } else if pin.signal == "VOUT" { |
| 1427 | // Impl OutputPin for the VOUT pin | 1429 | // Impl OutputPin for the VOUT pin |
| 1428 | let peri = format_ident!("{}", p.name); | ||
| 1429 | let pin_name = format_ident!("{}", pin.pin); | ||
| 1430 | g.extend(quote! { | 1430 | g.extend(quote! { |
| 1431 | impl_opamp_vout_pin!( #peri, #pin_name ); | 1431 | impl_opamp_vout_pin!( #peri, #pin_name ); |
| 1432 | }) | 1432 | }) |
| @@ -1482,10 +1482,10 @@ fn main() { | |||
| 1482 | (("hash", "IN"), quote!(crate::hash::Dma)), | 1482 | (("hash", "IN"), quote!(crate::hash::Dma)), |
| 1483 | (("cryp", "IN"), quote!(crate::cryp::DmaIn)), | 1483 | (("cryp", "IN"), quote!(crate::cryp::DmaIn)), |
| 1484 | (("cryp", "OUT"), quote!(crate::cryp::DmaOut)), | 1484 | (("cryp", "OUT"), quote!(crate::cryp::DmaOut)), |
| 1485 | (("timer", "CH1"), quote!(crate::timer::Ch1Dma)), | 1485 | (("timer", "CH1"), quote!(crate::timer::Dma<Ch1>)), |
| 1486 | (("timer", "CH2"), quote!(crate::timer::Ch2Dma)), | 1486 | (("timer", "CH2"), quote!(crate::timer::Dma<Ch2>)), |
| 1487 | (("timer", "CH3"), quote!(crate::timer::Ch3Dma)), | 1487 | (("timer", "CH3"), quote!(crate::timer::Dma<Ch3>)), |
| 1488 | (("timer", "CH4"), quote!(crate::timer::Ch4Dma)), | 1488 | (("timer", "CH4"), quote!(crate::timer::Dma<Ch4>)), |
| 1489 | (("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), // FIXME: stm32u5a crash on Cordic driver | 1489 | (("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), // FIXME: stm32u5a crash on Cordic driver |
| 1490 | (("cordic", "READ"), quote!(crate::cordic::ReadDma)), // FIXME: stm32u5a crash on Cordic driver | 1490 | (("cordic", "READ"), quote!(crate::cordic::ReadDma)), // FIXME: stm32u5a crash on Cordic driver |
| 1491 | ] | 1491 | ] |
| @@ -1497,6 +1497,10 @@ fn main() { | |||
| 1497 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); | 1497 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); |
| 1498 | } | 1498 | } |
| 1499 | 1499 | ||
| 1500 | if chip_name.starts_with("stm32wba") { | ||
| 1501 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); | ||
| 1502 | } | ||
| 1503 | |||
| 1500 | if chip_name.starts_with("stm32g4") { | 1504 | if chip_name.starts_with("stm32g4") { |
| 1501 | let line_number = chip_name.chars().skip(8).next().unwrap(); | 1505 | let line_number = chip_name.chars().skip(8).next().unwrap(); |
| 1502 | if line_number == '3' || line_number == '4' { | 1506 | if line_number == '3' || line_number == '4' { |
| @@ -1554,9 +1558,35 @@ fn main() { | |||
| 1554 | quote!(()) | 1558 | quote!(()) |
| 1555 | }; | 1559 | }; |
| 1556 | 1560 | ||
| 1561 | let mut remap = quote!(); | ||
| 1562 | for remap_info in ch.remap { | ||
| 1563 | let register = format_ident!("{}", remap_info.register.to_lowercase()); | ||
| 1564 | let setter = format_ident!("set_{}", remap_info.field.to_lowercase()); | ||
| 1565 | |||
| 1566 | let field_metadata = METADATA | ||
| 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); | ||
| 1578 | quote!(#bool_value) | ||
| 1579 | } else { | ||
| 1580 | let value = remap_info.value; | ||
| 1581 | quote!(#value.into()) | ||
| 1582 | }; | ||
| 1583 | |||
| 1584 | remap.extend(quote!(crate::pac::SYSCFG.#register().modify(|w| w.#setter(#value));)); | ||
| 1585 | } | ||
| 1586 | |||
| 1557 | let channel = format_ident!("{}", channel); | 1587 | let channel = format_ident!("{}", channel); |
| 1558 | g.extend(quote! { | 1588 | g.extend(quote! { |
| 1559 | dma_trait_impl!(#tr, #peri, #channel, #request); | 1589 | dma_trait_impl!(#tr, #peri, #channel, #request, {#remap}); |
| 1560 | }); | 1590 | }); |
| 1561 | } | 1591 | } |
| 1562 | } | 1592 | } |
| @@ -1888,9 +1918,9 @@ fn main() { | |||
| 1888 | } | 1918 | } |
| 1889 | 1919 | ||
| 1890 | g.extend(quote!( | 1920 | g.extend(quote!( |
| 1891 | pub fn gpio_block(n: usize) -> crate::pac::gpio::Gpio {{ | 1921 | pub fn gpio_block(n: usize) -> crate::pac::gpio::Gpio { |
| 1892 | unsafe {{ crate::pac::gpio::Gpio::from_ptr((#gpio_base + #gpio_stride*n) as _) }} | 1922 | unsafe { crate::pac::gpio::Gpio::from_ptr((#gpio_base + #gpio_stride*n) as _) } |
| 1893 | }} | 1923 | } |
| 1894 | )); | 1924 | )); |
| 1895 | 1925 | ||
| 1896 | // ======== | 1926 | // ======== |
diff --git a/embassy-stm32/src/adc/u5_adc4.rs b/embassy-stm32/src/adc/adc4.rs index 1dd664366..31cbdc0d7 100644 --- a/embassy-stm32/src/adc/u5_adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs | |||
| @@ -1,10 +1,19 @@ | |||
| 1 | #[cfg(stm32u5)] | ||
| 2 | use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingRatio as OversamplingRatio}; | ||
| 1 | #[allow(unused)] | 3 | #[allow(unused)] |
| 2 | use pac::adc::vals::{Adc4Dmacfg, Adc4Exten, Adc4OversamplingRatio}; | 4 | #[cfg(stm32wba)] |
| 5 | use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; | ||
| 3 | 6 | ||
| 4 | use super::{blocking_delay_us, AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel}; | 7 | use super::{blocking_delay_us, AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel}; |
| 5 | use crate::dma::Transfer; | 8 | use crate::dma::Transfer; |
| 6 | pub use crate::pac::adc::regs::Adc4Chselrmod0; | 9 | #[cfg(stm32u5)] |
| 10 | pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; | ||
| 11 | #[cfg(stm32wba)] | ||
| 12 | pub use crate::pac::adc::regs::Chselr; | ||
| 13 | #[cfg(stm32u5)] | ||
| 7 | pub use crate::pac::adc::vals::{Adc4Presc as Presc, Adc4Res as Resolution, Adc4SampleTime as SampleTime}; | 14 | pub use crate::pac::adc::vals::{Adc4Presc as Presc, Adc4Res as Resolution, Adc4SampleTime as SampleTime}; |
| 15 | #[cfg(stm32wba)] | ||
| 16 | pub use crate::pac::adc::vals::{Presc, Res as Resolution, SampleTime}; | ||
| 8 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 9 | use crate::{pac, rcc, Peri}; | 18 | use crate::{pac, rcc, Peri}; |
| 10 | 19 | ||
| @@ -67,12 +76,14 @@ impl<T: Instance> SealedAdcChannel<T> for Vcore { | |||
| 67 | } | 76 | } |
| 68 | } | 77 | } |
| 69 | 78 | ||
| 79 | #[derive(Copy, Clone)] | ||
| 70 | pub enum DacChannel { | 80 | pub enum DacChannel { |
| 71 | OUT1, | 81 | OUT1, |
| 72 | OUT2, | 82 | OUT2, |
| 73 | } | 83 | } |
| 74 | 84 | ||
| 75 | /// Number of samples used for averaging. | 85 | /// Number of samples used for averaging. |
| 86 | #[derive(Copy, Clone)] | ||
| 76 | pub enum Averaging { | 87 | pub enum Averaging { |
| 77 | Disabled, | 88 | Disabled, |
| 78 | Samples2, | 89 | Samples2, |
| @@ -178,7 +189,7 @@ pub struct Adc4<'d, T: Instance> { | |||
| 178 | adc: crate::Peri<'d, T>, | 189 | adc: crate::Peri<'d, T>, |
| 179 | } | 190 | } |
| 180 | 191 | ||
| 181 | #[derive(Debug)] | 192 | #[derive(Copy, Clone, Debug)] |
| 182 | pub enum Adc4Error { | 193 | pub enum Adc4Error { |
| 183 | InvalidSequence, | 194 | InvalidSequence, |
| 184 | DMAError, | 195 | DMAError, |
| @@ -242,17 +253,28 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 242 | fn configure(&mut self) { | 253 | fn configure(&mut self) { |
| 243 | // single conversion mode, software trigger | 254 | // single conversion mode, software trigger |
| 244 | T::regs().cfgr1().modify(|w| { | 255 | T::regs().cfgr1().modify(|w| { |
| 256 | #[cfg(stm32u5)] | ||
| 245 | w.set_cont(false); | 257 | w.set_cont(false); |
| 258 | #[cfg(stm32wba)] | ||
| 259 | w.set_cont(Cont::SINGLE); | ||
| 246 | w.set_discen(false); | 260 | w.set_discen(false); |
| 247 | w.set_exten(Adc4Exten::DISABLED); | 261 | w.set_exten(Exten::DISABLED); |
| 262 | #[cfg(stm32u5)] | ||
| 248 | w.set_chselrmod(false); | 263 | w.set_chselrmod(false); |
| 264 | #[cfg(stm32wba)] | ||
| 265 | w.set_chselrmod(Chselrmod::ENABLE_INPUT); | ||
| 249 | }); | 266 | }); |
| 250 | 267 | ||
| 251 | // only use one channel at the moment | 268 | // only use one channel at the moment |
| 252 | T::regs().smpr().modify(|w| { | 269 | T::regs().smpr().modify(|w| { |
| 270 | #[cfg(stm32u5)] | ||
| 253 | for i in 0..24 { | 271 | for i in 0..24 { |
| 254 | w.set_smpsel(i, false); | 272 | w.set_smpsel(i, false); |
| 255 | } | 273 | } |
| 274 | #[cfg(stm32wba)] | ||
| 275 | for i in 0..14 { | ||
| 276 | w.set_smpsel(i, Smpsel::SMP1); | ||
| 277 | } | ||
| 256 | }); | 278 | }); |
| 257 | } | 279 | } |
| 258 | 280 | ||
| @@ -275,6 +297,7 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 275 | } | 297 | } |
| 276 | 298 | ||
| 277 | /// Enable reading the vbat internal channel. | 299 | /// Enable reading the vbat internal channel. |
| 300 | #[cfg(stm32u5)] | ||
| 278 | pub fn enable_vbat(&self) -> Vbat { | 301 | pub fn enable_vbat(&self) -> Vbat { |
| 279 | T::regs().ccr().modify(|w| { | 302 | T::regs().ccr().modify(|w| { |
| 280 | w.set_vbaten(true); | 303 | w.set_vbaten(true); |
| @@ -289,6 +312,7 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 289 | } | 312 | } |
| 290 | 313 | ||
| 291 | /// Enable reading the vbat internal channel. | 314 | /// Enable reading the vbat internal channel. |
| 315 | #[cfg(stm32u5)] | ||
| 292 | pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { | 316 | pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { |
| 293 | let mux; | 317 | let mux; |
| 294 | match dac { | 318 | match dac { |
| @@ -317,17 +341,38 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 317 | } | 341 | } |
| 318 | 342 | ||
| 319 | /// Set hardware averaging. | 343 | /// Set hardware averaging. |
| 344 | #[cfg(stm32u5)] | ||
| 345 | pub fn set_averaging(&mut self, averaging: Averaging) { | ||
| 346 | let (enable, samples, right_shift) = match averaging { | ||
| 347 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), | ||
| 348 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), | ||
| 349 | Averaging::Samples4 => (true, OversamplingRatio::OVERSAMPLE4X, 2), | ||
| 350 | Averaging::Samples8 => (true, OversamplingRatio::OVERSAMPLE8X, 3), | ||
| 351 | Averaging::Samples16 => (true, OversamplingRatio::OVERSAMPLE16X, 4), | ||
| 352 | Averaging::Samples32 => (true, OversamplingRatio::OVERSAMPLE32X, 5), | ||
| 353 | Averaging::Samples64 => (true, OversamplingRatio::OVERSAMPLE64X, 6), | ||
| 354 | Averaging::Samples128 => (true, OversamplingRatio::OVERSAMPLE128X, 7), | ||
| 355 | Averaging::Samples256 => (true, OversamplingRatio::OVERSAMPLE256X, 8), | ||
| 356 | }; | ||
| 357 | |||
| 358 | T::regs().cfgr2().modify(|w| { | ||
| 359 | w.set_ovsr(samples); | ||
| 360 | w.set_ovss(right_shift); | ||
| 361 | w.set_ovse(enable) | ||
| 362 | }) | ||
| 363 | } | ||
| 364 | #[cfg(stm32wba)] | ||
| 320 | pub fn set_averaging(&mut self, averaging: Averaging) { | 365 | pub fn set_averaging(&mut self, averaging: Averaging) { |
| 321 | let (enable, samples, right_shift) = match averaging { | 366 | let (enable, samples, right_shift) = match averaging { |
| 322 | Averaging::Disabled => (false, Adc4OversamplingRatio::OVERSAMPLE2X, 0), | 367 | Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), |
| 323 | Averaging::Samples2 => (true, Adc4OversamplingRatio::OVERSAMPLE2X, 1), | 368 | Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), |
| 324 | Averaging::Samples4 => (true, Adc4OversamplingRatio::OVERSAMPLE4X, 2), | 369 | Averaging::Samples4 => (true, OversamplingRatio::OVERSAMPLE4X, Ovss::SHIFT2), |
| 325 | Averaging::Samples8 => (true, Adc4OversamplingRatio::OVERSAMPLE8X, 3), | 370 | Averaging::Samples8 => (true, OversamplingRatio::OVERSAMPLE8X, Ovss::SHIFT3), |
| 326 | Averaging::Samples16 => (true, Adc4OversamplingRatio::OVERSAMPLE16X, 4), | 371 | Averaging::Samples16 => (true, OversamplingRatio::OVERSAMPLE16X, Ovss::SHIFT4), |
| 327 | Averaging::Samples32 => (true, Adc4OversamplingRatio::OVERSAMPLE32X, 5), | 372 | Averaging::Samples32 => (true, OversamplingRatio::OVERSAMPLE32X, Ovss::SHIFT5), |
| 328 | Averaging::Samples64 => (true, Adc4OversamplingRatio::OVERSAMPLE64X, 6), | 373 | Averaging::Samples64 => (true, OversamplingRatio::OVERSAMPLE64X, Ovss::SHIFT6), |
| 329 | Averaging::Samples128 => (true, Adc4OversamplingRatio::OVERSAMPLE128X, 7), | 374 | Averaging::Samples128 => (true, OversamplingRatio::OVERSAMPLE128X, Ovss::SHIFT7), |
| 330 | Averaging::Samples256 => (true, Adc4OversamplingRatio::OVERSAMPLE256X, 8), | 375 | Averaging::Samples256 => (true, OversamplingRatio::OVERSAMPLE256X, Ovss::SHIFT8), |
| 331 | }; | 376 | }; |
| 332 | 377 | ||
| 333 | T::regs().cfgr2().modify(|w| { | 378 | T::regs().cfgr2().modify(|w| { |
| @@ -342,10 +387,20 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 342 | channel.setup(); | 387 | channel.setup(); |
| 343 | 388 | ||
| 344 | // Select channel | 389 | // Select channel |
| 345 | T::regs().chselrmod0().write_value(Adc4Chselrmod0(0_u32)); | 390 | #[cfg(stm32wba)] |
| 346 | T::regs().chselrmod0().modify(|w| { | 391 | { |
| 347 | w.set_chsel(channel.channel() as usize, true); | 392 | T::regs().chselr().write_value(Chselr(0_u32)); |
| 348 | }); | 393 | T::regs().chselr().modify(|w| { |
| 394 | w.set_chsel0(channel.channel() as usize, true); | ||
| 395 | }); | ||
| 396 | } | ||
| 397 | #[cfg(stm32u5)] | ||
| 398 | { | ||
| 399 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 400 | T::regs().chselrmod0().modify(|w| { | ||
| 401 | w.set_chsel(channel.channel() as usize, true); | ||
| 402 | }); | ||
| 403 | } | ||
| 349 | 404 | ||
| 350 | // Reset interrupts | 405 | // Reset interrupts |
| 351 | T::regs().isr().modify(|reg| { | 406 | T::regs().isr().modify(|reg| { |
| @@ -415,13 +470,19 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 415 | 470 | ||
| 416 | T::regs().cfgr1().modify(|reg| { | 471 | T::regs().cfgr1().modify(|reg| { |
| 417 | reg.set_dmaen(true); | 472 | reg.set_dmaen(true); |
| 418 | reg.set_dmacfg(Adc4Dmacfg::ONE_SHOT); | 473 | reg.set_dmacfg(Dmacfg::ONE_SHOT); |
| 474 | #[cfg(stm32u5)] | ||
| 419 | reg.set_chselrmod(false); | 475 | reg.set_chselrmod(false); |
| 476 | #[cfg(stm32wba)] | ||
| 477 | reg.set_chselrmod(Chselrmod::ENABLE_INPUT) | ||
| 420 | }); | 478 | }); |
| 421 | 479 | ||
| 422 | // Verify and activate sequence | 480 | // Verify and activate sequence |
| 423 | let mut prev_channel: i16 = -1; | 481 | let mut prev_channel: i16 = -1; |
| 424 | T::regs().chselrmod0().write_value(Adc4Chselrmod0(0_u32)); | 482 | #[cfg(stm32wba)] |
| 483 | T::regs().chselr().write_value(Chselr(0_u32)); | ||
| 484 | #[cfg(stm32u5)] | ||
| 485 | T::regs().chselrmod0().write_value(Chselr(0_u32)); | ||
| 425 | for channel in sequence { | 486 | for channel in sequence { |
| 426 | let channel_num = channel.channel; | 487 | let channel_num = channel.channel; |
| 427 | if channel_num as i16 <= prev_channel { | 488 | if channel_num as i16 <= prev_channel { |
| @@ -429,6 +490,11 @@ impl<'d, T: Instance> Adc4<'d, T> { | |||
| 429 | }; | 490 | }; |
| 430 | prev_channel = channel_num as i16; | 491 | prev_channel = channel_num as i16; |
| 431 | 492 | ||
| 493 | #[cfg(stm32wba)] | ||
| 494 | T::regs().chselr().modify(|w| { | ||
| 495 | w.set_chsel0(channel.channel as usize, true); | ||
| 496 | }); | ||
| 497 | #[cfg(stm32u5)] | ||
| 432 | T::regs().chselrmod0().modify(|w| { | 498 | T::regs().chselrmod0().modify(|w| { |
| 433 | w.set_chsel(channel.channel as usize, true); | 499 | w.set_chsel(channel.channel as usize, true); |
| 434 | }); | 500 | }); |
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index 936ad7413..f5870801e 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs | |||
| @@ -48,7 +48,7 @@ impl<T: Instance> SealedAdcChannel<T> for Temperature { | |||
| 48 | } | 48 | } |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | #[derive(Debug)] | 51 | #[derive(Copy, Clone, Debug)] |
| 52 | pub enum Prescaler { | 52 | pub enum Prescaler { |
| 53 | NotDivided, | 53 | NotDivided, |
| 54 | DividedBy2, | 54 | DividedBy2, |
| @@ -138,6 +138,7 @@ 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 | pub enum Averaging { | 142 | pub enum Averaging { |
| 142 | Disabled, | 143 | Disabled, |
| 143 | Samples2, | 144 | Samples2, |
diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs index 944e971bb..84613078c 100644 --- a/embassy-stm32/src/adc/f3_v1_1.rs +++ b/embassy-stm32/src/adc/f3_v1_1.rs | |||
| @@ -17,6 +17,7 @@ pub const VDDA_CALIB_MV: u32 = 3300; | |||
| 17 | pub const ADC_MAX: u32 = (1 << 12) - 1; | 17 | pub const ADC_MAX: u32 = (1 << 12) - 1; |
| 18 | pub const VREF_INT: u32 = 1230; | 18 | pub const VREF_INT: u32 = 1230; |
| 19 | 19 | ||
| 20 | #[derive(Copy, Clone)] | ||
| 20 | pub enum AdcPowerMode { | 21 | pub enum AdcPowerMode { |
| 21 | AlwaysOn, | 22 | AlwaysOn, |
| 22 | DelayOff, | 23 | DelayOff, |
| @@ -24,6 +25,7 @@ pub enum AdcPowerMode { | |||
| 24 | DelayIdleOff, | 25 | DelayIdleOff, |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 28 | #[derive(Copy, Clone)] | ||
| 27 | pub enum Prescaler { | 29 | pub enum Prescaler { |
| 28 | Div1, | 30 | Div1, |
| 29 | Div2, | 31 | Div2, |
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 1fce3085a..43498966f 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -24,44 +24,31 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | |||
| 24 | #[cfg(stm32h7)] | 24 | #[cfg(stm32h7)] |
| 25 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | 25 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); |
| 26 | 26 | ||
| 27 | #[cfg(stm32g4)] | ||
| 28 | const VREF_CHANNEL: u8 = 18; | ||
| 29 | #[cfg(stm32g4)] | ||
| 30 | const TEMP_CHANNEL: u8 = 16; | ||
| 31 | |||
| 32 | #[cfg(stm32h7)] | ||
| 33 | const VREF_CHANNEL: u8 = 19; | ||
| 34 | #[cfg(stm32h7)] | ||
| 35 | const TEMP_CHANNEL: u8 = 18; | ||
| 36 | |||
| 37 | // TODO this should be 14 for H7a/b/35 | ||
| 38 | const VBAT_CHANNEL: u8 = 17; | ||
| 39 | |||
| 40 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs | 27 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs |
| 41 | /// Internal voltage reference channel. | 28 | /// Internal voltage reference channel. |
| 42 | pub struct VrefInt; | 29 | pub struct VrefInt; |
| 43 | impl<T: Instance> AdcChannel<T> for VrefInt {} | 30 | impl<T: Instance + VrefChannel> AdcChannel<T> for VrefInt {} |
| 44 | impl<T: Instance> super::SealedAdcChannel<T> for VrefInt { | 31 | impl<T: Instance + VrefChannel> super::SealedAdcChannel<T> for VrefInt { |
| 45 | fn channel(&self) -> u8 { | 32 | fn channel(&self) -> u8 { |
| 46 | VREF_CHANNEL | 33 | T::CHANNEL |
| 47 | } | 34 | } |
| 48 | } | 35 | } |
| 49 | 36 | ||
| 50 | /// Internal temperature channel. | 37 | /// Internal temperature channel. |
| 51 | pub struct Temperature; | 38 | pub struct Temperature; |
| 52 | impl<T: Instance> AdcChannel<T> for Temperature {} | 39 | impl<T: Instance + TemperatureChannel> AdcChannel<T> for Temperature {} |
| 53 | impl<T: Instance> super::SealedAdcChannel<T> for Temperature { | 40 | impl<T: Instance + TemperatureChannel> super::SealedAdcChannel<T> for Temperature { |
| 54 | fn channel(&self) -> u8 { | 41 | fn channel(&self) -> u8 { |
| 55 | TEMP_CHANNEL | 42 | T::CHANNEL |
| 56 | } | 43 | } |
| 57 | } | 44 | } |
| 58 | 45 | ||
| 59 | /// Internal battery voltage channel. | 46 | /// Internal battery voltage channel. |
| 60 | pub struct Vbat; | 47 | pub struct Vbat; |
| 61 | impl<T: Instance> AdcChannel<T> for Vbat {} | 48 | impl<T: Instance + VBatChannel> AdcChannel<T> for Vbat {} |
| 62 | impl<T: Instance> super::SealedAdcChannel<T> for Vbat { | 49 | impl<T: Instance + VBatChannel> super::SealedAdcChannel<T> for Vbat { |
| 63 | fn channel(&self) -> u8 { | 50 | fn channel(&self) -> u8 { |
| 64 | VBAT_CHANNEL | 51 | T::CHANNEL |
| 65 | } | 52 | } |
| 66 | } | 53 | } |
| 67 | 54 | ||
| @@ -234,7 +221,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 234 | } | 221 | } |
| 235 | 222 | ||
| 236 | /// Enable reading the voltage reference internal channel. | 223 | /// Enable reading the voltage reference internal channel. |
| 237 | pub fn enable_vrefint(&self) -> VrefInt { | 224 | pub fn enable_vrefint(&self) -> VrefInt |
| 225 | where | ||
| 226 | T: VrefChannel, | ||
| 227 | { | ||
| 238 | T::common_regs().ccr().modify(|reg| { | 228 | T::common_regs().ccr().modify(|reg| { |
| 239 | reg.set_vrefen(true); | 229 | reg.set_vrefen(true); |
| 240 | }); | 230 | }); |
| @@ -243,7 +233,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 243 | } | 233 | } |
| 244 | 234 | ||
| 245 | /// Enable reading the temperature internal channel. | 235 | /// Enable reading the temperature internal channel. |
| 246 | pub fn enable_temperature(&self) -> Temperature { | 236 | pub fn enable_temperature(&self) -> Temperature |
| 237 | where | ||
| 238 | T: TemperatureChannel, | ||
| 239 | { | ||
| 247 | T::common_regs().ccr().modify(|reg| { | 240 | T::common_regs().ccr().modify(|reg| { |
| 248 | reg.set_vsenseen(true); | 241 | reg.set_vsenseen(true); |
| 249 | }); | 242 | }); |
| @@ -252,7 +245,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 252 | } | 245 | } |
| 253 | 246 | ||
| 254 | /// Enable reading the vbat internal channel. | 247 | /// Enable reading the vbat internal channel. |
| 255 | pub fn enable_vbat(&self) -> Vbat { | 248 | pub fn enable_vbat(&self) -> Vbat |
| 249 | where | ||
| 250 | T: VBatChannel, | ||
| 251 | { | ||
| 256 | T::common_regs().ccr().modify(|reg| { | 252 | T::common_regs().ccr().modify(|reg| { |
| 257 | reg.set_vbaten(true); | 253 | reg.set_vbaten(true); |
| 258 | }); | 254 | }); |
| @@ -519,3 +515,78 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 519 | } | 515 | } |
| 520 | } | 516 | } |
| 521 | } | 517 | } |
| 518 | |||
| 519 | /// Implemented for ADCs that have a Temperature channel | ||
| 520 | pub trait TemperatureChannel { | ||
| 521 | const CHANNEL: u8; | ||
| 522 | } | ||
| 523 | /// Implemented for ADCs that have a Vref channel | ||
| 524 | pub trait VrefChannel { | ||
| 525 | const CHANNEL: u8; | ||
| 526 | } | ||
| 527 | /// Implemented for ADCs that have a VBat channel | ||
| 528 | pub trait VBatChannel { | ||
| 529 | const CHANNEL: u8; | ||
| 530 | } | ||
| 531 | |||
| 532 | #[cfg(stm32g4)] | ||
| 533 | mod g4 { | ||
| 534 | pub use super::*; | ||
| 535 | |||
| 536 | impl TemperatureChannel for crate::peripherals::ADC1 { | ||
| 537 | const CHANNEL: u8 = 16; | ||
| 538 | } | ||
| 539 | |||
| 540 | impl VrefChannel for crate::peripherals::ADC1 { | ||
| 541 | const CHANNEL: u8 = 18; | ||
| 542 | } | ||
| 543 | |||
| 544 | impl VBatChannel for crate::peripherals::ADC1 { | ||
| 545 | const CHANNEL: u8 = 17; | ||
| 546 | } | ||
| 547 | |||
| 548 | #[cfg(peri_adc3_common)] | ||
| 549 | impl VrefChannel for crate::peripherals::ADC3 { | ||
| 550 | const CHANNEL: u8 = 18; | ||
| 551 | } | ||
| 552 | |||
| 553 | #[cfg(peri_adc3_common)] | ||
| 554 | impl VBatChannel for crate::peripherals::ADC3 { | ||
| 555 | const CHANNEL: u8 = 17; | ||
| 556 | } | ||
| 557 | |||
| 558 | #[cfg(not(stm32g4x1))] | ||
| 559 | impl VrefChannel for crate::peripherals::ADC4 { | ||
| 560 | const CHANNEL: u8 = 18; | ||
| 561 | } | ||
| 562 | |||
| 563 | #[cfg(not(stm32g4x1))] | ||
| 564 | impl TemperatureChannel for crate::peripherals::ADC5 { | ||
| 565 | const CHANNEL: u8 = 4; | ||
| 566 | } | ||
| 567 | |||
| 568 | #[cfg(not(stm32g4x1))] | ||
| 569 | impl VrefChannel for crate::peripherals::ADC5 { | ||
| 570 | const CHANNEL: u8 = 18; | ||
| 571 | } | ||
| 572 | |||
| 573 | #[cfg(not(stm32g4x1))] | ||
| 574 | impl VBatChannel for crate::peripherals::ADC5 { | ||
| 575 | const CHANNEL: u8 = 17; | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | // TODO this should look at each ADC individually and impl the correct channels | ||
| 580 | #[cfg(stm32h7)] | ||
| 581 | mod h7 { | ||
| 582 | impl<T: Instance> TemperatureChannel for T { | ||
| 583 | const CHANNEL: u8 = 18; | ||
| 584 | } | ||
| 585 | impl<T: Instance> VrefChannel for T { | ||
| 586 | const CHANNEL: u8 = 19; | ||
| 587 | } | ||
| 588 | impl<T: Instance> VBatChannel for T { | ||
| 589 | // TODO this should be 14 for H7a/b/35 | ||
| 590 | const CHANNEL: u8 = 17; | ||
| 591 | } | ||
| 592 | } | ||
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index f46e87f38..778edc6f6 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #![allow(missing_docs)] // TODO | 4 | #![allow(missing_docs)] // TODO |
| 5 | #![cfg_attr(adc_f3_v2, allow(unused))] | 5 | #![cfg_attr(adc_f3_v2, allow(unused))] |
| 6 | 6 | ||
| 7 | #[cfg(not(any(adc_f3_v2)))] | 7 | #[cfg(not(any(adc_f3_v2, 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_f3, path = "f3.rs")] |
| 10 | #[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] | 10 | #[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] |
| @@ -20,14 +20,14 @@ 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)))] | 23 | #[cfg(not(any(adc_f3_v2, 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_f3, adc_v1, adc_l0, adc_f3_v1_1))] |
| 27 | use embassy_sync::waitqueue::AtomicWaker; | 27 | use embassy_sync::waitqueue::AtomicWaker; |
| 28 | 28 | ||
| 29 | #[cfg(adc_u5)] | 29 | #[cfg(any(adc_u5, adc_wba))] |
| 30 | #[path = "u5_adc4.rs"] | 30 | #[path = "adc4.rs"] |
| 31 | pub mod adc4; | 31 | pub mod adc4; |
| 32 | 32 | ||
| 33 | pub use crate::pac::adc::vals; | 33 | pub use crate::pac::adc::vals; |
| @@ -36,15 +36,18 @@ 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; |
| 38 | 38 | ||
| 39 | #[cfg(not(adc_wba))] | ||
| 39 | dma_trait!(RxDma, Instance); | 40 | dma_trait!(RxDma, Instance); |
| 40 | #[cfg(adc_u5)] | 41 | #[cfg(adc_u5)] |
| 41 | dma_trait!(RxDma4, adc4::Instance); | 42 | dma_trait!(RxDma4, adc4::Instance); |
| 43 | #[cfg(adc_wba)] | ||
| 44 | dma_trait!(RxDma4, adc4::Instance); | ||
| 42 | 45 | ||
| 43 | /// Analog to Digital driver. | 46 | /// Analog to Digital driver. |
| 44 | pub struct Adc<'d, T: Instance> { | 47 | pub struct Adc<'d, T: Instance> { |
| 45 | #[allow(unused)] | 48 | #[allow(unused)] |
| 46 | adc: crate::Peri<'d, T>, | 49 | adc: crate::Peri<'d, T>, |
| 47 | #[cfg(not(any(adc_f3_v2, adc_f3_v1_1)))] | 50 | #[cfg(not(any(adc_f3_v2, adc_f3_v1_1, adc_wba)))] |
| 48 | sample_time: SampleTime, | 51 | sample_time: SampleTime, |
| 49 | } | 52 | } |
| 50 | 53 | ||
| @@ -63,6 +66,7 @@ impl State { | |||
| 63 | } | 66 | } |
| 64 | 67 | ||
| 65 | trait SealedInstance { | 68 | trait SealedInstance { |
| 69 | #[cfg(not(adc_wba))] | ||
| 66 | #[allow(unused)] | 70 | #[allow(unused)] |
| 67 | fn regs() -> crate::pac::adc::Adc; | 71 | fn regs() -> crate::pac::adc::Adc; |
| 68 | #[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_f3_v2, adc_f3_v1_1, adc_g0)))] |
| @@ -73,7 +77,7 @@ trait SealedInstance { | |||
| 73 | } | 77 | } |
| 74 | 78 | ||
| 75 | pub(crate) trait SealedAdcChannel<T> { | 79 | pub(crate) trait SealedAdcChannel<T> { |
| 76 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] | 80 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] |
| 77 | fn setup(&mut self) {} | 81 | fn setup(&mut self) {} |
| 78 | 82 | ||
| 79 | #[allow(unused)] | 83 | #[allow(unused)] |
| @@ -110,7 +114,8 @@ pub(crate) fn blocking_delay_us(us: u32) { | |||
| 110 | adc_h5, | 114 | adc_h5, |
| 111 | adc_h7rs, | 115 | adc_h7rs, |
| 112 | adc_u5, | 116 | adc_u5, |
| 113 | adc_c0 | 117 | adc_c0, |
| 118 | adc_wba, | ||
| 114 | )))] | 119 | )))] |
| 115 | #[allow(private_bounds)] | 120 | #[allow(private_bounds)] |
| 116 | pub trait Instance: SealedInstance + crate::PeripheralType { | 121 | pub trait Instance: SealedInstance + crate::PeripheralType { |
| @@ -132,7 +137,8 @@ pub trait Instance: SealedInstance + crate::PeripheralType { | |||
| 132 | adc_h5, | 137 | adc_h5, |
| 133 | adc_h7rs, | 138 | adc_h7rs, |
| 134 | adc_u5, | 139 | adc_u5, |
| 135 | adc_c0 | 140 | adc_c0, |
| 141 | adc_wba, | ||
| 136 | ))] | 142 | ))] |
| 137 | #[allow(private_bounds)] | 143 | #[allow(private_bounds)] |
| 138 | pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { | 144 | pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { |
| @@ -144,7 +150,7 @@ pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeri | |||
| 144 | pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { | 150 | pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { |
| 145 | #[allow(unused_mut)] | 151 | #[allow(unused_mut)] |
| 146 | fn degrade_adc(mut self) -> AnyAdcChannel<T> { | 152 | fn degrade_adc(mut self) -> AnyAdcChannel<T> { |
| 147 | #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] | 153 | #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] |
| 148 | self.setup(); | 154 | self.setup(); |
| 149 | 155 | ||
| 150 | AnyAdcChannel { | 156 | AnyAdcChannel { |
| @@ -176,6 +182,36 @@ impl<T> AnyAdcChannel<T> { | |||
| 176 | self.channel | 182 | self.channel |
| 177 | } | 183 | } |
| 178 | } | 184 | } |
| 185 | #[cfg(adc_wba)] | ||
| 186 | foreach_adc!( | ||
| 187 | (ADC4, $common_inst:ident, $clock:ident) => { | ||
| 188 | impl crate::adc::adc4::SealedInstance for peripherals::ADC4 { | ||
| 189 | fn regs() -> crate::pac::adc::Adc4 { | ||
| 190 | crate::pac::ADC4 | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | impl crate::adc::adc4::Instance for peripherals::ADC4 { | ||
| 195 | type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL; | ||
| 196 | } | ||
| 197 | }; | ||
| 198 | |||
| 199 | ($inst:ident, $common_inst:ident, $clock:ident) => { | ||
| 200 | impl crate::adc::SealedInstance for peripherals::$inst { | ||
| 201 | fn regs() -> crate::pac::adc::Adc { | ||
| 202 | crate::pac::$inst | ||
| 203 | } | ||
| 204 | |||
| 205 | fn common_regs() -> crate::pac::adccommon::AdcCommon { | ||
| 206 | return crate::pac::$common_inst | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | impl crate::adc::Instance for peripherals::$inst { | ||
| 211 | type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL; | ||
| 212 | } | ||
| 213 | }; | ||
| 214 | ); | ||
| 179 | 215 | ||
| 180 | #[cfg(adc_u5)] | 216 | #[cfg(adc_u5)] |
| 181 | foreach_adc!( | 217 | foreach_adc!( |
| @@ -208,15 +244,21 @@ foreach_adc!( | |||
| 208 | }; | 244 | }; |
| 209 | ); | 245 | ); |
| 210 | 246 | ||
| 211 | #[cfg(not(adc_u5))] | 247 | #[cfg(not(any(adc_u5, adc_wba)))] |
| 212 | foreach_adc!( | 248 | foreach_adc!( |
| 213 | ($inst:ident, $common_inst:ident, $clock:ident) => { | 249 | ($inst:ident, $common_inst:ident, $clock:ident) => { |
| 214 | impl crate::adc::SealedInstance for peripherals::$inst { | 250 | impl crate::adc::SealedInstance for peripherals::$inst { |
| 251 | #[cfg(not(adc_wba))] | ||
| 215 | fn regs() -> crate::pac::adc::Adc { | 252 | fn regs() -> crate::pac::adc::Adc { |
| 216 | crate::pac::$inst | 253 | crate::pac::$inst |
| 217 | } | 254 | } |
| 218 | 255 | ||
| 219 | #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] | 256 | #[cfg(adc_wba)] |
| 257 | fn regs() -> crate::pac::adc::Adc4 { | ||
| 258 | crate::pac::$inst | ||
| 259 | } | ||
| 260 | |||
| 261 | #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0, adc_u5, adc_wba)))] | ||
| 220 | fn common_regs() -> crate::pac::adccommon::AdcCommon { | 262 | fn common_regs() -> crate::pac::adccommon::AdcCommon { |
| 221 | return crate::pac::$common_inst | 263 | return crate::pac::$common_inst |
| 222 | } | 264 | } |
| @@ -238,7 +280,7 @@ macro_rules! impl_adc_pin { | |||
| 238 | ($inst:ident, $pin:ident, $ch:expr) => { | 280 | ($inst:ident, $pin:ident, $ch:expr) => { |
| 239 | impl crate::adc::AdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {} | 281 | impl crate::adc::AdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {} |
| 240 | impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> { | 282 | impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> { |
| 241 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] | 283 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] |
| 242 | fn setup(&mut self) { | 284 | fn setup(&mut self) { |
| 243 | <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(self); | 285 | <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(self); |
| 244 | } | 286 | } |
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index fb6f5b7d0..7fe502da0 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs | |||
| @@ -11,6 +11,9 @@ use crate::interrupt::typelevel::Interrupt; | |||
| 11 | use crate::peripherals::ADC1; | 11 | use crate::peripherals::ADC1; |
| 12 | use crate::{interrupt, rcc, Peri}; | 12 | use crate::{interrupt, rcc, Peri}; |
| 13 | 13 | ||
| 14 | mod watchdog_v1; | ||
| 15 | pub use watchdog_v1::WatchdogChannels; | ||
| 16 | |||
| 14 | pub const VDDA_CALIB_MV: u32 = 3300; | 17 | pub const VDDA_CALIB_MV: u32 = 3300; |
| 15 | pub const VREF_INT: u32 = 1230; | 18 | pub const VREF_INT: u32 = 1230; |
| 16 | 19 | ||
| @@ -21,8 +24,15 @@ pub struct InterruptHandler<T: Instance> { | |||
| 21 | 24 | ||
| 22 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | 25 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { |
| 23 | unsafe fn on_interrupt() { | 26 | unsafe fn on_interrupt() { |
| 24 | if T::regs().isr().read().eoc() { | 27 | let isr = T::regs().isr().read(); |
| 28 | let ier = T::regs().ier().read(); | ||
| 29 | if ier.eocie() && isr.eoc() { | ||
| 30 | // eocie is set during adc.read() | ||
| 25 | T::regs().ier().modify(|w| w.set_eocie(false)); | 31 | T::regs().ier().modify(|w| w.set_eocie(false)); |
| 32 | } else if ier.awdie() && isr.awd() { | ||
| 33 | // awdie is set during adc.monitor_watchdog() | ||
| 34 | T::regs().cr().read().set_adstp(true); | ||
| 35 | T::regs().ier().modify(|w| w.set_awdie(false)); | ||
| 26 | } else { | 36 | } else { |
| 27 | return; | 37 | return; |
| 28 | } | 38 | } |
| @@ -186,16 +196,21 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 186 | 196 | ||
| 187 | T::regs().dr().read().data() | 197 | T::regs().dr().read().data() |
| 188 | } | 198 | } |
| 189 | } | ||
| 190 | 199 | ||
| 191 | impl<'d, T: Instance> Drop for Adc<'d, T> { | 200 | fn teardown_adc() { |
| 192 | fn drop(&mut self) { | ||
| 193 | // A.7.3 ADC disable code example | 201 | // A.7.3 ADC disable code example |
| 194 | T::regs().cr().modify(|reg| reg.set_adstp(true)); | 202 | T::regs().cr().modify(|reg| reg.set_adstp(true)); |
| 195 | while T::regs().cr().read().adstp() {} | 203 | while T::regs().cr().read().adstp() {} |
| 196 | 204 | ||
| 197 | T::regs().cr().modify(|reg| reg.set_addis(true)); | 205 | T::regs().cr().modify(|reg| reg.set_addis(true)); |
| 198 | while T::regs().cr().read().aden() {} | 206 | while T::regs().cr().read().aden() {} |
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | impl<'d, T: Instance> Drop for Adc<'d, T> { | ||
| 211 | fn drop(&mut self) { | ||
| 212 | Self::teardown_adc(); | ||
| 213 | Self::teardown_awd(); | ||
| 199 | 214 | ||
| 200 | rcc::disable::<T>(); | 215 | rcc::disable::<T>(); |
| 201 | } | 216 | } |
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 7b5df80b8..805dae564 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | use cfg_if::cfg_if; | 1 | use cfg_if::cfg_if; |
| 2 | use pac::adc::vals::Dmacfg; | 2 | use pac::adc::vals::Dmacfg; |
| 3 | #[cfg(adc_v3)] | ||
| 4 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; | ||
| 3 | 5 | ||
| 4 | use super::{ | 6 | use super::{ |
| 5 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 7 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, |
| @@ -506,6 +508,23 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 506 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); | 508 | T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); |
| 507 | } | 509 | } |
| 508 | 510 | ||
| 511 | #[cfg(adc_v3)] | ||
| 512 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 513 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 514 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 515 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 516 | } | ||
| 517 | |||
| 518 | #[cfg(adc_v3)] | ||
| 519 | pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) { | ||
| 520 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 521 | } | ||
| 522 | |||
| 523 | #[cfg(adc_v3)] | ||
| 524 | pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) { | ||
| 525 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 526 | } | ||
| 527 | |||
| 509 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | 528 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { |
| 510 | cfg_if! { | 529 | cfg_if! { |
| 511 | if #[cfg(any(adc_g0, adc_u0))] { | 530 | if #[cfg(any(adc_g0, adc_u0))] { |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 39e0d51b9..b0871019a 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -142,6 +142,7 @@ 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 | pub enum Averaging { | 146 | pub enum Averaging { |
| 146 | Disabled, | 147 | Disabled, |
| 147 | Samples2, | 148 | Samples2, |
diff --git a/embassy-stm32/src/adc/watchdog_v1.rs b/embassy-stm32/src/adc/watchdog_v1.rs new file mode 100644 index 000000000..bbe8e1971 --- /dev/null +++ b/embassy-stm32/src/adc/watchdog_v1.rs | |||
| @@ -0,0 +1,188 @@ | |||
| 1 | use core::future::poll_fn; | ||
| 2 | use core::task::Poll; | ||
| 3 | |||
| 4 | use stm32_metapac::adc::vals::{Align, Awdsgl, Res}; | ||
| 5 | |||
| 6 | use crate::adc::{Adc, AdcChannel, Instance}; | ||
| 7 | |||
| 8 | /// This enum is passed into `Adc::init_watchdog` to specify the channels for the watchdog to monitor | ||
| 9 | pub enum WatchdogChannels { | ||
| 10 | // Single channel identified by index | ||
| 11 | Single(u8), | ||
| 12 | // Multiple channels identified by mask | ||
| 13 | Multiple(u16), | ||
| 14 | } | ||
| 15 | |||
| 16 | impl WatchdogChannels { | ||
| 17 | pub fn from_channel<T>(channel: &impl AdcChannel<T>) -> Self { | ||
| 18 | Self::Single(channel.channel()) | ||
| 19 | } | ||
| 20 | |||
| 21 | pub fn add_channel<T>(self, channel: &impl AdcChannel<T>) -> Self { | ||
| 22 | WatchdogChannels::Multiple( | ||
| 23 | (match self { | ||
| 24 | WatchdogChannels::Single(ch) => 1 << ch, | ||
| 25 | WatchdogChannels::Multiple(ch) => ch, | ||
| 26 | }) | 1 << channel.channel(), | ||
| 27 | ) | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | impl<'d, T: Instance> Adc<'d, T> { | ||
| 32 | /// Configure the analog window watchdog to monitor one or more ADC channels | ||
| 33 | /// | ||
| 34 | /// `high_threshold` and `low_threshold` are expressed in the same way as ADC results. The format | ||
| 35 | /// depends on the values of CFGR1.ALIGN and CFGR1.RES. | ||
| 36 | pub fn init_watchdog(&mut self, channels: WatchdogChannels, low_threshold: u16, high_threshold: u16) { | ||
| 37 | Self::stop_awd(); | ||
| 38 | |||
| 39 | match channels { | ||
| 40 | WatchdogChannels::Single(ch) => { | ||
| 41 | T::regs().chselr().modify(|w| { | ||
| 42 | w.set_chsel_x(ch.into(), true); | ||
| 43 | }); | ||
| 44 | T::regs().cfgr1().modify(|w| { | ||
| 45 | w.set_awdch(ch); | ||
| 46 | w.set_awdsgl(Awdsgl::SINGLE_CHANNEL) | ||
| 47 | }); | ||
| 48 | } | ||
| 49 | WatchdogChannels::Multiple(ch) => { | ||
| 50 | T::regs().chselr().modify(|w| w.0 = ch.into()); | ||
| 51 | T::regs().cfgr1().modify(|w| { | ||
| 52 | w.set_awdch(0); | ||
| 53 | w.set_awdsgl(Awdsgl::ALL_CHANNELS) | ||
| 54 | }); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | Self::set_watchdog_thresholds(low_threshold, high_threshold); | ||
| 59 | Self::setup_awd(); | ||
| 60 | } | ||
| 61 | |||
| 62 | /// Monitor the voltage on the selected channels; return when it crosses the thresholds. | ||
| 63 | /// | ||
| 64 | /// ```rust,ignore | ||
| 65 | /// // Wait for pin to go high | ||
| 66 | /// adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0, 0x07F); | ||
| 67 | /// let v_high = adc.monitor_watchdog().await; | ||
| 68 | /// info!("ADC sample is high {}", v_high); | ||
| 69 | /// ``` | ||
| 70 | pub async fn monitor_watchdog(&mut self) -> u16 { | ||
| 71 | assert!( | ||
| 72 | match T::regs().cfgr1().read().awdsgl() { | ||
| 73 | Awdsgl::SINGLE_CHANNEL => T::regs().cfgr1().read().awdch() != 0, | ||
| 74 | Awdsgl::ALL_CHANNELS => T::regs().cfgr1().read().awdch() == 0, | ||
| 75 | }, | ||
| 76 | "`set_channel` should be called before `monitor`", | ||
| 77 | ); | ||
| 78 | assert!(T::regs().chselr().read().0 != 0); | ||
| 79 | T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); | ||
| 80 | Self::start_awd(); | ||
| 81 | |||
| 82 | let sample = poll_fn(|cx| { | ||
| 83 | T::state().waker.register(cx.waker()); | ||
| 84 | |||
| 85 | if T::regs().isr().read().awd() { | ||
| 86 | Poll::Ready(T::regs().dr().read().data()) | ||
| 87 | } else { | ||
| 88 | Poll::Pending | ||
| 89 | } | ||
| 90 | }) | ||
| 91 | .await; | ||
| 92 | |||
| 93 | self.stop_watchdog(); | ||
| 94 | sample | ||
| 95 | } | ||
| 96 | |||
| 97 | /// Stop monitoring the selected channels | ||
| 98 | pub fn stop_watchdog(&mut self) { | ||
| 99 | Self::stop_awd(); | ||
| 100 | } | ||
| 101 | |||
| 102 | fn set_watchdog_thresholds(low_threshold: u16, high_threshold: u16) { | ||
| 103 | // This function takes `high_threshold` and `low_threshold` in the same alignment and resolution | ||
| 104 | // as ADC results, and programs them into ADC_DR. Because ADC_DR is always right-aligned on 12 bits, | ||
| 105 | // some bit-shifting may be necessary. See more in table 47 §13.7.1 Analog Watchdog Comparison | ||
| 106 | |||
| 107 | // Verify that the thresholds are in the correct bit positions according to alignment and resolution | ||
| 108 | let threshold_mask = match (T::regs().cfgr1().read().align(), T::regs().cfgr1().read().res()) { | ||
| 109 | (Align::LEFT, Res::BITS6) => 0x00FC, | ||
| 110 | (Align::LEFT, Res::BITS8) => 0xFF00, | ||
| 111 | (Align::LEFT, Res::BITS10) => 0xFFC0, | ||
| 112 | (Align::LEFT, Res::BITS12) => 0xFFF0, | ||
| 113 | (Align::RIGHT, Res::BITS6) => 0x003F, | ||
| 114 | (Align::RIGHT, Res::BITS8) => 0x00FF, | ||
| 115 | (Align::RIGHT, Res::BITS10) => 0x03FF, | ||
| 116 | (Align::RIGHT, Res::BITS12) => 0x0FFF, | ||
| 117 | }; | ||
| 118 | assert!( | ||
| 119 | high_threshold & !threshold_mask == 0, | ||
| 120 | "High threshold {:x} is invalid — only bits {:x} are allowed", | ||
| 121 | high_threshold, | ||
| 122 | threshold_mask | ||
| 123 | ); | ||
| 124 | assert!( | ||
| 125 | low_threshold & !threshold_mask == 0, | ||
| 126 | "Low threshold {:x} is invalid — only bits {:x} are allowed", | ||
| 127 | low_threshold, | ||
| 128 | threshold_mask | ||
| 129 | ); | ||
| 130 | |||
| 131 | T::regs().tr().modify(|w| { | ||
| 132 | w.set_lt(low_threshold << threshold_mask.leading_zeros() >> 4); | ||
| 133 | w.set_ht(high_threshold << threshold_mask.leading_zeros() >> 4); | ||
| 134 | }) | ||
| 135 | } | ||
| 136 | |||
| 137 | fn setup_awd() { | ||
| 138 | // Configure AWD | ||
| 139 | assert!(!T::regs().cr().read().adstart()); | ||
| 140 | T::regs().cfgr1().modify(|w| w.set_awden(true)); | ||
| 141 | } | ||
| 142 | |||
| 143 | fn start_awd() { | ||
| 144 | // Clear AWD interrupt flag | ||
| 145 | while T::regs().isr().read().awd() { | ||
| 146 | T::regs().isr().modify(|regs| { | ||
| 147 | regs.set_awd(true); | ||
| 148 | }) | ||
| 149 | } | ||
| 150 | |||
| 151 | // Enable AWD interrupt | ||
| 152 | assert!(!T::regs().cr().read().adstart()); | ||
| 153 | T::regs().ier().modify(|w| { | ||
| 154 | w.set_eocie(false); | ||
| 155 | w.set_awdie(true) | ||
| 156 | }); | ||
| 157 | |||
| 158 | // Start conversion | ||
| 159 | T::regs().cfgr1().modify(|w| w.set_cont(true)); | ||
| 160 | T::regs().cr().modify(|w| w.set_adstart(true)); | ||
| 161 | } | ||
| 162 | |||
| 163 | fn stop_awd() { | ||
| 164 | // Stop conversion | ||
| 165 | while T::regs().cr().read().addis() {} | ||
| 166 | if T::regs().cr().read().adstart() { | ||
| 167 | T::regs().cr().write(|x| x.set_adstp(true)); | ||
| 168 | while T::regs().cr().read().adstp() {} | ||
| 169 | } | ||
| 170 | T::regs().cfgr1().modify(|w| w.set_cont(false)); | ||
| 171 | |||
| 172 | // Disable AWD interrupt | ||
| 173 | assert!(!T::regs().cr().read().adstart()); | ||
| 174 | T::regs().ier().modify(|w| w.set_awdie(false)); | ||
| 175 | |||
| 176 | // Clear AWD interrupt flag | ||
| 177 | while T::regs().isr().read().awd() { | ||
| 178 | T::regs().isr().modify(|regs| { | ||
| 179 | regs.set_awd(true); | ||
| 180 | }) | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | pub(crate) fn teardown_awd() { | ||
| 185 | Self::stop_awd(); | ||
| 186 | T::regs().cfgr1().modify(|w| w.set_awden(false)); | ||
| 187 | } | ||
| 188 | } | ||
diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 305666d5b..4c0795a2a 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs | |||
| @@ -15,9 +15,10 @@ pub use embedded_can::{ExtendedId, Id, StandardId}; | |||
| 15 | use self::filter::MasterFilters; | 15 | use self::filter::MasterFilters; |
| 16 | use self::registers::{Registers, RxFifo}; | 16 | use self::registers::{Registers, RxFifo}; |
| 17 | pub use super::common::{BufferedCanReceiver, BufferedCanSender}; | 17 | pub use super::common::{BufferedCanReceiver, BufferedCanSender}; |
| 18 | use super::common::{InfoRef, RxInfoRef, TxInfoRef}; | ||
| 18 | use super::frame::{Envelope, Frame}; | 19 | use super::frame::{Envelope, Frame}; |
| 19 | use super::util; | 20 | use super::util; |
| 20 | use crate::can::enums::{BusError, InternalOperation, TryReadError}; | 21 | use crate::can::enums::{BusError, RefCountOp, TryReadError}; |
| 21 | use crate::gpio::{AfType, OutputType, Pull, Speed}; | 22 | use crate::gpio::{AfType, OutputType, Pull, Speed}; |
| 22 | use crate::interrupt::typelevel::Interrupt; | 23 | use crate::interrupt::typelevel::Interrupt; |
| 23 | use crate::rcc::{self, RccPeripheral}; | 24 | use crate::rcc::{self, RccPeripheral}; |
| @@ -35,7 +36,9 @@ impl<T: Instance> interrupt::typelevel::Handler<T::TXInterrupt> for TxInterruptH | |||
| 35 | v.set_rqcp(1, true); | 36 | v.set_rqcp(1, true); |
| 36 | v.set_rqcp(2, true); | 37 | v.set_rqcp(2, true); |
| 37 | }); | 38 | }); |
| 38 | T::state().tx_mode.on_interrupt::<T>(); | 39 | T::info().state.lock(|state| { |
| 40 | state.borrow().tx_mode.on_interrupt::<T>(); | ||
| 41 | }); | ||
| 39 | } | 42 | } |
| 40 | } | 43 | } |
| 41 | 44 | ||
| @@ -46,7 +49,9 @@ pub struct Rx0InterruptHandler<T: Instance> { | |||
| 46 | 49 | ||
| 47 | impl<T: Instance> interrupt::typelevel::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> { | 50 | impl<T: Instance> interrupt::typelevel::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> { |
| 48 | unsafe fn on_interrupt() { | 51 | unsafe fn on_interrupt() { |
| 49 | T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo0); | 52 | T::info().state.lock(|state| { |
| 53 | state.borrow().rx_mode.on_interrupt::<T>(RxFifo::Fifo0); | ||
| 54 | }); | ||
| 50 | } | 55 | } |
| 51 | } | 56 | } |
| 52 | 57 | ||
| @@ -57,7 +62,9 @@ pub struct Rx1InterruptHandler<T: Instance> { | |||
| 57 | 62 | ||
| 58 | impl<T: Instance> interrupt::typelevel::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> { | 63 | impl<T: Instance> interrupt::typelevel::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> { |
| 59 | unsafe fn on_interrupt() { | 64 | unsafe fn on_interrupt() { |
| 60 | T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo1); | 65 | T::info().state.lock(|state| { |
| 66 | state.borrow().rx_mode.on_interrupt::<T>(RxFifo::Fifo1); | ||
| 67 | }); | ||
| 61 | } | 68 | } |
| 62 | } | 69 | } |
| 63 | 70 | ||
| @@ -73,7 +80,9 @@ impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup | |||
| 73 | 80 | ||
| 74 | if msr_val.slaki() { | 81 | if msr_val.slaki() { |
| 75 | msr.modify(|m| m.set_slaki(true)); | 82 | msr.modify(|m| m.set_slaki(true)); |
| 76 | T::state().err_waker.wake(); | 83 | T::info().state.lock(|state| { |
| 84 | state.borrow().err_waker.wake(); | ||
| 85 | }); | ||
| 77 | } else if msr_val.erri() { | 86 | } else if msr_val.erri() { |
| 78 | // Disable the interrupt, but don't acknowledge the error, so that it can be | 87 | // Disable the interrupt, but don't acknowledge the error, so that it can be |
| 79 | // forwarded off the bus message consumer. If we don't provide some way for | 88 | // forwarded off the bus message consumer. If we don't provide some way for |
| @@ -82,8 +91,9 @@ impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup | |||
| 82 | // an indefinite amount of time. | 91 | // an indefinite amount of time. |
| 83 | let ier = T::regs().ier(); | 92 | let ier = T::regs().ier(); |
| 84 | ier.modify(|i| i.set_errie(false)); | 93 | ier.modify(|i| i.set_errie(false)); |
| 85 | 94 | T::info().state.lock(|state| { | |
| 86 | T::state().err_waker.wake(); | 95 | state.borrow().err_waker.wake(); |
| 96 | }); | ||
| 87 | } | 97 | } |
| 88 | } | 98 | } |
| 89 | } | 99 | } |
| @@ -91,7 +101,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup | |||
| 91 | /// Configuration proxy returned by [`Can::modify_config`]. | 101 | /// Configuration proxy returned by [`Can::modify_config`]. |
| 92 | pub struct CanConfig<'a> { | 102 | pub struct CanConfig<'a> { |
| 93 | phantom: PhantomData<&'a ()>, | 103 | phantom: PhantomData<&'a ()>, |
| 94 | info: &'static Info, | 104 | info: InfoRef, |
| 95 | periph_clock: crate::time::Hertz, | 105 | periph_clock: crate::time::Hertz, |
| 96 | } | 106 | } |
| 97 | 107 | ||
| @@ -156,8 +166,7 @@ impl Drop for CanConfig<'_> { | |||
| 156 | /// CAN driver | 166 | /// CAN driver |
| 157 | pub struct Can<'d> { | 167 | pub struct Can<'d> { |
| 158 | phantom: PhantomData<&'d ()>, | 168 | phantom: PhantomData<&'d ()>, |
| 159 | info: &'static Info, | 169 | info: InfoRef, |
| 160 | state: &'static State, | ||
| 161 | periph_clock: crate::time::Hertz, | 170 | periph_clock: crate::time::Hertz, |
| 162 | } | 171 | } |
| 163 | 172 | ||
| @@ -227,8 +236,7 @@ impl<'d> Can<'d> { | |||
| 227 | 236 | ||
| 228 | Self { | 237 | Self { |
| 229 | phantom: PhantomData, | 238 | phantom: PhantomData, |
| 230 | info: T::info(), | 239 | info: InfoRef::new(T::info()), |
| 231 | state: T::state(), | ||
| 232 | periph_clock: T::frequency(), | 240 | periph_clock: T::frequency(), |
| 233 | } | 241 | } |
| 234 | } | 242 | } |
| @@ -248,7 +256,7 @@ impl<'d> Can<'d> { | |||
| 248 | 256 | ||
| 249 | CanConfig { | 257 | CanConfig { |
| 250 | phantom: self.phantom, | 258 | phantom: self.phantom, |
| 251 | info: self.info, | 259 | info: InfoRef::new(&self.info), |
| 252 | periph_clock: self.periph_clock, | 260 | periph_clock: self.periph_clock, |
| 253 | } | 261 | } |
| 254 | } | 262 | } |
| @@ -297,7 +305,9 @@ impl<'d> Can<'d> { | |||
| 297 | self.info.regs.0.mcr().modify(|m| m.set_sleep(true)); | 305 | self.info.regs.0.mcr().modify(|m| m.set_sleep(true)); |
| 298 | 306 | ||
| 299 | poll_fn(|cx| { | 307 | poll_fn(|cx| { |
| 300 | self.state.err_waker.register(cx.waker()); | 308 | self.info.state.lock(|s| { |
| 309 | s.borrow().err_waker.register(cx.waker()); | ||
| 310 | }); | ||
| 301 | if self.is_sleeping() { | 311 | if self.is_sleeping() { |
| 302 | Poll::Ready(()) | 312 | Poll::Ready(()) |
| 303 | } else { | 313 | } else { |
| @@ -350,8 +360,7 @@ impl<'d> Can<'d> { | |||
| 350 | pub async fn flush(&self, mb: Mailbox) { | 360 | pub async fn flush(&self, mb: Mailbox) { |
| 351 | CanTx { | 361 | CanTx { |
| 352 | _phantom: PhantomData, | 362 | _phantom: PhantomData, |
| 353 | info: self.info, | 363 | info: TxInfoRef::new(&self.info), |
| 354 | state: self.state, | ||
| 355 | } | 364 | } |
| 356 | .flush_inner(mb) | 365 | .flush_inner(mb) |
| 357 | .await; | 366 | .await; |
| @@ -366,8 +375,7 @@ impl<'d> Can<'d> { | |||
| 366 | pub async fn flush_any(&self) { | 375 | pub async fn flush_any(&self) { |
| 367 | CanTx { | 376 | CanTx { |
| 368 | _phantom: PhantomData, | 377 | _phantom: PhantomData, |
| 369 | info: self.info, | 378 | info: TxInfoRef::new(&self.info), |
| 370 | state: self.state, | ||
| 371 | } | 379 | } |
| 372 | .flush_any_inner() | 380 | .flush_any_inner() |
| 373 | .await | 381 | .await |
| @@ -377,8 +385,7 @@ impl<'d> Can<'d> { | |||
| 377 | pub async fn flush_all(&self) { | 385 | pub async fn flush_all(&self) { |
| 378 | CanTx { | 386 | CanTx { |
| 379 | _phantom: PhantomData, | 387 | _phantom: PhantomData, |
| 380 | info: self.info, | 388 | info: TxInfoRef::new(&self.info), |
| 381 | state: self.state, | ||
| 382 | } | 389 | } |
| 383 | .flush_all_inner() | 390 | .flush_all_inner() |
| 384 | .await | 391 | .await |
| @@ -406,19 +413,19 @@ impl<'d> Can<'d> { | |||
| 406 | /// | 413 | /// |
| 407 | /// Returns a tuple of the time the message was received and the message frame | 414 | /// Returns a tuple of the time the message was received and the message frame |
| 408 | pub async fn read(&mut self) -> Result<Envelope, BusError> { | 415 | pub async fn read(&mut self) -> Result<Envelope, BusError> { |
| 409 | self.state.rx_mode.read(self.info, self.state).await | 416 | RxMode::read(&self.info).await |
| 410 | } | 417 | } |
| 411 | 418 | ||
| 412 | /// Attempts to read a CAN frame without blocking. | 419 | /// Attempts to read a CAN frame without blocking. |
| 413 | /// | 420 | /// |
| 414 | /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. | 421 | /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. |
| 415 | pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { | 422 | pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { |
| 416 | self.state.rx_mode.try_read(self.info) | 423 | RxMode::try_read(&self.info) |
| 417 | } | 424 | } |
| 418 | 425 | ||
| 419 | /// Waits while receive queue is empty. | 426 | /// Waits while receive queue is empty. |
| 420 | pub async fn wait_not_empty(&mut self) { | 427 | pub async fn wait_not_empty(&mut self) { |
| 421 | self.state.rx_mode.wait_not_empty(self.info, self.state).await | 428 | RxMode::wait_not_empty(&self.info).await |
| 422 | } | 429 | } |
| 423 | 430 | ||
| 424 | /// Split the CAN driver into transmit and receive halves. | 431 | /// Split the CAN driver into transmit and receive halves. |
| @@ -428,13 +435,11 @@ impl<'d> Can<'d> { | |||
| 428 | ( | 435 | ( |
| 429 | CanTx { | 436 | CanTx { |
| 430 | _phantom: PhantomData, | 437 | _phantom: PhantomData, |
| 431 | info: self.info, | 438 | info: TxInfoRef::new(&self.info), |
| 432 | state: self.state, | ||
| 433 | }, | 439 | }, |
| 434 | CanRx { | 440 | CanRx { |
| 435 | _phantom: PhantomData, | 441 | _phantom: PhantomData, |
| 436 | info: self.info, | 442 | info: RxInfoRef::new(&self.info), |
| 437 | state: self.state, | ||
| 438 | }, | 443 | }, |
| 439 | ) | 444 | ) |
| 440 | } | 445 | } |
| @@ -459,7 +464,7 @@ impl<'d> Can<'d> { | |||
| 459 | /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master | 464 | /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master |
| 460 | /// peripheral instead. | 465 | /// peripheral instead. |
| 461 | pub fn modify_filters(&mut self) -> MasterFilters<'_> { | 466 | pub fn modify_filters(&mut self) -> MasterFilters<'_> { |
| 462 | unsafe { MasterFilters::new(self.info) } | 467 | unsafe { MasterFilters::new(&self.info) } |
| 463 | } | 468 | } |
| 464 | } | 469 | } |
| 465 | 470 | ||
| @@ -514,8 +519,7 @@ impl<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_ | |||
| 514 | /// CAN driver, transmit half. | 519 | /// CAN driver, transmit half. |
| 515 | pub struct CanTx<'d> { | 520 | pub struct CanTx<'d> { |
| 516 | _phantom: PhantomData<&'d ()>, | 521 | _phantom: PhantomData<&'d ()>, |
| 517 | info: &'static Info, | 522 | info: TxInfoRef, |
| 518 | state: &'static State, | ||
| 519 | } | 523 | } |
| 520 | 524 | ||
| 521 | impl<'d> CanTx<'d> { | 525 | impl<'d> CanTx<'d> { |
| @@ -524,7 +528,9 @@ impl<'d> CanTx<'d> { | |||
| 524 | /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. | 528 | /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. |
| 525 | pub async fn write(&mut self, frame: &Frame) -> TransmitStatus { | 529 | pub async fn write(&mut self, frame: &Frame) -> TransmitStatus { |
| 526 | poll_fn(|cx| { | 530 | poll_fn(|cx| { |
| 527 | self.state.tx_mode.register(cx.waker()); | 531 | self.info.state.lock(|s| { |
| 532 | s.borrow().tx_mode.register(cx.waker()); | ||
| 533 | }); | ||
| 528 | if let Ok(status) = self.info.regs.transmit(frame) { | 534 | if let Ok(status) = self.info.regs.transmit(frame) { |
| 529 | return Poll::Ready(status); | 535 | return Poll::Ready(status); |
| 530 | } | 536 | } |
| @@ -549,7 +555,9 @@ impl<'d> CanTx<'d> { | |||
| 549 | 555 | ||
| 550 | async fn flush_inner(&self, mb: Mailbox) { | 556 | async fn flush_inner(&self, mb: Mailbox) { |
| 551 | poll_fn(|cx| { | 557 | poll_fn(|cx| { |
| 552 | self.state.tx_mode.register(cx.waker()); | 558 | self.info.state.lock(|s| { |
| 559 | s.borrow().tx_mode.register(cx.waker()); | ||
| 560 | }); | ||
| 553 | if self.info.regs.0.tsr().read().tme(mb.index()) { | 561 | if self.info.regs.0.tsr().read().tme(mb.index()) { |
| 554 | return Poll::Ready(()); | 562 | return Poll::Ready(()); |
| 555 | } | 563 | } |
| @@ -566,7 +574,9 @@ impl<'d> CanTx<'d> { | |||
| 566 | 574 | ||
| 567 | async fn flush_any_inner(&self) { | 575 | async fn flush_any_inner(&self) { |
| 568 | poll_fn(|cx| { | 576 | poll_fn(|cx| { |
| 569 | self.state.tx_mode.register(cx.waker()); | 577 | self.info.state.lock(|s| { |
| 578 | s.borrow().tx_mode.register(cx.waker()); | ||
| 579 | }); | ||
| 570 | 580 | ||
| 571 | let tsr = self.info.regs.0.tsr().read(); | 581 | let tsr = self.info.regs.0.tsr().read(); |
| 572 | if tsr.tme(Mailbox::Mailbox0.index()) | 582 | if tsr.tme(Mailbox::Mailbox0.index()) |
| @@ -593,7 +603,9 @@ impl<'d> CanTx<'d> { | |||
| 593 | 603 | ||
| 594 | async fn flush_all_inner(&self) { | 604 | async fn flush_all_inner(&self) { |
| 595 | poll_fn(|cx| { | 605 | poll_fn(|cx| { |
| 596 | self.state.tx_mode.register(cx.waker()); | 606 | self.info.state.lock(|s| { |
| 607 | s.borrow().tx_mode.register(cx.waker()); | ||
| 608 | }); | ||
| 597 | 609 | ||
| 598 | let tsr = self.info.regs.0.tsr().read(); | 610 | let tsr = self.info.regs.0.tsr().read(); |
| 599 | if tsr.tme(Mailbox::Mailbox0.index()) | 611 | if tsr.tme(Mailbox::Mailbox0.index()) |
| @@ -634,7 +646,7 @@ impl<'d> CanTx<'d> { | |||
| 634 | self, | 646 | self, |
| 635 | txb: &'static mut TxBuf<TX_BUF_SIZE>, | 647 | txb: &'static mut TxBuf<TX_BUF_SIZE>, |
| 636 | ) -> BufferedCanTx<'d, TX_BUF_SIZE> { | 648 | ) -> BufferedCanTx<'d, TX_BUF_SIZE> { |
| 637 | BufferedCanTx::new(self.info, self.state, self, txb) | 649 | BufferedCanTx::new(&self.info, self, txb) |
| 638 | } | 650 | } |
| 639 | } | 651 | } |
| 640 | 652 | ||
| @@ -643,17 +655,15 @@ pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, | |||
| 643 | 655 | ||
| 644 | /// Buffered CAN driver, transmit half. | 656 | /// Buffered CAN driver, transmit half. |
| 645 | pub struct BufferedCanTx<'d, const TX_BUF_SIZE: usize> { | 657 | pub struct BufferedCanTx<'d, const TX_BUF_SIZE: usize> { |
| 646 | info: &'static Info, | 658 | info: TxInfoRef, |
| 647 | state: &'static State, | ||
| 648 | _tx: CanTx<'d>, | 659 | _tx: CanTx<'d>, |
| 649 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, | 660 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, |
| 650 | } | 661 | } |
| 651 | 662 | ||
| 652 | impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> { | 663 | impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> { |
| 653 | fn new(info: &'static Info, state: &'static State, _tx: CanTx<'d>, tx_buf: &'static TxBuf<TX_BUF_SIZE>) -> Self { | 664 | fn new(info: &'static Info, _tx: CanTx<'d>, tx_buf: &'static TxBuf<TX_BUF_SIZE>) -> Self { |
| 654 | Self { | 665 | Self { |
| 655 | info, | 666 | info: TxInfoRef::new(info), |
| 656 | state, | ||
| 657 | _tx, | 667 | _tx, |
| 658 | tx_buf, | 668 | tx_buf, |
| 659 | } | 669 | } |
| @@ -666,11 +676,9 @@ impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> { | |||
| 666 | let tx_inner = super::common::ClassicBufferedTxInner { | 676 | let tx_inner = super::common::ClassicBufferedTxInner { |
| 667 | tx_receiver: self.tx_buf.receiver().into(), | 677 | tx_receiver: self.tx_buf.receiver().into(), |
| 668 | }; | 678 | }; |
| 669 | let state = self.state as *const State; | 679 | self.info.state.lock(|s| { |
| 670 | unsafe { | 680 | s.borrow_mut().tx_mode = TxMode::Buffered(tx_inner); |
| 671 | let mut_state = state as *mut State; | 681 | }); |
| 672 | (*mut_state).tx_mode = TxMode::Buffered(tx_inner); | ||
| 673 | } | ||
| 674 | }); | 682 | }); |
| 675 | self | 683 | self |
| 676 | } | 684 | } |
| @@ -684,27 +692,18 @@ impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> { | |||
| 684 | 692 | ||
| 685 | /// Returns a sender that can be used for sending CAN frames. | 693 | /// Returns a sender that can be used for sending CAN frames. |
| 686 | pub fn writer(&self) -> BufferedCanSender { | 694 | pub fn writer(&self) -> BufferedCanSender { |
| 687 | (self.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 688 | BufferedCanSender { | 695 | BufferedCanSender { |
| 689 | tx_buf: self.tx_buf.sender().into(), | 696 | tx_buf: self.tx_buf.sender().into(), |
| 690 | waker: self.info.tx_waker, | 697 | info: TxInfoRef::new(&self.info), |
| 691 | internal_operation: self.info.internal_operation, | ||
| 692 | } | 698 | } |
| 693 | } | 699 | } |
| 694 | } | 700 | } |
| 695 | 701 | ||
| 696 | impl<'d, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, TX_BUF_SIZE> { | ||
| 697 | fn drop(&mut self) { | ||
| 698 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); | ||
| 699 | } | ||
| 700 | } | ||
| 701 | |||
| 702 | /// CAN driver, receive half. | 702 | /// CAN driver, receive half. |
| 703 | #[allow(dead_code)] | 703 | #[allow(dead_code)] |
| 704 | pub struct CanRx<'d> { | 704 | pub struct CanRx<'d> { |
| 705 | _phantom: PhantomData<&'d ()>, | 705 | _phantom: PhantomData<&'d ()>, |
| 706 | info: &'static Info, | 706 | info: RxInfoRef, |
| 707 | state: &'static State, | ||
| 708 | } | 707 | } |
| 709 | 708 | ||
| 710 | impl<'d> CanRx<'d> { | 709 | impl<'d> CanRx<'d> { |
| @@ -714,19 +713,19 @@ impl<'d> CanRx<'d> { | |||
| 714 | /// | 713 | /// |
| 715 | /// Returns a tuple of the time the message was received and the message frame | 714 | /// Returns a tuple of the time the message was received and the message frame |
| 716 | pub async fn read(&mut self) -> Result<Envelope, BusError> { | 715 | pub async fn read(&mut self) -> Result<Envelope, BusError> { |
| 717 | self.state.rx_mode.read(self.info, self.state).await | 716 | RxMode::read(&self.info).await |
| 718 | } | 717 | } |
| 719 | 718 | ||
| 720 | /// Attempts to read a CAN frame without blocking. | 719 | /// Attempts to read a CAN frame without blocking. |
| 721 | /// | 720 | /// |
| 722 | /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. | 721 | /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. |
| 723 | pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { | 722 | pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { |
| 724 | self.state.rx_mode.try_read(self.info) | 723 | RxMode::try_read(&self.info) |
| 725 | } | 724 | } |
| 726 | 725 | ||
| 727 | /// Waits while receive queue is empty. | 726 | /// Waits while receive queue is empty. |
| 728 | pub async fn wait_not_empty(&mut self) { | 727 | pub async fn wait_not_empty(&mut self) { |
| 729 | self.state.rx_mode.wait_not_empty(self.info, self.state).await | 728 | RxMode::wait_not_empty(&self.info).await |
| 730 | } | 729 | } |
| 731 | 730 | ||
| 732 | /// Return a buffered instance of driver. User must supply Buffers | 731 | /// Return a buffered instance of driver. User must supply Buffers |
| @@ -734,7 +733,7 @@ impl<'d> CanRx<'d> { | |||
| 734 | self, | 733 | self, |
| 735 | rxb: &'static mut RxBuf<RX_BUF_SIZE>, | 734 | rxb: &'static mut RxBuf<RX_BUF_SIZE>, |
| 736 | ) -> BufferedCanRx<'d, RX_BUF_SIZE> { | 735 | ) -> BufferedCanRx<'d, RX_BUF_SIZE> { |
| 737 | BufferedCanRx::new(self.info, self.state, self, rxb) | 736 | BufferedCanRx::new(&self.info, self, rxb) |
| 738 | } | 737 | } |
| 739 | 738 | ||
| 740 | /// Accesses the filter banks owned by this CAN peripheral. | 739 | /// Accesses the filter banks owned by this CAN peripheral. |
| @@ -742,7 +741,7 @@ impl<'d> CanRx<'d> { | |||
| 742 | /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master | 741 | /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master |
| 743 | /// peripheral instead. | 742 | /// peripheral instead. |
| 744 | pub fn modify_filters(&mut self) -> MasterFilters<'_> { | 743 | pub fn modify_filters(&mut self) -> MasterFilters<'_> { |
| 745 | unsafe { MasterFilters::new(self.info) } | 744 | unsafe { MasterFilters::new(&self.info) } |
| 746 | } | 745 | } |
| 747 | } | 746 | } |
| 748 | 747 | ||
| @@ -751,17 +750,15 @@ pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result< | |||
| 751 | 750 | ||
| 752 | /// CAN driver, receive half in Buffered mode. | 751 | /// CAN driver, receive half in Buffered mode. |
| 753 | pub struct BufferedCanRx<'d, const RX_BUF_SIZE: usize> { | 752 | pub struct BufferedCanRx<'d, const RX_BUF_SIZE: usize> { |
| 754 | info: &'static Info, | 753 | info: RxInfoRef, |
| 755 | state: &'static State, | ||
| 756 | rx: CanRx<'d>, | 754 | rx: CanRx<'d>, |
| 757 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, | 755 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, |
| 758 | } | 756 | } |
| 759 | 757 | ||
| 760 | impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { | 758 | impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { |
| 761 | fn new(info: &'static Info, state: &'static State, rx: CanRx<'d>, rx_buf: &'static RxBuf<RX_BUF_SIZE>) -> Self { | 759 | fn new(info: &'static Info, rx: CanRx<'d>, rx_buf: &'static RxBuf<RX_BUF_SIZE>) -> Self { |
| 762 | BufferedCanRx { | 760 | BufferedCanRx { |
| 763 | info, | 761 | info: RxInfoRef::new(info), |
| 764 | state, | ||
| 765 | rx, | 762 | rx, |
| 766 | rx_buf, | 763 | rx_buf, |
| 767 | } | 764 | } |
| @@ -774,11 +771,9 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { | |||
| 774 | let rx_inner = super::common::ClassicBufferedRxInner { | 771 | let rx_inner = super::common::ClassicBufferedRxInner { |
| 775 | rx_sender: self.rx_buf.sender().into(), | 772 | rx_sender: self.rx_buf.sender().into(), |
| 776 | }; | 773 | }; |
| 777 | let state = self.state as *const State; | 774 | self.info.state.lock(|s| { |
| 778 | unsafe { | 775 | s.borrow_mut().rx_mode = RxMode::Buffered(rx_inner); |
| 779 | let mut_state = state as *mut State; | 776 | }); |
| 780 | (*mut_state).rx_mode = RxMode::Buffered(rx_inner); | ||
| 781 | } | ||
| 782 | }); | 777 | }); |
| 783 | self | 778 | self |
| 784 | } | 779 | } |
| @@ -792,7 +787,7 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { | |||
| 792 | /// | 787 | /// |
| 793 | /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. | 788 | /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. |
| 794 | pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { | 789 | pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { |
| 795 | match &self.state.rx_mode { | 790 | self.info.state.lock(|s| match &s.borrow().rx_mode { |
| 796 | RxMode::Buffered(_) => { | 791 | RxMode::Buffered(_) => { |
| 797 | if let Ok(result) = self.rx_buf.try_receive() { | 792 | if let Ok(result) = self.rx_buf.try_receive() { |
| 798 | match result { | 793 | match result { |
| @@ -810,7 +805,7 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { | |||
| 810 | _ => { | 805 | _ => { |
| 811 | panic!("Bad Mode") | 806 | panic!("Bad Mode") |
| 812 | } | 807 | } |
| 813 | } | 808 | }) |
| 814 | } | 809 | } |
| 815 | 810 | ||
| 816 | /// Waits while receive queue is empty. | 811 | /// Waits while receive queue is empty. |
| @@ -820,10 +815,9 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { | |||
| 820 | 815 | ||
| 821 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. | 816 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. |
| 822 | pub fn reader(&self) -> BufferedCanReceiver { | 817 | pub fn reader(&self) -> BufferedCanReceiver { |
| 823 | (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 824 | BufferedCanReceiver { | 818 | BufferedCanReceiver { |
| 825 | rx_buf: self.rx_buf.receiver().into(), | 819 | rx_buf: self.rx_buf.receiver().into(), |
| 826 | internal_operation: self.info.internal_operation, | 820 | info: RxInfoRef::new(&self.info), |
| 827 | } | 821 | } |
| 828 | } | 822 | } |
| 829 | 823 | ||
| @@ -836,12 +830,6 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { | |||
| 836 | } | 830 | } |
| 837 | } | 831 | } |
| 838 | 832 | ||
| 839 | impl<'d, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, RX_BUF_SIZE> { | ||
| 840 | fn drop(&mut self) { | ||
| 841 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); | ||
| 842 | } | ||
| 843 | } | ||
| 844 | |||
| 845 | impl Drop for Can<'_> { | 833 | impl Drop for Can<'_> { |
| 846 | fn drop(&mut self) { | 834 | fn drop(&mut self) { |
| 847 | // Cannot call `free()` because it moves the instance. | 835 | // Cannot call `free()` because it moves the instance. |
| @@ -929,27 +917,30 @@ impl RxMode { | |||
| 929 | } | 917 | } |
| 930 | } | 918 | } |
| 931 | 919 | ||
| 932 | pub(crate) async fn read(&self, info: &Info, state: &State) -> Result<Envelope, BusError> { | 920 | pub(crate) async fn read(info: &Info) -> Result<Envelope, BusError> { |
| 933 | match self { | 921 | poll_fn(|cx| { |
| 934 | Self::NonBuffered(waker) => { | 922 | info.state.lock(|state| { |
| 935 | poll_fn(|cx| { | 923 | let state = state.borrow(); |
| 936 | state.err_waker.register(cx.waker()); | 924 | state.err_waker.register(cx.waker()); |
| 937 | waker.register(cx.waker()); | 925 | match &state.rx_mode { |
| 938 | match self.try_read(info) { | 926 | Self::NonBuffered(waker) => { |
| 939 | Ok(result) => Poll::Ready(Ok(result)), | 927 | waker.register(cx.waker()); |
| 940 | Err(TryReadError::Empty) => Poll::Pending, | ||
| 941 | Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)), | ||
| 942 | } | 928 | } |
| 943 | }) | 929 | _ => { |
| 944 | .await | 930 | panic!("Bad Mode") |
| 945 | } | 931 | } |
| 946 | _ => { | 932 | } |
| 947 | panic!("Bad Mode") | 933 | }); |
| 934 | match RxMode::try_read(info) { | ||
| 935 | Ok(result) => Poll::Ready(Ok(result)), | ||
| 936 | Err(TryReadError::Empty) => Poll::Pending, | ||
| 937 | Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)), | ||
| 948 | } | 938 | } |
| 949 | } | 939 | }) |
| 940 | .await | ||
| 950 | } | 941 | } |
| 951 | pub(crate) fn try_read(&self, info: &Info) -> Result<Envelope, TryReadError> { | 942 | pub(crate) fn try_read(info: &Info) -> Result<Envelope, TryReadError> { |
| 952 | match self { | 943 | info.state.lock(|state| match state.borrow().rx_mode { |
| 953 | Self::NonBuffered(_) => { | 944 | Self::NonBuffered(_) => { |
| 954 | let registers = &info.regs; | 945 | let registers = &info.regs; |
| 955 | if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) { | 946 | if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) { |
| @@ -975,25 +966,28 @@ impl RxMode { | |||
| 975 | _ => { | 966 | _ => { |
| 976 | panic!("Bad Mode") | 967 | panic!("Bad Mode") |
| 977 | } | 968 | } |
| 978 | } | 969 | }) |
| 979 | } | 970 | } |
| 980 | pub(crate) async fn wait_not_empty(&self, info: &Info, state: &State) { | 971 | pub(crate) async fn wait_not_empty(info: &Info) { |
| 981 | match &state.rx_mode { | 972 | poll_fn(|cx| { |
| 982 | Self::NonBuffered(waker) => { | 973 | info.state.lock(|s| { |
| 983 | poll_fn(|cx| { | 974 | let state = s.borrow(); |
| 984 | waker.register(cx.waker()); | 975 | match &state.rx_mode { |
| 985 | if info.regs.receive_frame_available() { | 976 | Self::NonBuffered(waker) => { |
| 986 | Poll::Ready(()) | 977 | waker.register(cx.waker()); |
| 987 | } else { | ||
| 988 | Poll::Pending | ||
| 989 | } | 978 | } |
| 990 | }) | 979 | _ => { |
| 991 | .await | 980 | panic!("Bad Mode") |
| 992 | } | 981 | } |
| 993 | _ => { | 982 | } |
| 994 | panic!("Bad Mode") | 983 | }); |
| 984 | if info.regs.receive_frame_available() { | ||
| 985 | Poll::Ready(()) | ||
| 986 | } else { | ||
| 987 | Poll::Pending | ||
| 995 | } | 988 | } |
| 996 | } | 989 | }) |
| 990 | .await | ||
| 997 | } | 991 | } |
| 998 | } | 992 | } |
| 999 | 993 | ||
| @@ -1008,21 +1002,25 @@ impl TxMode { | |||
| 1008 | tsr.tme(Mailbox::Mailbox0.index()) || tsr.tme(Mailbox::Mailbox1.index()) || tsr.tme(Mailbox::Mailbox2.index()) | 1002 | tsr.tme(Mailbox::Mailbox0.index()) || tsr.tme(Mailbox::Mailbox1.index()) || tsr.tme(Mailbox::Mailbox2.index()) |
| 1009 | } | 1003 | } |
| 1010 | pub fn on_interrupt<T: Instance>(&self) { | 1004 | pub fn on_interrupt<T: Instance>(&self) { |
| 1011 | match &T::state().tx_mode { | 1005 | T::info().state.lock(|state| { |
| 1012 | TxMode::NonBuffered(waker) => waker.wake(), | 1006 | let tx_mode = &state.borrow().tx_mode; |
| 1013 | TxMode::Buffered(buf) => { | 1007 | |
| 1014 | while self.buffer_free::<T>() { | 1008 | match tx_mode { |
| 1015 | match buf.tx_receiver.try_receive() { | 1009 | TxMode::NonBuffered(waker) => waker.wake(), |
| 1016 | Ok(frame) => { | 1010 | TxMode::Buffered(buf) => { |
| 1017 | _ = Registers(T::regs()).transmit(&frame); | 1011 | while self.buffer_free::<T>() { |
| 1018 | } | 1012 | match buf.tx_receiver.try_receive() { |
| 1019 | Err(_) => { | 1013 | Ok(frame) => { |
| 1020 | break; | 1014 | _ = Registers(T::regs()).transmit(&frame); |
| 1015 | } | ||
| 1016 | Err(_) => { | ||
| 1017 | break; | ||
| 1018 | } | ||
| 1021 | } | 1019 | } |
| 1022 | } | 1020 | } |
| 1023 | } | 1021 | } |
| 1024 | } | 1022 | } |
| 1025 | } | 1023 | }); |
| 1026 | } | 1024 | } |
| 1027 | 1025 | ||
| 1028 | fn register(&self, arg: &core::task::Waker) { | 1026 | fn register(&self, arg: &core::task::Waker) { |
| @@ -1057,14 +1055,15 @@ impl State { | |||
| 1057 | } | 1055 | } |
| 1058 | } | 1056 | } |
| 1059 | 1057 | ||
| 1058 | type SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<State>>; | ||
| 1060 | pub(crate) struct Info { | 1059 | pub(crate) struct Info { |
| 1061 | regs: Registers, | 1060 | regs: Registers, |
| 1062 | tx_interrupt: crate::interrupt::Interrupt, | 1061 | tx_interrupt: crate::interrupt::Interrupt, |
| 1063 | rx0_interrupt: crate::interrupt::Interrupt, | 1062 | rx0_interrupt: crate::interrupt::Interrupt, |
| 1064 | rx1_interrupt: crate::interrupt::Interrupt, | 1063 | rx1_interrupt: crate::interrupt::Interrupt, |
| 1065 | sce_interrupt: crate::interrupt::Interrupt, | 1064 | sce_interrupt: crate::interrupt::Interrupt, |
| 1066 | tx_waker: fn(), | 1065 | pub(crate) tx_waker: fn(), |
| 1067 | internal_operation: fn(InternalOperation), | 1066 | state: SharedState, |
| 1068 | 1067 | ||
| 1069 | /// The total number of filter banks available to the instance. | 1068 | /// The total number of filter banks available to the instance. |
| 1070 | /// | 1069 | /// |
| @@ -1072,12 +1071,37 @@ pub(crate) struct Info { | |||
| 1072 | num_filter_banks: u8, | 1071 | num_filter_banks: u8, |
| 1073 | } | 1072 | } |
| 1074 | 1073 | ||
| 1074 | impl Info { | ||
| 1075 | pub(crate) fn adjust_reference_counter(&self, val: RefCountOp) { | ||
| 1076 | self.state.lock(|s| { | ||
| 1077 | let mut mut_state = s.borrow_mut(); | ||
| 1078 | match val { | ||
| 1079 | RefCountOp::NotifySenderCreated => { | ||
| 1080 | mut_state.sender_instance_count += 1; | ||
| 1081 | } | ||
| 1082 | RefCountOp::NotifySenderDestroyed => { | ||
| 1083 | mut_state.sender_instance_count -= 1; | ||
| 1084 | if 0 == mut_state.sender_instance_count { | ||
| 1085 | (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 1086 | } | ||
| 1087 | } | ||
| 1088 | RefCountOp::NotifyReceiverCreated => { | ||
| 1089 | mut_state.receiver_instance_count += 1; | ||
| 1090 | } | ||
| 1091 | RefCountOp::NotifyReceiverDestroyed => { | ||
| 1092 | mut_state.receiver_instance_count -= 1; | ||
| 1093 | if 0 == mut_state.receiver_instance_count { | ||
| 1094 | (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 1095 | } | ||
| 1096 | } | ||
| 1097 | } | ||
| 1098 | }); | ||
| 1099 | } | ||
| 1100 | } | ||
| 1101 | |||
| 1075 | trait SealedInstance { | 1102 | trait SealedInstance { |
| 1076 | fn info() -> &'static Info; | 1103 | fn info() -> &'static Info; |
| 1077 | fn regs() -> crate::pac::can::Can; | 1104 | fn regs() -> crate::pac::can::Can; |
| 1078 | fn state() -> &'static State; | ||
| 1079 | unsafe fn mut_state() -> &'static mut State; | ||
| 1080 | fn internal_operation(val: InternalOperation); | ||
| 1081 | } | 1105 | } |
| 1082 | 1106 | ||
| 1083 | /// CAN instance trait. | 1107 | /// CAN instance trait. |
| @@ -1135,53 +1159,14 @@ foreach_peripheral!( | |||
| 1135 | rx1_interrupt: crate::_generated::peripheral_interrupts::$inst::RX1::IRQ, | 1159 | rx1_interrupt: crate::_generated::peripheral_interrupts::$inst::RX1::IRQ, |
| 1136 | sce_interrupt: crate::_generated::peripheral_interrupts::$inst::SCE::IRQ, | 1160 | sce_interrupt: crate::_generated::peripheral_interrupts::$inst::SCE::IRQ, |
| 1137 | tx_waker: crate::_generated::peripheral_interrupts::$inst::TX::pend, | 1161 | tx_waker: crate::_generated::peripheral_interrupts::$inst::TX::pend, |
| 1138 | internal_operation: peripherals::$inst::internal_operation, | ||
| 1139 | num_filter_banks: peripherals::$inst::NUM_FILTER_BANKS, | 1162 | num_filter_banks: peripherals::$inst::NUM_FILTER_BANKS, |
| 1163 | state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())), | ||
| 1140 | }; | 1164 | }; |
| 1141 | &INFO | 1165 | &INFO |
| 1142 | } | 1166 | } |
| 1143 | fn regs() -> crate::pac::can::Can { | 1167 | fn regs() -> crate::pac::can::Can { |
| 1144 | crate::pac::$inst | 1168 | crate::pac::$inst |
| 1145 | } | 1169 | } |
| 1146 | |||
| 1147 | unsafe fn mut_state() -> & 'static mut State { | ||
| 1148 | static mut STATE: State = State::new(); | ||
| 1149 | &mut *core::ptr::addr_of_mut!(STATE) | ||
| 1150 | } | ||
| 1151 | fn state() -> &'static State { | ||
| 1152 | unsafe { peripherals::$inst::mut_state() } | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | |||
| 1156 | fn internal_operation(val: InternalOperation) { | ||
| 1157 | critical_section::with(|_| { | ||
| 1158 | //let state = self.state as *const State; | ||
| 1159 | unsafe { | ||
| 1160 | //let mut_state = state as *mut State; | ||
| 1161 | let mut_state = peripherals::$inst::mut_state(); | ||
| 1162 | match val { | ||
| 1163 | InternalOperation::NotifySenderCreated => { | ||
| 1164 | mut_state.sender_instance_count += 1; | ||
| 1165 | } | ||
| 1166 | InternalOperation::NotifySenderDestroyed => { | ||
| 1167 | mut_state.sender_instance_count -= 1; | ||
| 1168 | if ( 0 == mut_state.sender_instance_count) { | ||
| 1169 | (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 1170 | } | ||
| 1171 | } | ||
| 1172 | InternalOperation::NotifyReceiverCreated => { | ||
| 1173 | mut_state.receiver_instance_count += 1; | ||
| 1174 | } | ||
| 1175 | InternalOperation::NotifyReceiverDestroyed => { | ||
| 1176 | mut_state.receiver_instance_count -= 1; | ||
| 1177 | if ( 0 == mut_state.receiver_instance_count) { | ||
| 1178 | (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 1179 | } | ||
| 1180 | } | ||
| 1181 | } | ||
| 1182 | } | ||
| 1183 | }); | ||
| 1184 | } | ||
| 1185 | } | 1170 | } |
| 1186 | 1171 | ||
| 1187 | impl Instance for peripherals::$inst { | 1172 | impl Instance for peripherals::$inst { |
diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs index 386d4467c..980f33a04 100644 --- a/embassy-stm32/src/can/common.rs +++ b/embassy-stm32/src/can/common.rs | |||
| @@ -24,22 +24,21 @@ pub(crate) struct FdBufferedTxInner { | |||
| 24 | /// Sender that can be used for sending CAN frames. | 24 | /// Sender that can be used for sending CAN frames. |
| 25 | pub struct BufferedSender<'ch, FRAME> { | 25 | pub struct BufferedSender<'ch, FRAME> { |
| 26 | pub(crate) tx_buf: embassy_sync::channel::SendDynamicSender<'ch, FRAME>, | 26 | pub(crate) tx_buf: embassy_sync::channel::SendDynamicSender<'ch, FRAME>, |
| 27 | pub(crate) waker: fn(), | 27 | pub(crate) info: TxInfoRef, |
| 28 | pub(crate) internal_operation: fn(InternalOperation), | ||
| 29 | } | 28 | } |
| 30 | 29 | ||
| 31 | impl<'ch, FRAME> BufferedSender<'ch, FRAME> { | 30 | impl<'ch, FRAME> BufferedSender<'ch, FRAME> { |
| 32 | /// Async write frame to TX buffer. | 31 | /// Async write frame to TX buffer. |
| 33 | pub fn try_write(&mut self, frame: FRAME) -> Result<(), embassy_sync::channel::TrySendError<FRAME>> { | 32 | pub fn try_write(&mut self, frame: FRAME) -> Result<(), embassy_sync::channel::TrySendError<FRAME>> { |
| 34 | self.tx_buf.try_send(frame)?; | 33 | self.tx_buf.try_send(frame)?; |
| 35 | (self.waker)(); | 34 | (self.info.tx_waker)(); |
| 36 | Ok(()) | 35 | Ok(()) |
| 37 | } | 36 | } |
| 38 | 37 | ||
| 39 | /// Async write frame to TX buffer. | 38 | /// Async write frame to TX buffer. |
| 40 | pub async fn write(&mut self, frame: FRAME) { | 39 | pub async fn write(&mut self, frame: FRAME) { |
| 41 | self.tx_buf.send(frame).await; | 40 | self.tx_buf.send(frame).await; |
| 42 | (self.waker)(); | 41 | (self.info.tx_waker)(); |
| 43 | } | 42 | } |
| 44 | 43 | ||
| 45 | /// Allows a poll_fn to poll until the channel is ready to write | 44 | /// Allows a poll_fn to poll until the channel is ready to write |
| @@ -50,28 +49,20 @@ impl<'ch, FRAME> BufferedSender<'ch, FRAME> { | |||
| 50 | 49 | ||
| 51 | impl<'ch, FRAME> Clone for BufferedSender<'ch, FRAME> { | 50 | impl<'ch, FRAME> Clone for BufferedSender<'ch, FRAME> { |
| 52 | fn clone(&self) -> Self { | 51 | fn clone(&self) -> Self { |
| 53 | (self.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 54 | Self { | 52 | Self { |
| 55 | tx_buf: self.tx_buf, | 53 | tx_buf: self.tx_buf, |
| 56 | waker: self.waker, | 54 | info: TxInfoRef::new(&self.info), |
| 57 | internal_operation: self.internal_operation, | ||
| 58 | } | 55 | } |
| 59 | } | 56 | } |
| 60 | } | 57 | } |
| 61 | 58 | ||
| 62 | impl<'ch, FRAME> Drop for BufferedSender<'ch, FRAME> { | ||
| 63 | fn drop(&mut self) { | ||
| 64 | (self.internal_operation)(InternalOperation::NotifySenderDestroyed); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Sender that can be used for sending Classic CAN frames. | 59 | /// Sender that can be used for sending Classic CAN frames. |
| 69 | pub type BufferedCanSender = BufferedSender<'static, Frame>; | 60 | pub type BufferedCanSender = BufferedSender<'static, Frame>; |
| 70 | 61 | ||
| 71 | /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. | 62 | /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. |
| 72 | pub struct BufferedReceiver<'ch, ENVELOPE> { | 63 | pub struct BufferedReceiver<'ch, ENVELOPE> { |
| 73 | pub(crate) rx_buf: embassy_sync::channel::SendDynamicReceiver<'ch, Result<ENVELOPE, BusError>>, | 64 | pub(crate) rx_buf: embassy_sync::channel::SendDynamicReceiver<'ch, Result<ENVELOPE, BusError>>, |
| 74 | pub(crate) internal_operation: fn(InternalOperation), | 65 | pub(crate) info: RxInfoRef, |
| 75 | } | 66 | } |
| 76 | 67 | ||
| 77 | impl<'ch, ENVELOPE> BufferedReceiver<'ch, ENVELOPE> { | 68 | impl<'ch, ENVELOPE> BufferedReceiver<'ch, ENVELOPE> { |
| @@ -106,19 +97,99 @@ impl<'ch, ENVELOPE> BufferedReceiver<'ch, ENVELOPE> { | |||
| 106 | 97 | ||
| 107 | impl<'ch, ENVELOPE> Clone for BufferedReceiver<'ch, ENVELOPE> { | 98 | impl<'ch, ENVELOPE> Clone for BufferedReceiver<'ch, ENVELOPE> { |
| 108 | fn clone(&self) -> Self { | 99 | fn clone(&self) -> Self { |
| 109 | (self.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 110 | Self { | 100 | Self { |
| 111 | rx_buf: self.rx_buf, | 101 | rx_buf: self.rx_buf, |
| 112 | internal_operation: self.internal_operation, | 102 | info: RxInfoRef::new(&self.info), |
| 113 | } | 103 | } |
| 114 | } | 104 | } |
| 115 | } | 105 | } |
| 116 | 106 | ||
| 117 | impl<'ch, ENVELOPE> Drop for BufferedReceiver<'ch, ENVELOPE> { | 107 | /// A BufferedCanReceiver for Classic CAN frames. |
| 108 | pub type BufferedCanReceiver = BufferedReceiver<'static, Envelope>; | ||
| 109 | |||
| 110 | /// Provides a reference to the driver internals and implements RAII for the internal reference | ||
| 111 | /// counting. Each type that can operate on the driver should contain either InfoRef | ||
| 112 | /// or the similar TxInfoRef or RxInfoRef. The new method and the Drop impl will automatically | ||
| 113 | /// call the reference counting function. Like this, the reference counting function does not | ||
| 114 | /// need to be called manually for each type. | ||
| 115 | pub(crate) struct InfoRef { | ||
| 116 | info: &'static super::Info, | ||
| 117 | } | ||
| 118 | impl InfoRef { | ||
| 119 | pub(crate) fn new(info: &'static super::Info) -> Self { | ||
| 120 | info.adjust_reference_counter(RefCountOp::NotifyReceiverCreated); | ||
| 121 | info.adjust_reference_counter(RefCountOp::NotifySenderCreated); | ||
| 122 | Self { info } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | impl Drop for InfoRef { | ||
| 118 | fn drop(&mut self) { | 127 | fn drop(&mut self) { |
| 119 | (self.internal_operation)(InternalOperation::NotifyReceiverDestroyed); | 128 | self.info.adjust_reference_counter(RefCountOp::NotifyReceiverDestroyed); |
| 129 | self.info.adjust_reference_counter(RefCountOp::NotifySenderDestroyed); | ||
| 120 | } | 130 | } |
| 121 | } | 131 | } |
| 122 | 132 | ||
| 123 | /// A BufferedCanReceiver for Classic CAN frames. | 133 | impl core::ops::Deref for InfoRef { |
| 124 | pub type BufferedCanReceiver = BufferedReceiver<'static, Envelope>; | 134 | type Target = &'static super::Info; |
| 135 | |||
| 136 | fn deref(&self) -> &Self::Target { | ||
| 137 | &self.info | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Provides a reference to the driver internals and implements RAII for the internal reference | ||
| 142 | /// counting for Tx only types. | ||
| 143 | /// See InfoRef for further doc. | ||
| 144 | pub(crate) struct TxInfoRef { | ||
| 145 | info: &'static super::Info, | ||
| 146 | } | ||
| 147 | |||
| 148 | impl TxInfoRef { | ||
| 149 | pub(crate) fn new(info: &'static super::Info) -> Self { | ||
| 150 | info.adjust_reference_counter(RefCountOp::NotifySenderCreated); | ||
| 151 | Self { info } | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | impl Drop for TxInfoRef { | ||
| 156 | fn drop(&mut self) { | ||
| 157 | self.info.adjust_reference_counter(RefCountOp::NotifySenderDestroyed); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | impl core::ops::Deref for TxInfoRef { | ||
| 162 | type Target = &'static super::Info; | ||
| 163 | |||
| 164 | fn deref(&self) -> &Self::Target { | ||
| 165 | &self.info | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | /// Provides a reference to the driver internals and implements RAII for the internal reference | ||
| 170 | /// counting for Rx only types. | ||
| 171 | /// See InfoRef for further doc. | ||
| 172 | pub(crate) struct RxInfoRef { | ||
| 173 | info: &'static super::Info, | ||
| 174 | } | ||
| 175 | |||
| 176 | impl RxInfoRef { | ||
| 177 | pub(crate) fn new(info: &'static super::Info) -> Self { | ||
| 178 | info.adjust_reference_counter(RefCountOp::NotifyReceiverCreated); | ||
| 179 | Self { info } | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | impl Drop for RxInfoRef { | ||
| 184 | fn drop(&mut self) { | ||
| 185 | self.info.adjust_reference_counter(RefCountOp::NotifyReceiverDestroyed); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | impl core::ops::Deref for RxInfoRef { | ||
| 190 | type Target = &'static super::Info; | ||
| 191 | |||
| 192 | fn deref(&self) -> &Self::Target { | ||
| 193 | &self.info | ||
| 194 | } | ||
| 195 | } | ||
diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs index 97cb47640..6d91020fc 100644 --- a/embassy-stm32/src/can/enums.rs +++ b/embassy-stm32/src/can/enums.rs | |||
| @@ -72,7 +72,7 @@ pub enum TryReadError { | |||
| 72 | /// Internal Operation | 72 | /// Internal Operation |
| 73 | #[derive(Debug)] | 73 | #[derive(Debug)] |
| 74 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 74 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 75 | pub enum InternalOperation { | 75 | pub enum RefCountOp { |
| 76 | /// Notify receiver created | 76 | /// Notify receiver created |
| 77 | NotifyReceiverCreated, | 77 | NotifyReceiverCreated, |
| 78 | /// Notify receiver destroyed | 78 | /// Notify receiver destroyed |
diff --git a/embassy-stm32/src/can/fd/message_ram/extended_filter.rs b/embassy-stm32/src/can/fd/message_ram/extended_filter.rs index 453e9056e..ac47901a8 100644 --- a/embassy-stm32/src/can/fd/message_ram/extended_filter.rs +++ b/embassy-stm32/src/can/fd/message_ram/extended_filter.rs | |||
| @@ -115,22 +115,22 @@ impl R { | |||
| 115 | impl W { | 115 | impl W { |
| 116 | #[doc = "Byte 0 - Bits 0:28 - EFID1"] | 116 | #[doc = "Byte 0 - Bits 0:28 - EFID1"] |
| 117 | #[inline(always)] | 117 | #[inline(always)] |
| 118 | pub fn efid1(&mut self) -> EFID1_W { | 118 | pub fn efid1(&mut self) -> EFID1_W<'_> { |
| 119 | EFID1_W { w: self } | 119 | EFID1_W { w: self } |
| 120 | } | 120 | } |
| 121 | #[doc = "Byte 0 - Bits 29:31 - EFEC"] | 121 | #[doc = "Byte 0 - Bits 29:31 - EFEC"] |
| 122 | #[inline(always)] | 122 | #[inline(always)] |
| 123 | pub fn efec(&mut self) -> EFEC_W { | 123 | pub fn efec(&mut self) -> EFEC_W<'_> { |
| 124 | EFEC_W { w: self } | 124 | EFEC_W { w: self } |
| 125 | } | 125 | } |
| 126 | #[doc = "Byte 1 - Bits 0:28 - EFID2"] | 126 | #[doc = "Byte 1 - Bits 0:28 - EFID2"] |
| 127 | #[inline(always)] | 127 | #[inline(always)] |
| 128 | pub fn efid2(&mut self) -> EFID2_W { | 128 | pub fn efid2(&mut self) -> EFID2_W<'_> { |
| 129 | EFID2_W { w: self } | 129 | EFID2_W { w: self } |
| 130 | } | 130 | } |
| 131 | #[doc = "Byte 1 - Bits 30:31 - EFT"] | 131 | #[doc = "Byte 1 - Bits 30:31 - EFT"] |
| 132 | #[inline(always)] | 132 | #[inline(always)] |
| 133 | pub fn eft(&mut self) -> EFT_W { | 133 | pub fn eft(&mut self) -> EFT_W<'_> { |
| 134 | EFT_W { w: self } | 134 | EFT_W { w: self } |
| 135 | } | 135 | } |
| 136 | } | 136 | } |
diff --git a/embassy-stm32/src/can/fd/message_ram/standard_filter.rs b/embassy-stm32/src/can/fd/message_ram/standard_filter.rs index 3a3bbcf12..f52646bfe 100644 --- a/embassy-stm32/src/can/fd/message_ram/standard_filter.rs +++ b/embassy-stm32/src/can/fd/message_ram/standard_filter.rs | |||
| @@ -115,22 +115,22 @@ impl R { | |||
| 115 | impl W { | 115 | impl W { |
| 116 | #[doc = "Bits 0:10 - SFID2"] | 116 | #[doc = "Bits 0:10 - SFID2"] |
| 117 | #[inline(always)] | 117 | #[inline(always)] |
| 118 | pub fn sfid2(&mut self) -> SFID2_W { | 118 | pub fn sfid2(&mut self) -> SFID2_W<'_> { |
| 119 | SFID2_W { w: self } | 119 | SFID2_W { w: self } |
| 120 | } | 120 | } |
| 121 | #[doc = "Bits 16:26 - SFID1"] | 121 | #[doc = "Bits 16:26 - SFID1"] |
| 122 | #[inline(always)] | 122 | #[inline(always)] |
| 123 | pub fn sfid1(&mut self) -> SFID1_W { | 123 | pub fn sfid1(&mut self) -> SFID1_W<'_> { |
| 124 | SFID1_W { w: self } | 124 | SFID1_W { w: self } |
| 125 | } | 125 | } |
| 126 | #[doc = "Bits 27:29 - SFEC"] | 126 | #[doc = "Bits 27:29 - SFEC"] |
| 127 | #[inline(always)] | 127 | #[inline(always)] |
| 128 | pub fn sfec(&mut self) -> SFEC_W { | 128 | pub fn sfec(&mut self) -> SFEC_W<'_> { |
| 129 | SFEC_W { w: self } | 129 | SFEC_W { w: self } |
| 130 | } | 130 | } |
| 131 | #[doc = "Bits 30:31 - SFT"] | 131 | #[doc = "Bits 30:31 - SFT"] |
| 132 | #[inline(always)] | 132 | #[inline(always)] |
| 133 | pub fn sft(&mut self) -> SFT_W { | 133 | pub fn sft(&mut self) -> SFT_W<'_> { |
| 134 | SFT_W { w: self } | 134 | SFT_W { w: self } |
| 135 | } | 135 | } |
| 136 | } | 136 | } |
diff --git a/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs b/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs index 455406a1c..6d65a86cb 100644 --- a/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs +++ b/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs | |||
| @@ -376,47 +376,47 @@ impl R { | |||
| 376 | impl W { | 376 | impl W { |
| 377 | #[doc = "Byte 0 - Bits 0:28 - ID"] | 377 | #[doc = "Byte 0 - Bits 0:28 - ID"] |
| 378 | #[inline(always)] | 378 | #[inline(always)] |
| 379 | pub fn id(&mut self) -> ID_W { | 379 | pub fn id(&mut self) -> ID_W<'_> { |
| 380 | ID_W { w: self } | 380 | ID_W { w: self } |
| 381 | } | 381 | } |
| 382 | #[doc = "Byte 0 - Bit 29 - RTR"] | 382 | #[doc = "Byte 0 - Bit 29 - RTR"] |
| 383 | #[inline(always)] | 383 | #[inline(always)] |
| 384 | pub fn rtr(&mut self) -> RTR_W { | 384 | pub fn rtr(&mut self) -> RTR_W<'_> { |
| 385 | RTR_W { w: self } | 385 | RTR_W { w: self } |
| 386 | } | 386 | } |
| 387 | #[doc = "Byte 0 - Bit 30 - XTD"] | 387 | #[doc = "Byte 0 - Bit 30 - XTD"] |
| 388 | #[inline(always)] | 388 | #[inline(always)] |
| 389 | pub fn xtd(&mut self) -> XTD_W { | 389 | pub fn xtd(&mut self) -> XTD_W<'_> { |
| 390 | XTD_W { w: self } | 390 | XTD_W { w: self } |
| 391 | } | 391 | } |
| 392 | #[doc = "Byte 0 - Bit 31 - ESI"] | 392 | #[doc = "Byte 0 - Bit 31 - ESI"] |
| 393 | #[inline(always)] | 393 | #[inline(always)] |
| 394 | pub fn esi(&mut self) -> ESI_W { | 394 | pub fn esi(&mut self) -> ESI_W<'_> { |
| 395 | ESI_W { w: self } | 395 | ESI_W { w: self } |
| 396 | } | 396 | } |
| 397 | #[doc = "Byte 1 - Bit 16:19 - DLC"] | 397 | #[doc = "Byte 1 - Bit 16:19 - DLC"] |
| 398 | #[inline(always)] | 398 | #[inline(always)] |
| 399 | pub fn dlc(&mut self) -> DLC_W { | 399 | pub fn dlc(&mut self) -> DLC_W<'_> { |
| 400 | DLC_W { w: self } | 400 | DLC_W { w: self } |
| 401 | } | 401 | } |
| 402 | #[doc = "Byte 1 - Bit 20 - BRS"] | 402 | #[doc = "Byte 1 - Bit 20 - BRS"] |
| 403 | #[inline(always)] | 403 | #[inline(always)] |
| 404 | pub fn brs(&mut self) -> BRS_W { | 404 | pub fn brs(&mut self) -> BRS_W<'_> { |
| 405 | BRS_W { w: self } | 405 | BRS_W { w: self } |
| 406 | } | 406 | } |
| 407 | #[doc = "Byte 1 - Bit 21 - FDF"] | 407 | #[doc = "Byte 1 - Bit 21 - FDF"] |
| 408 | #[inline(always)] | 408 | #[inline(always)] |
| 409 | pub fn fdf(&mut self) -> FDF_W { | 409 | pub fn fdf(&mut self) -> FDF_W<'_> { |
| 410 | FDF_W { w: self } | 410 | FDF_W { w: self } |
| 411 | } | 411 | } |
| 412 | #[doc = "Byte 1 - Bit 23 - EFC"] | 412 | #[doc = "Byte 1 - Bit 23 - EFC"] |
| 413 | #[inline(always)] | 413 | #[inline(always)] |
| 414 | pub fn efc(&mut self) -> EFC_W { | 414 | pub fn efc(&mut self) -> EFC_W<'_> { |
| 415 | EFC_W { w: self } | 415 | EFC_W { w: self } |
| 416 | } | 416 | } |
| 417 | #[doc = "Byte 1 - Bit 24:31 - MM"] | 417 | #[doc = "Byte 1 - Bit 24:31 - MM"] |
| 418 | #[inline(always)] | 418 | #[inline(always)] |
| 419 | pub fn mm(&mut self) -> MM_W { | 419 | pub fn mm(&mut self) -> MM_W<'_> { |
| 420 | MM_W { w: self } | 420 | MM_W { w: self } |
| 421 | } | 421 | } |
| 422 | #[doc = "Convenience function for setting the data length and frame format"] | 422 | #[doc = "Convenience function for setting the data length and frame format"] |
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 97d22315a..99e40ba62 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs | |||
| @@ -21,6 +21,7 @@ use self::fd::config::*; | |||
| 21 | use self::fd::filter::*; | 21 | use self::fd::filter::*; |
| 22 | pub use self::fd::{config, filter}; | 22 | pub use self::fd::{config, filter}; |
| 23 | pub use super::common::{BufferedCanReceiver, BufferedCanSender}; | 23 | pub use super::common::{BufferedCanReceiver, BufferedCanSender}; |
| 24 | use super::common::{InfoRef, RxInfoRef, TxInfoRef}; | ||
| 24 | use super::enums::*; | 25 | use super::enums::*; |
| 25 | use super::frame::*; | 26 | use super::frame::*; |
| 26 | use super::util; | 27 | use super::util; |
| @@ -167,10 +168,10 @@ fn calc_ns_per_timer_tick( | |||
| 167 | pub struct CanConfigurator<'d> { | 168 | pub struct CanConfigurator<'d> { |
| 168 | _phantom: PhantomData<&'d ()>, | 169 | _phantom: PhantomData<&'d ()>, |
| 169 | config: crate::can::fd::config::FdCanConfig, | 170 | config: crate::can::fd::config::FdCanConfig, |
| 170 | info: &'static Info, | ||
| 171 | /// Reference to internals. | 171 | /// Reference to internals. |
| 172 | properties: Properties, | 172 | properties: Properties, |
| 173 | periph_clock: crate::time::Hertz, | 173 | periph_clock: crate::time::Hertz, |
| 174 | info: InfoRef, | ||
| 174 | } | 175 | } |
| 175 | 176 | ||
| 176 | impl<'d> CanConfigurator<'d> { | 177 | impl<'d> CanConfigurator<'d> { |
| @@ -194,8 +195,6 @@ impl<'d> CanConfigurator<'d> { | |||
| 194 | s.borrow_mut().tx_pin_port = Some(tx.pin_port()); | 195 | s.borrow_mut().tx_pin_port = Some(tx.pin_port()); |
| 195 | s.borrow_mut().rx_pin_port = Some(rx.pin_port()); | 196 | s.borrow_mut().rx_pin_port = Some(rx.pin_port()); |
| 196 | }); | 197 | }); |
| 197 | (info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 198 | (info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 199 | 198 | ||
| 200 | let mut config = crate::can::fd::config::FdCanConfig::default(); | 199 | let mut config = crate::can::fd::config::FdCanConfig::default(); |
| 201 | config.timestamp_source = TimestampSource::Prescaler(TimestampPrescaler::_1); | 200 | config.timestamp_source = TimestampSource::Prescaler(TimestampPrescaler::_1); |
| @@ -211,9 +210,9 @@ impl<'d> CanConfigurator<'d> { | |||
| 211 | Self { | 210 | Self { |
| 212 | _phantom: PhantomData, | 211 | _phantom: PhantomData, |
| 213 | config, | 212 | config, |
| 214 | info, | ||
| 215 | properties: Properties::new(T::info()), | 213 | properties: Properties::new(T::info()), |
| 216 | periph_clock: T::frequency(), | 214 | periph_clock: T::frequency(), |
| 215 | info: InfoRef::new(info), | ||
| 217 | } | 216 | } |
| 218 | } | 217 | } |
| 219 | 218 | ||
| @@ -262,19 +261,17 @@ impl<'d> CanConfigurator<'d> { | |||
| 262 | 261 | ||
| 263 | /// Start in mode. | 262 | /// Start in mode. |
| 264 | pub fn start(self, mode: OperatingMode) -> Can<'d> { | 263 | pub fn start(self, mode: OperatingMode) -> Can<'d> { |
| 265 | let ns_per_timer_tick = calc_ns_per_timer_tick(self.info, self.periph_clock, self.config.frame_transmit); | 264 | let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit); |
| 266 | self.info.state.lock(|s| { | 265 | self.info.state.lock(|s| { |
| 267 | s.borrow_mut().ns_per_timer_tick = ns_per_timer_tick; | 266 | s.borrow_mut().ns_per_timer_tick = ns_per_timer_tick; |
| 268 | }); | 267 | }); |
| 269 | self.info.regs.into_mode(self.config, mode); | 268 | self.info.regs.into_mode(self.config, mode); |
| 270 | (self.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 271 | (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 272 | Can { | 269 | Can { |
| 273 | _phantom: PhantomData, | 270 | _phantom: PhantomData, |
| 274 | config: self.config, | 271 | config: self.config, |
| 275 | info: self.info, | ||
| 276 | _mode: mode, | 272 | _mode: mode, |
| 277 | properties: Properties::new(self.info), | 273 | properties: Properties::new(&self.info), |
| 274 | info: InfoRef::new(&self.info), | ||
| 278 | } | 275 | } |
| 279 | } | 276 | } |
| 280 | 277 | ||
| @@ -294,20 +291,13 @@ impl<'d> CanConfigurator<'d> { | |||
| 294 | } | 291 | } |
| 295 | } | 292 | } |
| 296 | 293 | ||
| 297 | impl<'d> Drop for CanConfigurator<'d> { | ||
| 298 | fn drop(&mut self) { | ||
| 299 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); | ||
| 300 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | /// FDCAN Instance | 294 | /// FDCAN Instance |
| 305 | pub struct Can<'d> { | 295 | pub struct Can<'d> { |
| 306 | _phantom: PhantomData<&'d ()>, | 296 | _phantom: PhantomData<&'d ()>, |
| 307 | config: crate::can::fd::config::FdCanConfig, | 297 | config: crate::can::fd::config::FdCanConfig, |
| 308 | info: &'static Info, | ||
| 309 | _mode: OperatingMode, | 298 | _mode: OperatingMode, |
| 310 | properties: Properties, | 299 | properties: Properties, |
| 300 | info: InfoRef, | ||
| 311 | } | 301 | } |
| 312 | 302 | ||
| 313 | impl<'d> Can<'d> { | 303 | impl<'d> Can<'d> { |
| @@ -341,12 +331,12 @@ impl<'d> Can<'d> { | |||
| 341 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 331 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 342 | /// transmitted, then tries again. | 332 | /// transmitted, then tries again. |
| 343 | pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { | 333 | pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { |
| 344 | TxMode::write(self.info, frame).await | 334 | TxMode::write(&self.info, frame).await |
| 345 | } | 335 | } |
| 346 | 336 | ||
| 347 | /// Returns the next received message frame | 337 | /// Returns the next received message frame |
| 348 | pub async fn read(&mut self) -> Result<Envelope, BusError> { | 338 | pub async fn read(&mut self) -> Result<Envelope, BusError> { |
| 349 | RxMode::read_classic(self.info).await | 339 | RxMode::read_classic(&self.info).await |
| 350 | } | 340 | } |
| 351 | 341 | ||
| 352 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | 342 | /// Queues the message to be sent but exerts backpressure. If a lower-priority |
| @@ -354,29 +344,27 @@ impl<'d> Can<'d> { | |||
| 354 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 344 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 355 | /// transmitted, then tries again. | 345 | /// transmitted, then tries again. |
| 356 | pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { | 346 | pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { |
| 357 | TxMode::write_fd(self.info, frame).await | 347 | TxMode::write_fd(&self.info, frame).await |
| 358 | } | 348 | } |
| 359 | 349 | ||
| 360 | /// Returns the next received message frame | 350 | /// Returns the next received message frame |
| 361 | pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> { | 351 | pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> { |
| 362 | RxMode::read_fd(self.info).await | 352 | RxMode::read_fd(&self.info).await |
| 363 | } | 353 | } |
| 364 | 354 | ||
| 365 | /// Split instance into separate portions: Tx(write), Rx(read), common properties | 355 | /// Split instance into separate portions: Tx(write), Rx(read), common properties |
| 366 | pub fn split(self) -> (CanTx<'d>, CanRx<'d>, Properties) { | 356 | pub fn split(self) -> (CanTx<'d>, CanRx<'d>, Properties) { |
| 367 | (self.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 368 | (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 369 | ( | 357 | ( |
| 370 | CanTx { | 358 | CanTx { |
| 371 | _phantom: PhantomData, | 359 | _phantom: PhantomData, |
| 372 | info: self.info, | ||
| 373 | config: self.config, | 360 | config: self.config, |
| 374 | _mode: self._mode, | 361 | _mode: self._mode, |
| 362 | info: TxInfoRef::new(&self.info), | ||
| 375 | }, | 363 | }, |
| 376 | CanRx { | 364 | CanRx { |
| 377 | _phantom: PhantomData, | 365 | _phantom: PhantomData, |
| 378 | info: self.info, | ||
| 379 | _mode: self._mode, | 366 | _mode: self._mode, |
| 367 | info: RxInfoRef::new(&self.info), | ||
| 380 | }, | 368 | }, |
| 381 | Properties { | 369 | Properties { |
| 382 | info: self.properties.info, | 370 | info: self.properties.info, |
| @@ -385,14 +373,12 @@ impl<'d> Can<'d> { | |||
| 385 | } | 373 | } |
| 386 | /// Join split rx and tx portions back together | 374 | /// Join split rx and tx portions back together |
| 387 | pub fn join(tx: CanTx<'d>, rx: CanRx<'d>) -> Self { | 375 | pub fn join(tx: CanTx<'d>, rx: CanRx<'d>) -> Self { |
| 388 | (tx.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 389 | (tx.info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 390 | Can { | 376 | Can { |
| 391 | _phantom: PhantomData, | 377 | _phantom: PhantomData, |
| 392 | config: tx.config, | 378 | config: tx.config, |
| 393 | info: tx.info, | ||
| 394 | _mode: rx._mode, | 379 | _mode: rx._mode, |
| 395 | properties: Properties::new(tx.info), | 380 | properties: Properties::new(&tx.info), |
| 381 | info: InfoRef::new(&tx.info), | ||
| 396 | } | 382 | } |
| 397 | } | 383 | } |
| 398 | 384 | ||
| @@ -402,7 +388,7 @@ impl<'d> Can<'d> { | |||
| 402 | tx_buf: &'static mut TxBuf<TX_BUF_SIZE>, | 388 | tx_buf: &'static mut TxBuf<TX_BUF_SIZE>, |
| 403 | rxb: &'static mut RxBuf<RX_BUF_SIZE>, | 389 | rxb: &'static mut RxBuf<RX_BUF_SIZE>, |
| 404 | ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | 390 | ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { |
| 405 | BufferedCan::new(self.info, self._mode, tx_buf, rxb) | 391 | BufferedCan::new(&self.info, self._mode, tx_buf, rxb) |
| 406 | } | 392 | } |
| 407 | 393 | ||
| 408 | /// Return a buffered instance of driver with CAN FD support. User must supply Buffers | 394 | /// Return a buffered instance of driver with CAN FD support. User must supply Buffers |
| @@ -411,14 +397,7 @@ impl<'d> Can<'d> { | |||
| 411 | tx_buf: &'static mut TxFdBuf<TX_BUF_SIZE>, | 397 | tx_buf: &'static mut TxFdBuf<TX_BUF_SIZE>, |
| 412 | rxb: &'static mut RxFdBuf<RX_BUF_SIZE>, | 398 | rxb: &'static mut RxFdBuf<RX_BUF_SIZE>, |
| 413 | ) -> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | 399 | ) -> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { |
| 414 | BufferedCanFd::new(self.info, self._mode, tx_buf, rxb) | 400 | BufferedCanFd::new(&self.info, self._mode, tx_buf, rxb) |
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | impl<'d> Drop for Can<'d> { | ||
| 419 | fn drop(&mut self) { | ||
| 420 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); | ||
| 421 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); | ||
| 422 | } | 401 | } |
| 423 | } | 402 | } |
| 424 | 403 | ||
| @@ -431,11 +410,11 @@ pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, | |||
| 431 | /// Buffered FDCAN Instance | 410 | /// Buffered FDCAN Instance |
| 432 | pub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { | 411 | pub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { |
| 433 | _phantom: PhantomData<&'d ()>, | 412 | _phantom: PhantomData<&'d ()>, |
| 434 | info: &'static Info, | ||
| 435 | _mode: OperatingMode, | 413 | _mode: OperatingMode, |
| 436 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, | 414 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, |
| 437 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, | 415 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, |
| 438 | properties: Properties, | 416 | properties: Properties, |
| 417 | info: InfoRef, | ||
| 439 | } | 418 | } |
| 440 | 419 | ||
| 441 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | 420 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { |
| @@ -445,15 +424,13 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, | |||
| 445 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, | 424 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, |
| 446 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, | 425 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, |
| 447 | ) -> Self { | 426 | ) -> Self { |
| 448 | (info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 449 | (info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 450 | BufferedCan { | 427 | BufferedCan { |
| 451 | _phantom: PhantomData, | 428 | _phantom: PhantomData, |
| 452 | info, | ||
| 453 | _mode, | 429 | _mode, |
| 454 | tx_buf, | 430 | tx_buf, |
| 455 | rx_buf, | 431 | rx_buf, |
| 456 | properties: Properties::new(info), | 432 | properties: Properties::new(info), |
| 433 | info: InfoRef::new(info), | ||
| 457 | } | 434 | } |
| 458 | .setup() | 435 | .setup() |
| 459 | } | 436 | } |
| @@ -492,31 +469,21 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, | |||
| 492 | 469 | ||
| 493 | /// Returns a sender that can be used for sending CAN frames. | 470 | /// Returns a sender that can be used for sending CAN frames. |
| 494 | pub fn writer(&self) -> BufferedCanSender { | 471 | pub fn writer(&self) -> BufferedCanSender { |
| 495 | (self.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 496 | BufferedCanSender { | 472 | BufferedCanSender { |
| 497 | tx_buf: self.tx_buf.sender().into(), | 473 | tx_buf: self.tx_buf.sender().into(), |
| 498 | waker: self.info.tx_waker, | 474 | info: TxInfoRef::new(&self.info), |
| 499 | internal_operation: self.info.internal_operation, | ||
| 500 | } | 475 | } |
| 501 | } | 476 | } |
| 502 | 477 | ||
| 503 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. | 478 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. |
| 504 | pub fn reader(&self) -> BufferedCanReceiver { | 479 | pub fn reader(&self) -> BufferedCanReceiver { |
| 505 | (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 506 | BufferedCanReceiver { | 480 | BufferedCanReceiver { |
| 507 | rx_buf: self.rx_buf.receiver().into(), | 481 | rx_buf: self.rx_buf.receiver().into(), |
| 508 | internal_operation: self.info.internal_operation, | 482 | info: RxInfoRef::new(&self.info), |
| 509 | } | 483 | } |
| 510 | } | 484 | } |
| 511 | } | 485 | } |
| 512 | 486 | ||
| 513 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | ||
| 514 | fn drop(&mut self) { | ||
| 515 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); | ||
| 516 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); | ||
| 517 | } | ||
| 518 | } | ||
| 519 | |||
| 520 | /// User supplied buffer for RX Buffering | 487 | /// User supplied buffer for RX Buffering |
| 521 | pub type RxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<FdEnvelope, BusError>, BUF_SIZE>; | 488 | pub type RxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<FdEnvelope, BusError>, BUF_SIZE>; |
| 522 | 489 | ||
| @@ -532,11 +499,11 @@ pub type BufferedFdCanReceiver = super::common::BufferedReceiver<'static, FdEnve | |||
| 532 | /// Buffered FDCAN Instance | 499 | /// Buffered FDCAN Instance |
| 533 | pub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { | 500 | pub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { |
| 534 | _phantom: PhantomData<&'d ()>, | 501 | _phantom: PhantomData<&'d ()>, |
| 535 | info: &'static Info, | ||
| 536 | _mode: OperatingMode, | 502 | _mode: OperatingMode, |
| 537 | tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, | 503 | tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, |
| 538 | rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, | 504 | rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, |
| 539 | properties: Properties, | 505 | properties: Properties, |
| 506 | info: InfoRef, | ||
| 540 | } | 507 | } |
| 541 | 508 | ||
| 542 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | 509 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { |
| @@ -546,15 +513,13 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' | |||
| 546 | tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, | 513 | tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, |
| 547 | rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, | 514 | rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, |
| 548 | ) -> Self { | 515 | ) -> Self { |
| 549 | (info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 550 | (info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 551 | BufferedCanFd { | 516 | BufferedCanFd { |
| 552 | _phantom: PhantomData, | 517 | _phantom: PhantomData, |
| 553 | info, | ||
| 554 | _mode, | 518 | _mode, |
| 555 | tx_buf, | 519 | tx_buf, |
| 556 | rx_buf, | 520 | rx_buf, |
| 557 | properties: Properties::new(info), | 521 | properties: Properties::new(info), |
| 522 | info: InfoRef::new(info), | ||
| 558 | } | 523 | } |
| 559 | .setup() | 524 | .setup() |
| 560 | } | 525 | } |
| @@ -593,36 +558,26 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' | |||
| 593 | 558 | ||
| 594 | /// Returns a sender that can be used for sending CAN frames. | 559 | /// Returns a sender that can be used for sending CAN frames. |
| 595 | pub fn writer(&self) -> BufferedFdCanSender { | 560 | pub fn writer(&self) -> BufferedFdCanSender { |
| 596 | (self.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 597 | BufferedFdCanSender { | 561 | BufferedFdCanSender { |
| 598 | tx_buf: self.tx_buf.sender().into(), | 562 | tx_buf: self.tx_buf.sender().into(), |
| 599 | waker: self.info.tx_waker, | 563 | info: TxInfoRef::new(&self.info), |
| 600 | internal_operation: self.info.internal_operation, | ||
| 601 | } | 564 | } |
| 602 | } | 565 | } |
| 603 | 566 | ||
| 604 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. | 567 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. |
| 605 | pub fn reader(&self) -> BufferedFdCanReceiver { | 568 | pub fn reader(&self) -> BufferedFdCanReceiver { |
| 606 | (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 607 | BufferedFdCanReceiver { | 569 | BufferedFdCanReceiver { |
| 608 | rx_buf: self.rx_buf.receiver().into(), | 570 | rx_buf: self.rx_buf.receiver().into(), |
| 609 | internal_operation: self.info.internal_operation, | 571 | info: RxInfoRef::new(&self.info), |
| 610 | } | 572 | } |
| 611 | } | 573 | } |
| 612 | } | 574 | } |
| 613 | 575 | ||
| 614 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | ||
| 615 | fn drop(&mut self) { | ||
| 616 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); | ||
| 617 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); | ||
| 618 | } | ||
| 619 | } | ||
| 620 | |||
| 621 | /// FDCAN Rx only Instance | 576 | /// FDCAN Rx only Instance |
| 622 | pub struct CanRx<'d> { | 577 | pub struct CanRx<'d> { |
| 623 | _phantom: PhantomData<&'d ()>, | 578 | _phantom: PhantomData<&'d ()>, |
| 624 | info: &'static Info, | ||
| 625 | _mode: OperatingMode, | 579 | _mode: OperatingMode, |
| 580 | info: RxInfoRef, | ||
| 626 | } | 581 | } |
| 627 | 582 | ||
| 628 | impl<'d> CanRx<'d> { | 583 | impl<'d> CanRx<'d> { |
| @@ -637,18 +592,12 @@ impl<'d> CanRx<'d> { | |||
| 637 | } | 592 | } |
| 638 | } | 593 | } |
| 639 | 594 | ||
| 640 | impl<'d> Drop for CanRx<'d> { | ||
| 641 | fn drop(&mut self) { | ||
| 642 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); | ||
| 643 | } | ||
| 644 | } | ||
| 645 | |||
| 646 | /// FDCAN Tx only Instance | 595 | /// FDCAN Tx only Instance |
| 647 | pub struct CanTx<'d> { | 596 | pub struct CanTx<'d> { |
| 648 | _phantom: PhantomData<&'d ()>, | 597 | _phantom: PhantomData<&'d ()>, |
| 649 | info: &'static Info, | ||
| 650 | config: crate::can::fd::config::FdCanConfig, | 598 | config: crate::can::fd::config::FdCanConfig, |
| 651 | _mode: OperatingMode, | 599 | _mode: OperatingMode, |
| 600 | info: TxInfoRef, | ||
| 652 | } | 601 | } |
| 653 | 602 | ||
| 654 | impl<'c, 'd> CanTx<'d> { | 603 | impl<'c, 'd> CanTx<'d> { |
| @@ -657,7 +606,7 @@ impl<'c, 'd> CanTx<'d> { | |||
| 657 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 606 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 658 | /// transmitted, then tries again. | 607 | /// transmitted, then tries again. |
| 659 | pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { | 608 | pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { |
| 660 | TxMode::write(self.info, frame).await | 609 | TxMode::write(&self.info, frame).await |
| 661 | } | 610 | } |
| 662 | 611 | ||
| 663 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | 612 | /// Queues the message to be sent but exerts backpressure. If a lower-priority |
| @@ -665,13 +614,7 @@ impl<'c, 'd> CanTx<'d> { | |||
| 665 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 614 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 666 | /// transmitted, then tries again. | 615 | /// transmitted, then tries again. |
| 667 | pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { | 616 | pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { |
| 668 | TxMode::write_fd(self.info, frame).await | 617 | TxMode::write_fd(&self.info, frame).await |
| 669 | } | ||
| 670 | } | ||
| 671 | |||
| 672 | impl<'d> Drop for CanTx<'d> { | ||
| 673 | fn drop(&mut self) { | ||
| 674 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); | ||
| 675 | } | 618 | } |
| 676 | } | 619 | } |
| 677 | 620 | ||
| @@ -938,21 +881,56 @@ impl State { | |||
| 938 | } | 881 | } |
| 939 | 882 | ||
| 940 | type SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<State>>; | 883 | type SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<State>>; |
| 941 | struct Info { | 884 | pub(crate) struct Info { |
| 942 | regs: Registers, | 885 | regs: Registers, |
| 943 | interrupt0: crate::interrupt::Interrupt, | 886 | interrupt0: crate::interrupt::Interrupt, |
| 944 | _interrupt1: crate::interrupt::Interrupt, | 887 | _interrupt1: crate::interrupt::Interrupt, |
| 945 | tx_waker: fn(), | 888 | pub(crate) tx_waker: fn(), |
| 946 | internal_operation: fn(InternalOperation), | ||
| 947 | state: SharedState, | 889 | state: SharedState, |
| 948 | } | 890 | } |
| 949 | 891 | ||
| 892 | impl Info { | ||
| 893 | pub(crate) fn adjust_reference_counter(&self, val: RefCountOp) { | ||
| 894 | self.state.lock(|s| { | ||
| 895 | let mut mut_state = s.borrow_mut(); | ||
| 896 | match val { | ||
| 897 | RefCountOp::NotifySenderCreated => { | ||
| 898 | mut_state.sender_instance_count += 1; | ||
| 899 | } | ||
| 900 | RefCountOp::NotifySenderDestroyed => { | ||
| 901 | mut_state.sender_instance_count -= 1; | ||
| 902 | if 0 == mut_state.sender_instance_count { | ||
| 903 | (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 904 | } | ||
| 905 | } | ||
| 906 | RefCountOp::NotifyReceiverCreated => { | ||
| 907 | mut_state.receiver_instance_count += 1; | ||
| 908 | } | ||
| 909 | RefCountOp::NotifyReceiverDestroyed => { | ||
| 910 | mut_state.receiver_instance_count -= 1; | ||
| 911 | if 0 == mut_state.receiver_instance_count { | ||
| 912 | (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 913 | } | ||
| 914 | } | ||
| 915 | } | ||
| 916 | if mut_state.sender_instance_count == 0 && mut_state.receiver_instance_count == 0 { | ||
| 917 | unsafe { | ||
| 918 | let tx_pin = crate::gpio::AnyPin::steal(mut_state.tx_pin_port.unwrap()); | ||
| 919 | tx_pin.set_as_disconnected(); | ||
| 920 | let rx_pin = crate::gpio::AnyPin::steal(mut_state.rx_pin_port.unwrap()); | ||
| 921 | rx_pin.set_as_disconnected(); | ||
| 922 | self.interrupt0.disable(); | ||
| 923 | } | ||
| 924 | } | ||
| 925 | }); | ||
| 926 | } | ||
| 927 | } | ||
| 928 | |||
| 950 | trait SealedInstance { | 929 | trait SealedInstance { |
| 951 | const MSG_RAM_OFFSET: usize; | 930 | const MSG_RAM_OFFSET: usize; |
| 952 | 931 | ||
| 953 | fn info() -> &'static Info; | 932 | fn info() -> &'static Info; |
| 954 | fn registers() -> crate::can::fd::peripheral::Registers; | 933 | fn registers() -> crate::can::fd::peripheral::Registers; |
| 955 | fn internal_operation(val: InternalOperation); | ||
| 956 | } | 934 | } |
| 957 | 935 | ||
| 958 | /// Instance trait | 936 | /// Instance trait |
| @@ -974,41 +952,6 @@ macro_rules! impl_fdcan { | |||
| 974 | impl SealedInstance for peripherals::$inst { | 952 | impl SealedInstance for peripherals::$inst { |
| 975 | const MSG_RAM_OFFSET: usize = $msg_ram_offset; | 953 | const MSG_RAM_OFFSET: usize = $msg_ram_offset; |
| 976 | 954 | ||
| 977 | fn internal_operation(val: InternalOperation) { | ||
| 978 | peripherals::$inst::info().state.lock(|s| { | ||
| 979 | let mut mut_state = s.borrow_mut(); | ||
| 980 | match val { | ||
| 981 | InternalOperation::NotifySenderCreated => { | ||
| 982 | mut_state.sender_instance_count += 1; | ||
| 983 | } | ||
| 984 | InternalOperation::NotifySenderDestroyed => { | ||
| 985 | mut_state.sender_instance_count -= 1; | ||
| 986 | if ( 0 == mut_state.sender_instance_count) { | ||
| 987 | (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 988 | } | ||
| 989 | } | ||
| 990 | InternalOperation::NotifyReceiverCreated => { | ||
| 991 | mut_state.receiver_instance_count += 1; | ||
| 992 | } | ||
| 993 | InternalOperation::NotifyReceiverDestroyed => { | ||
| 994 | mut_state.receiver_instance_count -= 1; | ||
| 995 | if ( 0 == mut_state.receiver_instance_count) { | ||
| 996 | (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 997 | } | ||
| 998 | } | ||
| 999 | } | ||
| 1000 | if mut_state.sender_instance_count == 0 && mut_state.receiver_instance_count == 0 { | ||
| 1001 | unsafe { | ||
| 1002 | let tx_pin = crate::gpio::AnyPin::steal(mut_state.tx_pin_port.unwrap()); | ||
| 1003 | tx_pin.set_as_disconnected(); | ||
| 1004 | let rx_pin = crate::gpio::AnyPin::steal(mut_state.rx_pin_port.unwrap()); | ||
| 1005 | rx_pin.set_as_disconnected(); | ||
| 1006 | rcc::disable::<peripherals::$inst>(); | ||
| 1007 | } | ||
| 1008 | } | ||
| 1009 | }); | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | fn info() -> &'static Info { | 955 | fn info() -> &'static Info { |
| 1013 | 956 | ||
| 1014 | static INFO: Info = Info { | 957 | static INFO: Info = Info { |
| @@ -1016,7 +959,6 @@ macro_rules! impl_fdcan { | |||
| 1016 | interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ, | 959 | interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ, |
| 1017 | _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ, | 960 | _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ, |
| 1018 | tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend, | 961 | tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend, |
| 1019 | internal_operation: peripherals::$inst::internal_operation, | ||
| 1020 | state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())), | 962 | state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())), |
| 1021 | }; | 963 | }; |
| 1022 | &INFO | 964 | &INFO |
diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs index 008f50270..9afa8ef53 100644 --- a/embassy-stm32/src/cordic/utils.rs +++ b/embassy-stm32/src/cordic/utils.rs | |||
| @@ -5,7 +5,7 @@ macro_rules! floating_fixed_convert { | |||
| 5 | ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => { | 5 | ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => { |
| 6 | /// convert float point to fixed point format | 6 | /// convert float point to fixed point format |
| 7 | pub fn $f_to_q(value: $float_ty) -> Result<$unsigned_bin_typ, NumberOutOfRange> { | 7 | pub fn $f_to_q(value: $float_ty) -> Result<$unsigned_bin_typ, NumberOutOfRange> { |
| 8 | const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; | 8 | const MIN_POSITIVE: $float_ty = <$float_ty>::from_bits($min_positive); |
| 9 | 9 | ||
| 10 | if value < -1.0 { | 10 | if value < -1.0 { |
| 11 | return Err(NumberOutOfRange::BelowLowerBound) | 11 | return Err(NumberOutOfRange::BelowLowerBound) |
diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index fba3c0fd7..35d9f8cce 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] | 2 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] |
| 3 | use core::cmp::min; | 3 | use core::cmp::min; |
| 4 | use core::marker::PhantomData; | 4 | use core::marker::PhantomData; |
| 5 | use core::ptr; | ||
| 6 | 5 | ||
| 7 | use embassy_hal_internal::{Peri, PeripheralType}; | 6 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 8 | use embassy_sync::waitqueue::AtomicWaker; | 7 | use embassy_sync::waitqueue::AtomicWaker; |
| @@ -1814,14 +1813,12 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { | |||
| 1814 | assert_eq!(blocks.len() % block_size, 0); | 1813 | assert_eq!(blocks.len() % block_size, 0); |
| 1815 | // Configure DMA to transfer input to crypto core. | 1814 | // Configure DMA to transfer input to crypto core. |
| 1816 | let dst_ptr: *mut u32 = T::regs().din().as_ptr(); | 1815 | let dst_ptr: *mut u32 = T::regs().din().as_ptr(); |
| 1817 | let num_words = blocks.len() / 4; | ||
| 1818 | let src_ptr: *const [u8] = ptr::slice_from_raw_parts(blocks.as_ptr().cast(), num_words); | ||
| 1819 | let options = TransferOptions { | 1816 | let options = TransferOptions { |
| 1820 | #[cfg(not(gpdma))] | 1817 | #[cfg(not(gpdma))] |
| 1821 | priority: crate::dma::Priority::High, | 1818 | priority: crate::dma::Priority::High, |
| 1822 | ..Default::default() | 1819 | ..Default::default() |
| 1823 | }; | 1820 | }; |
| 1824 | let dma_transfer = unsafe { dma.write_raw(src_ptr, dst_ptr, options) }; | 1821 | let dma_transfer = unsafe { dma.write_raw(blocks, dst_ptr, options) }; |
| 1825 | T::regs().dmacr().modify(|w| w.set_dien(true)); | 1822 | T::regs().dmacr().modify(|w| w.set_dien(true)); |
| 1826 | // Wait for the transfer to complete. | 1823 | // Wait for the transfer to complete. |
| 1827 | dma_transfer.await; | 1824 | dma_transfer.await; |
| @@ -1836,14 +1833,12 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { | |||
| 1836 | assert_eq!((blocks.len() * 4) % block_size, 0); | 1833 | assert_eq!((blocks.len() * 4) % block_size, 0); |
| 1837 | // Configure DMA to transfer input to crypto core. | 1834 | // Configure DMA to transfer input to crypto core. |
| 1838 | let dst_ptr: *mut u32 = T::regs().din().as_ptr(); | 1835 | let dst_ptr: *mut u32 = T::regs().din().as_ptr(); |
| 1839 | let num_words = blocks.len(); | ||
| 1840 | let src_ptr: *const [u32] = ptr::slice_from_raw_parts(blocks.as_ptr().cast(), num_words); | ||
| 1841 | let options = TransferOptions { | 1836 | let options = TransferOptions { |
| 1842 | #[cfg(not(gpdma))] | 1837 | #[cfg(not(gpdma))] |
| 1843 | priority: crate::dma::Priority::High, | 1838 | priority: crate::dma::Priority::High, |
| 1844 | ..Default::default() | 1839 | ..Default::default() |
| 1845 | }; | 1840 | }; |
| 1846 | let dma_transfer = unsafe { dma.write_raw(src_ptr, dst_ptr, options) }; | 1841 | let dma_transfer = unsafe { dma.write_raw(blocks, dst_ptr, options) }; |
| 1847 | T::regs().dmacr().modify(|w| w.set_dien(true)); | 1842 | T::regs().dmacr().modify(|w| w.set_dien(true)); |
| 1848 | // Wait for the transfer to complete. | 1843 | // Wait for the transfer to complete. |
| 1849 | dma_transfer.await; | 1844 | dma_transfer.await; |
| @@ -1857,14 +1852,12 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { | |||
| 1857 | assert_eq!(blocks.len() % block_size, 0); | 1852 | assert_eq!(blocks.len() % block_size, 0); |
| 1858 | // Configure DMA to get output from crypto core. | 1853 | // Configure DMA to get output from crypto core. |
| 1859 | let src_ptr = T::regs().dout().as_ptr(); | 1854 | let src_ptr = T::regs().dout().as_ptr(); |
| 1860 | let num_words = blocks.len() / 4; | ||
| 1861 | let dst_ptr = ptr::slice_from_raw_parts_mut(blocks.as_mut_ptr().cast(), num_words); | ||
| 1862 | let options = TransferOptions { | 1855 | let options = TransferOptions { |
| 1863 | #[cfg(not(gpdma))] | 1856 | #[cfg(not(gpdma))] |
| 1864 | priority: crate::dma::Priority::VeryHigh, | 1857 | priority: crate::dma::Priority::VeryHigh, |
| 1865 | ..Default::default() | 1858 | ..Default::default() |
| 1866 | }; | 1859 | }; |
| 1867 | let dma_transfer = unsafe { dma.read_raw(src_ptr, dst_ptr, options) }; | 1860 | let dma_transfer = unsafe { dma.read_raw(src_ptr, blocks, options) }; |
| 1868 | T::regs().dmacr().modify(|w| w.set_doen(true)); | 1861 | T::regs().dmacr().modify(|w| w.set_doen(true)); |
| 1869 | // Wait for the transfer to complete. | 1862 | // Wait for the transfer to complete. |
| 1870 | dma_transfer.await; | 1863 | dma_transfer.await; |
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 30046849b..d8f1f96f2 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs | |||
| @@ -403,6 +403,46 @@ impl<'d, T: Instance> Dac<'d, T, Async> { | |||
| 403 | Mode::NormalExternalBuffered, | 403 | Mode::NormalExternalBuffered, |
| 404 | ) | 404 | ) |
| 405 | } | 405 | } |
| 406 | /// Create a new `Dac` instance with external output pins and unbuffered mode. | ||
| 407 | /// | ||
| 408 | /// This function consumes the underlying DAC peripheral and allows access to both channels. | ||
| 409 | /// The channels are configured for external output with the buffer disabled. | ||
| 410 | /// | ||
| 411 | /// The channels are enabled on creation and begin to drive their output pins. | ||
| 412 | /// Note that some methods, such as `set_trigger()` and `set_mode()`, will | ||
| 413 | /// disable the channel; you must re-enable them with `enable()`. | ||
| 414 | /// | ||
| 415 | /// By default, triggering is disabled, but it can be enabled using the `set_trigger()` | ||
| 416 | /// method on the underlying channels. | ||
| 417 | /// | ||
| 418 | /// # Arguments | ||
| 419 | /// | ||
| 420 | /// * `peri` - The DAC peripheral instance. | ||
| 421 | /// * `dma_ch1` - The DMA channel for DAC channel 1. | ||
| 422 | /// * `dma_ch2` - The DMA channel for DAC channel 2. | ||
| 423 | /// * `pin_ch1` - The GPIO pin for DAC channel 1 output. | ||
| 424 | /// * `pin_ch2` - The GPIO pin for DAC channel 2 output. | ||
| 425 | /// | ||
| 426 | /// # Returns | ||
| 427 | /// | ||
| 428 | /// A new `Dac` instance in unbuffered mode. | ||
| 429 | pub fn new_unbuffered( | ||
| 430 | peri: Peri<'d, T>, | ||
| 431 | dma_ch1: Peri<'d, impl Dma<T, Ch1>>, | ||
| 432 | dma_ch2: Peri<'d, impl Dma<T, Ch2>>, | ||
| 433 | pin_ch1: Peri<'d, impl DacPin<T, Ch1> + crate::gpio::Pin>, | ||
| 434 | pin_ch2: Peri<'d, impl DacPin<T, Ch2> + crate::gpio::Pin>, | ||
| 435 | ) -> Self { | ||
| 436 | pin_ch1.set_as_analog(); | ||
| 437 | pin_ch2.set_as_analog(); | ||
| 438 | Self::new_inner( | ||
| 439 | peri, | ||
| 440 | new_dma!(dma_ch1), | ||
| 441 | new_dma!(dma_ch2), | ||
| 442 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] | ||
| 443 | Mode::NormalExternalUnbuffered, | ||
| 444 | ) | ||
| 445 | } | ||
| 406 | 446 | ||
| 407 | /// Create a new `Dac` instance where the external output pins are not used, | 447 | /// Create a new `Dac` instance where the external output pins are not used, |
| 408 | /// so the DAC can only be used to generate internal signals but the GPIO | 448 | /// so the DAC can only be used to generate internal signals but the GPIO |
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 7dbbe7b72..464823bfc 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs | |||
| @@ -341,9 +341,12 @@ impl AnyChannel { | |||
| 341 | mem_len: usize, | 341 | mem_len: usize, |
| 342 | incr_mem: bool, | 342 | incr_mem: bool, |
| 343 | mem_size: WordSize, | 343 | mem_size: WordSize, |
| 344 | peripheral_size: WordSize, | 344 | peri_size: WordSize, |
| 345 | options: TransferOptions, | 345 | options: TransferOptions, |
| 346 | ) { | 346 | ) { |
| 347 | // "Preceding reads and writes cannot be moved past subsequent writes." | ||
| 348 | fence(Ordering::SeqCst); | ||
| 349 | |||
| 347 | let info = self.info(); | 350 | let info = self.info(); |
| 348 | #[cfg(feature = "_dual-core")] | 351 | #[cfg(feature = "_dual-core")] |
| 349 | { | 352 | { |
| @@ -354,28 +357,48 @@ impl AnyChannel { | |||
| 354 | #[cfg(dmamux)] | 357 | #[cfg(dmamux)] |
| 355 | super::dmamux::configure_dmamux(&info.dmamux, _request); | 358 | super::dmamux::configure_dmamux(&info.dmamux, _request); |
| 356 | 359 | ||
| 357 | assert!(mem_len > 0 && mem_len <= 0xFFFF); | ||
| 358 | |||
| 359 | match self.info().dma { | 360 | match self.info().dma { |
| 360 | #[cfg(dma)] | 361 | #[cfg(dma)] |
| 361 | DmaInfo::Dma(r) => { | 362 | DmaInfo::Dma(r) => { |
| 362 | let state: &ChannelState = &STATE[self.id as usize]; | 363 | let state: &ChannelState = &STATE[self.id as usize]; |
| 363 | let ch = r.st(info.num); | 364 | let ch = r.st(info.num); |
| 364 | 365 | ||
| 365 | // "Preceding reads and writes cannot be moved past subsequent writes." | ||
| 366 | fence(Ordering::SeqCst); | ||
| 367 | |||
| 368 | state.complete_count.store(0, Ordering::Release); | 366 | state.complete_count.store(0, Ordering::Release); |
| 369 | self.clear_irqs(); | 367 | self.clear_irqs(); |
| 370 | 368 | ||
| 369 | // NDTR is the number of transfers in the *peripheral* word size. | ||
| 370 | // ex: if mem_size=1, peri_size=4 and ndtr=3 it'll do 12 mem transfers, 3 peri transfers. | ||
| 371 | let ndtr = match (mem_size, peri_size) { | ||
| 372 | (WordSize::FourBytes, WordSize::OneByte) => mem_len * 4, | ||
| 373 | (WordSize::FourBytes, WordSize::TwoBytes) | (WordSize::TwoBytes, WordSize::OneByte) => mem_len * 2, | ||
| 374 | (WordSize::FourBytes, WordSize::FourBytes) | ||
| 375 | | (WordSize::TwoBytes, WordSize::TwoBytes) | ||
| 376 | | (WordSize::OneByte, WordSize::OneByte) => mem_len, | ||
| 377 | (WordSize::TwoBytes, WordSize::FourBytes) | (WordSize::OneByte, WordSize::TwoBytes) => { | ||
| 378 | assert!(mem_len % 2 == 0); | ||
| 379 | mem_len / 2 | ||
| 380 | } | ||
| 381 | (WordSize::OneByte, WordSize::FourBytes) => { | ||
| 382 | assert!(mem_len % 4 == 0); | ||
| 383 | mem_len / 4 | ||
| 384 | } | ||
| 385 | }; | ||
| 386 | |||
| 387 | assert!(ndtr > 0 && ndtr <= 0xFFFF); | ||
| 388 | |||
| 371 | ch.par().write_value(peri_addr as u32); | 389 | ch.par().write_value(peri_addr as u32); |
| 372 | ch.m0ar().write_value(mem_addr as u32); | 390 | ch.m0ar().write_value(mem_addr as u32); |
| 373 | ch.ndtr().write_value(pac::dma::regs::Ndtr(mem_len as _)); | 391 | ch.ndtr().write_value(pac::dma::regs::Ndtr(ndtr as _)); |
| 374 | ch.fcr().write(|w| { | 392 | ch.fcr().write(|w| { |
| 375 | if let Some(fth) = options.fifo_threshold { | 393 | if let Some(fth) = options.fifo_threshold { |
| 376 | // FIFO mode | 394 | // FIFO mode |
| 377 | w.set_dmdis(pac::dma::vals::Dmdis::DISABLED); | 395 | w.set_dmdis(pac::dma::vals::Dmdis::DISABLED); |
| 378 | w.set_fth(fth.into()); | 396 | w.set_fth(fth.into()); |
| 397 | } else if mem_size != peri_size { | ||
| 398 | // force FIFO mode if msize != psize | ||
| 399 | // packing/unpacking doesn't work in direct mode. | ||
| 400 | w.set_dmdis(pac::dma::vals::Dmdis::DISABLED); | ||
| 401 | w.set_fth(FifoThreshold::Half.into()); | ||
| 379 | } else { | 402 | } else { |
| 380 | // Direct mode | 403 | // Direct mode |
| 381 | w.set_dmdis(pac::dma::vals::Dmdis::ENABLED); | 404 | w.set_dmdis(pac::dma::vals::Dmdis::ENABLED); |
| @@ -384,7 +407,7 @@ impl AnyChannel { | |||
| 384 | ch.cr().write(|w| { | 407 | ch.cr().write(|w| { |
| 385 | w.set_dir(dir.into()); | 408 | w.set_dir(dir.into()); |
| 386 | w.set_msize(mem_size.into()); | 409 | w.set_msize(mem_size.into()); |
| 387 | w.set_psize(peripheral_size.into()); | 410 | w.set_psize(peri_size.into()); |
| 388 | w.set_pl(options.priority.into()); | 411 | w.set_pl(options.priority.into()); |
| 389 | w.set_minc(incr_mem); | 412 | w.set_minc(incr_mem); |
| 390 | w.set_pinc(false); | 413 | w.set_pinc(false); |
| @@ -404,6 +427,8 @@ impl AnyChannel { | |||
| 404 | } | 427 | } |
| 405 | #[cfg(bdma)] | 428 | #[cfg(bdma)] |
| 406 | DmaInfo::Bdma(r) => { | 429 | DmaInfo::Bdma(r) => { |
| 430 | assert!(mem_len > 0 && mem_len <= 0xFFFF); | ||
| 431 | |||
| 407 | #[cfg(bdma_v2)] | 432 | #[cfg(bdma_v2)] |
| 408 | critical_section::with(|_| r.cselr().modify(|w| w.set_cs(info.num, _request))); | 433 | critical_section::with(|_| r.cselr().modify(|w| w.set_cs(info.num, _request))); |
| 409 | 434 | ||
| @@ -417,7 +442,7 @@ impl AnyChannel { | |||
| 417 | ch.mar().write_value(mem_addr as u32); | 442 | ch.mar().write_value(mem_addr as u32); |
| 418 | ch.ndtr().write(|w| w.set_ndt(mem_len as u16)); | 443 | ch.ndtr().write(|w| w.set_ndt(mem_len as u16)); |
| 419 | ch.cr().write(|w| { | 444 | ch.cr().write(|w| { |
| 420 | w.set_psize(peripheral_size.into()); | 445 | w.set_psize(peri_size.into()); |
| 421 | w.set_msize(mem_size.into()); | 446 | w.set_msize(mem_size.into()); |
| 422 | w.set_minc(incr_mem); | 447 | w.set_minc(incr_mem); |
| 423 | w.set_dir(dir.into()); | 448 | w.set_dir(dir.into()); |
| @@ -587,11 +612,11 @@ impl<'a> Transfer<'a> { | |||
| 587 | } | 612 | } |
| 588 | 613 | ||
| 589 | /// Create a new read DMA transfer (peripheral to memory), using raw pointers. | 614 | /// Create a new read DMA transfer (peripheral to memory), using raw pointers. |
| 590 | pub unsafe fn new_read_raw<W: Word>( | 615 | pub unsafe fn new_read_raw<MW: Word, PW: Word>( |
| 591 | channel: Peri<'a, impl Channel>, | 616 | channel: Peri<'a, impl Channel>, |
| 592 | request: Request, | 617 | request: Request, |
| 593 | peri_addr: *mut W, | 618 | peri_addr: *mut PW, |
| 594 | buf: *mut [W], | 619 | buf: *mut [MW], |
| 595 | options: TransferOptions, | 620 | options: TransferOptions, |
| 596 | ) -> Self { | 621 | ) -> Self { |
| 597 | Self::new_inner( | 622 | Self::new_inner( |
| @@ -599,11 +624,11 @@ impl<'a> Transfer<'a> { | |||
| 599 | request, | 624 | request, |
| 600 | Dir::PeripheralToMemory, | 625 | Dir::PeripheralToMemory, |
| 601 | peri_addr as *const u32, | 626 | peri_addr as *const u32, |
| 602 | buf as *mut W as *mut u32, | 627 | buf as *mut MW as *mut u32, |
| 603 | buf.len(), | 628 | buf.len(), |
| 604 | true, | 629 | true, |
| 605 | W::size(), | 630 | MW::size(), |
| 606 | W::size(), | 631 | PW::size(), |
| 607 | options, | 632 | options, |
| 608 | ) | 633 | ) |
| 609 | } | 634 | } |
| @@ -672,22 +697,14 @@ impl<'a> Transfer<'a> { | |||
| 672 | mem_addr: *mut u32, | 697 | mem_addr: *mut u32, |
| 673 | mem_len: usize, | 698 | mem_len: usize, |
| 674 | incr_mem: bool, | 699 | incr_mem: bool, |
| 675 | data_size: WordSize, | 700 | mem_size: WordSize, |
| 676 | peripheral_size: WordSize, | 701 | peri_size: WordSize, |
| 677 | options: TransferOptions, | 702 | options: TransferOptions, |
| 678 | ) -> Self { | 703 | ) -> Self { |
| 679 | assert!(mem_len > 0 && mem_len <= 0xFFFF); | 704 | assert!(mem_len > 0 && mem_len <= 0xFFFF); |
| 680 | 705 | ||
| 681 | channel.configure( | 706 | channel.configure( |
| 682 | _request, | 707 | _request, dir, peri_addr, mem_addr, mem_len, incr_mem, mem_size, peri_size, options, |
| 683 | dir, | ||
| 684 | peri_addr, | ||
| 685 | mem_addr, | ||
| 686 | mem_len, | ||
| 687 | incr_mem, | ||
| 688 | data_size, | ||
| 689 | peripheral_size, | ||
| 690 | options, | ||
| 691 | ); | 708 | ); |
| 692 | channel.start(); | 709 | channel.start(); |
| 693 | Self { channel } | 710 | Self { channel } |
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index ade70fb55..151e4ab9f 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs | |||
| @@ -125,11 +125,11 @@ impl<'a> Transfer<'a> { | |||
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | /// Create a new read DMA transfer (peripheral to memory), using raw pointers. | 127 | /// Create a new read DMA transfer (peripheral to memory), using raw pointers. |
| 128 | pub unsafe fn new_read_raw<W: Word>( | 128 | pub unsafe fn new_read_raw<MW: Word, PW: Word>( |
| 129 | channel: Peri<'a, impl Channel>, | 129 | channel: Peri<'a, impl Channel>, |
| 130 | request: Request, | 130 | request: Request, |
| 131 | peri_addr: *mut W, | 131 | peri_addr: *mut PW, |
| 132 | buf: *mut [W], | 132 | buf: *mut [MW], |
| 133 | options: TransferOptions, | 133 | options: TransferOptions, |
| 134 | ) -> Self { | 134 | ) -> Self { |
| 135 | Self::new_inner( | 135 | Self::new_inner( |
| @@ -137,11 +137,11 @@ impl<'a> Transfer<'a> { | |||
| 137 | request, | 137 | request, |
| 138 | Dir::PeripheralToMemory, | 138 | Dir::PeripheralToMemory, |
| 139 | peri_addr as *const u32, | 139 | peri_addr as *const u32, |
| 140 | buf as *mut W as *mut u32, | 140 | buf as *mut MW as *mut u32, |
| 141 | buf.len(), | 141 | buf.len(), |
| 142 | true, | 142 | true, |
| 143 | W::size(), | 143 | PW::size(), |
| 144 | W::size(), | 144 | MW::size(), |
| 145 | options, | 145 | options, |
| 146 | ) | 146 | ) |
| 147 | } | 147 | } |
diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs index 8bf89e2fe..3245887c1 100644 --- a/embassy-stm32/src/dma/util.rs +++ b/embassy-stm32/src/dma/util.rs | |||
| @@ -20,10 +20,10 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 20 | Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options) | 20 | Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options) |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | pub unsafe fn read_raw<'a, W: Word>( | 23 | pub unsafe fn read_raw<'a, MW: Word, PW: Word>( |
| 24 | &'a mut self, | 24 | &'a mut self, |
| 25 | peri_addr: *mut W, | 25 | peri_addr: *mut PW, |
| 26 | buf: *mut [W], | 26 | buf: *mut [MW], |
| 27 | options: TransferOptions, | 27 | options: TransferOptions, |
| 28 | ) -> Transfer<'a> { | 28 | ) -> Transfer<'a> { |
| 29 | Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options) | 29 | Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options) |
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 01e321bce..b9746231f 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs | |||
| @@ -122,7 +122,10 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 122 | 122 | ||
| 123 | // Select RMII (Reduced Media Independent Interface) | 123 | // Select RMII (Reduced Media Independent Interface) |
| 124 | // Must be done prior to enabling peripheral clock | 124 | // Must be done prior to enabling peripheral clock |
| 125 | AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); | 125 | AFIO.mapr().modify(|w| { |
| 126 | w.set_mii_rmii_sel(true); | ||
| 127 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 128 | }); | ||
| 126 | 129 | ||
| 127 | RCC.ahbenr().modify(|w| { | 130 | RCC.ahbenr().modify(|w| { |
| 128 | w.set_ethen(true); | 131 | w.set_ethen(true); |
| @@ -316,7 +319,10 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 316 | 319 | ||
| 317 | // Select MII (Media Independent Interface) | 320 | // Select MII (Media Independent Interface) |
| 318 | // Must be done prior to enabling peripheral clock | 321 | // Must be done prior to enabling peripheral clock |
| 319 | AFIO.mapr().modify(|w| w.set_mii_rmii_sel(false)); | 322 | AFIO.mapr().modify(|w| { |
| 323 | w.set_mii_rmii_sel(false); | ||
| 324 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 325 | }); | ||
| 320 | 326 | ||
| 321 | RCC.ahbenr().modify(|w| { | 327 | RCC.ahbenr().modify(|w| { |
| 322 | w.set_ethen(true); | 328 | w.set_ethen(true); |
diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 131caa195..6c3d4b422 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs | |||
| @@ -30,19 +30,19 @@ pub(crate) unsafe fn enable_blocking_write() { | |||
| 30 | 30 | ||
| 31 | #[cfg(feature = "trustzone-secure")] | 31 | #[cfg(feature = "trustzone-secure")] |
| 32 | pac::FLASH.seccr().write(|w| { | 32 | pac::FLASH.seccr().write(|w| { |
| 33 | w.set_pg(pac::flash::vals::SeccrPg::B_0X1); | 33 | w.set_pg(true); |
| 34 | }); | 34 | }); |
| 35 | #[cfg(not(feature = "trustzone-secure"))] | 35 | #[cfg(not(feature = "trustzone-secure"))] |
| 36 | pac::FLASH.nscr().write(|w| { | 36 | pac::FLASH.nscr().write(|w| { |
| 37 | w.set_pg(pac::flash::vals::NscrPg::B_0X1); | 37 | w.set_pg(true); |
| 38 | }); | 38 | }); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | pub(crate) unsafe fn disable_blocking_write() { | 41 | pub(crate) unsafe fn disable_blocking_write() { |
| 42 | #[cfg(feature = "trustzone-secure")] | 42 | #[cfg(feature = "trustzone-secure")] |
| 43 | pac::FLASH.seccr().write(|w| w.set_pg(pac::flash::vals::SeccrPg::B_0X0)); | 43 | pac::FLASH.seccr().write(|w| w.set_pg(false)); |
| 44 | #[cfg(not(feature = "trustzone-secure"))] | 44 | #[cfg(not(feature = "trustzone-secure"))] |
| 45 | pac::FLASH.nscr().write(|w| w.set_pg(pac::flash::vals::NscrPg::B_0X0)); | 45 | pac::FLASH.nscr().write(|w| w.set_pg(false)); |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | 48 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { |
| @@ -65,19 +65,19 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 65 | w.set_pnb(sector.index_in_bank); | 65 | w.set_pnb(sector.index_in_bank); |
| 66 | // TODO: add check for bank swap | 66 | // TODO: add check for bank swap |
| 67 | w.set_bker(match sector.bank { | 67 | w.set_bker(match sector.bank { |
| 68 | FlashBank::Bank1 => pac::flash::vals::SeccrBker::B_0X0, | 68 | FlashBank::Bank1 => false, |
| 69 | FlashBank::Bank2 => pac::flash::vals::SeccrBker::B_0X1, | 69 | FlashBank::Bank2 => true, |
| 70 | _ => unreachable!(), | 70 | _ => unreachable!(), |
| 71 | }); | 71 | }); |
| 72 | }); | 72 | }); |
| 73 | #[cfg(not(feature = "trustzone-secure"))] | 73 | #[cfg(not(feature = "trustzone-secure"))] |
| 74 | pac::FLASH.nscr().modify(|w| { | 74 | pac::FLASH.nscr().modify(|w| { |
| 75 | w.set_per(pac::flash::vals::NscrPer::B_0X1); | 75 | w.set_per(true); |
| 76 | w.set_pnb(sector.index_in_bank); | 76 | w.set_pnb(sector.index_in_bank); |
| 77 | // TODO: add check for bank swap | 77 | // TODO: add check for bank swap |
| 78 | w.set_bker(match sector.bank { | 78 | w.set_bker(match sector.bank { |
| 79 | FlashBank::Bank1 => pac::flash::vals::NscrBker::B_0X0, | 79 | FlashBank::Bank1 => false, |
| 80 | FlashBank::Bank2 => pac::flash::vals::NscrBker::B_0X1, | 80 | FlashBank::Bank2 => true, |
| 81 | _ => unreachable!(), | 81 | _ => unreachable!(), |
| 82 | }); | 82 | }); |
| 83 | }); | 83 | }); |
| @@ -93,13 +93,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 93 | 93 | ||
| 94 | let ret: Result<(), Error> = blocking_wait_ready(); | 94 | let ret: Result<(), Error> = blocking_wait_ready(); |
| 95 | #[cfg(feature = "trustzone-secure")] | 95 | #[cfg(feature = "trustzone-secure")] |
| 96 | pac::FLASH | 96 | pac::FLASH.seccr().modify(|w| w.set_per(false)); |
| 97 | .seccr() | ||
| 98 | .modify(|w| w.set_per(pac::flash::vals::SeccrPer::B_0X0)); | ||
| 99 | #[cfg(not(feature = "trustzone-secure"))] | 97 | #[cfg(not(feature = "trustzone-secure"))] |
| 100 | pac::FLASH | 98 | pac::FLASH.nscr().modify(|w| w.set_per(false)); |
| 101 | .nscr() | ||
| 102 | .modify(|w| w.set_per(pac::flash::vals::NscrPer::B_0X0)); | ||
| 103 | clear_all_err(); | 99 | clear_all_err(); |
| 104 | ret | 100 | ret |
| 105 | } | 101 | } |
diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 31527bcdb..573a1851d 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | use embassy_hal_internal::PeripheralType; | 3 | use embassy_hal_internal::PeripheralType; |
| 4 | 4 | ||
| 5 | use crate::pac; | 5 | use crate::pac; |
| 6 | use crate::rcc::RccPeripheral; | 6 | use crate::rcc::{self, RccPeripheral}; |
| 7 | // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. | 7 | // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. |
| 8 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, | 8 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, |
| 9 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), | 9 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), |
| @@ -46,7 +46,7 @@ pub enum CoreId { | |||
| 46 | #[inline(always)] | 46 | #[inline(always)] |
| 47 | pub fn get_current_coreid() -> CoreId { | 47 | pub fn get_current_coreid() -> CoreId { |
| 48 | let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; | 48 | let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; |
| 49 | match cpuid & 0x000000F0 { | 49 | match (cpuid & 0x000000F0) >> 4 { |
| 50 | #[cfg(any(stm32wb, stm32wl))] | 50 | #[cfg(any(stm32wb, stm32wl))] |
| 51 | 0x0 => CoreId::Core1, | 51 | 0x0 => CoreId::Core1, |
| 52 | 52 | ||
| @@ -80,6 +80,8 @@ pub struct HardwareSemaphore<'d, T: Instance> { | |||
| 80 | impl<'d, T: Instance> HardwareSemaphore<'d, T> { | 80 | impl<'d, T: Instance> HardwareSemaphore<'d, T> { |
| 81 | /// Creates a new HardwareSemaphore instance. | 81 | /// Creates a new HardwareSemaphore instance. |
| 82 | pub fn new(peripheral: Peri<'d, T>) -> Self { | 82 | pub fn new(peripheral: Peri<'d, T>) -> Self { |
| 83 | rcc::enable_and_reset::<T>(); | ||
| 84 | |||
| 83 | HardwareSemaphore { _peri: peripheral } | 85 | HardwareSemaphore { _peri: peripheral } |
| 84 | } | 86 | } |
| 85 | 87 | ||
diff --git a/embassy-stm32/src/i2c/config.rs b/embassy-stm32/src/i2c/config.rs new file mode 100644 index 000000000..daae43bcd --- /dev/null +++ b/embassy-stm32/src/i2c/config.rs | |||
| @@ -0,0 +1,170 @@ | |||
| 1 | #[cfg(gpio_v2)] | ||
| 2 | use crate::gpio::Pull; | ||
| 3 | use crate::gpio::{AfType, OutputType, Speed}; | ||
| 4 | |||
| 5 | #[repr(u8)] | ||
| 6 | #[derive(Copy, Clone)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | /// Bits of the I2C OA2 register to mask out. | ||
| 9 | pub enum AddrMask { | ||
| 10 | /// No mask | ||
| 11 | NOMASK, | ||
| 12 | /// OA2\[1\] is masked and don’t care. Only OA2\[7:2\] are compared. | ||
| 13 | MASK1, | ||
| 14 | /// OA2\[2:1\] are masked and don’t care. Only OA2\[7:3\] are compared. | ||
| 15 | MASK2, | ||
| 16 | /// OA2\[3:1\] are masked and don’t care. Only OA2\[7:4\] are compared. | ||
| 17 | MASK3, | ||
| 18 | /// OA2\[4:1\] are masked and don’t care. Only OA2\[7:5\] are compared. | ||
| 19 | MASK4, | ||
| 20 | /// OA2\[5:1\] are masked and don’t care. Only OA2\[7:6\] are compared. | ||
| 21 | MASK5, | ||
| 22 | /// OA2\[6:1\] are masked and don’t care. Only OA2\[7:6\] are compared. | ||
| 23 | MASK6, | ||
| 24 | /// OA2\[7:1\] are masked and don’t care. No comparison is done, and all (except reserved) 7-bit received addresses are acknowledged | ||
| 25 | MASK7, | ||
| 26 | } | ||
| 27 | |||
| 28 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 29 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 30 | /// An I2C address. Either 7 or 10 bit. | ||
| 31 | pub enum Address { | ||
| 32 | /// A 7 bit address | ||
| 33 | SevenBit(u8), | ||
| 34 | /// A 10 bit address. | ||
| 35 | /// | ||
| 36 | /// When using an address to configure the Own Address, only the OA1 register can be set to a 10-bit address. | ||
| 37 | TenBit(u16), | ||
| 38 | } | ||
| 39 | impl From<u8> for Address { | ||
| 40 | fn from(value: u8) -> Self { | ||
| 41 | Address::SevenBit(value) | ||
| 42 | } | ||
| 43 | } | ||
| 44 | impl From<u16> for Address { | ||
| 45 | fn from(value: u16) -> Self { | ||
| 46 | assert!(value < 0x400, "Ten bit address must be less than 0x400"); | ||
| 47 | Address::TenBit(value) | ||
| 48 | } | ||
| 49 | } | ||
| 50 | impl Address { | ||
| 51 | /// Get the inner address as a u16. | ||
| 52 | /// | ||
| 53 | /// For 7 bit addresses, the u8 that was used to store the address is returned as a u16. | ||
| 54 | pub fn addr(&self) -> u16 { | ||
| 55 | match self { | ||
| 56 | Address::SevenBit(addr) => *addr as u16, | ||
| 57 | Address::TenBit(addr) => *addr, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | #[derive(Copy, Clone)] | ||
| 63 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 64 | /// The second Own Address register. | ||
| 65 | pub struct OA2 { | ||
| 66 | /// The address. | ||
| 67 | pub addr: u8, | ||
| 68 | /// The bit mask that will affect how the own address 2 register is compared. | ||
| 69 | pub mask: AddrMask, | ||
| 70 | } | ||
| 71 | |||
| 72 | #[derive(Copy, Clone)] | ||
| 73 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 74 | /// The Own Address(es) of the I2C peripheral. | ||
| 75 | pub enum OwnAddresses { | ||
| 76 | /// Configuration for only the OA1 register. | ||
| 77 | OA1(Address), | ||
| 78 | /// Configuration for only the OA2 register. | ||
| 79 | OA2(OA2), | ||
| 80 | /// Configuration for both the OA1 and OA2 registers. | ||
| 81 | Both { | ||
| 82 | /// The [Address] for the OA1 register. | ||
| 83 | oa1: Address, | ||
| 84 | /// The [OA2] configuration. | ||
| 85 | oa2: OA2, | ||
| 86 | }, | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Slave Configuration | ||
| 90 | #[derive(Copy, Clone)] | ||
| 91 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 92 | pub struct SlaveAddrConfig { | ||
| 93 | /// Target Address(es) | ||
| 94 | pub addr: OwnAddresses, | ||
| 95 | /// Control if the peripheral should respond to the general call address | ||
| 96 | pub general_call: bool, | ||
| 97 | } | ||
| 98 | impl SlaveAddrConfig { | ||
| 99 | /// Create a new slave address configuration with only the OA1 register set in 7 bit mode and the general call disabled. | ||
| 100 | pub fn basic(addr: u8) -> Self { | ||
| 101 | Self { | ||
| 102 | addr: OwnAddresses::OA1(Address::SevenBit(addr)), | ||
| 103 | general_call: false, | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | /// I2C config | ||
| 109 | #[non_exhaustive] | ||
| 110 | #[derive(Copy, Clone)] | ||
| 111 | pub struct Config { | ||
| 112 | /// Enable internal pullup on SDA. | ||
| 113 | /// | ||
| 114 | /// Using external pullup resistors is recommended for I2C. If you do | ||
| 115 | /// have external pullups you should not enable this. | ||
| 116 | #[cfg(gpio_v2)] | ||
| 117 | pub sda_pullup: bool, | ||
| 118 | /// Enable internal pullup on SCL. | ||
| 119 | /// | ||
| 120 | /// Using external pullup resistors is recommended for I2C. If you do | ||
| 121 | /// have external pullups you should not enable this. | ||
| 122 | #[cfg(gpio_v2)] | ||
| 123 | pub scl_pullup: bool, | ||
| 124 | /// Timeout. | ||
| 125 | #[cfg(feature = "time")] | ||
| 126 | pub timeout: embassy_time::Duration, | ||
| 127 | } | ||
| 128 | |||
| 129 | impl Default for Config { | ||
| 130 | fn default() -> Self { | ||
| 131 | Self { | ||
| 132 | #[cfg(gpio_v2)] | ||
| 133 | sda_pullup: false, | ||
| 134 | #[cfg(gpio_v2)] | ||
| 135 | scl_pullup: false, | ||
| 136 | #[cfg(feature = "time")] | ||
| 137 | timeout: embassy_time::Duration::from_millis(1000), | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | impl Config { | ||
| 143 | pub(super) fn scl_af(&self) -> AfType { | ||
| 144 | #[cfg(gpio_v1)] | ||
| 145 | return AfType::output(OutputType::OpenDrain, Speed::Medium); | ||
| 146 | #[cfg(gpio_v2)] | ||
| 147 | return AfType::output_pull( | ||
| 148 | OutputType::OpenDrain, | ||
| 149 | Speed::Medium, | ||
| 150 | match self.scl_pullup { | ||
| 151 | true => Pull::Up, | ||
| 152 | false => Pull::Down, | ||
| 153 | }, | ||
| 154 | ); | ||
| 155 | } | ||
| 156 | |||
| 157 | pub(super) fn sda_af(&self) -> AfType { | ||
| 158 | #[cfg(gpio_v1)] | ||
| 159 | return AfType::output(OutputType::OpenDrain, Speed::Medium); | ||
| 160 | #[cfg(gpio_v2)] | ||
| 161 | return AfType::output_pull( | ||
| 162 | OutputType::OpenDrain, | ||
| 163 | Speed::Medium, | ||
| 164 | match self.sda_pullup { | ||
| 165 | true => Pull::Up, | ||
| 166 | false => Pull::Down, | ||
| 167 | }, | ||
| 168 | ); | ||
| 169 | } | ||
| 170 | } | ||
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 1689fdb84..825dd240c 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs | |||
| @@ -5,19 +5,22 @@ | |||
| 5 | #[cfg_attr(any(i2c_v2, i2c_v3), path = "v2.rs")] | 5 | #[cfg_attr(any(i2c_v2, i2c_v3), path = "v2.rs")] |
| 6 | mod _version; | 6 | mod _version; |
| 7 | 7 | ||
| 8 | mod config; | ||
| 9 | |||
| 8 | use core::future::Future; | 10 | use core::future::Future; |
| 9 | use core::iter; | 11 | use core::iter; |
| 10 | use core::marker::PhantomData; | 12 | use core::marker::PhantomData; |
| 11 | 13 | ||
| 14 | pub use config::*; | ||
| 12 | use embassy_hal_internal::Peri; | 15 | use embassy_hal_internal::Peri; |
| 13 | use embassy_sync::waitqueue::AtomicWaker; | 16 | use embassy_sync::waitqueue::AtomicWaker; |
| 14 | #[cfg(feature = "time")] | 17 | #[cfg(feature = "time")] |
| 15 | use embassy_time::{Duration, Instant}; | 18 | use embassy_time::{Duration, Instant}; |
| 19 | use mode::MasterMode; | ||
| 20 | pub use mode::{Master, MultiMaster}; | ||
| 16 | 21 | ||
| 17 | use crate::dma::ChannelAndRequest; | 22 | use crate::dma::ChannelAndRequest; |
| 18 | #[cfg(gpio_v2)] | 23 | use crate::gpio::{AnyPin, SealedPin as _}; |
| 19 | use crate::gpio::Pull; | ||
| 20 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed}; | ||
| 21 | use crate::interrupt::typelevel::Interrupt; | 24 | use crate::interrupt::typelevel::Interrupt; |
| 22 | use crate::mode::{Async, Blocking, Mode}; | 25 | use crate::mode::{Async, Blocking, Mode}; |
| 23 | use crate::rcc::{RccInfo, SealedRccPeripheral}; | 26 | use crate::rcc::{RccInfo, SealedRccPeripheral}; |
| @@ -62,85 +65,89 @@ impl core::fmt::Display for Error { | |||
| 62 | 65 | ||
| 63 | impl core::error::Error for Error {} | 66 | impl core::error::Error for Error {} |
| 64 | 67 | ||
| 65 | /// I2C config | 68 | /// I2C modes |
| 66 | #[non_exhaustive] | 69 | pub mod mode { |
| 67 | #[derive(Copy, Clone)] | 70 | trait SealedMode {} |
| 68 | pub struct Config { | 71 | |
| 69 | /// Enable internal pullup on SDA. | 72 | /// Trait for I2C master operations. |
| 70 | /// | 73 | #[allow(private_bounds)] |
| 71 | /// Using external pullup resistors is recommended for I2C. If you do | 74 | pub trait MasterMode: SealedMode {} |
| 72 | /// have external pullups you should not enable this. | 75 | |
| 73 | #[cfg(gpio_v2)] | 76 | /// Mode allowing for I2C master operations. |
| 74 | pub sda_pullup: bool, | 77 | pub struct Master; |
| 75 | /// Enable internal pullup on SCL. | 78 | /// Mode allowing for I2C master and slave operations. |
| 76 | /// | 79 | pub struct MultiMaster; |
| 77 | /// Using external pullup resistors is recommended for I2C. If you do | 80 | |
| 78 | /// have external pullups you should not enable this. | 81 | impl SealedMode for Master {} |
| 79 | #[cfg(gpio_v2)] | 82 | impl MasterMode for Master {} |
| 80 | pub scl_pullup: bool, | 83 | |
| 81 | /// Timeout. | 84 | impl SealedMode for MultiMaster {} |
| 82 | #[cfg(feature = "time")] | 85 | impl MasterMode for MultiMaster {} |
| 83 | pub timeout: embassy_time::Duration, | ||
| 84 | } | 86 | } |
| 85 | 87 | ||
| 86 | impl Default for Config { | 88 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 87 | fn default() -> Self { | 89 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 88 | Self { | 90 | /// The command kind to the slave from the master |
| 89 | #[cfg(gpio_v2)] | 91 | pub enum SlaveCommandKind { |
| 90 | sda_pullup: false, | 92 | /// Write to the slave |
| 91 | #[cfg(gpio_v2)] | 93 | Write, |
| 92 | scl_pullup: false, | 94 | /// Read from the slave |
| 93 | #[cfg(feature = "time")] | 95 | Read, |
| 94 | timeout: embassy_time::Duration::from_millis(1000), | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | impl Config { | 98 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 100 | fn scl_af(&self) -> AfType { | 99 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 101 | #[cfg(gpio_v1)] | 100 | /// The command kind to the slave from the master and the address that the slave matched |
| 102 | return AfType::output(OutputType::OpenDrain, Speed::Medium); | 101 | pub struct SlaveCommand { |
| 103 | #[cfg(gpio_v2)] | 102 | /// The kind of command |
| 104 | return AfType::output_pull( | 103 | pub kind: SlaveCommandKind, |
| 105 | OutputType::OpenDrain, | 104 | /// The address that the slave matched |
| 106 | Speed::Medium, | 105 | pub address: Address, |
| 107 | match self.scl_pullup { | 106 | } |
| 108 | true => Pull::Up, | ||
| 109 | false => Pull::None, | ||
| 110 | }, | ||
| 111 | ); | ||
| 112 | } | ||
| 113 | 107 | ||
| 114 | fn sda_af(&self) -> AfType { | 108 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 115 | #[cfg(gpio_v1)] | 109 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 116 | return AfType::output(OutputType::OpenDrain, Speed::Medium); | 110 | /// The status of the slave send operation |
| 117 | #[cfg(gpio_v2)] | 111 | pub enum SendStatus { |
| 118 | return AfType::output_pull( | 112 | /// The slave send operation is done, all bytes have been sent and the master is not requesting more |
| 119 | OutputType::OpenDrain, | 113 | Done, |
| 120 | Speed::Medium, | 114 | /// The slave send operation is done, but there are leftover bytes that the master did not read |
| 121 | match self.sda_pullup { | 115 | LeftoverBytes(usize), |
| 122 | true => Pull::Up, | 116 | } |
| 123 | false => Pull::None, | 117 | |
| 124 | }, | 118 | struct I2CDropGuard<'d> { |
| 125 | ); | 119 | info: &'static Info, |
| 120 | scl: Option<Peri<'d, AnyPin>>, | ||
| 121 | sda: Option<Peri<'d, AnyPin>>, | ||
| 122 | } | ||
| 123 | impl<'d> Drop for I2CDropGuard<'d> { | ||
| 124 | fn drop(&mut self) { | ||
| 125 | if let Some(x) = self.scl.as_ref() { | ||
| 126 | x.set_as_disconnected() | ||
| 127 | } | ||
| 128 | if let Some(x) = self.sda.as_ref() { | ||
| 129 | x.set_as_disconnected() | ||
| 130 | } | ||
| 131 | |||
| 132 | self.info.rcc.disable(); | ||
| 126 | } | 133 | } |
| 127 | } | 134 | } |
| 128 | 135 | ||
| 129 | /// I2C driver. | 136 | /// I2C driver. |
| 130 | pub struct I2c<'d, M: Mode> { | 137 | pub struct I2c<'d, M: Mode, IM: MasterMode> { |
| 131 | info: &'static Info, | 138 | info: &'static Info, |
| 132 | state: &'static State, | 139 | state: &'static State, |
| 133 | kernel_clock: Hertz, | 140 | kernel_clock: Hertz, |
| 134 | scl: Option<Peri<'d, AnyPin>>, | ||
| 135 | sda: Option<Peri<'d, AnyPin>>, | ||
| 136 | tx_dma: Option<ChannelAndRequest<'d>>, | 141 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 137 | rx_dma: Option<ChannelAndRequest<'d>>, | 142 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 138 | #[cfg(feature = "time")] | 143 | #[cfg(feature = "time")] |
| 139 | timeout: Duration, | 144 | timeout: Duration, |
| 140 | _phantom: PhantomData<M>, | 145 | _phantom: PhantomData<M>, |
| 146 | _phantom2: PhantomData<IM>, | ||
| 147 | _drop_guard: I2CDropGuard<'d>, | ||
| 141 | } | 148 | } |
| 142 | 149 | ||
| 143 | impl<'d> I2c<'d, Async> { | 150 | impl<'d> I2c<'d, Async, Master> { |
| 144 | /// Create a new I2C driver. | 151 | /// Create a new I2C driver. |
| 145 | pub fn new<T: Instance>( | 152 | pub fn new<T: Instance>( |
| 146 | peri: Peri<'d, T>, | 153 | peri: Peri<'d, T>, |
| @@ -166,7 +173,7 @@ impl<'d> I2c<'d, Async> { | |||
| 166 | } | 173 | } |
| 167 | } | 174 | } |
| 168 | 175 | ||
| 169 | impl<'d> I2c<'d, Blocking> { | 176 | impl<'d> I2c<'d, Blocking, Master> { |
| 170 | /// Create a new blocking I2C driver. | 177 | /// Create a new blocking I2C driver. |
| 171 | pub fn new_blocking<T: Instance>( | 178 | pub fn new_blocking<T: Instance>( |
| 172 | peri: Peri<'d, T>, | 179 | peri: Peri<'d, T>, |
| @@ -187,7 +194,7 @@ impl<'d> I2c<'d, Blocking> { | |||
| 187 | } | 194 | } |
| 188 | } | 195 | } |
| 189 | 196 | ||
| 190 | impl<'d, M: Mode> I2c<'d, M> { | 197 | impl<'d, M: Mode> I2c<'d, M, Master> { |
| 191 | /// Create a new I2C driver. | 198 | /// Create a new I2C driver. |
| 192 | fn new_inner<T: Instance>( | 199 | fn new_inner<T: Instance>( |
| 193 | _peri: Peri<'d, T>, | 200 | _peri: Peri<'d, T>, |
| @@ -205,15 +212,20 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 205 | info: T::info(), | 212 | info: T::info(), |
| 206 | state: T::state(), | 213 | state: T::state(), |
| 207 | kernel_clock: T::frequency(), | 214 | kernel_clock: T::frequency(), |
| 208 | scl, | ||
| 209 | sda, | ||
| 210 | tx_dma, | 215 | tx_dma, |
| 211 | rx_dma, | 216 | rx_dma, |
| 212 | #[cfg(feature = "time")] | 217 | #[cfg(feature = "time")] |
| 213 | timeout: config.timeout, | 218 | timeout: config.timeout, |
| 214 | _phantom: PhantomData, | 219 | _phantom: PhantomData, |
| 220 | _phantom2: PhantomData, | ||
| 221 | _drop_guard: I2CDropGuard { | ||
| 222 | info: T::info(), | ||
| 223 | scl, | ||
| 224 | sda, | ||
| 225 | }, | ||
| 215 | }; | 226 | }; |
| 216 | this.enable_and_init(freq, config); | 227 | this.enable_and_init(freq, config); |
| 228 | |||
| 217 | this | 229 | this |
| 218 | } | 230 | } |
| 219 | 231 | ||
| @@ -221,7 +233,9 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 221 | self.info.rcc.enable_and_reset(); | 233 | self.info.rcc.enable_and_reset(); |
| 222 | self.init(freq, config); | 234 | self.init(freq, config); |
| 223 | } | 235 | } |
| 236 | } | ||
| 224 | 237 | ||
| 238 | impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | ||
| 225 | fn timeout(&self) -> Timeout { | 239 | fn timeout(&self) -> Timeout { |
| 226 | Timeout { | 240 | Timeout { |
| 227 | #[cfg(feature = "time")] | 241 | #[cfg(feature = "time")] |
| @@ -230,15 +244,6 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 230 | } | 244 | } |
| 231 | } | 245 | } |
| 232 | 246 | ||
| 233 | impl<'d, M: Mode> Drop for I2c<'d, M> { | ||
| 234 | fn drop(&mut self) { | ||
| 235 | self.scl.as_ref().map(|x| x.set_as_disconnected()); | ||
| 236 | self.sda.as_ref().map(|x| x.set_as_disconnected()); | ||
| 237 | |||
| 238 | self.info.rcc.disable() | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | #[derive(Copy, Clone)] | 247 | #[derive(Copy, Clone)] |
| 243 | struct Timeout { | 248 | struct Timeout { |
| 244 | #[cfg(feature = "time")] | 249 | #[cfg(feature = "time")] |
| @@ -347,7 +352,7 @@ foreach_peripheral!( | |||
| 347 | }; | 352 | }; |
| 348 | ); | 353 | ); |
| 349 | 354 | ||
| 350 | impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> { | 355 | impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> { |
| 351 | type Error = Error; | 356 | type Error = Error; |
| 352 | 357 | ||
| 353 | fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { | 358 | fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { |
| @@ -355,7 +360,7 @@ impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> { | |||
| 355 | } | 360 | } |
| 356 | } | 361 | } |
| 357 | 362 | ||
| 358 | impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> { | 363 | impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> { |
| 359 | type Error = Error; | 364 | type Error = Error; |
| 360 | 365 | ||
| 361 | fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { | 366 | fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { |
| @@ -363,7 +368,7 @@ impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> { | |||
| 363 | } | 368 | } |
| 364 | } | 369 | } |
| 365 | 370 | ||
| 366 | impl<'d, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M> { | 371 | impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> { |
| 367 | type Error = Error; | 372 | type Error = Error; |
| 368 | 373 | ||
| 369 | fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { | 374 | fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { |
| @@ -387,11 +392,11 @@ impl embedded_hal_1::i2c::Error for Error { | |||
| 387 | } | 392 | } |
| 388 | } | 393 | } |
| 389 | 394 | ||
| 390 | impl<'d, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, M> { | 395 | impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::ErrorType for I2c<'d, M, IM> { |
| 391 | type Error = Error; | 396 | type Error = Error; |
| 392 | } | 397 | } |
| 393 | 398 | ||
| 394 | impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> { | 399 | impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> { |
| 395 | fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { | 400 | fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { |
| 396 | self.blocking_read(address, read) | 401 | self.blocking_read(address, read) |
| 397 | } | 402 | } |
| @@ -413,7 +418,7 @@ impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> { | |||
| 413 | } | 418 | } |
| 414 | } | 419 | } |
| 415 | 420 | ||
| 416 | impl<'d> embedded_hal_async::i2c::I2c for I2c<'d, Async> { | 421 | impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> { |
| 417 | async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { | 422 | async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { |
| 418 | self.read(address, read).await | 423 | self.read(address, read).await |
| 419 | } | 424 | } |
| @@ -529,9 +534,7 @@ fn operation_frames<'a, 'b: 'a>( | |||
| 529 | let mut next_first_frame = true; | 534 | let mut next_first_frame = true; |
| 530 | 535 | ||
| 531 | Ok(iter::from_fn(move || { | 536 | Ok(iter::from_fn(move || { |
| 532 | let Some(op) = operations.next() else { | 537 | let op = operations.next()?; |
| 533 | return None; | ||
| 534 | }; | ||
| 535 | 538 | ||
| 536 | // Is `op` first frame of its type? | 539 | // Is `op` first frame of its type? |
| 537 | let first_frame = next_first_frame; | 540 | let first_frame = next_first_frame; |
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 28026f83c..35f13ab46 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs | |||
| @@ -11,6 +11,7 @@ use embassy_embedded_hal::SetConfig; | |||
| 11 | use embassy_futures::select::{select, Either}; | 11 | use embassy_futures::select::{select, Either}; |
| 12 | use embassy_hal_internal::drop::OnDrop; | 12 | use embassy_hal_internal::drop::OnDrop; |
| 13 | use embedded_hal_1::i2c::Operation; | 13 | use embedded_hal_1::i2c::Operation; |
| 14 | use mode::Master; | ||
| 14 | 15 | ||
| 15 | use super::*; | 16 | use super::*; |
| 16 | use crate::mode::Mode as PeriMode; | 17 | use crate::mode::Mode as PeriMode; |
| @@ -41,7 +42,7 @@ pub unsafe fn on_interrupt<T: Instance>() { | |||
| 41 | }); | 42 | }); |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | impl<'d, M: PeriMode> I2c<'d, M> { | 45 | impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { |
| 45 | pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { | 46 | pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { |
| 46 | self.info.regs.cr1().modify(|reg| { | 47 | self.info.regs.cr1().modify(|reg| { |
| 47 | reg.set_pe(false); | 48 | reg.set_pe(false); |
| @@ -354,7 +355,7 @@ impl<'d, M: PeriMode> I2c<'d, M> { | |||
| 354 | } | 355 | } |
| 355 | } | 356 | } |
| 356 | 357 | ||
| 357 | impl<'d> I2c<'d, Async> { | 358 | impl<'d, IM: MasterMode> I2c<'d, Async, IM> { |
| 358 | async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { | 359 | async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { |
| 359 | self.info.regs.cr2().modify(|w| { | 360 | self.info.regs.cr2().modify(|w| { |
| 360 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for | 361 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for |
| @@ -800,7 +801,7 @@ impl Timings { | |||
| 800 | } | 801 | } |
| 801 | } | 802 | } |
| 802 | 803 | ||
| 803 | impl<'d, M: PeriMode> SetConfig for I2c<'d, M> { | 804 | impl<'d, M: PeriMode> SetConfig for I2c<'d, M, Master> { |
| 804 | type Config = Hertz; | 805 | type Config = Hertz; |
| 805 | type ConfigError = (); | 806 | type ConfigError = (); |
| 806 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { | 807 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { |
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 50a25754e..e24cce5c6 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -2,22 +2,87 @@ use core::cmp; | |||
| 2 | use core::future::poll_fn; | 2 | use core::future::poll_fn; |
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use config::{Address, OwnAddresses, OA2}; | ||
| 5 | use embassy_embedded_hal::SetConfig; | 6 | use embassy_embedded_hal::SetConfig; |
| 6 | use embassy_hal_internal::drop::OnDrop; | 7 | use embassy_hal_internal::drop::OnDrop; |
| 7 | use embedded_hal_1::i2c::Operation; | 8 | use embedded_hal_1::i2c::Operation; |
| 9 | use mode::{Master, MultiMaster}; | ||
| 10 | use stm32_metapac::i2c::vals::{Addmode, Oamsk}; | ||
| 8 | 11 | ||
| 9 | use super::*; | 12 | use super::*; |
| 10 | use crate::pac::i2c; | 13 | use crate::pac::i2c; |
| 11 | 14 | ||
| 15 | impl From<AddrMask> for Oamsk { | ||
| 16 | fn from(value: AddrMask) -> Self { | ||
| 17 | match value { | ||
| 18 | AddrMask::NOMASK => Oamsk::NO_MASK, | ||
| 19 | AddrMask::MASK1 => Oamsk::MASK1, | ||
| 20 | AddrMask::MASK2 => Oamsk::MASK2, | ||
| 21 | AddrMask::MASK3 => Oamsk::MASK3, | ||
| 22 | AddrMask::MASK4 => Oamsk::MASK4, | ||
| 23 | AddrMask::MASK5 => Oamsk::MASK5, | ||
| 24 | AddrMask::MASK6 => Oamsk::MASK6, | ||
| 25 | AddrMask::MASK7 => Oamsk::MASK7, | ||
| 26 | } | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | impl Address { | ||
| 31 | pub(super) fn add_mode(&self) -> stm32_metapac::i2c::vals::Addmode { | ||
| 32 | match self { | ||
| 33 | Address::SevenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT7, | ||
| 34 | Address::TenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT10, | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | enum ReceiveResult { | ||
| 40 | DataAvailable, | ||
| 41 | StopReceived, | ||
| 42 | NewStart, | ||
| 43 | } | ||
| 44 | |||
| 45 | fn debug_print_interrupts(isr: stm32_metapac::i2c::regs::Isr) { | ||
| 46 | if isr.tcr() { | ||
| 47 | trace!("interrupt: tcr"); | ||
| 48 | } | ||
| 49 | if isr.tc() { | ||
| 50 | trace!("interrupt: tc"); | ||
| 51 | } | ||
| 52 | if isr.addr() { | ||
| 53 | trace!("interrupt: addr"); | ||
| 54 | } | ||
| 55 | if isr.stopf() { | ||
| 56 | trace!("interrupt: stopf"); | ||
| 57 | } | ||
| 58 | if isr.nackf() { | ||
| 59 | trace!("interrupt: nackf"); | ||
| 60 | } | ||
| 61 | if isr.berr() { | ||
| 62 | trace!("interrupt: berr"); | ||
| 63 | } | ||
| 64 | if isr.arlo() { | ||
| 65 | trace!("interrupt: arlo"); | ||
| 66 | } | ||
| 67 | if isr.ovr() { | ||
| 68 | trace!("interrupt: ovr"); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 12 | pub(crate) unsafe fn on_interrupt<T: Instance>() { | 72 | pub(crate) unsafe fn on_interrupt<T: Instance>() { |
| 13 | let regs = T::info().regs; | 73 | let regs = T::info().regs; |
| 14 | let isr = regs.isr().read(); | 74 | let isr = regs.isr().read(); |
| 15 | 75 | ||
| 16 | if isr.tcr() || isr.tc() || isr.nackf() || isr.berr() || isr.arlo() || isr.ovr() { | 76 | if isr.tcr() || isr.tc() || isr.addr() || isr.stopf() || isr.nackf() || isr.berr() || isr.arlo() || isr.ovr() { |
| 77 | debug_print_interrupts(isr); | ||
| 78 | |||
| 17 | T::state().waker.wake(); | 79 | T::state().waker.wake(); |
| 18 | } | 80 | } |
| 81 | |||
| 19 | critical_section::with(|_| { | 82 | critical_section::with(|_| { |
| 20 | regs.cr1().modify(|w| { | 83 | regs.cr1().modify(|w| { |
| 84 | w.set_addrie(false); | ||
| 85 | w.set_stopie(false); | ||
| 21 | // The flag can only be cleared by writting to nbytes, we won't do that here | 86 | // The flag can only be cleared by writting to nbytes, we won't do that here |
| 22 | w.set_tcie(false); | 87 | w.set_tcie(false); |
| 23 | // Error flags are to be read in the routines, so we also don't clear them here | 88 | // Error flags are to be read in the routines, so we also don't clear them here |
| @@ -27,7 +92,7 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() { | |||
| 27 | }); | 92 | }); |
| 28 | } | 93 | } |
| 29 | 94 | ||
| 30 | impl<'d, M: Mode> I2c<'d, M> { | 95 | impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { |
| 31 | pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { | 96 | pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { |
| 32 | self.info.regs.cr1().modify(|reg| { | 97 | self.info.regs.cr1().modify(|reg| { |
| 33 | reg.set_pe(false); | 98 | reg.set_pe(false); |
| @@ -55,7 +120,7 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 55 | 120 | ||
| 56 | fn master_read( | 121 | fn master_read( |
| 57 | info: &'static Info, | 122 | info: &'static Info, |
| 58 | address: u8, | 123 | address: Address, |
| 59 | length: usize, | 124 | length: usize, |
| 60 | stop: Stop, | 125 | stop: Stop, |
| 61 | reload: bool, | 126 | reload: bool, |
| @@ -84,8 +149,8 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 84 | }; | 149 | }; |
| 85 | 150 | ||
| 86 | info.regs.cr2().modify(|w| { | 151 | info.regs.cr2().modify(|w| { |
| 87 | w.set_sadd((address << 1 | 0) as u16); | 152 | w.set_sadd(address.addr() << 1); |
| 88 | w.set_add10(i2c::vals::Addmode::BIT7); | 153 | w.set_add10(address.add_mode()); |
| 89 | w.set_dir(i2c::vals::Dir::READ); | 154 | w.set_dir(i2c::vals::Dir::READ); |
| 90 | w.set_nbytes(length as u8); | 155 | w.set_nbytes(length as u8); |
| 91 | w.set_start(true); | 156 | w.set_start(true); |
| @@ -98,7 +163,7 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 98 | 163 | ||
| 99 | fn master_write( | 164 | fn master_write( |
| 100 | info: &'static Info, | 165 | info: &'static Info, |
| 101 | address: u8, | 166 | address: Address, |
| 102 | length: usize, | 167 | length: usize, |
| 103 | stop: Stop, | 168 | stop: Stop, |
| 104 | reload: bool, | 169 | reload: bool, |
| @@ -128,8 +193,8 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 128 | // START bit can be set even if the bus is BUSY or | 193 | // START bit can be set even if the bus is BUSY or |
| 129 | // I2C is in slave mode. | 194 | // I2C is in slave mode. |
| 130 | info.regs.cr2().modify(|w| { | 195 | info.regs.cr2().modify(|w| { |
| 131 | w.set_sadd((address << 1 | 0) as u16); | 196 | w.set_sadd(address.addr() << 1); |
| 132 | w.set_add10(i2c::vals::Addmode::BIT7); | 197 | w.set_add10(address.add_mode()); |
| 133 | w.set_dir(i2c::vals::Dir::WRITE); | 198 | w.set_dir(i2c::vals::Dir::WRITE); |
| 134 | w.set_nbytes(length as u8); | 199 | w.set_nbytes(length as u8); |
| 135 | w.set_start(true); | 200 | w.set_start(true); |
| @@ -140,14 +205,14 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 140 | Ok(()) | 205 | Ok(()) |
| 141 | } | 206 | } |
| 142 | 207 | ||
| 143 | fn master_continue(info: &'static Info, length: usize, reload: bool, timeout: Timeout) -> Result<(), Error> { | 208 | fn reload(info: &'static Info, length: usize, will_reload: bool, timeout: Timeout) -> Result<(), Error> { |
| 144 | assert!(length < 256 && length > 0); | 209 | assert!(length < 256 && length > 0); |
| 145 | 210 | ||
| 146 | while !info.regs.isr().read().tcr() { | 211 | while !info.regs.isr().read().tcr() { |
| 147 | timeout.check()?; | 212 | timeout.check()?; |
| 148 | } | 213 | } |
| 149 | 214 | ||
| 150 | let reload = if reload { | 215 | let will_reload = if will_reload { |
| 151 | i2c::vals::Reload::NOT_COMPLETED | 216 | i2c::vals::Reload::NOT_COMPLETED |
| 152 | } else { | 217 | } else { |
| 153 | i2c::vals::Reload::COMPLETED | 218 | i2c::vals::Reload::COMPLETED |
| @@ -155,7 +220,7 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 155 | 220 | ||
| 156 | info.regs.cr2().modify(|w| { | 221 | info.regs.cr2().modify(|w| { |
| 157 | w.set_nbytes(length as u8); | 222 | w.set_nbytes(length as u8); |
| 158 | w.set_reload(reload); | 223 | w.set_reload(will_reload); |
| 159 | }); | 224 | }); |
| 160 | 225 | ||
| 161 | Ok(()) | 226 | Ok(()) |
| @@ -163,49 +228,132 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 163 | 228 | ||
| 164 | fn flush_txdr(&self) { | 229 | fn flush_txdr(&self) { |
| 165 | if self.info.regs.isr().read().txis() { | 230 | if self.info.regs.isr().read().txis() { |
| 166 | self.info.regs.txdr().write(|w| w.set_txdata(0)); | 231 | trace!("Flush TXDATA with zeroes"); |
| 232 | self.info.regs.txdr().modify(|w| w.set_txdata(0)); | ||
| 167 | } | 233 | } |
| 168 | if !self.info.regs.isr().read().txe() { | 234 | if !self.info.regs.isr().read().txe() { |
| 235 | trace!("Flush TXDR"); | ||
| 169 | self.info.regs.isr().modify(|w| w.set_txe(true)) | 236 | self.info.regs.isr().modify(|w| w.set_txe(true)) |
| 170 | } | 237 | } |
| 171 | } | 238 | } |
| 172 | 239 | ||
| 173 | fn wait_txe(&self, timeout: Timeout) -> Result<(), Error> { | 240 | fn error_occurred(&self, isr: &i2c::regs::Isr, timeout: Timeout) -> Result<(), Error> { |
| 241 | if isr.nackf() { | ||
| 242 | trace!("NACK triggered."); | ||
| 243 | self.info.regs.icr().modify(|reg| reg.set_nackcf(true)); | ||
| 244 | // NACK should be followed by STOP | ||
| 245 | if let Ok(()) = self.wait_stop(timeout) { | ||
| 246 | trace!("Got STOP after NACK, clearing flag."); | ||
| 247 | self.info.regs.icr().modify(|reg| reg.set_stopcf(true)); | ||
| 248 | } | ||
| 249 | self.flush_txdr(); | ||
| 250 | return Err(Error::Nack); | ||
| 251 | } else if isr.berr() { | ||
| 252 | trace!("BERR triggered."); | ||
| 253 | self.info.regs.icr().modify(|reg| reg.set_berrcf(true)); | ||
| 254 | self.flush_txdr(); | ||
| 255 | return Err(Error::Bus); | ||
| 256 | } else if isr.arlo() { | ||
| 257 | trace!("ARLO triggered."); | ||
| 258 | self.info.regs.icr().modify(|reg| reg.set_arlocf(true)); | ||
| 259 | self.flush_txdr(); | ||
| 260 | return Err(Error::Arbitration); | ||
| 261 | } else if isr.ovr() { | ||
| 262 | trace!("OVR triggered."); | ||
| 263 | self.info.regs.icr().modify(|reg| reg.set_ovrcf(true)); | ||
| 264 | return Err(Error::Overrun); | ||
| 265 | } | ||
| 266 | return Ok(()); | ||
| 267 | } | ||
| 268 | |||
| 269 | fn wait_txis(&self, timeout: Timeout) -> Result<(), Error> { | ||
| 270 | let mut first_loop = true; | ||
| 271 | |||
| 174 | loop { | 272 | loop { |
| 175 | let isr = self.info.regs.isr().read(); | 273 | let isr = self.info.regs.isr().read(); |
| 176 | if isr.txe() { | 274 | self.error_occurred(&isr, timeout)?; |
| 275 | if isr.txis() { | ||
| 276 | trace!("TXIS"); | ||
| 177 | return Ok(()); | 277 | return Ok(()); |
| 178 | } else if isr.berr() { | ||
| 179 | self.info.regs.icr().write(|reg| reg.set_berrcf(true)); | ||
| 180 | return Err(Error::Bus); | ||
| 181 | } else if isr.arlo() { | ||
| 182 | self.info.regs.icr().write(|reg| reg.set_arlocf(true)); | ||
| 183 | return Err(Error::Arbitration); | ||
| 184 | } else if isr.nackf() { | ||
| 185 | self.info.regs.icr().write(|reg| reg.set_nackcf(true)); | ||
| 186 | self.flush_txdr(); | ||
| 187 | return Err(Error::Nack); | ||
| 188 | } | 278 | } |
| 189 | 279 | ||
| 280 | { | ||
| 281 | if first_loop { | ||
| 282 | trace!("Waiting for TXIS..."); | ||
| 283 | first_loop = false; | ||
| 284 | } | ||
| 285 | } | ||
| 286 | timeout.check()?; | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | fn wait_stop_or_err(&self, timeout: Timeout) -> Result<(), Error> { | ||
| 291 | loop { | ||
| 292 | let isr = self.info.regs.isr().read(); | ||
| 293 | self.error_occurred(&isr, timeout)?; | ||
| 294 | if isr.stopf() { | ||
| 295 | trace!("STOP triggered."); | ||
| 296 | self.info.regs.icr().modify(|reg| reg.set_stopcf(true)); | ||
| 297 | return Ok(()); | ||
| 298 | } | ||
| 299 | timeout.check()?; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | fn wait_stop(&self, timeout: Timeout) -> Result<(), Error> { | ||
| 303 | loop { | ||
| 304 | let isr = self.info.regs.isr().read(); | ||
| 305 | if isr.stopf() { | ||
| 306 | trace!("STOP triggered."); | ||
| 307 | self.info.regs.icr().modify(|reg| reg.set_stopcf(true)); | ||
| 308 | return Ok(()); | ||
| 309 | } | ||
| 190 | timeout.check()?; | 310 | timeout.check()?; |
| 191 | } | 311 | } |
| 192 | } | 312 | } |
| 193 | 313 | ||
| 194 | fn wait_rxne(&self, timeout: Timeout) -> Result<(), Error> { | 314 | fn wait_af(&self, timeout: Timeout) -> Result<(), Error> { |
| 195 | loop { | 315 | loop { |
| 196 | let isr = self.info.regs.isr().read(); | 316 | let isr = self.info.regs.isr().read(); |
| 197 | if isr.rxne() { | 317 | if isr.nackf() { |
| 318 | trace!("AF triggered."); | ||
| 319 | self.info.regs.icr().modify(|reg| reg.set_nackcf(true)); | ||
| 198 | return Ok(()); | 320 | return Ok(()); |
| 199 | } else if isr.berr() { | 321 | } |
| 200 | self.info.regs.icr().write(|reg| reg.set_berrcf(true)); | 322 | timeout.check()?; |
| 201 | return Err(Error::Bus); | 323 | } |
| 202 | } else if isr.arlo() { | 324 | } |
| 203 | self.info.regs.icr().write(|reg| reg.set_arlocf(true)); | 325 | |
| 204 | return Err(Error::Arbitration); | 326 | fn wait_rxne(&self, timeout: Timeout) -> Result<ReceiveResult, Error> { |
| 205 | } else if isr.nackf() { | 327 | let mut first_loop = true; |
| 206 | self.info.regs.icr().write(|reg| reg.set_nackcf(true)); | 328 | |
| 207 | self.flush_txdr(); | 329 | loop { |
| 208 | return Err(Error::Nack); | 330 | let isr = self.info.regs.isr().read(); |
| 331 | self.error_occurred(&isr, timeout)?; | ||
| 332 | if isr.stopf() { | ||
| 333 | trace!("STOP when waiting for RXNE."); | ||
| 334 | if self.info.regs.isr().read().rxne() { | ||
| 335 | trace!("Data received with STOP."); | ||
| 336 | return Ok(ReceiveResult::DataAvailable); | ||
| 337 | } | ||
| 338 | trace!("STOP triggered without data."); | ||
| 339 | return Ok(ReceiveResult::StopReceived); | ||
| 340 | } else if isr.rxne() { | ||
| 341 | trace!("RXNE."); | ||
| 342 | return Ok(ReceiveResult::DataAvailable); | ||
| 343 | } else if isr.addr() { | ||
| 344 | // Another addr event received, which means START was sent again | ||
| 345 | // which happens when accessing memory registers (common i2c interface design) | ||
| 346 | // e.g. master sends: START, write 1 byte (register index), START, read N bytes (until NACK) | ||
| 347 | // Possible to receive this flag at the same time as rxne, so check rxne first | ||
| 348 | trace!("START when waiting for RXNE. Ending receive loop."); | ||
| 349 | // Return without clearing ADDR so `listen` can catch it | ||
| 350 | return Ok(ReceiveResult::NewStart); | ||
| 351 | } | ||
| 352 | { | ||
| 353 | if first_loop { | ||
| 354 | trace!("Waiting for RXNE..."); | ||
| 355 | first_loop = false; | ||
| 356 | } | ||
| 209 | } | 357 | } |
| 210 | 358 | ||
| 211 | timeout.check()?; | 359 | timeout.check()?; |
| @@ -215,25 +363,21 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 215 | fn wait_tc(&self, timeout: Timeout) -> Result<(), Error> { | 363 | fn wait_tc(&self, timeout: Timeout) -> Result<(), Error> { |
| 216 | loop { | 364 | loop { |
| 217 | let isr = self.info.regs.isr().read(); | 365 | let isr = self.info.regs.isr().read(); |
| 366 | self.error_occurred(&isr, timeout)?; | ||
| 218 | if isr.tc() { | 367 | if isr.tc() { |
| 219 | return Ok(()); | 368 | return Ok(()); |
| 220 | } else if isr.berr() { | ||
| 221 | self.info.regs.icr().write(|reg| reg.set_berrcf(true)); | ||
| 222 | return Err(Error::Bus); | ||
| 223 | } else if isr.arlo() { | ||
| 224 | self.info.regs.icr().write(|reg| reg.set_arlocf(true)); | ||
| 225 | return Err(Error::Arbitration); | ||
| 226 | } else if isr.nackf() { | ||
| 227 | self.info.regs.icr().write(|reg| reg.set_nackcf(true)); | ||
| 228 | self.flush_txdr(); | ||
| 229 | return Err(Error::Nack); | ||
| 230 | } | 369 | } |
| 231 | |||
| 232 | timeout.check()?; | 370 | timeout.check()?; |
| 233 | } | 371 | } |
| 234 | } | 372 | } |
| 235 | 373 | ||
| 236 | fn read_internal(&mut self, address: u8, read: &mut [u8], restart: bool, timeout: Timeout) -> Result<(), Error> { | 374 | fn read_internal( |
| 375 | &mut self, | ||
| 376 | address: Address, | ||
| 377 | read: &mut [u8], | ||
| 378 | restart: bool, | ||
| 379 | timeout: Timeout, | ||
| 380 | ) -> Result<(), Error> { | ||
| 237 | let completed_chunks = read.len() / 255; | 381 | let completed_chunks = read.len() / 255; |
| 238 | let total_chunks = if completed_chunks * 255 == read.len() { | 382 | let total_chunks = if completed_chunks * 255 == read.len() { |
| 239 | completed_chunks | 383 | completed_chunks |
| @@ -254,7 +398,7 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 254 | 398 | ||
| 255 | for (number, chunk) in read.chunks_mut(255).enumerate() { | 399 | for (number, chunk) in read.chunks_mut(255).enumerate() { |
| 256 | if number != 0 { | 400 | if number != 0 { |
| 257 | Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 401 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; |
| 258 | } | 402 | } |
| 259 | 403 | ||
| 260 | for byte in chunk { | 404 | for byte in chunk { |
| @@ -267,7 +411,13 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 267 | Ok(()) | 411 | Ok(()) |
| 268 | } | 412 | } |
| 269 | 413 | ||
| 270 | fn write_internal(&mut self, address: u8, write: &[u8], send_stop: bool, timeout: Timeout) -> Result<(), Error> { | 414 | fn write_internal( |
| 415 | &mut self, | ||
| 416 | address: Address, | ||
| 417 | write: &[u8], | ||
| 418 | send_stop: bool, | ||
| 419 | timeout: Timeout, | ||
| 420 | ) -> Result<(), Error> { | ||
| 271 | let completed_chunks = write.len() / 255; | 421 | let completed_chunks = write.len() / 255; |
| 272 | let total_chunks = if completed_chunks * 255 == write.len() { | 422 | let total_chunks = if completed_chunks * 255 == write.len() { |
| 273 | completed_chunks | 423 | completed_chunks |
| @@ -295,14 +445,14 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 295 | 445 | ||
| 296 | for (number, chunk) in write.chunks(255).enumerate() { | 446 | for (number, chunk) in write.chunks(255).enumerate() { |
| 297 | if number != 0 { | 447 | if number != 0 { |
| 298 | Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | 448 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; |
| 299 | } | 449 | } |
| 300 | 450 | ||
| 301 | for byte in chunk { | 451 | for byte in chunk { |
| 302 | // Wait until we are allowed to send data | 452 | // Wait until we are allowed to send data |
| 303 | // (START has been ACKed or last byte when | 453 | // (START has been ACKed or last byte when |
| 304 | // through) | 454 | // through) |
| 305 | if let Err(err) = self.wait_txe(timeout) { | 455 | if let Err(err) = self.wait_txis(timeout) { |
| 306 | if send_stop { | 456 | if send_stop { |
| 307 | self.master_stop(); | 457 | self.master_stop(); |
| 308 | } | 458 | } |
| @@ -325,20 +475,20 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 325 | 475 | ||
| 326 | /// Blocking read. | 476 | /// Blocking read. |
| 327 | pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { | 477 | pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { |
| 328 | self.read_internal(address, read, false, self.timeout()) | 478 | self.read_internal(address.into(), read, false, self.timeout()) |
| 329 | // Automatic Stop | 479 | // Automatic Stop |
| 330 | } | 480 | } |
| 331 | 481 | ||
| 332 | /// Blocking write. | 482 | /// Blocking write. |
| 333 | pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 483 | pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { |
| 334 | self.write_internal(address, write, true, self.timeout()) | 484 | self.write_internal(address.into(), write, true, self.timeout()) |
| 335 | } | 485 | } |
| 336 | 486 | ||
| 337 | /// Blocking write, restart, read. | 487 | /// Blocking write, restart, read. |
| 338 | pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 488 | pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { |
| 339 | let timeout = self.timeout(); | 489 | let timeout = self.timeout(); |
| 340 | self.write_internal(address, write, false, timeout)?; | 490 | self.write_internal(address.into(), write, false, timeout)?; |
| 341 | self.read_internal(address, read, true, timeout) | 491 | self.read_internal(address.into(), read, true, timeout) |
| 342 | // Automatic Stop | 492 | // Automatic Stop |
| 343 | } | 493 | } |
| 344 | 494 | ||
| @@ -368,7 +518,7 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 368 | 518 | ||
| 369 | if let Err(err) = Self::master_write( | 519 | if let Err(err) = Self::master_write( |
| 370 | self.info, | 520 | self.info, |
| 371 | address, | 521 | address.into(), |
| 372 | first_length.min(255), | 522 | first_length.min(255), |
| 373 | Stop::Software, | 523 | Stop::Software, |
| 374 | (first_length > 255) || (last_slice_index != 0), | 524 | (first_length > 255) || (last_slice_index != 0), |
| @@ -389,7 +539,7 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 389 | let last_chunk_idx = total_chunks.saturating_sub(1); | 539 | let last_chunk_idx = total_chunks.saturating_sub(1); |
| 390 | 540 | ||
| 391 | if idx != 0 { | 541 | if idx != 0 { |
| 392 | if let Err(err) = Self::master_continue( | 542 | if let Err(err) = Self::reload( |
| 393 | self.info, | 543 | self.info, |
| 394 | slice_len.min(255), | 544 | slice_len.min(255), |
| 395 | (idx != last_slice_index) || (slice_len > 255), | 545 | (idx != last_slice_index) || (slice_len > 255), |
| @@ -402,7 +552,7 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 402 | 552 | ||
| 403 | for (number, chunk) in slice.chunks(255).enumerate() { | 553 | for (number, chunk) in slice.chunks(255).enumerate() { |
| 404 | if number != 0 { | 554 | if number != 0 { |
| 405 | if let Err(err) = Self::master_continue( | 555 | if let Err(err) = Self::reload( |
| 406 | self.info, | 556 | self.info, |
| 407 | chunk.len(), | 557 | chunk.len(), |
| 408 | (number != last_chunk_idx) || (idx != last_slice_index), | 558 | (number != last_chunk_idx) || (idx != last_slice_index), |
| @@ -417,7 +567,7 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 417 | // Wait until we are allowed to send data | 567 | // Wait until we are allowed to send data |
| 418 | // (START has been ACKed or last byte when | 568 | // (START has been ACKed or last byte when |
| 419 | // through) | 569 | // through) |
| 420 | if let Err(err) = self.wait_txe(timeout) { | 570 | if let Err(err) = self.wait_txis(timeout) { |
| 421 | self.master_stop(); | 571 | self.master_stop(); |
| 422 | return Err(err); | 572 | return Err(err); |
| 423 | } | 573 | } |
| @@ -435,10 +585,10 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 435 | } | 585 | } |
| 436 | } | 586 | } |
| 437 | 587 | ||
| 438 | impl<'d> I2c<'d, Async> { | 588 | impl<'d, IM: MasterMode> I2c<'d, Async, IM> { |
| 439 | async fn write_dma_internal( | 589 | async fn write_dma_internal( |
| 440 | &mut self, | 590 | &mut self, |
| 441 | address: u8, | 591 | address: Address, |
| 442 | write: &[u8], | 592 | write: &[u8], |
| 443 | first_slice: bool, | 593 | first_slice: bool, |
| 444 | last_slice: bool, | 594 | last_slice: bool, |
| @@ -512,7 +662,7 @@ impl<'d> I2c<'d, Async> { | |||
| 512 | timeout, | 662 | timeout, |
| 513 | )?; | 663 | )?; |
| 514 | } else { | 664 | } else { |
| 515 | Self::master_continue(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; | 665 | Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; |
| 516 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 666 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 517 | } | 667 | } |
| 518 | } else if !(isr.tcr() || isr.tc()) { | 668 | } else if !(isr.tcr() || isr.tc()) { |
| @@ -523,7 +673,7 @@ impl<'d> I2c<'d, Async> { | |||
| 523 | } else { | 673 | } else { |
| 524 | let last_piece = (remaining_len <= 255) && last_slice; | 674 | let last_piece = (remaining_len <= 255) && last_slice; |
| 525 | 675 | ||
| 526 | if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) { | 676 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { |
| 527 | return Poll::Ready(Err(e)); | 677 | return Poll::Ready(Err(e)); |
| 528 | } | 678 | } |
| 529 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 679 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| @@ -551,7 +701,7 @@ impl<'d> I2c<'d, Async> { | |||
| 551 | 701 | ||
| 552 | async fn read_dma_internal( | 702 | async fn read_dma_internal( |
| 553 | &mut self, | 703 | &mut self, |
| 554 | address: u8, | 704 | address: Address, |
| 555 | buffer: &mut [u8], | 705 | buffer: &mut [u8], |
| 556 | restart: bool, | 706 | restart: bool, |
| 557 | timeout: Timeout, | 707 | timeout: Timeout, |
| @@ -626,7 +776,7 @@ impl<'d> I2c<'d, Async> { | |||
| 626 | } else { | 776 | } else { |
| 627 | let last_piece = remaining_len <= 255; | 777 | let last_piece = remaining_len <= 255; |
| 628 | 778 | ||
| 629 | if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) { | 779 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { |
| 630 | return Poll::Ready(Err(e)); | 780 | return Poll::Ready(Err(e)); |
| 631 | } | 781 | } |
| 632 | // Return here if we are on last chunk, | 782 | // Return here if we are on last chunk, |
| @@ -647,7 +797,6 @@ impl<'d> I2c<'d, Async> { | |||
| 647 | 797 | ||
| 648 | Ok(()) | 798 | Ok(()) |
| 649 | } | 799 | } |
| 650 | |||
| 651 | // ========================= | 800 | // ========================= |
| 652 | // Async public API | 801 | // Async public API |
| 653 | 802 | ||
| @@ -655,10 +804,10 @@ impl<'d> I2c<'d, Async> { | |||
| 655 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 804 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { |
| 656 | let timeout = self.timeout(); | 805 | let timeout = self.timeout(); |
| 657 | if write.is_empty() { | 806 | if write.is_empty() { |
| 658 | self.write_internal(address, write, true, timeout) | 807 | self.write_internal(address.into(), write, true, timeout) |
| 659 | } else { | 808 | } else { |
| 660 | timeout | 809 | timeout |
| 661 | .with(self.write_dma_internal(address, write, true, true, true, timeout)) | 810 | .with(self.write_dma_internal(address.into(), write, true, true, true, timeout)) |
| 662 | .await | 811 | .await |
| 663 | } | 812 | } |
| 664 | } | 813 | } |
| @@ -666,7 +815,7 @@ impl<'d> I2c<'d, Async> { | |||
| 666 | /// Write multiple buffers. | 815 | /// Write multiple buffers. |
| 667 | /// | 816 | /// |
| 668 | /// The buffers are concatenated in a single write transaction. | 817 | /// The buffers are concatenated in a single write transaction. |
| 669 | pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { | 818 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { |
| 670 | let timeout = self.timeout(); | 819 | let timeout = self.timeout(); |
| 671 | 820 | ||
| 672 | if write.is_empty() { | 821 | if write.is_empty() { |
| @@ -693,9 +842,9 @@ impl<'d> I2c<'d, Async> { | |||
| 693 | let timeout = self.timeout(); | 842 | let timeout = self.timeout(); |
| 694 | 843 | ||
| 695 | if buffer.is_empty() { | 844 | if buffer.is_empty() { |
| 696 | self.read_internal(address, buffer, false, timeout) | 845 | self.read_internal(address.into(), buffer, false, timeout) |
| 697 | } else { | 846 | } else { |
| 698 | let fut = self.read_dma_internal(address, buffer, false, timeout); | 847 | let fut = self.read_dma_internal(address.into(), buffer, false, timeout); |
| 699 | timeout.with(fut).await | 848 | timeout.with(fut).await |
| 700 | } | 849 | } |
| 701 | } | 850 | } |
| @@ -705,16 +854,16 @@ impl<'d> I2c<'d, Async> { | |||
| 705 | let timeout = self.timeout(); | 854 | let timeout = self.timeout(); |
| 706 | 855 | ||
| 707 | if write.is_empty() { | 856 | if write.is_empty() { |
| 708 | self.write_internal(address, write, false, timeout)?; | 857 | self.write_internal(address.into(), write, false, timeout)?; |
| 709 | } else { | 858 | } else { |
| 710 | let fut = self.write_dma_internal(address, write, true, true, false, timeout); | 859 | let fut = self.write_dma_internal(address.into(), write, true, true, false, timeout); |
| 711 | timeout.with(fut).await?; | 860 | timeout.with(fut).await?; |
| 712 | } | 861 | } |
| 713 | 862 | ||
| 714 | if read.is_empty() { | 863 | if read.is_empty() { |
| 715 | self.read_internal(address, read, true, timeout)?; | 864 | self.read_internal(address.into(), read, true, timeout)?; |
| 716 | } else { | 865 | } else { |
| 717 | let fut = self.read_dma_internal(address, read, true, timeout); | 866 | let fut = self.read_dma_internal(address.into(), read, true, timeout); |
| 718 | timeout.with(fut).await?; | 867 | timeout.with(fut).await?; |
| 719 | } | 868 | } |
| 720 | 869 | ||
| @@ -733,6 +882,414 @@ impl<'d> I2c<'d, Async> { | |||
| 733 | } | 882 | } |
| 734 | } | 883 | } |
| 735 | 884 | ||
| 885 | impl<'d, M: Mode> I2c<'d, M, Master> { | ||
| 886 | /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) | ||
| 887 | pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { | ||
| 888 | let mut slave = I2c { | ||
| 889 | info: self.info, | ||
| 890 | state: self.state, | ||
| 891 | kernel_clock: self.kernel_clock, | ||
| 892 | tx_dma: self.tx_dma.take(), | ||
| 893 | rx_dma: self.rx_dma.take(), | ||
| 894 | #[cfg(feature = "time")] | ||
| 895 | timeout: self.timeout, | ||
| 896 | _phantom: PhantomData, | ||
| 897 | _phantom2: PhantomData, | ||
| 898 | _drop_guard: self._drop_guard, | ||
| 899 | }; | ||
| 900 | slave.init_slave(slave_addr_config); | ||
| 901 | slave | ||
| 902 | } | ||
| 903 | } | ||
| 904 | |||
| 905 | impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | ||
| 906 | pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { | ||
| 907 | self.info.regs.cr1().modify(|reg| { | ||
| 908 | reg.set_pe(false); | ||
| 909 | }); | ||
| 910 | |||
| 911 | self.info.regs.cr1().modify(|reg| { | ||
| 912 | reg.set_nostretch(false); | ||
| 913 | reg.set_gcen(config.general_call); | ||
| 914 | reg.set_sbc(true); | ||
| 915 | reg.set_pe(true); | ||
| 916 | }); | ||
| 917 | |||
| 918 | self.reconfigure_addresses(config.addr); | ||
| 919 | } | ||
| 920 | |||
| 921 | /// Configure the slave address. | ||
| 922 | pub fn reconfigure_addresses(&mut self, addresses: OwnAddresses) { | ||
| 923 | match addresses { | ||
| 924 | OwnAddresses::OA1(oa1) => self.configure_oa1(oa1), | ||
| 925 | OwnAddresses::OA2(oa2) => self.configure_oa2(oa2), | ||
| 926 | OwnAddresses::Both { oa1, oa2 } => { | ||
| 927 | self.configure_oa1(oa1); | ||
| 928 | self.configure_oa2(oa2); | ||
| 929 | } | ||
| 930 | } | ||
| 931 | } | ||
| 932 | |||
| 933 | fn configure_oa1(&mut self, oa1: Address) { | ||
| 934 | match oa1 { | ||
| 935 | Address::SevenBit(addr) => self.info.regs.oar1().write(|reg| { | ||
| 936 | reg.set_oa1en(false); | ||
| 937 | reg.set_oa1((addr << 1) as u16); | ||
| 938 | reg.set_oa1mode(Addmode::BIT7); | ||
| 939 | reg.set_oa1en(true); | ||
| 940 | }), | ||
| 941 | Address::TenBit(addr) => self.info.regs.oar1().write(|reg| { | ||
| 942 | reg.set_oa1en(false); | ||
| 943 | reg.set_oa1(addr); | ||
| 944 | reg.set_oa1mode(Addmode::BIT10); | ||
| 945 | reg.set_oa1en(true); | ||
| 946 | }), | ||
| 947 | } | ||
| 948 | } | ||
| 949 | |||
| 950 | fn configure_oa2(&mut self, oa2: OA2) { | ||
| 951 | self.info.regs.oar2().write(|reg| { | ||
| 952 | reg.set_oa2en(false); | ||
| 953 | reg.set_oa2msk(oa2.mask.into()); | ||
| 954 | reg.set_oa2(oa2.addr << 1); | ||
| 955 | reg.set_oa2en(true); | ||
| 956 | }); | ||
| 957 | } | ||
| 958 | |||
| 959 | fn determine_matched_address(&self) -> Result<Address, Error> { | ||
| 960 | let matched = self.info.regs.isr().read().addcode(); | ||
| 961 | |||
| 962 | if matched >> 3 == 0b11110 { | ||
| 963 | // is 10-bit address and we need to get the other 8 bits from the rxdr | ||
| 964 | // we do this by doing a blocking read of 1 byte | ||
| 965 | let mut buffer = [0]; | ||
| 966 | self.slave_read_internal(&mut buffer, self.timeout())?; | ||
| 967 | Ok(Address::TenBit((matched as u16) << 6 | buffer[0] as u16)) | ||
| 968 | } else { | ||
| 969 | Ok(Address::SevenBit(matched)) | ||
| 970 | } | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 974 | impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | ||
| 975 | /// # Safety | ||
| 976 | /// This function will clear the address flag which will stop the clock stretching. | ||
| 977 | /// This should only be done after the dma transfer has been set up. | ||
| 978 | fn slave_start(info: &'static Info, length: usize, reload: bool) { | ||
| 979 | assert!(length < 256); | ||
| 980 | |||
| 981 | let reload = if reload { | ||
| 982 | i2c::vals::Reload::NOT_COMPLETED | ||
| 983 | } else { | ||
| 984 | i2c::vals::Reload::COMPLETED | ||
| 985 | }; | ||
| 986 | |||
| 987 | info.regs.cr2().modify(|w| { | ||
| 988 | w.set_nbytes(length as u8); | ||
| 989 | w.set_reload(reload); | ||
| 990 | }); | ||
| 991 | |||
| 992 | // clear the address flag, will stop the clock stretching. | ||
| 993 | // this should only be done after the dma transfer has been set up. | ||
| 994 | info.regs.icr().modify(|reg| reg.set_addrcf(true)); | ||
| 995 | trace!("ADDRCF cleared (ADDR interrupt enabled, clock stretching ended)"); | ||
| 996 | } | ||
| 997 | |||
| 998 | // A blocking read operation | ||
| 999 | fn slave_read_internal(&self, read: &mut [u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 1000 | let completed_chunks = read.len() / 255; | ||
| 1001 | let total_chunks = if completed_chunks * 255 == read.len() { | ||
| 1002 | completed_chunks | ||
| 1003 | } else { | ||
| 1004 | completed_chunks + 1 | ||
| 1005 | }; | ||
| 1006 | let last_chunk_idx = total_chunks.saturating_sub(1); | ||
| 1007 | let total_len = read.len(); | ||
| 1008 | let mut remaining_len = total_len; | ||
| 1009 | |||
| 1010 | for (number, chunk) in read.chunks_mut(255).enumerate() { | ||
| 1011 | trace!( | ||
| 1012 | "--- Slave RX transmission start - chunk: {}, expected (max) size: {}", | ||
| 1013 | number, | ||
| 1014 | chunk.len() | ||
| 1015 | ); | ||
| 1016 | if number == 0 { | ||
| 1017 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | ||
| 1018 | } else { | ||
| 1019 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | let mut index = 0; | ||
| 1023 | |||
| 1024 | for byte in chunk { | ||
| 1025 | // Wait until we have received something | ||
| 1026 | match self.wait_rxne(timeout) { | ||
| 1027 | Ok(ReceiveResult::StopReceived) | Ok(ReceiveResult::NewStart) => { | ||
| 1028 | trace!("--- Slave RX transmission end (early)"); | ||
| 1029 | return Ok(total_len - remaining_len); // Return N bytes read | ||
| 1030 | } | ||
| 1031 | Ok(ReceiveResult::DataAvailable) => { | ||
| 1032 | *byte = self.info.regs.rxdr().read().rxdata(); | ||
| 1033 | remaining_len = remaining_len.saturating_sub(1); | ||
| 1034 | { | ||
| 1035 | trace!("Slave RX data {}: {:#04x}", index, byte); | ||
| 1036 | index = index + 1; | ||
| 1037 | } | ||
| 1038 | } | ||
| 1039 | Err(e) => return Err(e), | ||
| 1040 | }; | ||
| 1041 | } | ||
| 1042 | } | ||
| 1043 | self.wait_stop_or_err(timeout)?; | ||
| 1044 | |||
| 1045 | trace!("--- Slave RX transmission end"); | ||
| 1046 | Ok(total_len - remaining_len) // Return N bytes read | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | // A blocking write operation | ||
| 1050 | fn slave_write_internal(&mut self, write: &[u8], timeout: Timeout) -> Result<(), Error> { | ||
| 1051 | let completed_chunks = write.len() / 255; | ||
| 1052 | let total_chunks = if completed_chunks * 255 == write.len() { | ||
| 1053 | completed_chunks | ||
| 1054 | } else { | ||
| 1055 | completed_chunks + 1 | ||
| 1056 | }; | ||
| 1057 | let last_chunk_idx = total_chunks.saturating_sub(1); | ||
| 1058 | |||
| 1059 | for (number, chunk) in write.chunks(255).enumerate() { | ||
| 1060 | trace!( | ||
| 1061 | "--- Slave TX transmission start - chunk: {}, size: {}", | ||
| 1062 | number, | ||
| 1063 | chunk.len() | ||
| 1064 | ); | ||
| 1065 | if number == 0 { | ||
| 1066 | Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); | ||
| 1067 | } else { | ||
| 1068 | Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | let mut index = 0; | ||
| 1072 | |||
| 1073 | for byte in chunk { | ||
| 1074 | // Wait until we are allowed to send data | ||
| 1075 | // (START has been ACKed or last byte when through) | ||
| 1076 | self.wait_txis(timeout)?; | ||
| 1077 | |||
| 1078 | { | ||
| 1079 | trace!("Slave TX data {}: {:#04x}", index, byte); | ||
| 1080 | index = index + 1; | ||
| 1081 | } | ||
| 1082 | self.info.regs.txdr().write(|w| w.set_txdata(*byte)); | ||
| 1083 | } | ||
| 1084 | } | ||
| 1085 | self.wait_af(timeout)?; | ||
| 1086 | self.flush_txdr(); | ||
| 1087 | self.wait_stop_or_err(timeout)?; | ||
| 1088 | |||
| 1089 | trace!("--- Slave TX transmission end"); | ||
| 1090 | Ok(()) | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | /// Listen for incoming I2C messages. | ||
| 1094 | /// | ||
| 1095 | /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. | ||
| 1096 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 1097 | let state = self.state; | ||
| 1098 | self.info.regs.cr1().modify(|reg| { | ||
| 1099 | reg.set_addrie(true); | ||
| 1100 | trace!("Enable ADDRIE"); | ||
| 1101 | }); | ||
| 1102 | |||
| 1103 | poll_fn(|cx| { | ||
| 1104 | state.waker.register(cx.waker()); | ||
| 1105 | let isr = self.info.regs.isr().read(); | ||
| 1106 | if !isr.addr() { | ||
| 1107 | Poll::Pending | ||
| 1108 | } else { | ||
| 1109 | trace!("ADDR triggered (address match)"); | ||
| 1110 | // we do not clear the address flag here as it will be cleared by the dma read/write | ||
| 1111 | // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it | ||
| 1112 | match isr.dir() { | ||
| 1113 | i2c::vals::Dir::WRITE => { | ||
| 1114 | trace!("DIR: write"); | ||
| 1115 | Poll::Ready(Ok(SlaveCommand { | ||
| 1116 | kind: SlaveCommandKind::Write, | ||
| 1117 | address: self.determine_matched_address()?, | ||
| 1118 | })) | ||
| 1119 | } | ||
| 1120 | i2c::vals::Dir::READ => { | ||
| 1121 | trace!("DIR: read"); | ||
| 1122 | Poll::Ready(Ok(SlaveCommand { | ||
| 1123 | kind: SlaveCommandKind::Read, | ||
| 1124 | address: self.determine_matched_address()?, | ||
| 1125 | })) | ||
| 1126 | } | ||
| 1127 | } | ||
| 1128 | } | ||
| 1129 | }) | ||
| 1130 | .await | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | /// Respond to a write command. | ||
| 1134 | /// | ||
| 1135 | /// Returns total number of bytes received. | ||
| 1136 | pub fn blocking_respond_to_write(&self, read: &mut [u8]) -> Result<usize, Error> { | ||
| 1137 | let timeout = self.timeout(); | ||
| 1138 | self.slave_read_internal(read, timeout) | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | /// Respond to a read command. | ||
| 1142 | pub fn blocking_respond_to_read(&mut self, write: &[u8]) -> Result<(), Error> { | ||
| 1143 | let timeout = self.timeout(); | ||
| 1144 | self.slave_write_internal(write, timeout) | ||
| 1145 | } | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | impl<'d> I2c<'d, Async, MultiMaster> { | ||
| 1149 | /// Respond to a write command. | ||
| 1150 | /// | ||
| 1151 | /// Returns the total number of bytes received. | ||
| 1152 | pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 1153 | let timeout = self.timeout(); | ||
| 1154 | timeout.with(self.read_dma_internal_slave(buffer, timeout)).await | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | /// Respond to a read request from an I2C master. | ||
| 1158 | pub async fn respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> { | ||
| 1159 | let timeout = self.timeout(); | ||
| 1160 | timeout.with(self.write_dma_internal_slave(write, timeout)).await | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | // for data reception in slave mode | ||
| 1164 | // | ||
| 1165 | // returns the total number of bytes received | ||
| 1166 | async fn read_dma_internal_slave(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 1167 | let total_len = buffer.len(); | ||
| 1168 | let mut remaining_len = total_len; | ||
| 1169 | |||
| 1170 | let regs = self.info.regs; | ||
| 1171 | |||
| 1172 | let dma_transfer = unsafe { | ||
| 1173 | regs.cr1().modify(|w| { | ||
| 1174 | w.set_rxdmaen(true); | ||
| 1175 | w.set_stopie(true); | ||
| 1176 | w.set_tcie(true); | ||
| 1177 | }); | ||
| 1178 | let src = regs.rxdr().as_ptr() as *mut u8; | ||
| 1179 | |||
| 1180 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1181 | }; | ||
| 1182 | |||
| 1183 | let state = self.state; | ||
| 1184 | |||
| 1185 | let on_drop = OnDrop::new(|| { | ||
| 1186 | regs.cr1().modify(|w| { | ||
| 1187 | w.set_rxdmaen(false); | ||
| 1188 | w.set_stopie(false); | ||
| 1189 | w.set_tcie(false); | ||
| 1190 | }); | ||
| 1191 | }); | ||
| 1192 | |||
| 1193 | let total_received = poll_fn(|cx| { | ||
| 1194 | state.waker.register(cx.waker()); | ||
| 1195 | |||
| 1196 | let isr = regs.isr().read(); | ||
| 1197 | |||
| 1198 | if remaining_len == total_len { | ||
| 1199 | Self::slave_start(self.info, total_len.min(255), total_len > 255); | ||
| 1200 | remaining_len = remaining_len.saturating_sub(255); | ||
| 1201 | Poll::Pending | ||
| 1202 | } else if isr.tcr() { | ||
| 1203 | let is_last_slice = remaining_len <= 255; | ||
| 1204 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | ||
| 1205 | return Poll::Ready(Err(e)); | ||
| 1206 | } | ||
| 1207 | remaining_len = remaining_len.saturating_sub(255); | ||
| 1208 | regs.cr1().modify(|w| w.set_tcie(true)); | ||
| 1209 | Poll::Pending | ||
| 1210 | } else if isr.stopf() { | ||
| 1211 | regs.icr().write(|reg| reg.set_stopcf(true)); | ||
| 1212 | let poll = Poll::Ready(Ok(total_len - remaining_len)); | ||
| 1213 | poll | ||
| 1214 | } else { | ||
| 1215 | Poll::Pending | ||
| 1216 | } | ||
| 1217 | }) | ||
| 1218 | .await?; | ||
| 1219 | |||
| 1220 | dma_transfer.await; | ||
| 1221 | |||
| 1222 | drop(on_drop); | ||
| 1223 | |||
| 1224 | Ok(total_received) | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | async fn write_dma_internal_slave(&mut self, buffer: &[u8], timeout: Timeout) -> Result<SendStatus, Error> { | ||
| 1228 | let total_len = buffer.len(); | ||
| 1229 | let mut remaining_len = total_len; | ||
| 1230 | |||
| 1231 | let mut dma_transfer = unsafe { | ||
| 1232 | let regs = self.info.regs; | ||
| 1233 | regs.cr1().modify(|w| { | ||
| 1234 | w.set_txdmaen(true); | ||
| 1235 | w.set_stopie(true); | ||
| 1236 | w.set_tcie(true); | ||
| 1237 | }); | ||
| 1238 | let dst = regs.txdr().as_ptr() as *mut u8; | ||
| 1239 | |||
| 1240 | self.tx_dma.as_mut().unwrap().write(buffer, dst, Default::default()) | ||
| 1241 | }; | ||
| 1242 | |||
| 1243 | let on_drop = OnDrop::new(|| { | ||
| 1244 | let regs = self.info.regs; | ||
| 1245 | regs.cr1().modify(|w| { | ||
| 1246 | w.set_txdmaen(false); | ||
| 1247 | w.set_stopie(false); | ||
| 1248 | w.set_tcie(false); | ||
| 1249 | }) | ||
| 1250 | }); | ||
| 1251 | |||
| 1252 | let state = self.state; | ||
| 1253 | |||
| 1254 | let size = poll_fn(|cx| { | ||
| 1255 | state.waker.register(cx.waker()); | ||
| 1256 | |||
| 1257 | let isr = self.info.regs.isr().read(); | ||
| 1258 | |||
| 1259 | if remaining_len == total_len { | ||
| 1260 | Self::slave_start(self.info, total_len.min(255), total_len > 255); | ||
| 1261 | remaining_len = remaining_len.saturating_sub(255); | ||
| 1262 | Poll::Pending | ||
| 1263 | } else if isr.tcr() { | ||
| 1264 | let is_last_slice = remaining_len <= 255; | ||
| 1265 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { | ||
| 1266 | return Poll::Ready(Err(e)); | ||
| 1267 | } | ||
| 1268 | remaining_len = remaining_len.saturating_sub(255); | ||
| 1269 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | ||
| 1270 | Poll::Pending | ||
| 1271 | } else if isr.stopf() { | ||
| 1272 | self.info.regs.icr().write(|reg| reg.set_stopcf(true)); | ||
| 1273 | if remaining_len > 0 { | ||
| 1274 | dma_transfer.request_stop(); | ||
| 1275 | Poll::Ready(Ok(SendStatus::LeftoverBytes(remaining_len as usize))) | ||
| 1276 | } else { | ||
| 1277 | Poll::Ready(Ok(SendStatus::Done)) | ||
| 1278 | } | ||
| 1279 | } else { | ||
| 1280 | Poll::Pending | ||
| 1281 | } | ||
| 1282 | }) | ||
| 1283 | .await?; | ||
| 1284 | |||
| 1285 | dma_transfer.await; | ||
| 1286 | |||
| 1287 | drop(on_drop); | ||
| 1288 | |||
| 1289 | Ok(size) | ||
| 1290 | } | ||
| 1291 | } | ||
| 1292 | |||
| 736 | /// I2C Stop Configuration | 1293 | /// I2C Stop Configuration |
| 737 | /// | 1294 | /// |
| 738 | /// Peripheral options for generating the STOP condition | 1295 | /// Peripheral options for generating the STOP condition |
| @@ -857,7 +1414,7 @@ impl Timings { | |||
| 857 | } | 1414 | } |
| 858 | } | 1415 | } |
| 859 | 1416 | ||
| 860 | impl<'d, M: Mode> SetConfig for I2c<'d, M> { | 1417 | impl<'d, M: Mode> SetConfig for I2c<'d, M, Master> { |
| 861 | type Config = Hertz; | 1418 | type Config = Hertz; |
| 862 | type ConfigError = (); | 1419 | type ConfigError = (); |
| 863 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { | 1420 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { |
| @@ -882,3 +1439,21 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M> { | |||
| 882 | Ok(()) | 1439 | Ok(()) |
| 883 | } | 1440 | } |
| 884 | } | 1441 | } |
| 1442 | |||
| 1443 | impl<'d, M: Mode> SetConfig for I2c<'d, M, MultiMaster> { | ||
| 1444 | type Config = (Hertz, SlaveAddrConfig); | ||
| 1445 | type ConfigError = (); | ||
| 1446 | fn set_config(&mut self, (config, addr_config): &Self::Config) -> Result<(), ()> { | ||
| 1447 | let timings = Timings::new(self.kernel_clock, *config); | ||
| 1448 | self.info.regs.timingr().write(|reg| { | ||
| 1449 | reg.set_presc(timings.prescale); | ||
| 1450 | reg.set_scll(timings.scll); | ||
| 1451 | reg.set_sclh(timings.sclh); | ||
| 1452 | reg.set_sdadel(timings.sdadel); | ||
| 1453 | reg.set_scldel(timings.scldel); | ||
| 1454 | }); | ||
| 1455 | self.init_slave(*addr_config); | ||
| 1456 | |||
| 1457 | Ok(()) | ||
| 1458 | } | ||
| 1459 | } | ||
diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 20cd20dca..670d8332c 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs | |||
| @@ -104,9 +104,6 @@ impl Ipcc { | |||
| 104 | rcc::enable_and_reset::<IPCC>(); | 104 | rcc::enable_and_reset::<IPCC>(); |
| 105 | IPCC::set_cpu2(true); | 105 | IPCC::set_cpu2(true); |
| 106 | 106 | ||
| 107 | // set RF wake-up clock = LSE | ||
| 108 | crate::pac::RCC.csr().modify(|w| w.set_rfwkpsel(0b01)); | ||
| 109 | |||
| 110 | let regs = IPCC::regs(); | 107 | let regs = IPCC::regs(); |
| 111 | 108 | ||
| 112 | regs.cpu(0).cr().modify(|w| { | 109 | regs.cpu(0).cr().modify(|w| { |
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 973acc9bb..c7a33ed72 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -178,7 +178,7 @@ pub use crate::_generated::interrupt; | |||
| 178 | macro_rules! bind_interrupts { | 178 | macro_rules! bind_interrupts { |
| 179 | ($(#[$outer:meta])* $vis:vis struct $name:ident { | 179 | ($(#[$outer:meta])* $vis:vis struct $name:ident { |
| 180 | $( | 180 | $( |
| 181 | $(#[$inner:meta])* | 181 | $(#[doc = $doc:literal])* |
| 182 | $(#[cfg($cond_irq:meta)])? | 182 | $(#[cfg($cond_irq:meta)])? |
| 183 | $irq:ident => $( | 183 | $irq:ident => $( |
| 184 | $(#[cfg($cond_handler:meta)])? | 184 | $(#[cfg($cond_handler:meta)])? |
| @@ -194,13 +194,15 @@ macro_rules! bind_interrupts { | |||
| 194 | #[allow(non_snake_case)] | 194 | #[allow(non_snake_case)] |
| 195 | #[no_mangle] | 195 | #[no_mangle] |
| 196 | $(#[cfg($cond_irq)])? | 196 | $(#[cfg($cond_irq)])? |
| 197 | $(#[$inner])* | 197 | $(#[doc = $doc])* |
| 198 | unsafe extern "C" fn $irq() { | 198 | unsafe extern "C" fn $irq() { |
| 199 | $( | 199 | unsafe { |
| 200 | $(#[cfg($cond_handler)])? | 200 | $( |
| 201 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); | 201 | $(#[cfg($cond_handler)])? |
| 202 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); | ||
| 202 | 203 | ||
| 203 | )* | 204 | )* |
| 205 | } | ||
| 204 | } | 206 | } |
| 205 | 207 | ||
| 206 | $(#[cfg($cond_irq)])? | 208 | $(#[cfg($cond_irq)])? |
| @@ -242,12 +244,12 @@ pub struct Config { | |||
| 242 | #[cfg(dbgmcu)] | 244 | #[cfg(dbgmcu)] |
| 243 | pub enable_debug_during_sleep: bool, | 245 | pub enable_debug_during_sleep: bool, |
| 244 | 246 | ||
| 245 | /// On low-power boards (eg. `stm32l4`, `stm32l5` and `stm32u5`), | 247 | /// On low-power boards (eg. `stm32l4`, `stm32l5`, `stm32wba` and `stm32u5`), |
| 246 | /// some GPIO pins are powered by an auxiliary, independent power supply (`VDDIO2`), | 248 | /// some GPIO pins are powered by an auxiliary, independent power supply (`VDDIO2`), |
| 247 | /// which needs to be enabled before these pins can be used. | 249 | /// which needs to be enabled before these pins can be used. |
| 248 | /// | 250 | /// |
| 249 | /// May increase power consumption. Defaults to true. | 251 | /// May increase power consumption. Defaults to true. |
| 250 | #[cfg(any(stm32l4, stm32l5, stm32u5))] | 252 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] |
| 251 | pub enable_independent_io_supply: bool, | 253 | pub enable_independent_io_supply: bool, |
| 252 | 254 | ||
| 253 | /// On the U5 series all analog peripherals are powered by a separate supply. | 255 | /// On the U5 series all analog peripherals are powered by a separate supply. |
| @@ -291,7 +293,7 @@ impl Default for Config { | |||
| 291 | rcc: Default::default(), | 293 | rcc: Default::default(), |
| 292 | #[cfg(dbgmcu)] | 294 | #[cfg(dbgmcu)] |
| 293 | enable_debug_during_sleep: true, | 295 | enable_debug_during_sleep: true, |
| 294 | #[cfg(any(stm32l4, stm32l5, stm32u5))] | 296 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] |
| 295 | enable_independent_io_supply: true, | 297 | enable_independent_io_supply: true, |
| 296 | #[cfg(stm32u5)] | 298 | #[cfg(stm32u5)] |
| 297 | enable_independent_analog_supply: true, | 299 | enable_independent_analog_supply: true, |
| @@ -540,6 +542,13 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 540 | w.set_iosv(config.enable_independent_io_supply); | 542 | w.set_iosv(config.enable_independent_io_supply); |
| 541 | }); | 543 | }); |
| 542 | } | 544 | } |
| 545 | #[cfg(stm32wba)] | ||
| 546 | { | ||
| 547 | use crate::pac::pwr::vals; | ||
| 548 | crate::pac::PWR.svmcr().modify(|w| { | ||
| 549 | w.set_io2sv(vals::Io2sv::B_0X1); | ||
| 550 | }); | ||
| 551 | } | ||
| 543 | #[cfg(stm32u5)] | 552 | #[cfg(stm32u5)] |
| 544 | { | 553 | { |
| 545 | crate::pac::PWR.svmcr().modify(|w| { | 554 | crate::pac::PWR.svmcr().modify(|w| { |
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 7734365f1..d13df5a6b 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs | |||
| @@ -124,15 +124,18 @@ pub enum StopMode { | |||
| 124 | Stop2, | 124 | Stop2, |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] | 127 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] |
| 128 | use stm32_metapac::pwr::vals::Lpms; | 128 | use stm32_metapac::pwr::vals::Lpms; |
| 129 | 129 | ||
| 130 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] | 130 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] |
| 131 | impl Into<Lpms> for StopMode { | 131 | impl Into<Lpms> for StopMode { |
| 132 | fn into(self) -> Lpms { | 132 | fn into(self) -> Lpms { |
| 133 | match self { | 133 | match self { |
| 134 | StopMode::Stop1 => Lpms::STOP1, | 134 | StopMode::Stop1 => Lpms::STOP1, |
| 135 | #[cfg(not(stm32wba))] | ||
| 135 | StopMode::Stop2 => Lpms::STOP2, | 136 | StopMode::Stop2 => Lpms::STOP2, |
| 137 | #[cfg(stm32wba)] | ||
| 138 | StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? | ||
| 136 | } | 139 | } |
| 137 | } | 140 | } |
| 138 | } | 141 | } |
| @@ -198,7 +201,7 @@ impl Executor { | |||
| 198 | 201 | ||
| 199 | #[allow(unused_variables)] | 202 | #[allow(unused_variables)] |
| 200 | fn configure_stop(&mut self, stop_mode: StopMode) { | 203 | fn configure_stop(&mut self, stop_mode: StopMode) { |
| 201 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] | 204 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba))] |
| 202 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); | 205 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); |
| 203 | #[cfg(stm32h5)] | 206 | #[cfg(stm32h5)] |
| 204 | crate::pac::PWR.pmcr().modify(|v| { | 207 | crate::pac::PWR.pmcr().modify(|v| { |
diff --git a/embassy-stm32/src/lptim/timer/mod.rs b/embassy-stm32/src/lptim/timer/mod.rs index a629be62b..648da5940 100644 --- a/embassy-stm32/src/lptim/timer/mod.rs +++ b/embassy-stm32/src/lptim/timer/mod.rs | |||
| @@ -115,6 +115,31 @@ impl<'d, T: Instance> Timer<'d, T> { | |||
| 115 | .ccmr(0) | 115 | .ccmr(0) |
| 116 | .modify(|w| w.set_ccsel(channel.index(), direction.into())); | 116 | .modify(|w| w.set_ccsel(channel.index(), direction.into())); |
| 117 | } | 117 | } |
| 118 | |||
| 119 | /// Enable the timer interrupt. | ||
| 120 | pub fn enable_interrupt(&self) { | ||
| 121 | T::regs().dier().modify(|w| w.set_arrmie(true)); | ||
| 122 | } | ||
| 123 | |||
| 124 | /// Disable the timer interrupt. | ||
| 125 | pub fn disable_interrupt(&self) { | ||
| 126 | T::regs().dier().modify(|w| w.set_arrmie(false)); | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Check if the timer interrupt is enabled. | ||
| 130 | pub fn is_interrupt_enabled(&self) -> bool { | ||
| 131 | T::regs().dier().read().arrmie() | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Check if the timer interrupt is pending. | ||
| 135 | pub fn is_interrupt_pending(&self) -> bool { | ||
| 136 | T::regs().isr().read().arrm() | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Clear the timer interrupt. | ||
| 140 | pub fn clear_interrupt(&self) { | ||
| 141 | T::regs().icr().write(|w| w.set_arrmcf(true)); | ||
| 142 | } | ||
| 118 | } | 143 | } |
| 119 | 144 | ||
| 120 | #[cfg(not(any(lptim_v2a, lptim_v2b)))] | 145 | #[cfg(not(any(lptim_v2a, lptim_v2b)))] |
| @@ -128,4 +153,29 @@ impl<'d, T: Instance> Timer<'d, T> { | |||
| 128 | pub fn get_compare_value(&self) -> u16 { | 153 | pub fn get_compare_value(&self) -> u16 { |
| 129 | T::regs().cmp().read().cmp() | 154 | T::regs().cmp().read().cmp() |
| 130 | } | 155 | } |
| 156 | |||
| 157 | /// Enable the timer interrupt. | ||
| 158 | pub fn enable_interrupt(&self) { | ||
| 159 | T::regs().ier().modify(|w| w.set_arrmie(true)); | ||
| 160 | } | ||
| 161 | |||
| 162 | /// Disable the timer interrupt. | ||
| 163 | pub fn disable_interrupt(&self) { | ||
| 164 | T::regs().ier().modify(|w| w.set_arrmie(false)); | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Check if the timer interrupt is enabled. | ||
| 168 | pub fn is_interrupt_enabled(&self) -> bool { | ||
| 169 | T::regs().ier().read().arrmie() | ||
| 170 | } | ||
| 171 | |||
| 172 | /// Check if the timer interrupt is pending. | ||
| 173 | pub fn is_interrupt_pending(&self) -> bool { | ||
| 174 | T::regs().isr().read().arrm() | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Clear the timer interrupt. | ||
| 178 | pub fn clear_interrupt(&self) { | ||
| 179 | T::regs().icr().write(|w| w.set_arrmcf(true)); | ||
| 180 | } | ||
| 131 | } | 181 | } |
diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index 7526bb180..3a0b490ba 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs | |||
| @@ -81,17 +81,28 @@ macro_rules! dma_trait { | |||
| 81 | /// Note: in some chips, ST calls this the "channel", and calls channels "streams". | 81 | /// Note: in some chips, ST calls this the "channel", and calls channels "streams". |
| 82 | /// `embassy-stm32` always uses the "channel" and "request number" names. | 82 | /// `embassy-stm32` always uses the "channel" and "request number" names. |
| 83 | fn request(&self) -> crate::dma::Request; | 83 | fn request(&self) -> crate::dma::Request; |
| 84 | #[doc = "Remap the DMA channel"] | ||
| 85 | fn remap(&self); | ||
| 84 | } | 86 | } |
| 85 | }; | 87 | }; |
| 86 | } | 88 | } |
| 87 | 89 | ||
| 88 | #[allow(unused)] | 90 | #[allow(unused)] |
| 89 | macro_rules! dma_trait_impl { | 91 | macro_rules! dma_trait_impl { |
| 90 | (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $channel:ident, $request:expr) => { | 92 | (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $channel:ident, $request:expr, $remap:expr) => { |
| 91 | impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$channel { | 93 | impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$channel { |
| 92 | fn request(&self) -> crate::dma::Request { | 94 | fn request(&self) -> crate::dma::Request { |
| 93 | $request | 95 | $request |
| 94 | } | 96 | } |
| 97 | |||
| 98 | fn remap(&self) { | ||
| 99 | critical_section::with(|_| { | ||
| 100 | #[allow(unused_unsafe)] | ||
| 101 | unsafe { | ||
| 102 | $remap; | ||
| 103 | } | ||
| 104 | }); | ||
| 105 | } | ||
| 95 | } | 106 | } |
| 96 | }; | 107 | }; |
| 97 | } | 108 | } |
| @@ -111,6 +122,7 @@ macro_rules! new_dma_nonopt { | |||
| 111 | macro_rules! new_dma { | 122 | macro_rules! new_dma { |
| 112 | ($name:ident) => {{ | 123 | ($name:ident) => {{ |
| 113 | let dma = $name; | 124 | let dma = $name; |
| 125 | dma.remap(); | ||
| 114 | let request = dma.request(); | 126 | let request = dma.request(); |
| 115 | Some(crate::dma::ChannelAndRequest { | 127 | Some(crate::dma::ChannelAndRequest { |
| 116 | channel: dma.into(), | 128 | channel: dma.into(), |
diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index 2eb2e61c1..e36719ef3 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs | |||
| @@ -4,10 +4,12 @@ | |||
| 4 | use embassy_hal_internal::PeripheralType; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | 5 | ||
| 6 | use crate::pac::opamp::vals::*; | 6 | use crate::pac::opamp::vals::*; |
| 7 | #[cfg(not(any(stm32g4, stm32f3)))] | ||
| 8 | use crate::rcc::RccInfo; | ||
| 7 | use crate::Peri; | 9 | use crate::Peri; |
| 8 | 10 | ||
| 9 | /// Performs a busy-wait delay for a specified number of microseconds. | 11 | /// Performs a busy-wait delay for a specified number of microseconds. |
| 10 | #[cfg(opamp_g4)] | 12 | #[cfg(opamp_v5)] |
| 11 | fn blocking_delay_ms(ms: u32) { | 13 | fn blocking_delay_ms(ms: u32) { |
| 12 | #[cfg(feature = "time")] | 14 | #[cfg(feature = "time")] |
| 13 | embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); | 15 | embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); |
| @@ -23,13 +25,13 @@ pub enum OpAmpGain { | |||
| 23 | Mul4, | 25 | Mul4, |
| 24 | Mul8, | 26 | Mul8, |
| 25 | Mul16, | 27 | Mul16, |
| 26 | #[cfg(opamp_g4)] | 28 | #[cfg(opamp_v5)] |
| 27 | Mul32, | 29 | Mul32, |
| 28 | #[cfg(opamp_g4)] | 30 | #[cfg(opamp_v5)] |
| 29 | Mul64, | 31 | Mul64, |
| 30 | } | 32 | } |
| 31 | 33 | ||
| 32 | #[cfg(opamp_g4)] | 34 | #[cfg(opamp_v5)] |
| 33 | enum OpAmpDifferentialPair { | 35 | enum OpAmpDifferentialPair { |
| 34 | P, | 36 | P, |
| 35 | N, | 37 | N, |
| @@ -53,7 +55,7 @@ pub struct OpAmpOutput<'d, T: Instance> { | |||
| 53 | /// OpAmp internal outputs, wired directly to ADC inputs. | 55 | /// OpAmp internal outputs, wired directly to ADC inputs. |
| 54 | /// | 56 | /// |
| 55 | /// This struct can be used as an ADC input. | 57 | /// This struct can be used as an ADC input. |
| 56 | #[cfg(opamp_g4)] | 58 | #[cfg(opamp_v5)] |
| 57 | pub struct OpAmpInternalOutput<'d, T: Instance> { | 59 | pub struct OpAmpInternalOutput<'d, T: Instance> { |
| 58 | _inner: &'d OpAmp<'d, T>, | 60 | _inner: &'d OpAmp<'d, T>, |
| 59 | } | 61 | } |
| @@ -67,8 +69,10 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 67 | /// Create a new driver instance. | 69 | /// Create a new driver instance. |
| 68 | /// | 70 | /// |
| 69 | /// Does not enable the opamp, but does set the speed mode on some families. | 71 | /// Does not enable the opamp, but does set the speed mode on some families. |
| 70 | pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self { | 72 | pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_v5)] speed: OpAmpSpeed) -> Self { |
| 71 | #[cfg(opamp_g4)] | 73 | #[cfg(not(any(stm32g4, stm32f3)))] |
| 74 | T::info().rcc.enable_and_reset(); | ||
| 75 | #[cfg(opamp_v5)] | ||
| 72 | T::regs().csr().modify(|w| { | 76 | T::regs().csr().modify(|w| { |
| 73 | w.set_opahsm(speed == OpAmpSpeed::HighSpeed); | 77 | w.set_opahsm(speed == OpAmpSpeed::HighSpeed); |
| 74 | }); | 78 | }); |
| @@ -94,15 +98,15 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 94 | in_pin.set_as_analog(); | 98 | in_pin.set_as_analog(); |
| 95 | out_pin.set_as_analog(); | 99 | out_pin.set_as_analog(); |
| 96 | 100 | ||
| 97 | #[cfg(opamp_g4)] | 101 | #[cfg(opamp_v5)] |
| 98 | let vm_sel = VmSel::OUTPUT; | 102 | let vm_sel = VmSel::OUTPUT; |
| 99 | #[cfg(not(opamp_g4))] | 103 | #[cfg(not(opamp_v5))] |
| 100 | let vm_sel = VmSel::from_bits(0b11); | 104 | let vm_sel = VmSel::from_bits(0b11); |
| 101 | 105 | ||
| 102 | T::regs().csr().modify(|w| { | 106 | T::regs().csr().modify(|w| { |
| 103 | w.set_vp_sel(VpSel::from_bits(in_pin.channel())); | 107 | w.set_vp_sel(VpSel::from_bits(in_pin.channel())); |
| 104 | w.set_vm_sel(vm_sel); | 108 | w.set_vm_sel(vm_sel); |
| 105 | #[cfg(opamp_g4)] | 109 | #[cfg(opamp_v5)] |
| 106 | w.set_opaintoen(false); | 110 | w.set_opaintoen(false); |
| 107 | w.set_opampen(true); | 111 | w.set_opampen(true); |
| 108 | }); | 112 | }); |
| @@ -129,12 +133,12 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 129 | in_pin.set_as_analog(); | 133 | in_pin.set_as_analog(); |
| 130 | out_pin.set_as_analog(); | 134 | out_pin.set_as_analog(); |
| 131 | 135 | ||
| 132 | #[cfg(opamp_g4)] | 136 | #[cfg(opamp_v5)] |
| 133 | let vm_sel = VmSel::PGA; | 137 | let vm_sel = VmSel::PGA; |
| 134 | #[cfg(not(opamp_g4))] | 138 | #[cfg(not(opamp_v5))] |
| 135 | let vm_sel = VmSel::from_bits(0b10); | 139 | let vm_sel = VmSel::from_bits(0b10); |
| 136 | 140 | ||
| 137 | #[cfg(opamp_g4)] | 141 | #[cfg(opamp_v5)] |
| 138 | let pga_gain = match gain { | 142 | let pga_gain = match gain { |
| 139 | OpAmpGain::Mul2 => PgaGain::GAIN2, | 143 | OpAmpGain::Mul2 => PgaGain::GAIN2, |
| 140 | OpAmpGain::Mul4 => PgaGain::GAIN4, | 144 | OpAmpGain::Mul4 => PgaGain::GAIN4, |
| @@ -143,7 +147,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 143 | OpAmpGain::Mul32 => PgaGain::GAIN32, | 147 | OpAmpGain::Mul32 => PgaGain::GAIN32, |
| 144 | OpAmpGain::Mul64 => PgaGain::GAIN64, | 148 | OpAmpGain::Mul64 => PgaGain::GAIN64, |
| 145 | }; | 149 | }; |
| 146 | #[cfg(not(opamp_g4))] | 150 | #[cfg(not(opamp_v5))] |
| 147 | let pga_gain = PgaGain::from_bits(match gain { | 151 | let pga_gain = PgaGain::from_bits(match gain { |
| 148 | OpAmpGain::Mul2 => 0b00, | 152 | OpAmpGain::Mul2 => 0b00, |
| 149 | OpAmpGain::Mul4 => 0b01, | 153 | OpAmpGain::Mul4 => 0b01, |
| @@ -155,7 +159,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 155 | w.set_vp_sel(VpSel::from_bits(in_pin.channel())); | 159 | w.set_vp_sel(VpSel::from_bits(in_pin.channel())); |
| 156 | w.set_vm_sel(vm_sel); | 160 | w.set_vm_sel(vm_sel); |
| 157 | w.set_pga_gain(pga_gain); | 161 | w.set_pga_gain(pga_gain); |
| 158 | #[cfg(opamp_g4)] | 162 | #[cfg(opamp_v5)] |
| 159 | w.set_opaintoen(false); | 163 | w.set_opaintoen(false); |
| 160 | w.set_opampen(true); | 164 | w.set_opampen(true); |
| 161 | }); | 165 | }); |
| @@ -170,7 +174,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 170 | /// preventing it being used elsewhere. The `OpAmpOutput` can then be | 174 | /// preventing it being used elsewhere. The `OpAmpOutput` can then be |
| 171 | /// directly used as an ADC input. The opamp will be disabled when the | 175 | /// directly used as an ADC input. The opamp will be disabled when the |
| 172 | /// [`OpAmpOutput`] is dropped. | 176 | /// [`OpAmpOutput`] is dropped. |
| 173 | #[cfg(opamp_g4)] | 177 | #[cfg(opamp_v5)] |
| 174 | pub fn buffer_dac(&mut self, out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>) -> OpAmpOutput<'_, T> { | 178 | pub fn buffer_dac(&mut self, out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>) -> OpAmpOutput<'_, T> { |
| 175 | out_pin.set_as_analog(); | 179 | out_pin.set_as_analog(); |
| 176 | 180 | ||
| @@ -194,7 +198,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 194 | /// | 198 | /// |
| 195 | /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. | 199 | /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. |
| 196 | /// The opamp output will be disabled when it is dropped. | 200 | /// The opamp output will be disabled when it is dropped. |
| 197 | #[cfg(opamp_g4)] | 201 | #[cfg(opamp_v5)] |
| 198 | pub fn buffer_int( | 202 | pub fn buffer_int( |
| 199 | &mut self, | 203 | &mut self, |
| 200 | pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, | 204 | pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, |
| @@ -204,7 +208,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 204 | T::regs().csr().modify(|w| { | 208 | T::regs().csr().modify(|w| { |
| 205 | w.set_vp_sel(VpSel::from_bits(pin.channel())); | 209 | w.set_vp_sel(VpSel::from_bits(pin.channel())); |
| 206 | w.set_vm_sel(VmSel::OUTPUT); | 210 | w.set_vm_sel(VmSel::OUTPUT); |
| 207 | #[cfg(opamp_g4)] | 211 | #[cfg(opamp_v5)] |
| 208 | w.set_opaintoen(true); | 212 | w.set_opaintoen(true); |
| 209 | w.set_opampen(true); | 213 | w.set_opampen(true); |
| 210 | }); | 214 | }); |
| @@ -220,7 +224,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 220 | /// | 224 | /// |
| 221 | /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. | 225 | /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. |
| 222 | /// The opamp output will be disabled when it is dropped. | 226 | /// The opamp output will be disabled when it is dropped. |
| 223 | #[cfg(opamp_g4)] | 227 | #[cfg(opamp_v5)] |
| 224 | pub fn pga_int( | 228 | pub fn pga_int( |
| 225 | &mut self, | 229 | &mut self, |
| 226 | pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, | 230 | pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, |
| @@ -257,7 +261,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 257 | /// | 261 | /// |
| 258 | /// The returned `OpAmpInternalOutput` struct may be used as an ADC | 262 | /// The returned `OpAmpInternalOutput` struct may be used as an ADC |
| 259 | /// input. The opamp output will be disabled when it is dropped. | 263 | /// input. The opamp output will be disabled when it is dropped. |
| 260 | #[cfg(opamp_g4)] | 264 | #[cfg(opamp_v5)] |
| 261 | pub fn standalone_dac_int( | 265 | pub fn standalone_dac_int( |
| 262 | &mut self, | 266 | &mut self, |
| 263 | m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>, | 267 | m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>, |
| @@ -285,7 +289,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 285 | /// The output pin is held within the returned [`OpAmpOutput`] struct, | 289 | /// The output pin is held within the returned [`OpAmpOutput`] struct, |
| 286 | /// preventing it being used elsewhere. The opamp will be disabled when | 290 | /// preventing it being used elsewhere. The opamp will be disabled when |
| 287 | /// the [`OpAmpOutput`] is dropped. | 291 | /// the [`OpAmpOutput`] is dropped. |
| 288 | #[cfg(opamp_g4)] | 292 | #[cfg(opamp_v5)] |
| 289 | pub fn standalone_dac_ext( | 293 | pub fn standalone_dac_ext( |
| 290 | &mut self, | 294 | &mut self, |
| 291 | m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>, | 295 | m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>, |
| @@ -315,7 +319,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 315 | /// The output pin is held within the returned [`OpAmpOutput`] struct, | 319 | /// The output pin is held within the returned [`OpAmpOutput`] struct, |
| 316 | /// preventing it being used elsewhere. The opamp will be disabled when | 320 | /// preventing it being used elsewhere. The opamp will be disabled when |
| 317 | /// the [`OpAmpOutput`] is dropped. | 321 | /// the [`OpAmpOutput`] is dropped. |
| 318 | #[cfg(opamp_g4)] | 322 | #[cfg(opamp_v5)] |
| 319 | pub fn standalone_ext( | 323 | pub fn standalone_ext( |
| 320 | &mut self, | 324 | &mut self, |
| 321 | p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>, | 325 | p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>, |
| @@ -346,7 +350,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 346 | /// | 350 | /// |
| 347 | /// The returned `OpAmpOutput` struct may be used as an ADC | 351 | /// The returned `OpAmpOutput` struct may be used as an ADC |
| 348 | /// input. The opamp output will be disabled when it is dropped. | 352 | /// input. The opamp output will be disabled when it is dropped. |
| 349 | #[cfg(opamp_g4)] | 353 | #[cfg(opamp_v5)] |
| 350 | pub fn standalone_int( | 354 | pub fn standalone_int( |
| 351 | &mut self, | 355 | &mut self, |
| 352 | p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>, | 356 | p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>, |
| @@ -374,7 +378,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 374 | /// while for high-speed mode, only the P differential pair is calibrated. | 378 | /// while for high-speed mode, only the P differential pair is calibrated. |
| 375 | /// | 379 | /// |
| 376 | /// Calibrating a differential pair requires waiting 12ms in the worst case (binary method). | 380 | /// Calibrating a differential pair requires waiting 12ms in the worst case (binary method). |
| 377 | #[cfg(opamp_g4)] | 381 | #[cfg(opamp_v5)] |
| 378 | pub fn calibrate(&mut self) { | 382 | pub fn calibrate(&mut self) { |
| 379 | T::regs().csr().modify(|w| { | 383 | T::regs().csr().modify(|w| { |
| 380 | w.set_opampen(true); | 384 | w.set_opampen(true); |
| @@ -403,7 +407,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 403 | /// The calibration range is from 0 to 31. | 407 | /// The calibration range is from 0 to 31. |
| 404 | /// | 408 | /// |
| 405 | /// The result is stored in the OPAMP_CSR register. | 409 | /// The result is stored in the OPAMP_CSR register. |
| 406 | #[cfg(opamp_g4)] | 410 | #[cfg(opamp_v5)] |
| 407 | fn calibrate_differential_pair(&mut self, pair: OpAmpDifferentialPair) { | 411 | fn calibrate_differential_pair(&mut self, pair: OpAmpDifferentialPair) { |
| 408 | let mut low = 0; | 412 | let mut low = 0; |
| 409 | let mut high = 31; | 413 | let mut high = 31; |
| @@ -452,6 +456,13 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 452 | } | 456 | } |
| 453 | } | 457 | } |
| 454 | 458 | ||
| 459 | #[cfg(not(any(stm32g4, stm32f3)))] | ||
| 460 | impl<'d, T: Instance> Drop for OpAmp<'d, T> { | ||
| 461 | fn drop(&mut self) { | ||
| 462 | T::info().rcc.disable(); | ||
| 463 | } | ||
| 464 | } | ||
| 465 | |||
| 455 | impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> { | 466 | impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> { |
| 456 | fn drop(&mut self) { | 467 | fn drop(&mut self) { |
| 457 | T::regs().csr().modify(|w| { | 468 | T::regs().csr().modify(|w| { |
| @@ -460,7 +471,7 @@ impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> { | |||
| 460 | } | 471 | } |
| 461 | } | 472 | } |
| 462 | 473 | ||
| 463 | #[cfg(opamp_g4)] | 474 | #[cfg(opamp_v5)] |
| 464 | impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> { | 475 | impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> { |
| 465 | fn drop(&mut self) { | 476 | fn drop(&mut self) { |
| 466 | T::regs().csr().modify(|w| { | 477 | T::regs().csr().modify(|w| { |
| @@ -469,7 +480,14 @@ impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> { | |||
| 469 | } | 480 | } |
| 470 | } | 481 | } |
| 471 | 482 | ||
| 483 | #[cfg(not(any(stm32g4, stm32f3)))] | ||
| 484 | pub(crate) struct Info { | ||
| 485 | rcc: RccInfo, | ||
| 486 | } | ||
| 487 | |||
| 472 | pub(crate) trait SealedInstance { | 488 | pub(crate) trait SealedInstance { |
| 489 | #[cfg(not(any(stm32g4, stm32f3)))] | ||
| 490 | fn info() -> &'static Info; | ||
| 473 | fn regs() -> crate::pac::opamp::Opamp; | 491 | fn regs() -> crate::pac::opamp::Opamp; |
| 474 | } | 492 | } |
| 475 | 493 | ||
| @@ -545,7 +563,7 @@ foreach_peripheral!( | |||
| 545 | }; | 563 | }; |
| 546 | ); | 564 | ); |
| 547 | 565 | ||
| 548 | #[cfg(opamp_g4)] | 566 | #[cfg(opamp_v5)] |
| 549 | macro_rules! impl_opamp_internal_output { | 567 | macro_rules! impl_opamp_internal_output { |
| 550 | ($inst:ident, $adc:ident, $ch:expr) => { | 568 | ($inst:ident, $adc:ident, $ch:expr) => { |
| 551 | foreach_adc!( | 569 | foreach_adc!( |
| @@ -567,7 +585,7 @@ macro_rules! impl_opamp_internal_output { | |||
| 567 | }; | 585 | }; |
| 568 | } | 586 | } |
| 569 | 587 | ||
| 570 | #[cfg(opamp_g4)] | 588 | #[cfg(opamp_v5)] |
| 571 | foreach_peripheral!( | 589 | foreach_peripheral!( |
| 572 | (opamp, OPAMP1) => { | 590 | (opamp, OPAMP1) => { |
| 573 | impl_opamp_internal_output!(OPAMP1, ADC1, 13); | 591 | impl_opamp_internal_output!(OPAMP1, ADC1, 13); |
| @@ -600,6 +618,15 @@ foreach_peripheral!( | |||
| 600 | foreach_peripheral! { | 618 | foreach_peripheral! { |
| 601 | (opamp, $inst:ident) => { | 619 | (opamp, $inst:ident) => { |
| 602 | impl SealedInstance for crate::peripherals::$inst { | 620 | impl SealedInstance for crate::peripherals::$inst { |
| 621 | // G4 and F3 use SYSCFGEN, which is always enabled | ||
| 622 | #[cfg(not(any(stm32g4, stm32f3)))] | ||
| 623 | fn info() -> &'static Info { | ||
| 624 | use crate::rcc::SealedRccPeripheral; | ||
| 625 | static INFO: Info = Info { | ||
| 626 | rcc: crate::peripherals::$inst::RCC_INFO, | ||
| 627 | }; | ||
| 628 | &INFO | ||
| 629 | } | ||
| 603 | fn regs() -> crate::pac::opamp::Opamp { | 630 | fn regs() -> crate::pac::opamp::Opamp { |
| 604 | crate::pac::$inst | 631 | crate::pac::$inst |
| 605 | } | 632 | } |
diff --git a/embassy-stm32/src/qspi/enums.rs b/embassy-stm32/src/qspi/enums.rs index 9ec4c1b43..fa5e36d06 100644 --- a/embassy-stm32/src/qspi/enums.rs +++ b/embassy-stm32/src/qspi/enums.rs | |||
| @@ -331,3 +331,19 @@ impl From<DummyCycles> for u8 { | |||
| 331 | } | 331 | } |
| 332 | } | 332 | } |
| 333 | } | 333 | } |
| 334 | |||
| 335 | #[allow(missing_docs)] | ||
| 336 | #[derive(Copy, Clone)] | ||
| 337 | pub enum SampleShifting { | ||
| 338 | None, | ||
| 339 | HalfCycle, | ||
| 340 | } | ||
| 341 | |||
| 342 | impl From<SampleShifting> for bool { | ||
| 343 | fn from(value: SampleShifting) -> Self { | ||
| 344 | match value { | ||
| 345 | SampleShifting::None => false, | ||
| 346 | SampleShifting::HalfCycle => true, | ||
| 347 | } | ||
| 348 | } | ||
| 349 | } | ||
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 0df057c53..1e20d7cd3 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs | |||
| @@ -58,6 +58,8 @@ pub struct Config { | |||
| 58 | pub fifo_threshold: FIFOThresholdLevel, | 58 | pub fifo_threshold: FIFOThresholdLevel, |
| 59 | /// Minimum number of cycles that chip select must be high between issued commands | 59 | /// Minimum number of cycles that chip select must be high between issued commands |
| 60 | pub cs_high_time: ChipSelectHighTime, | 60 | pub cs_high_time: ChipSelectHighTime, |
| 61 | /// Shift sampling point of input data (none, or half-cycle) | ||
| 62 | pub sample_shifting: SampleShifting, | ||
| 61 | } | 63 | } |
| 62 | 64 | ||
| 63 | impl Default for Config { | 65 | impl Default for Config { |
| @@ -68,6 +70,7 @@ impl Default for Config { | |||
| 68 | prescaler: 128, | 70 | prescaler: 128, |
| 69 | fifo_threshold: FIFOThresholdLevel::_17Bytes, | 71 | fifo_threshold: FIFOThresholdLevel::_17Bytes, |
| 70 | cs_high_time: ChipSelectHighTime::_5Cycle, | 72 | cs_high_time: ChipSelectHighTime::_5Cycle, |
| 73 | sample_shifting: SampleShifting::None, | ||
| 71 | } | 74 | } |
| 72 | } | 75 | } |
| 73 | } | 76 | } |
| @@ -120,7 +123,7 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { | |||
| 120 | T::REGS.cr().modify(|w| { | 123 | T::REGS.cr().modify(|w| { |
| 121 | w.set_en(true); | 124 | w.set_en(true); |
| 122 | //w.set_tcen(false); | 125 | //w.set_tcen(false); |
| 123 | w.set_sshift(false); | 126 | w.set_sshift(config.sample_shifting.into()); |
| 124 | w.set_fthres(config.fifo_threshold.into()); | 127 | w.set_fthres(config.fifo_threshold.into()); |
| 125 | w.set_prescaler(config.prescaler); | 128 | w.set_prescaler(config.prescaler); |
| 126 | w.set_fsel(fsel.into()); | 129 | w.set_fsel(fsel.into()); |
diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index cac2a9149..c2295bab6 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs | |||
| @@ -190,6 +190,8 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 190 | // TODO | 190 | // TODO |
| 191 | lsi: None, | 191 | lsi: None, |
| 192 | lse: None, | 192 | lse: None, |
| 193 | #[cfg(crs)] | ||
| 194 | hsi48: None, | ||
| 193 | ); | 195 | ); |
| 194 | 196 | ||
| 195 | RCC.ccipr() | 197 | RCC.ccipr() |
diff --git a/embassy-stm32/src/rcc/hsi48.rs b/embassy-stm32/src/rcc/hsi48.rs index 3ea5c96c9..49be4af5e 100644 --- a/embassy-stm32/src/rcc/hsi48.rs +++ b/embassy-stm32/src/rcc/hsi48.rs | |||
| @@ -39,9 +39,9 @@ pub(crate) fn init_hsi48(config: Hsi48Config) -> Hertz { | |||
| 39 | }); | 39 | }); |
| 40 | 40 | ||
| 41 | // Enable HSI48 | 41 | // Enable HSI48 |
| 42 | #[cfg(not(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba, stm32f0)))] | 42 | #[cfg(not(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba, stm32f0, stm32c071)))] |
| 43 | let r = RCC.crrcr(); | 43 | let r = RCC.crrcr(); |
| 44 | #[cfg(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba))] | 44 | #[cfg(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba, stm32c071))] |
| 45 | let r = RCC.cr(); | 45 | let r = RCC.cr(); |
| 46 | #[cfg(any(stm32f0))] | 46 | #[cfg(any(stm32f0))] |
| 47 | let r = RCC.cr2(); | 47 | let r = RCC.cr2(); |
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index c50e071fb..96e628b1a 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs | |||
| @@ -74,7 +74,7 @@ macro_rules! impl_peri { | |||
| 74 | }; | 74 | }; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | #[cfg(any(rcc_c0, rcc_g0, rcc_u0))] | 77 | #[cfg(any(rcc_c0, rcc_c0v2, rcc_g0x0, rcc_g0x1, rcc_u0))] |
| 78 | #[allow(unused_imports)] | 78 | #[allow(unused_imports)] |
| 79 | use self::{McoSource as Mco1Source, McoSource as Mco2Source}; | 79 | use self::{McoSource as Mco1Source, McoSource as Mco2Source}; |
| 80 | 80 | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 3733fed56..c41f81816 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -95,6 +95,15 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks { | |||
| 95 | unwrap!(CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init_ref() | 95 | unwrap!(CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init_ref() |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | /// Get the current clock configuration of the chip. | ||
| 99 | pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clocks { | ||
| 100 | // Safety: the existence of a `Peri<RCC>` means that `rcc::init()` | ||
| 101 | // has already been called, so `CLOCK_FREQS` must be initialized. | ||
| 102 | // The clocks could be modified again by `reinit()`, but reinit | ||
| 103 | // (for this reason) requires an exclusive reference to `Peri<RCC>`. | ||
| 104 | unsafe { get_freqs() } | ||
| 105 | } | ||
| 106 | |||
| 98 | pub(crate) trait SealedRccPeripheral { | 107 | pub(crate) trait SealedRccPeripheral { |
| 99 | fn frequency() -> Hertz; | 108 | fn frequency() -> Hertz; |
| 100 | #[allow(dead_code)] | 109 | #[allow(dead_code)] |
| @@ -381,7 +390,7 @@ pub fn disable<T: RccPeripheral>() { | |||
| 381 | /// | 390 | /// |
| 382 | /// This should only be called after `init`. | 391 | /// This should only be called after `init`. |
| 383 | #[cfg(not(feature = "_dual-core"))] | 392 | #[cfg(not(feature = "_dual-core"))] |
| 384 | pub fn reinit(config: Config) { | 393 | pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) { |
| 385 | critical_section::with(|cs| init_rcc(cs, config)) | 394 | critical_section::with(|cs| init_rcc(cs, config)) |
| 386 | } | 395 | } |
| 387 | 396 | ||
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 97eb2eb6d..06895a99a 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs | |||
| @@ -5,7 +5,7 @@ pub use crate::pac::rcc::vals::{ | |||
| 5 | Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, | 5 | Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, |
| 6 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, | 6 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| 7 | }; | 7 | }; |
| 8 | use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; | 8 | use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge}; |
| 9 | #[cfg(all(peri_usb_otg_hs))] | 9 | #[cfg(all(peri_usb_otg_hs))] |
| 10 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; | 10 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; |
| 11 | use crate::pac::{FLASH, PWR, RCC}; | 11 | use crate::pac::{FLASH, PWR, RCC}; |
| @@ -64,6 +64,46 @@ pub struct Pll { | |||
| 64 | pub divr: Option<PllDiv>, | 64 | pub divr: Option<PllDiv>, |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | #[derive(Clone, Copy, PartialEq)] | ||
| 68 | pub enum MsiAutoCalibration { | ||
| 69 | /// MSI auto-calibration is disabled | ||
| 70 | Disabled, | ||
| 71 | /// MSIS is given priority for auto-calibration | ||
| 72 | MSIS, | ||
| 73 | /// MSIK is given priority for auto-calibration | ||
| 74 | MSIK, | ||
| 75 | /// MSIS with fast mode (always on) | ||
| 76 | MsisFast, | ||
| 77 | /// MSIK with fast mode (always on) | ||
| 78 | MsikFast, | ||
| 79 | } | ||
| 80 | |||
| 81 | impl MsiAutoCalibration { | ||
| 82 | const fn default() -> Self { | ||
| 83 | MsiAutoCalibration::Disabled | ||
| 84 | } | ||
| 85 | |||
| 86 | fn base_mode(&self) -> Self { | ||
| 87 | match self { | ||
| 88 | MsiAutoCalibration::Disabled => MsiAutoCalibration::Disabled, | ||
| 89 | MsiAutoCalibration::MSIS => MsiAutoCalibration::MSIS, | ||
| 90 | MsiAutoCalibration::MSIK => MsiAutoCalibration::MSIK, | ||
| 91 | MsiAutoCalibration::MsisFast => MsiAutoCalibration::MSIS, | ||
| 92 | MsiAutoCalibration::MsikFast => MsiAutoCalibration::MSIK, | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | fn is_fast(&self) -> bool { | ||
| 97 | matches!(self, MsiAutoCalibration::MsisFast | MsiAutoCalibration::MsikFast) | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | impl Default for MsiAutoCalibration { | ||
| 102 | fn default() -> Self { | ||
| 103 | Self::default() | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 67 | #[derive(Clone, Copy)] | 107 | #[derive(Clone, Copy)] |
| 68 | pub struct Config { | 108 | pub struct Config { |
| 69 | // base clock sources | 109 | // base clock sources |
| @@ -95,6 +135,7 @@ pub struct Config { | |||
| 95 | 135 | ||
| 96 | /// Per-peripheral kernel clock selection muxes | 136 | /// Per-peripheral kernel clock selection muxes |
| 97 | pub mux: super::mux::ClockMux, | 137 | pub mux: super::mux::ClockMux, |
| 138 | pub auto_calibration: MsiAutoCalibration, | ||
| 98 | } | 139 | } |
| 99 | 140 | ||
| 100 | impl Config { | 141 | impl Config { |
| @@ -116,6 +157,7 @@ impl Config { | |||
| 116 | voltage_range: VoltageScale::RANGE1, | 157 | voltage_range: VoltageScale::RANGE1, |
| 117 | ls: crate::rcc::LsConfig::new(), | 158 | ls: crate::rcc::LsConfig::new(), |
| 118 | mux: super::mux::ClockMux::default(), | 159 | mux: super::mux::ClockMux::default(), |
| 160 | auto_calibration: MsiAutoCalibration::default(), | ||
| 119 | } | 161 | } |
| 120 | } | 162 | } |
| 121 | } | 163 | } |
| @@ -131,7 +173,42 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 131 | PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); | 173 | PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); |
| 132 | while !PWR.vosr().read().vosrdy() {} | 174 | while !PWR.vosr().read().vosrdy() {} |
| 133 | 175 | ||
| 134 | let msis = config.msis.map(|range| { | 176 | let lse_calibration_freq = if config.auto_calibration != MsiAutoCalibration::Disabled { |
| 177 | // LSE must be configured and peripherals clocked for MSI auto-calibration | ||
| 178 | let lse_config = config | ||
| 179 | .ls | ||
| 180 | .lse | ||
| 181 | .clone() | ||
| 182 | .expect("LSE must be configured for MSI auto-calibration"); | ||
| 183 | assert!(lse_config.peripherals_clocked); | ||
| 184 | |||
| 185 | // Expect less than +/- 5% deviation for LSE frequency | ||
| 186 | if (31_100..=34_400).contains(&lse_config.frequency.0) { | ||
| 187 | // Check that the calibration is applied to an active clock | ||
| 188 | match ( | ||
| 189 | config.auto_calibration.base_mode(), | ||
| 190 | config.msis.is_some(), | ||
| 191 | config.msik.is_some(), | ||
| 192 | ) { | ||
| 193 | (MsiAutoCalibration::MSIS, true, _) => { | ||
| 194 | // MSIS is active and using LSE for auto-calibration | ||
| 195 | Some(lse_config.frequency) | ||
| 196 | } | ||
| 197 | (MsiAutoCalibration::MSIK, _, true) => { | ||
| 198 | // MSIK is active and using LSE for auto-calibration | ||
| 199 | Some(lse_config.frequency) | ||
| 200 | } | ||
| 201 | // improper configuration | ||
| 202 | _ => panic!("MSIx auto-calibration is enabled for a source that has not been configured."), | ||
| 203 | } | ||
| 204 | } else { | ||
| 205 | panic!("LSE frequency more than 5% off from 32.768 kHz, cannot use for MSI auto-calibration"); | ||
| 206 | } | ||
| 207 | } else { | ||
| 208 | None | ||
| 209 | }; | ||
| 210 | |||
| 211 | let mut msis = config.msis.map(|range| { | ||
| 135 | // Check MSI output per RM0456 § 11.4.10 | 212 | // Check MSI output per RM0456 § 11.4.10 |
| 136 | match config.voltage_range { | 213 | match config.voltage_range { |
| 137 | VoltageScale::RANGE4 => { | 214 | VoltageScale::RANGE4 => { |
| @@ -156,11 +233,21 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 156 | w.set_msipllen(false); | 233 | w.set_msipllen(false); |
| 157 | w.set_msison(true); | 234 | w.set_msison(true); |
| 158 | }); | 235 | }); |
| 236 | let msis = if let (Some(freq), MsiAutoCalibration::MSIS) = | ||
| 237 | (lse_calibration_freq, config.auto_calibration.base_mode()) | ||
| 238 | { | ||
| 239 | // Enable the MSIS auto-calibration feature | ||
| 240 | RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIS)); | ||
| 241 | RCC.cr().modify(|w| w.set_msipllen(true)); | ||
| 242 | calculate_calibrated_msi_frequency(range, freq) | ||
| 243 | } else { | ||
| 244 | msirange_to_hertz(range) | ||
| 245 | }; | ||
| 159 | while !RCC.cr().read().msisrdy() {} | 246 | while !RCC.cr().read().msisrdy() {} |
| 160 | msirange_to_hertz(range) | 247 | msis |
| 161 | }); | 248 | }); |
| 162 | 249 | ||
| 163 | let msik = config.msik.map(|range| { | 250 | let mut msik = config.msik.map(|range| { |
| 164 | // Check MSI output per RM0456 § 11.4.10 | 251 | // Check MSI output per RM0456 § 11.4.10 |
| 165 | match config.voltage_range { | 252 | match config.voltage_range { |
| 166 | VoltageScale::RANGE4 => { | 253 | VoltageScale::RANGE4 => { |
| @@ -184,10 +271,44 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 184 | RCC.cr().modify(|w| { | 271 | RCC.cr().modify(|w| { |
| 185 | w.set_msikon(true); | 272 | w.set_msikon(true); |
| 186 | }); | 273 | }); |
| 274 | let msik = if let (Some(freq), MsiAutoCalibration::MSIK) = | ||
| 275 | (lse_calibration_freq, config.auto_calibration.base_mode()) | ||
| 276 | { | ||
| 277 | // Enable the MSIK auto-calibration feature | ||
| 278 | RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIK)); | ||
| 279 | RCC.cr().modify(|w| w.set_msipllen(true)); | ||
| 280 | calculate_calibrated_msi_frequency(range, freq) | ||
| 281 | } else { | ||
| 282 | msirange_to_hertz(range) | ||
| 283 | }; | ||
| 187 | while !RCC.cr().read().msikrdy() {} | 284 | while !RCC.cr().read().msikrdy() {} |
| 188 | msirange_to_hertz(range) | 285 | msik |
| 189 | }); | 286 | }); |
| 190 | 287 | ||
| 288 | if let Some(lse_freq) = lse_calibration_freq { | ||
| 289 | // If both MSIS and MSIK are enabled, we need to check if they are using the same internal source. | ||
| 290 | if let (Some(msis_range), Some(msik_range)) = (config.msis, config.msik) { | ||
| 291 | if (msis_range as u8 >> 2) == (msik_range as u8 >> 2) { | ||
| 292 | // Clock source is shared, both will be auto calibrated, recalculate other frequency | ||
| 293 | match config.auto_calibration.base_mode() { | ||
| 294 | MsiAutoCalibration::MSIS => { | ||
| 295 | msik = Some(calculate_calibrated_msi_frequency(msik_range, lse_freq)); | ||
| 296 | } | ||
| 297 | MsiAutoCalibration::MSIK => { | ||
| 298 | msis = Some(calculate_calibrated_msi_frequency(msis_range, lse_freq)); | ||
| 299 | } | ||
| 300 | _ => {} | ||
| 301 | } | ||
| 302 | } | ||
| 303 | } | ||
| 304 | // Check if Fast mode should be used | ||
| 305 | if config.auto_calibration.is_fast() { | ||
| 306 | RCC.cr().modify(|w| { | ||
| 307 | w.set_msipllfast(Msipllfast::FAST); | ||
| 308 | }); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 191 | let hsi = config.hsi.then(|| { | 312 | let hsi = config.hsi.then(|| { |
| 192 | RCC.cr().modify(|w| w.set_hsion(true)); | 313 | RCC.cr().modify(|w| w.set_hsion(true)); |
| 193 | while !RCC.cr().read().hsirdy() {} | 314 | while !RCC.cr().read().hsirdy() {} |
| @@ -514,3 +635,37 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput, voltag | |||
| 514 | 635 | ||
| 515 | PllOutput { p, q, r } | 636 | PllOutput { p, q, r } |
| 516 | } | 637 | } |
| 638 | |||
| 639 | /// Fraction structure for MSI auto-calibration | ||
| 640 | /// Represents the multiplier as numerator/denominator that LSE frequency is multiplied by | ||
| 641 | #[derive(Debug, Clone, Copy)] | ||
| 642 | struct MsiFraction { | ||
| 643 | numerator: u32, | ||
| 644 | denominator: u32, | ||
| 645 | } | ||
| 646 | |||
| 647 | impl MsiFraction { | ||
| 648 | const fn new(numerator: u32, denominator: u32) -> Self { | ||
| 649 | Self { numerator, denominator } | ||
| 650 | } | ||
| 651 | |||
| 652 | /// Calculate the calibrated frequency given an LSE frequency | ||
| 653 | fn calculate_frequency(&self, lse_freq: Hertz) -> Hertz { | ||
| 654 | Hertz(lse_freq.0 * self.numerator / self.denominator) | ||
| 655 | } | ||
| 656 | } | ||
| 657 | |||
| 658 | fn get_msi_calibration_fraction(range: Msirange) -> MsiFraction { | ||
| 659 | // Exploiting the MSIx internals to make calculations compact | ||
| 660 | let denominator = (range as u32 & 0x03) + 1; | ||
| 661 | // Base multipliers are deduced from Table 82: MSI oscillator characteristics in data sheet | ||
| 662 | let numerator = [1465, 122, 94, 12][range as usize >> 2]; | ||
| 663 | |||
| 664 | MsiFraction::new(numerator, denominator) | ||
| 665 | } | ||
| 666 | |||
| 667 | /// Calculate the calibrated MSI frequency for a given range and LSE frequency | ||
| 668 | fn calculate_calibrated_msi_frequency(range: Msirange, lse_freq: Hertz) -> Hertz { | ||
| 669 | let fraction = get_msi_calibration_fraction(range); | ||
| 670 | fraction.calculate_frequency(lse_freq) | ||
| 671 | } | ||
diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs index b9fc4e423..b494997b3 100644 --- a/embassy-stm32/src/rcc/wba.rs +++ b/embassy-stm32/src/rcc/wba.rs | |||
| @@ -176,6 +176,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 176 | // TODO | 176 | // TODO |
| 177 | lse: None, | 177 | lse: None, |
| 178 | lsi: None, | 178 | lsi: None, |
| 179 | pll1_p: None, | ||
| 179 | pll1_q: None, | 180 | pll1_q: None, |
| 180 | ); | 181 | ); |
| 181 | } | 182 | } |
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index cd075f3de..78ccd3e6c 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs | |||
| @@ -66,7 +66,7 @@ pub(crate) enum WakeupPrescaler { | |||
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | #[cfg(any( | 68 | #[cfg(any( |
| 69 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0 | 69 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba |
| 70 | ))] | 70 | ))] |
| 71 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | 71 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { |
| 72 | fn from(val: WakeupPrescaler) -> Self { | 72 | fn from(val: WakeupPrescaler) -> Self { |
| @@ -82,7 +82,7 @@ impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | |||
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | #[cfg(any( | 84 | #[cfg(any( |
| 85 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0 | 85 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba |
| 86 | ))] | 86 | ))] |
| 87 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { | 87 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { |
| 88 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { | 88 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { |
| @@ -227,7 +227,7 @@ impl Rtc { | |||
| 227 | <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend(); | 227 | <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend(); |
| 228 | unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() }; | 228 | unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() }; |
| 229 | 229 | ||
| 230 | #[cfg(not(any(stm32u5, stm32u0)))] | 230 | #[cfg(not(any(stm32u5, stm32u0, stm32wba)))] |
| 231 | { | 231 | { |
| 232 | use crate::pac::EXTI; | 232 | use crate::pac::EXTI; |
| 233 | EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); | 233 | EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); |
| @@ -247,5 +247,11 @@ impl Rtc { | |||
| 247 | RCC.srdamr().modify(|w| w.set_rtcapbamen(true)); | 247 | RCC.srdamr().modify(|w| w.set_rtcapbamen(true)); |
| 248 | RCC.apb3smenr().modify(|w| w.set_rtcapbsmen(true)); | 248 | RCC.apb3smenr().modify(|w| w.set_rtcapbsmen(true)); |
| 249 | } | 249 | } |
| 250 | #[cfg(stm32wba)] | ||
| 251 | { | ||
| 252 | use crate::pac::RCC; | ||
| 253 | // RCC.srdamr().modify(|w| w.set_rtcapbamen(true)); | ||
| 254 | RCC.apb7smenr().modify(|w| w.set_rtcapbsmen(true)); | ||
| 255 | } | ||
| 250 | } | 256 | } |
| 251 | } | 257 | } |
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 49f423f37..449f3008a 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -25,7 +25,7 @@ use crate::time::Hertz; | |||
| 25 | ), | 25 | ), |
| 26 | path = "v2.rs" | 26 | path = "v2.rs" |
| 27 | )] | 27 | )] |
| 28 | #[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5, rtc_v3h7rs), path = "v3.rs")] | 28 | #[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5, rtc_v3h7rs, rtc_v3c0), path = "v3.rs")] |
| 29 | mod _version; | 29 | mod _version; |
| 30 | #[allow(unused_imports)] | 30 | #[allow(unused_imports)] |
| 31 | pub use _version::*; | 31 | pub use _version::*; |
| @@ -296,7 +296,7 @@ trait SealedInstance { | |||
| 296 | const BACKUP_REGISTER_COUNT: usize; | 296 | const BACKUP_REGISTER_COUNT: usize; |
| 297 | 297 | ||
| 298 | #[cfg(feature = "low-power")] | 298 | #[cfg(feature = "low-power")] |
| 299 | #[cfg(not(any(stm32u5, stm32u0)))] | 299 | #[cfg(not(any(stm32wba, stm32u5, stm32u0)))] |
| 300 | const EXTI_WAKEUP_LINE: usize; | 300 | const EXTI_WAKEUP_LINE: usize; |
| 301 | 301 | ||
| 302 | #[cfg(feature = "low-power")] | 302 | #[cfg(feature = "low-power")] |
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 39aa6c5cb..d0b52049e 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs | |||
| @@ -146,7 +146,7 @@ impl SealedInstance for crate::peripherals::RTC { | |||
| 146 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; | 146 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; |
| 147 | } else if #[cfg(any(stm32g0, stm32u0))] { | 147 | } else if #[cfg(any(stm32g0, stm32u0))] { |
| 148 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; | 148 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; |
| 149 | } else if #[cfg(any(stm32l5, stm32h5, stm32u5))] { | 149 | } else if #[cfg(any(stm32l5, stm32h5, stm32u5, stm32wba))] { |
| 150 | type WakeupInterrupt = crate::interrupt::typelevel::RTC; | 150 | type WakeupInterrupt = crate::interrupt::typelevel::RTC; |
| 151 | } | 151 | } |
| 152 | ); | 152 | ); |
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 6a02aae70..6e5d735d7 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -32,25 +32,48 @@ pub struct InterruptHandler<T: Instance> { | |||
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | impl<T: Instance> InterruptHandler<T> { | 34 | impl<T: Instance> InterruptHandler<T> { |
| 35 | fn data_interrupts(enable: bool) { | 35 | fn enable_interrupts() { |
| 36 | let regs = T::regs(); | 36 | let regs = T::regs(); |
| 37 | regs.maskr().write(|w| { | 37 | regs.maskr().write(|w| { |
| 38 | w.set_dcrcfailie(enable); | 38 | w.set_dcrcfailie(true); |
| 39 | w.set_dtimeoutie(enable); | 39 | w.set_dtimeoutie(true); |
| 40 | w.set_dataendie(enable); | 40 | w.set_dataendie(true); |
| 41 | w.set_dbckendie(true); | ||
| 41 | 42 | ||
| 42 | #[cfg(sdmmc_v1)] | 43 | #[cfg(sdmmc_v1)] |
| 43 | w.set_stbiterre(enable); | 44 | w.set_stbiterre(true); |
| 44 | #[cfg(sdmmc_v2)] | 45 | #[cfg(sdmmc_v2)] |
| 45 | w.set_dabortie(enable); | 46 | w.set_dabortie(true); |
| 46 | }); | 47 | }); |
| 47 | } | 48 | } |
| 48 | } | 49 | } |
| 49 | 50 | ||
| 50 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | 51 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { |
| 51 | unsafe fn on_interrupt() { | 52 | unsafe fn on_interrupt() { |
| 52 | Self::data_interrupts(false); | ||
| 53 | T::state().wake(); | 53 | T::state().wake(); |
| 54 | let status = T::regs().star().read(); | ||
| 55 | T::regs().maskr().modify(|w| { | ||
| 56 | if status.dcrcfail() { | ||
| 57 | w.set_dcrcfailie(false) | ||
| 58 | } | ||
| 59 | if status.dtimeout() { | ||
| 60 | w.set_dtimeoutie(false) | ||
| 61 | } | ||
| 62 | if status.dataend() { | ||
| 63 | w.set_dataendie(false) | ||
| 64 | } | ||
| 65 | if status.dbckend() { | ||
| 66 | w.set_dbckendie(false) | ||
| 67 | } | ||
| 68 | #[cfg(sdmmc_v1)] | ||
| 69 | if status.stbiterr() { | ||
| 70 | w.set_stbiterre(false) | ||
| 71 | } | ||
| 72 | #[cfg(sdmmc_v2)] | ||
| 73 | if status.dabort() { | ||
| 74 | w.set_dabortie(false) | ||
| 75 | } | ||
| 76 | }); | ||
| 54 | } | 77 | } |
| 55 | } | 78 | } |
| 56 | 79 | ||
| @@ -225,8 +248,7 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { | |||
| 225 | return Ok((true, 0, ker_ck)); | 248 | return Ok((true, 0, ker_ck)); |
| 226 | } | 249 | } |
| 227 | 250 | ||
| 228 | // `ker_ck / sdmmc_ck` rounded up | 251 | let clk_div = match ker_ck.0.div_ceil(sdmmc_ck) { |
| 229 | let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { | ||
| 230 | 0 | 1 => Ok(0), | 252 | 0 | 1 => Ok(0), |
| 231 | x @ 2..=258 => Ok((x - 2) as u8), | 253 | x @ 2..=258 => Ok((x - 2) as u8), |
| 232 | _ => Err(Error::BadClock), | 254 | _ => Err(Error::BadClock), |
| @@ -244,12 +266,11 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { | |||
| 244 | /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. | 266 | /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. |
| 245 | #[cfg(sdmmc_v2)] | 267 | #[cfg(sdmmc_v2)] |
| 246 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { | 268 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { |
| 247 | // `ker_ck / sdmmc_ck` rounded up | 269 | match ker_ck.0.div_ceil(sdmmc_ck) { |
| 248 | match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { | ||
| 249 | 0 | 1 => Ok((false, 0, ker_ck)), | 270 | 0 | 1 => Ok((false, 0, ker_ck)), |
| 250 | x @ 2..=2046 => { | 271 | x @ 2..=2046 => { |
| 251 | // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2] | 272 | // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2] |
| 252 | let clk_div = ((x + 1) / 2) as u16; | 273 | let clk_div = x.div_ceil(2) as u16; |
| 253 | let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); | 274 | let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); |
| 254 | 275 | ||
| 255 | Ok((false, clk_div, clk)) | 276 | Ok((false, clk_div, clk)) |
| @@ -751,7 +772,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 751 | Self::wait_idle(); | 772 | Self::wait_idle(); |
| 752 | Self::clear_interrupt_flags(); | 773 | Self::clear_interrupt_flags(); |
| 753 | 774 | ||
| 754 | regs.dtimer().write(|w| w.set_datatime(config.data_transfer_timeout)); | ||
| 755 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | 775 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); |
| 756 | 776 | ||
| 757 | #[cfg(sdmmc_v1)] | 777 | #[cfg(sdmmc_v1)] |
| @@ -789,8 +809,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 789 | Self::wait_idle(); | 809 | Self::wait_idle(); |
| 790 | Self::clear_interrupt_flags(); | 810 | Self::clear_interrupt_flags(); |
| 791 | 811 | ||
| 792 | regs.dtimer() | ||
| 793 | .write(|w| w.set_datatime(self.config.data_transfer_timeout)); | ||
| 794 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | 812 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); |
| 795 | 813 | ||
| 796 | #[cfg(sdmmc_v1)] | 814 | #[cfg(sdmmc_v1)] |
| @@ -1007,14 +1025,14 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1007 | // Wait for the abort | 1025 | // Wait for the abort |
| 1008 | while Self::data_active() {} | 1026 | while Self::data_active() {} |
| 1009 | } | 1027 | } |
| 1010 | InterruptHandler::<T>::data_interrupts(false); | 1028 | regs.maskr().write(|_| ()); // disable irqs |
| 1011 | Self::clear_interrupt_flags(); | 1029 | Self::clear_interrupt_flags(); |
| 1012 | Self::stop_datapath(); | 1030 | Self::stop_datapath(); |
| 1013 | } | 1031 | } |
| 1014 | 1032 | ||
| 1015 | /// Wait for a previously started datapath transfer to complete from an interrupt. | 1033 | /// Wait for a previously started datapath transfer to complete from an interrupt. |
| 1016 | #[inline] | 1034 | #[inline] |
| 1017 | async fn complete_datapath_transfer() -> Result<(), Error> { | 1035 | async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { |
| 1018 | let regs = T::regs(); | 1036 | let regs = T::regs(); |
| 1019 | 1037 | ||
| 1020 | let res = poll_fn(|cx| { | 1038 | let res = poll_fn(|cx| { |
| @@ -1034,7 +1052,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1034 | if status.stbiterr() { | 1052 | if status.stbiterr() { |
| 1035 | return Poll::Ready(Err(Error::StBitErr)); | 1053 | return Poll::Ready(Err(Error::StBitErr)); |
| 1036 | } | 1054 | } |
| 1037 | if status.dataend() { | 1055 | let done = match block { |
| 1056 | true => status.dbckend(), | ||
| 1057 | false => status.dataend(), | ||
| 1058 | }; | ||
| 1059 | if done { | ||
| 1038 | return Poll::Ready(Ok(())); | 1060 | return Poll::Ready(Ok(())); |
| 1039 | } | 1061 | } |
| 1040 | Poll::Pending | 1062 | Poll::Pending |
| @@ -1072,10 +1094,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1072 | 512, | 1094 | 512, |
| 1073 | 9, | 1095 | 9, |
| 1074 | ); | 1096 | ); |
| 1075 | InterruptHandler::<T>::data_interrupts(true); | 1097 | InterruptHandler::<T>::enable_interrupts(); |
| 1076 | Self::cmd(common_cmd::read_single_block(address), true)?; | 1098 | Self::cmd(common_cmd::read_single_block(address), true)?; |
| 1077 | 1099 | ||
| 1078 | let res = Self::complete_datapath_transfer().await; | 1100 | let res = Self::complete_datapath_transfer(true).await; |
| 1079 | 1101 | ||
| 1080 | if res.is_ok() { | 1102 | if res.is_ok() { |
| 1081 | on_drop.defuse(); | 1103 | on_drop.defuse(); |
| @@ -1105,7 +1127,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1105 | }; | 1127 | }; |
| 1106 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | 1128 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 |
| 1107 | 1129 | ||
| 1108 | let regs = T::regs(); | ||
| 1109 | let on_drop = OnDrop::new(|| Self::on_drop()); | 1130 | let on_drop = OnDrop::new(|| Self::on_drop()); |
| 1110 | 1131 | ||
| 1111 | let transfer = Self::prepare_datapath_read( | 1132 | let transfer = Self::prepare_datapath_read( |
| @@ -1116,30 +1137,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1116 | 512 * blocks.len() as u32, | 1137 | 512 * blocks.len() as u32, |
| 1117 | 9, | 1138 | 9, |
| 1118 | ); | 1139 | ); |
| 1119 | InterruptHandler::<T>::data_interrupts(true); | 1140 | InterruptHandler::<T>::enable_interrupts(); |
| 1120 | 1141 | ||
| 1121 | Self::cmd(common_cmd::read_multiple_blocks(address), true)?; | 1142 | Self::cmd(common_cmd::read_multiple_blocks(address), true)?; |
| 1122 | 1143 | ||
| 1123 | let res = poll_fn(|cx| { | 1144 | let res = Self::complete_datapath_transfer(false).await; |
| 1124 | T::state().register(cx.waker()); | ||
| 1125 | let status = regs.star().read(); | ||
| 1126 | |||
| 1127 | if status.dcrcfail() { | ||
| 1128 | return Poll::Ready(Err(Error::Crc)); | ||
| 1129 | } | ||
| 1130 | if status.dtimeout() { | ||
| 1131 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1132 | } | ||
| 1133 | #[cfg(sdmmc_v1)] | ||
| 1134 | if status.stbiterr() { | ||
| 1135 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 1136 | } | ||
| 1137 | if status.dataend() { | ||
| 1138 | return Poll::Ready(Ok(())); | ||
| 1139 | } | ||
| 1140 | Poll::Pending | ||
| 1141 | }) | ||
| 1142 | .await; | ||
| 1143 | 1145 | ||
| 1144 | Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 | 1146 | Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 |
| 1145 | Self::clear_interrupt_flags(); | 1147 | Self::clear_interrupt_flags(); |
| @@ -1174,12 +1176,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1174 | Self::cmd(common_cmd::write_single_block(address), true)?; | 1176 | Self::cmd(common_cmd::write_single_block(address), true)?; |
| 1175 | 1177 | ||
| 1176 | let transfer = self.prepare_datapath_write(buffer, 512, 9); | 1178 | let transfer = self.prepare_datapath_write(buffer, 512, 9); |
| 1177 | InterruptHandler::<T>::data_interrupts(true); | 1179 | InterruptHandler::<T>::enable_interrupts(); |
| 1178 | 1180 | ||
| 1179 | #[cfg(sdmmc_v2)] | 1181 | #[cfg(sdmmc_v2)] |
| 1180 | Self::cmd(common_cmd::write_single_block(address), true)?; | 1182 | Self::cmd(common_cmd::write_single_block(address), true)?; |
| 1181 | 1183 | ||
| 1182 | let res = Self::complete_datapath_transfer().await; | 1184 | let res = Self::complete_datapath_transfer(true).await; |
| 1183 | 1185 | ||
| 1184 | match res { | 1186 | match res { |
| 1185 | Ok(_) => { | 1187 | Ok(_) => { |
| @@ -1230,7 +1232,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1230 | 1232 | ||
| 1231 | let block_count = blocks.len(); | 1233 | let block_count = blocks.len(); |
| 1232 | 1234 | ||
| 1233 | let regs = T::regs(); | ||
| 1234 | let on_drop = OnDrop::new(|| Self::on_drop()); | 1235 | let on_drop = OnDrop::new(|| Self::on_drop()); |
| 1235 | 1236 | ||
| 1236 | #[cfg(sdmmc_v1)] | 1237 | #[cfg(sdmmc_v1)] |
| @@ -1238,36 +1239,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1238 | 1239 | ||
| 1239 | // Setup write command | 1240 | // Setup write command |
| 1240 | let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9); | 1241 | let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9); |
| 1241 | InterruptHandler::<T>::data_interrupts(true); | 1242 | InterruptHandler::<T>::enable_interrupts(); |
| 1242 | 1243 | ||
| 1243 | #[cfg(sdmmc_v2)] | 1244 | #[cfg(sdmmc_v2)] |
| 1244 | Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | 1245 | Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 |
| 1245 | 1246 | ||
| 1246 | let res = poll_fn(|cx| { | 1247 | let res = Self::complete_datapath_transfer(false).await; |
| 1247 | T::state().register(cx.waker()); | ||
| 1248 | |||
| 1249 | let status = regs.star().read(); | ||
| 1250 | |||
| 1251 | if status.dcrcfail() { | ||
| 1252 | return Poll::Ready(Err(Error::Crc)); | ||
| 1253 | } | ||
| 1254 | if status.dtimeout() { | ||
| 1255 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1256 | } | ||
| 1257 | if status.txunderr() { | ||
| 1258 | return Poll::Ready(Err(Error::Underrun)); | ||
| 1259 | } | ||
| 1260 | #[cfg(sdmmc_v1)] | ||
| 1261 | if status.stbiterr() { | ||
| 1262 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 1263 | } | ||
| 1264 | if status.dataend() { | ||
| 1265 | return Poll::Ready(Ok(())); | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | Poll::Pending | ||
| 1269 | }) | ||
| 1270 | .await; | ||
| 1271 | 1248 | ||
| 1272 | Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 | 1249 | Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 |
| 1273 | Self::clear_interrupt_flags(); | 1250 | Self::clear_interrupt_flags(); |
| @@ -1349,6 +1326,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1349 | #[cfg(sdmmc_v1)] | 1326 | #[cfg(sdmmc_v1)] |
| 1350 | w.set_bypass(_bypass); | 1327 | w.set_bypass(_bypass); |
| 1351 | }); | 1328 | }); |
| 1329 | regs.dtimer() | ||
| 1330 | .write(|w| w.set_datatime(self.config.data_transfer_timeout)); | ||
| 1352 | 1331 | ||
| 1353 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | 1332 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); |
| 1354 | Self::cmd(common_cmd::idle(), false)?; | 1333 | Self::cmd(common_cmd::idle(), false)?; |
| @@ -1600,10 +1579,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1600 | 64, | 1579 | 64, |
| 1601 | 6, | 1580 | 6, |
| 1602 | ); | 1581 | ); |
| 1603 | InterruptHandler::<T>::data_interrupts(true); | 1582 | InterruptHandler::<T>::enable_interrupts(); |
| 1604 | Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 | 1583 | Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 |
| 1605 | 1584 | ||
| 1606 | let res = Self::complete_datapath_transfer().await; | 1585 | let res = Self::complete_datapath_transfer(true).await; |
| 1607 | 1586 | ||
| 1608 | // Host is allowed to use the new functions at least 8 | 1587 | // Host is allowed to use the new functions at least 8 |
| 1609 | // clocks after the end of the switch command | 1588 | // clocks after the end of the switch command |
| @@ -1660,10 +1639,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1660 | 8, | 1639 | 8, |
| 1661 | 3, | 1640 | 3, |
| 1662 | ); | 1641 | ); |
| 1663 | InterruptHandler::<T>::data_interrupts(true); | 1642 | InterruptHandler::<T>::enable_interrupts(); |
| 1664 | Self::cmd(sd_cmd::send_scr(), true)?; | 1643 | Self::cmd(sd_cmd::send_scr(), true)?; |
| 1665 | 1644 | ||
| 1666 | let res = Self::complete_datapath_transfer().await; | 1645 | let res = Self::complete_datapath_transfer(true).await; |
| 1667 | 1646 | ||
| 1668 | if res.is_ok() { | 1647 | if res.is_ok() { |
| 1669 | on_drop.defuse(); | 1648 | on_drop.defuse(); |
| @@ -1706,10 +1685,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1706 | 64, | 1685 | 64, |
| 1707 | 6, | 1686 | 6, |
| 1708 | ); | 1687 | ); |
| 1709 | InterruptHandler::<T>::data_interrupts(true); | 1688 | InterruptHandler::<T>::enable_interrupts(); |
| 1710 | Self::cmd(sd_cmd::sd_status(), true)?; | 1689 | Self::cmd(sd_cmd::sd_status(), true)?; |
| 1711 | 1690 | ||
| 1712 | let res = Self::complete_datapath_transfer().await; | 1691 | let res = Self::complete_datapath_transfer(true).await; |
| 1713 | 1692 | ||
| 1714 | if res.is_ok() { | 1693 | if res.is_ok() { |
| 1715 | on_drop.defuse(); | 1694 | on_drop.defuse(); |
| @@ -1756,10 +1735,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1756 | 512, | 1735 | 512, |
| 1757 | 9, | 1736 | 9, |
| 1758 | ); | 1737 | ); |
| 1759 | InterruptHandler::<T>::data_interrupts(true); | 1738 | InterruptHandler::<T>::enable_interrupts(); |
| 1760 | Self::cmd(emmc_cmd::send_ext_csd(), true)?; | 1739 | Self::cmd(emmc_cmd::send_ext_csd(), true)?; |
| 1761 | 1740 | ||
| 1762 | let res = Self::complete_datapath_transfer().await; | 1741 | let res = Self::complete_datapath_transfer(true).await; |
| 1763 | 1742 | ||
| 1764 | if res.is_ok() { | 1743 | if res.is_ok() { |
| 1765 | on_drop.defuse(); | 1744 | on_drop.defuse(); |
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 9e2ba093a..c8d83f07e 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -843,7 +843,7 @@ impl<'d> Spi<'d, Async> { | |||
| 843 | 843 | ||
| 844 | set_rxdmaen(self.info.regs, true); | 844 | set_rxdmaen(self.info.regs, true); |
| 845 | 845 | ||
| 846 | let rx_src = self.info.regs.rx_ptr(); | 846 | let rx_src = self.info.regs.rx_ptr::<W>(); |
| 847 | let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; | 847 | let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; |
| 848 | 848 | ||
| 849 | let tx_dst: *mut W = self.info.regs.tx_ptr(); | 849 | let tx_dst: *mut W = self.info.regs.tx_ptr(); |
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 8eec6c0c7..b00cc18ad 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -5,14 +5,12 @@ use core::marker::PhantomData; | |||
| 5 | use stm32_metapac::timer::vals::Ckd; | 5 | use stm32_metapac::timer::vals::Ckd; |
| 6 | 6 | ||
| 7 | use super::low_level::{CountingMode, OutputPolarity, Timer}; | 7 | use super::low_level::{CountingMode, OutputPolarity, Timer}; |
| 8 | use super::simple_pwm::{Ch1, Ch2, Ch3, Ch4, PwmPin}; | 8 | use super::simple_pwm::PwmPin; |
| 9 | use super::{ | 9 | use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; |
| 10 | AdvancedInstance4Channel, Channel, Channel1ComplementaryPin, Channel2ComplementaryPin, Channel3ComplementaryPin, | ||
| 11 | Channel4ComplementaryPin, | ||
| 12 | }; | ||
| 13 | use crate::gpio::{AnyPin, OutputType}; | 10 | use crate::gpio::{AnyPin, OutputType}; |
| 14 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 15 | use crate::timer::low_level::OutputCompareMode; | 12 | use crate::timer::low_level::OutputCompareMode; |
| 13 | use crate::timer::TimerChannel; | ||
| 16 | use crate::Peri; | 14 | use crate::Peri; |
| 17 | 15 | ||
| 18 | /// Complementary PWM pin wrapper. | 16 | /// Complementary PWM pin wrapper. |
| @@ -23,32 +21,23 @@ pub struct ComplementaryPwmPin<'d, T, C> { | |||
| 23 | phantom: PhantomData<(T, C)>, | 21 | phantom: PhantomData<(T, C)>, |
| 24 | } | 22 | } |
| 25 | 23 | ||
| 26 | macro_rules! complementary_channel_impl { | 24 | impl<'d, T: AdvancedInstance4Channel, C: TimerChannel> ComplementaryPwmPin<'d, T, C> { |
| 27 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | 25 | /// Create a new complementary PWM pin instance. |
| 28 | impl<'d, T: AdvancedInstance4Channel> ComplementaryPwmPin<'d, T, $channel> { | 26 | pub fn new(pin: Peri<'d, impl TimerComplementaryPin<T, C>>, output_type: OutputType) -> Self { |
| 29 | #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] | 27 | critical_section::with(|_| { |
| 30 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, output_type: OutputType) -> Self { | 28 | pin.set_low(); |
| 31 | critical_section::with(|_| { | 29 | pin.set_as_af( |
| 32 | pin.set_low(); | 30 | pin.af_num(), |
| 33 | pin.set_as_af( | 31 | crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh), |
| 34 | pin.af_num(), | 32 | ); |
| 35 | crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh), | 33 | }); |
| 36 | ); | 34 | ComplementaryPwmPin { |
| 37 | }); | 35 | _pin: pin.into(), |
| 38 | ComplementaryPwmPin { | 36 | phantom: PhantomData, |
| 39 | _pin: pin.into(), | ||
| 40 | phantom: PhantomData, | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | 37 | } |
| 44 | }; | 38 | } |
| 45 | } | 39 | } |
| 46 | 40 | ||
| 47 | complementary_channel_impl!(new_ch1, Ch1, Channel1ComplementaryPin); | ||
| 48 | complementary_channel_impl!(new_ch2, Ch2, Channel2ComplementaryPin); | ||
| 49 | complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin); | ||
| 50 | complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin); | ||
| 51 | |||
| 52 | /// PWM driver with support for standard and complementary outputs. | 41 | /// PWM driver with support for standard and complementary outputs. |
| 53 | pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> { | 42 | pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> { |
| 54 | inner: Timer<'d, T>, | 43 | inner: Timer<'d, T>, |
| @@ -88,6 +77,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 88 | this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); | 77 | this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); |
| 89 | this.inner.set_output_compare_preload(channel, true); | 78 | this.inner.set_output_compare_preload(channel, true); |
| 90 | }); | 79 | }); |
| 80 | this.inner.set_autoreload_preload(true); | ||
| 91 | 81 | ||
| 92 | this | 82 | this |
| 93 | } | 83 | } |
| @@ -121,7 +111,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 121 | /// | 111 | /// |
| 122 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | 112 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
| 123 | pub fn get_max_duty(&self) -> u16 { | 113 | pub fn get_max_duty(&self) -> u16 { |
| 124 | self.inner.get_max_compare_value() as u16 + 1 | 114 | if self.inner.get_counting_mode().is_center_aligned() { |
| 115 | self.inner.get_max_compare_value() as u16 | ||
| 116 | } else { | ||
| 117 | self.inner.get_max_compare_value() as u16 + 1 | ||
| 118 | } | ||
| 125 | } | 119 | } |
| 126 | 120 | ||
| 127 | /// Set the duty for a given channel. | 121 | /// Set the duty for a given channel. |
| @@ -171,7 +165,11 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm< | |||
| 171 | } | 165 | } |
| 172 | 166 | ||
| 173 | fn get_max_duty(&self) -> Self::Duty { | 167 | fn get_max_duty(&self) -> Self::Duty { |
| 174 | self.inner.get_max_compare_value() as u16 + 1 | 168 | if self.inner.get_counting_mode().is_center_aligned() { |
| 169 | self.inner.get_max_compare_value() as u16 | ||
| 170 | } else { | ||
| 171 | self.inner.get_max_compare_value() as u16 + 1 | ||
| 172 | } | ||
| 175 | } | 173 | } |
| 176 | 174 | ||
| 177 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { | 175 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { |
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index ec8b1ddf1..dda33e7f1 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs | |||
| @@ -6,14 +6,12 @@ use core::pin::Pin; | |||
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; | 8 | use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; |
| 9 | use super::{ | 9 | use super::{CaptureCompareInterruptHandler, Channel, GeneralInstance4Channel, TimerPin}; |
| 10 | CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, | ||
| 11 | GeneralInstance4Channel, | ||
| 12 | }; | ||
| 13 | pub use super::{Ch1, Ch2, Ch3, Ch4}; | 10 | pub use super::{Ch1, Ch2, Ch3, Ch4}; |
| 14 | use crate::gpio::{AfType, AnyPin, Pull}; | 11 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 15 | use crate::interrupt::typelevel::{Binding, Interrupt}; | 12 | use crate::interrupt::typelevel::{Binding, Interrupt}; |
| 16 | use crate::time::Hertz; | 13 | use crate::time::Hertz; |
| 14 | use crate::timer::TimerChannel; | ||
| 17 | use crate::Peri; | 15 | use crate::Peri; |
| 18 | 16 | ||
| 19 | /// Capture pin wrapper. | 17 | /// Capture pin wrapper. |
| @@ -23,27 +21,17 @@ pub struct CapturePin<'d, T, C> { | |||
| 23 | _pin: Peri<'d, AnyPin>, | 21 | _pin: Peri<'d, AnyPin>, |
| 24 | phantom: PhantomData<(T, C)>, | 22 | phantom: PhantomData<(T, C)>, |
| 25 | } | 23 | } |
| 26 | 24 | impl<'d, T: GeneralInstance4Channel, C: TimerChannel> CapturePin<'d, T, C> { | |
| 27 | macro_rules! channel_impl { | 25 | /// Create a new capture pin instance. |
| 28 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | 26 | pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, pull: Pull) -> Self { |
| 29 | impl<'d, T: GeneralInstance4Channel> CapturePin<'d, T, $channel> { | 27 | pin.set_as_af(pin.af_num(), AfType::input(pull)); |
| 30 | #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")] | 28 | CapturePin { |
| 31 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, pull: Pull) -> Self { | 29 | _pin: pin.into(), |
| 32 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | 30 | phantom: PhantomData, |
| 33 | CapturePin { | ||
| 34 | _pin: pin.into(), | ||
| 35 | phantom: PhantomData, | ||
| 36 | } | ||
| 37 | } | ||
| 38 | } | 31 | } |
| 39 | }; | 32 | } |
| 40 | } | 33 | } |
| 41 | 34 | ||
| 42 | channel_impl!(new_ch1, Ch1, Channel1Pin); | ||
| 43 | channel_impl!(new_ch2, Ch2, Channel2Pin); | ||
| 44 | channel_impl!(new_ch3, Ch3, Channel3Pin); | ||
| 45 | channel_impl!(new_ch4, Ch4, Channel4Pin); | ||
| 46 | |||
| 47 | /// Input capture driver. | 35 | /// Input capture driver. |
| 48 | pub struct InputCapture<'d, T: GeneralInstance4Channel> { | 36 | pub struct InputCapture<'d, T: GeneralInstance4Channel> { |
| 49 | inner: Timer<'d, T>, | 37 | inner: Timer<'d, T>, |
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index b29382fc8..7062f5f4c 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -51,6 +51,80 @@ pub enum Ch3 {} | |||
| 51 | /// Channel 4 marker type. | 51 | /// Channel 4 marker type. |
| 52 | pub enum Ch4 {} | 52 | pub enum Ch4 {} |
| 53 | 53 | ||
| 54 | /// Timer channel trait. | ||
| 55 | #[allow(private_bounds)] | ||
| 56 | pub trait TimerChannel: SealedTimerChannel { | ||
| 57 | /// The runtime channel. | ||
| 58 | const CHANNEL: Channel; | ||
| 59 | } | ||
| 60 | |||
| 61 | trait SealedTimerChannel {} | ||
| 62 | |||
| 63 | impl TimerChannel for Ch1 { | ||
| 64 | const CHANNEL: Channel = Channel::Ch1; | ||
| 65 | } | ||
| 66 | |||
| 67 | impl TimerChannel for Ch2 { | ||
| 68 | const CHANNEL: Channel = Channel::Ch2; | ||
| 69 | } | ||
| 70 | |||
| 71 | impl TimerChannel for Ch3 { | ||
| 72 | const CHANNEL: Channel = Channel::Ch3; | ||
| 73 | } | ||
| 74 | |||
| 75 | impl TimerChannel for Ch4 { | ||
| 76 | const CHANNEL: Channel = Channel::Ch4; | ||
| 77 | } | ||
| 78 | |||
| 79 | impl SealedTimerChannel for Ch1 {} | ||
| 80 | impl SealedTimerChannel for Ch2 {} | ||
| 81 | impl SealedTimerChannel for Ch3 {} | ||
| 82 | impl SealedTimerChannel for Ch4 {} | ||
| 83 | |||
| 84 | /// Timer break input. | ||
| 85 | #[derive(Clone, Copy)] | ||
| 86 | pub enum BkIn { | ||
| 87 | /// Break input 1. | ||
| 88 | BkIn1, | ||
| 89 | /// Break input 2. | ||
| 90 | BkIn2, | ||
| 91 | } | ||
| 92 | |||
| 93 | impl BkIn { | ||
| 94 | /// Get the channel index (0..3) | ||
| 95 | pub fn index(&self) -> usize { | ||
| 96 | match self { | ||
| 97 | BkIn::BkIn1 => 0, | ||
| 98 | BkIn::BkIn2 => 1, | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Break input 1 marker type. | ||
| 104 | pub enum BkIn1 {} | ||
| 105 | /// Break input 2 marker type. | ||
| 106 | pub enum BkIn2 {} | ||
| 107 | |||
| 108 | /// Timer channel trait. | ||
| 109 | #[allow(private_bounds)] | ||
| 110 | pub trait BreakInput: SealedBreakInput { | ||
| 111 | /// The runtim timer channel. | ||
| 112 | const INPUT: BkIn; | ||
| 113 | } | ||
| 114 | |||
| 115 | trait SealedBreakInput {} | ||
| 116 | |||
| 117 | impl BreakInput for BkIn1 { | ||
| 118 | const INPUT: BkIn = BkIn::BkIn1; | ||
| 119 | } | ||
| 120 | |||
| 121 | impl BreakInput for BkIn2 { | ||
| 122 | const INPUT: BkIn = BkIn::BkIn2; | ||
| 123 | } | ||
| 124 | |||
| 125 | impl SealedBreakInput for BkIn1 {} | ||
| 126 | impl SealedBreakInput for BkIn2 {} | ||
| 127 | |||
| 54 | /// Amount of bits of a timer. | 128 | /// Amount of bits of a timer. |
| 55 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | 129 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
| 56 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 130 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| @@ -149,33 +223,20 @@ pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + Ad | |||
| 149 | /// Advanced 16-bit timer with 4 channels instance. | 223 | /// Advanced 16-bit timer with 4 channels instance. |
| 150 | pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} | 224 | pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} |
| 151 | 225 | ||
| 152 | pin_trait!(Channel1Pin, GeneralInstance4Channel); | 226 | pin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel); |
| 153 | pin_trait!(Channel2Pin, GeneralInstance4Channel); | ||
| 154 | pin_trait!(Channel3Pin, GeneralInstance4Channel); | ||
| 155 | pin_trait!(Channel4Pin, GeneralInstance4Channel); | ||
| 156 | pin_trait!(ExternalTriggerPin, GeneralInstance4Channel); | 227 | pin_trait!(ExternalTriggerPin, GeneralInstance4Channel); |
| 157 | 228 | ||
| 158 | pin_trait!(Channel1ComplementaryPin, AdvancedInstance4Channel); | 229 | pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel); |
| 159 | pin_trait!(Channel2ComplementaryPin, AdvancedInstance4Channel); | ||
| 160 | pin_trait!(Channel3ComplementaryPin, AdvancedInstance4Channel); | ||
| 161 | pin_trait!(Channel4ComplementaryPin, AdvancedInstance4Channel); | ||
| 162 | |||
| 163 | pin_trait!(BreakInputPin, AdvancedInstance4Channel); | ||
| 164 | pin_trait!(BreakInput2Pin, AdvancedInstance4Channel); | ||
| 165 | 230 | ||
| 166 | pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel); | 231 | pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput); |
| 167 | pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel); | ||
| 168 | 232 | ||
| 169 | pin_trait!(BreakInput2Comparator1Pin, AdvancedInstance4Channel); | 233 | pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput); |
| 170 | pin_trait!(BreakInput2Comparator2Pin, AdvancedInstance4Channel); | 234 | pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput); |
| 171 | 235 | ||
| 172 | // Update Event trigger DMA for every timer | 236 | // Update Event trigger DMA for every timer |
| 173 | dma_trait!(UpDma, BasicInstance); | 237 | dma_trait!(UpDma, BasicInstance); |
| 174 | 238 | ||
| 175 | dma_trait!(Ch1Dma, GeneralInstance4Channel); | 239 | dma_trait!(Dma, GeneralInstance4Channel, TimerChannel); |
| 176 | dma_trait!(Ch2Dma, GeneralInstance4Channel); | ||
| 177 | dma_trait!(Ch3Dma, GeneralInstance4Channel); | ||
| 178 | dma_trait!(Ch4Dma, GeneralInstance4Channel); | ||
| 179 | 240 | ||
| 180 | #[allow(unused)] | 241 | #[allow(unused)] |
| 181 | macro_rules! impl_core_timer { | 242 | macro_rules! impl_core_timer { |
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index 933165ef9..498d9c082 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs | |||
| @@ -7,11 +7,9 @@ use core::pin::Pin; | |||
| 7 | use core::task::{Context, Poll}; | 7 | use core::task::{Context, Poll}; |
| 8 | 8 | ||
| 9 | use super::low_level::{ | 9 | use super::low_level::{ |
| 10 | CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource, | 10 | CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource as Ts, |
| 11 | }; | ||
| 12 | use super::{ | ||
| 13 | CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, ExternalTriggerPin, GeneralInstance4Channel, | ||
| 14 | }; | 11 | }; |
| 12 | use super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin}; | ||
| 15 | pub use super::{Ch1, Ch2}; | 13 | pub use super::{Ch1, Ch2}; |
| 16 | use crate::gpio::{AfType, AnyPin, Pull}; | 14 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 17 | use crate::interrupt::typelevel::{Binding, Interrupt}; | 15 | use crate::interrupt::typelevel::{Binding, Interrupt}; |
| @@ -48,24 +46,78 @@ pub struct TriggerPin<'d, T, C> { | |||
| 48 | phantom: PhantomData<(T, C)>, | 46 | phantom: PhantomData<(T, C)>, |
| 49 | } | 47 | } |
| 50 | 48 | ||
| 51 | macro_rules! channel_impl { | 49 | trait SealedTriggerSource {} |
| 52 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | 50 | |
| 53 | impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, $channel> { | 51 | /// Marker trait for a trigger source. |
| 54 | #[doc = concat!("Create a new ", stringify!($channel), " trigger pin instance.")] | 52 | #[expect(private_bounds)] |
| 55 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, pull: Pull) -> Self { | 53 | pub trait TriggerSource: SealedTriggerSource {} |
| 56 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | 54 | |
| 57 | TriggerPin { | 55 | impl TriggerSource for Ch1 {} |
| 58 | _pin: pin.into(), | 56 | impl TriggerSource for Ch2 {} |
| 59 | phantom: PhantomData, | 57 | impl TriggerSource for Ext {} |
| 60 | } | 58 | |
| 61 | } | 59 | impl SealedTriggerSource for Ch1 {} |
| 62 | } | 60 | impl SealedTriggerSource for Ch2 {} |
| 63 | }; | 61 | impl SealedTriggerSource for Ext {} |
| 62 | |||
| 63 | trait SealedTimerTriggerPin<T, S>: crate::gpio::Pin {} | ||
| 64 | |||
| 65 | /// Marker trait for a trigger pin. | ||
| 66 | #[expect(private_bounds)] | ||
| 67 | // TODO: find better naming scheme than prefixing all pin traits with "Timer". | ||
| 68 | // The trait name cannot conflict with the corresponding type's name. | ||
| 69 | // Applies to other timer submodules as well. | ||
| 70 | pub trait TimerTriggerPin<T, S>: SealedTimerTriggerPin<T, S> { | ||
| 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 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | impl<T, P, C> SealedTimerTriggerPin<T, C> for P | ||
| 97 | where | ||
| 98 | T: GeneralInstance4Channel, | ||
| 99 | P: TimerPin<T, C>, | ||
| 100 | C: super::TimerChannel + TriggerSource, | ||
| 101 | { | ||
| 64 | } | 102 | } |
| 65 | 103 | ||
| 66 | channel_impl!(new_ch1, Ch1, Channel1Pin); | 104 | impl<T, P> SealedTimerTriggerPin<T, Ext> for P |
| 67 | channel_impl!(new_ch2, Ch2, Channel2Pin); | 105 | where |
| 68 | channel_impl!(new_ext, Ext, ExternalTriggerPin); | 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 { | ||
| 116 | _pin: pin.into(), | ||
| 117 | phantom: PhantomData, | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 69 | 121 | ||
| 70 | /// One pulse driver. | 122 | /// One pulse driver. |
| 71 | /// | 123 | /// |
| @@ -89,7 +141,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { | |||
| 89 | ) -> Self { | 141 | ) -> Self { |
| 90 | let mut this = Self { inner: Timer::new(tim) }; | 142 | let mut this = Self { inner: Timer::new(tim) }; |
| 91 | 143 | ||
| 92 | this.inner.set_trigger_source(TriggerSource::TI1F_ED); | 144 | this.inner.set_trigger_source(Ts::TI1F_ED); |
| 93 | this.inner | 145 | this.inner |
| 94 | .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); | 146 | .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); |
| 95 | this.inner | 147 | this.inner |
| @@ -114,7 +166,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { | |||
| 114 | ) -> Self { | 166 | ) -> Self { |
| 115 | let mut this = Self { inner: Timer::new(tim) }; | 167 | let mut this = Self { inner: Timer::new(tim) }; |
| 116 | 168 | ||
| 117 | this.inner.set_trigger_source(TriggerSource::TI1FP1); | 169 | this.inner.set_trigger_source(Ts::TI1FP1); |
| 118 | this.inner | 170 | this.inner |
| 119 | .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); | 171 | .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); |
| 120 | this.inner | 172 | this.inner |
| @@ -131,7 +183,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { | |||
| 131 | /// as an output. | 183 | /// as an output. |
| 132 | pub fn new_ch2( | 184 | pub fn new_ch2( |
| 133 | tim: Peri<'d, T>, | 185 | tim: Peri<'d, T>, |
| 134 | _pin: TriggerPin<'d, T, Ch1>, | 186 | _pin: TriggerPin<'d, T, Ch2>, |
| 135 | _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, | 187 | _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, |
| 136 | freq: Hertz, | 188 | freq: Hertz, |
| 137 | pulse_end: u32, | 189 | pulse_end: u32, |
| @@ -140,7 +192,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { | |||
| 140 | ) -> Self { | 192 | ) -> Self { |
| 141 | let mut this = Self { inner: Timer::new(tim) }; | 193 | let mut this = Self { inner: Timer::new(tim) }; |
| 142 | 194 | ||
| 143 | this.inner.set_trigger_source(TriggerSource::TI2FP2); | 195 | this.inner.set_trigger_source(Ts::TI2FP2); |
| 144 | this.inner | 196 | this.inner |
| 145 | .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal); | 197 | .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal); |
| 146 | this.inner | 198 | this.inner |
| @@ -172,7 +224,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { | |||
| 172 | // No filtering | 224 | // No filtering |
| 173 | r.set_etf(FilterValue::NO_FILTER); | 225 | r.set_etf(FilterValue::NO_FILTER); |
| 174 | }); | 226 | }); |
| 175 | this.inner.set_trigger_source(TriggerSource::ETRF); | 227 | this.inner.set_trigger_source(Ts::ETRF); |
| 176 | this.new_inner(freq, pulse_end, counting_mode); | 228 | this.new_inner(freq, pulse_end, counting_mode); |
| 177 | 229 | ||
| 178 | this | 230 | this |
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 98b798634..1e55f2919 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs | |||
| @@ -1,12 +1,16 @@ | |||
| 1 | //! PWM Input driver. | 1 | //! PWM Input driver. |
| 2 | 2 | ||
| 3 | use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; | 3 | use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; |
| 4 | use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel}; | 4 | use super::{Ch1, Ch2, Channel, GeneralInstance4Channel, TimerPin}; |
| 5 | use crate::gpio::{AfType, Pull}; | 5 | use crate::gpio::{AfType, Pull}; |
| 6 | use crate::time::Hertz; | 6 | use crate::time::Hertz; |
| 7 | use crate::Peri; | 7 | use crate::Peri; |
| 8 | 8 | ||
| 9 | /// PWM Input driver. | 9 | /// PWM Input driver. |
| 10 | /// | ||
| 11 | /// Only works with CH1 or CH2 | ||
| 12 | /// Note: Not all timer peripherals are supported | ||
| 13 | /// Double check your chips reference manual | ||
| 10 | pub struct PwmInput<'d, T: GeneralInstance4Channel> { | 14 | pub struct PwmInput<'d, T: GeneralInstance4Channel> { |
| 11 | channel: Channel, | 15 | channel: Channel, |
| 12 | inner: Timer<'d, T>, | 16 | inner: Timer<'d, T>, |
| @@ -14,14 +18,14 @@ pub struct PwmInput<'d, T: GeneralInstance4Channel> { | |||
| 14 | 18 | ||
| 15 | impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { | 19 | impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { |
| 16 | /// Create a new PWM input driver. | 20 | /// Create a new PWM input driver. |
| 17 | pub fn new(tim: Peri<'d, T>, pin: Peri<'d, impl Channel1Pin<T>>, pull: Pull, freq: Hertz) -> Self { | 21 | pub fn new_ch1(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin<T, Ch1>>, pull: Pull, freq: Hertz) -> Self { |
| 18 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | 22 | pin.set_as_af(pin.af_num(), AfType::input(pull)); |
| 19 | 23 | ||
| 20 | Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) | 24 | Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) |
| 21 | } | 25 | } |
| 22 | 26 | ||
| 23 | /// Create a new PWM input driver. | 27 | /// Create a new PWM input driver. |
| 24 | pub fn new_alt(tim: Peri<'d, T>, pin: Peri<'d, impl Channel2Pin<T>>, pull: Pull, freq: Hertz) -> Self { | 28 | pub fn new_ch2(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin<T, Ch2>>, pull: Pull, freq: Hertz) -> Self { |
| 25 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | 29 | pin.set_as_af(pin.af_num(), AfType::input(pull)); |
| 26 | 30 | ||
| 27 | Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) | 31 | Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) |
| @@ -37,6 +41,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { | |||
| 37 | 41 | ||
| 38 | // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 | 42 | // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 |
| 39 | // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode | 43 | // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode |
| 44 | // or ST RM0440 (STM32G4) chapter 30.4.8 PWM input mode | ||
| 40 | inner.set_input_ti_selection(ch1, InputTISelection::Normal); | 45 | inner.set_input_ti_selection(ch1, InputTISelection::Normal); |
| 41 | inner.set_input_capture_mode(ch1, InputCaptureMode::Rising); | 46 | inner.set_input_capture_mode(ch1, InputCaptureMode::Rising); |
| 42 | 47 | ||
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index f3c81667c..eabe1b22a 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs | |||
| @@ -6,8 +6,9 @@ use stm32_metapac::timer::vals; | |||
| 6 | 6 | ||
| 7 | use super::low_level::Timer; | 7 | use super::low_level::Timer; |
| 8 | pub use super::{Ch1, Ch2}; | 8 | pub use super::{Ch1, Ch2}; |
| 9 | use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel}; | 9 | use super::{GeneralInstance4Channel, TimerPin}; |
| 10 | use crate::gpio::{AfType, AnyPin, Pull}; | 10 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 11 | use crate::timer::TimerChannel; | ||
| 11 | use crate::Peri; | 12 | use crate::Peri; |
| 12 | 13 | ||
| 13 | /// Counting direction | 14 | /// Counting direction |
| @@ -24,26 +25,31 @@ pub struct QeiPin<'d, T, Channel> { | |||
| 24 | phantom: PhantomData<(T, Channel)>, | 25 | phantom: PhantomData<(T, Channel)>, |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | macro_rules! channel_impl { | 28 | impl<'d, T: GeneralInstance4Channel, C: QeiChannel> QeiPin<'d, T, C> { |
| 28 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | 29 | /// Create a new QEI pin instance. |
| 29 | impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> { | 30 | pub fn new(pin: Peri<'d, impl TimerPin<T, C>>) -> Self { |
| 30 | #[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")] | 31 | critical_section::with(|_| { |
| 31 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { | 32 | pin.set_low(); |
| 32 | critical_section::with(|_| { | 33 | pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); |
| 33 | pin.set_low(); | 34 | }); |
| 34 | pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); | 35 | QeiPin { |
| 35 | }); | 36 | _pin: pin.into(), |
| 36 | QeiPin { | 37 | phantom: PhantomData, |
| 37 | _pin: pin.into(), | ||
| 38 | phantom: PhantomData, | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | 38 | } |
| 42 | }; | 39 | } |
| 43 | } | 40 | } |
| 44 | 41 | ||
| 45 | channel_impl!(new_ch1, Ch1, Channel1Pin); | 42 | trait SealedQeiChannel: TimerChannel {} |
| 46 | channel_impl!(new_ch2, Ch2, Channel2Pin); | 43 | |
| 44 | /// Marker trait for a timer channel eligible for use with QEI. | ||
| 45 | #[expect(private_bounds)] | ||
| 46 | pub trait QeiChannel: SealedQeiChannel {} | ||
| 47 | |||
| 48 | impl QeiChannel for Ch1 {} | ||
| 49 | impl QeiChannel for Ch2 {} | ||
| 50 | |||
| 51 | impl SealedQeiChannel for Ch1 {} | ||
| 52 | impl SealedQeiChannel for Ch2 {} | ||
| 47 | 53 | ||
| 48 | /// Quadrature decoder driver. | 54 | /// Quadrature decoder driver. |
| 49 | pub struct Qei<'d, T: GeneralInstance4Channel> { | 55 | pub struct Qei<'d, T: GeneralInstance4Channel> { |
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index f7f433154..c04b1ab97 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -4,22 +4,13 @@ use core::marker::PhantomData; | |||
| 4 | use core::mem::ManuallyDrop; | 4 | use core::mem::ManuallyDrop; |
| 5 | 5 | ||
| 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; | 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; |
| 7 | use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel, TimerBits}; | 7 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; |
| 8 | #[cfg(gpio_v2)] | 8 | #[cfg(gpio_v2)] |
| 9 | use crate::gpio::Pull; | 9 | use crate::gpio::Pull; |
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 11 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 12 | use crate::Peri; | 12 | use crate::Peri; |
| 13 | 13 | ||
| 14 | /// Channel 1 marker type. | ||
| 15 | pub enum Ch1 {} | ||
| 16 | /// Channel 2 marker type. | ||
| 17 | pub enum Ch2 {} | ||
| 18 | /// Channel 3 marker type. | ||
| 19 | pub enum Ch3 {} | ||
| 20 | /// Channel 4 marker type. | ||
| 21 | pub enum Ch4 {} | ||
| 22 | |||
| 23 | /// PWM pin wrapper. | 14 | /// PWM pin wrapper. |
| 24 | /// | 15 | /// |
| 25 | /// This wraps a pin to make it usable with PWM. | 16 | /// This wraps a pin to make it usable with PWM. |
| @@ -43,46 +34,37 @@ pub struct PwmPinConfig { | |||
| 43 | pub pull: Pull, | 34 | pub pull: Pull, |
| 44 | } | 35 | } |
| 45 | 36 | ||
| 46 | macro_rules! channel_impl { | 37 | impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> { |
| 47 | ($new_chx:ident, $new_chx_with_config:ident, $channel:ident, $pin_trait:ident) => { | 38 | /// Create a new PWM pin instance. |
| 48 | impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> { | 39 | pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, output_type: OutputType) -> Self { |
| 49 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] | 40 | critical_section::with(|_| { |
| 50 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, output_type: OutputType) -> Self { | 41 | pin.set_low(); |
| 51 | critical_section::with(|_| { | 42 | pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); |
| 52 | pin.set_low(); | 43 | }); |
| 53 | pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); | 44 | PwmPin { |
| 54 | }); | 45 | _pin: pin.into(), |
| 55 | PwmPin { | 46 | phantom: PhantomData, |
| 56 | _pin: pin.into(), | ||
| 57 | phantom: PhantomData, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with config.")] | ||
| 62 | pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T>>, pin_config: PwmPinConfig) -> Self { | ||
| 63 | critical_section::with(|_| { | ||
| 64 | pin.set_low(); | ||
| 65 | pin.set_as_af( | ||
| 66 | pin.af_num(), | ||
| 67 | #[cfg(gpio_v1)] | ||
| 68 | AfType::output(pin_config.output_type, pin_config.speed), | ||
| 69 | #[cfg(gpio_v2)] | ||
| 70 | AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), | ||
| 71 | ); | ||
| 72 | }); | ||
| 73 | PwmPin { | ||
| 74 | _pin: pin.into(), | ||
| 75 | phantom: PhantomData, | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | 47 | } |
| 79 | }; | 48 | } |
| 80 | } | ||
| 81 | 49 | ||
| 82 | channel_impl!(new_ch1, new_ch1_with_config, Ch1, Channel1Pin); | 50 | /// Create a new PWM pin instance with config. |
| 83 | channel_impl!(new_ch2, new_ch2_with_config, Ch2, Channel2Pin); | 51 | pub fn new_with_config(pin: Peri<'d, impl TimerPin<T, C>>, pin_config: PwmPinConfig) -> Self { |
| 84 | channel_impl!(new_ch3, new_ch3_with_config, Ch3, Channel3Pin); | 52 | critical_section::with(|_| { |
| 85 | channel_impl!(new_ch4, new_ch4_with_config, Ch4, Channel4Pin); | 53 | pin.set_low(); |
| 54 | pin.set_as_af( | ||
| 55 | pin.af_num(), | ||
| 56 | #[cfg(gpio_v1)] | ||
| 57 | AfType::output(pin_config.output_type, pin_config.speed), | ||
| 58 | #[cfg(gpio_v2)] | ||
| 59 | AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), | ||
| 60 | ); | ||
| 61 | }); | ||
| 62 | PwmPin { | ||
| 63 | _pin: pin.into(), | ||
| 64 | phantom: PhantomData, | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 86 | 68 | ||
| 87 | /// A single channel of a pwm, obtained from [`SimplePwm::split`], | 69 | /// A single channel of a pwm, obtained from [`SimplePwm::split`], |
| 88 | /// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc. | 70 | /// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc. |
| @@ -466,107 +448,98 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 466 | } | 448 | } |
| 467 | } | 449 | } |
| 468 | 450 | ||
| 469 | macro_rules! impl_waveform_chx { | 451 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { |
| 470 | ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => { | 452 | /// Generate a sequence of PWM waveform |
| 471 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | 453 | pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) { |
| 472 | /// Generate a sequence of PWM waveform | 454 | use crate::pac::timer::vals::Ccds; |
| 473 | pub async fn $fn_name(&mut self, dma: Peri<'_, impl super::$dma_ch<T>>, duty: &[u16]) { | 455 | |
| 474 | use crate::pac::timer::vals::Ccds; | 456 | #[allow(clippy::let_unit_value)] // eg. stm32f334 |
| 457 | let req = dma.request(); | ||
| 475 | 458 | ||
| 476 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 459 | let cc_channel = C::CHANNEL; |
| 477 | let req = dma.request(); | ||
| 478 | 460 | ||
| 479 | let cc_channel = Channel::$cc_ch; | 461 | let original_duty_state = self.channel(cc_channel).current_duty_cycle(); |
| 462 | let original_enable_state = self.channel(cc_channel).is_enabled(); | ||
| 463 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; | ||
| 464 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); | ||
| 480 | 465 | ||
| 481 | let original_duty_state = self.channel(cc_channel).current_duty_cycle(); | 466 | // redirect CC DMA request onto Update Event |
| 482 | let original_enable_state = self.channel(cc_channel).is_enabled(); | 467 | if !original_cc_dma_on_update { |
| 483 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; | 468 | self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) |
| 484 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); | 469 | } |
| 485 | 470 | ||
| 486 | // redirect CC DMA request onto Update Event | 471 | if !original_cc_dma_enabled { |
| 487 | if !original_cc_dma_on_update { | 472 | self.inner.set_cc_dma_enable_state(cc_channel, true); |
| 488 | self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) | 473 | } |
| 489 | } | ||
| 490 | 474 | ||
| 491 | if !original_cc_dma_enabled { | 475 | if !original_enable_state { |
| 492 | self.inner.set_cc_dma_enable_state(cc_channel, true); | 476 | self.channel(cc_channel).enable(); |
| 493 | } | 477 | } |
| 494 | 478 | ||
| 495 | if !original_enable_state { | 479 | unsafe { |
| 496 | self.channel(cc_channel).enable(); | 480 | #[cfg(not(any(bdma, gpdma)))] |
| 497 | } | 481 | use crate::dma::{Burst, FifoThreshold}; |
| 482 | use crate::dma::{Transfer, TransferOptions}; | ||
| 498 | 483 | ||
| 499 | unsafe { | 484 | let dma_transfer_option = TransferOptions { |
| 485 | #[cfg(not(any(bdma, gpdma)))] | ||
| 486 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 487 | #[cfg(not(any(bdma, gpdma)))] | ||
| 488 | mburst: Burst::Incr8, | ||
| 489 | ..Default::default() | ||
| 490 | }; | ||
| 491 | |||
| 492 | match self.inner.bits() { | ||
| 493 | TimerBits::Bits16 => { | ||
| 494 | Transfer::new_write( | ||
| 495 | dma, | ||
| 496 | req, | ||
| 497 | duty, | ||
| 498 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16, | ||
| 499 | dma_transfer_option, | ||
| 500 | ) | ||
| 501 | .await | ||
| 502 | } | ||
| 503 | #[cfg(not(any(stm32l0)))] | ||
| 504 | TimerBits::Bits32 => { | ||
| 500 | #[cfg(not(any(bdma, gpdma)))] | 505 | #[cfg(not(any(bdma, gpdma)))] |
| 501 | use crate::dma::{Burst, FifoThreshold}; | 506 | panic!("unsupported timer bits"); |
| 502 | use crate::dma::{Transfer, TransferOptions}; | 507 | |
| 503 | 508 | #[cfg(any(bdma, gpdma))] | |
| 504 | let dma_transfer_option = TransferOptions { | 509 | Transfer::new_write( |
| 505 | #[cfg(not(any(bdma, gpdma)))] | 510 | dma, |
| 506 | fifo_threshold: Some(FifoThreshold::Full), | 511 | req, |
| 507 | #[cfg(not(any(bdma, gpdma)))] | 512 | duty, |
| 508 | mburst: Burst::Incr8, | 513 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32, |
| 509 | ..Default::default() | 514 | dma_transfer_option, |
| 510 | }; | 515 | ) |
| 511 | 516 | .await | |
| 512 | match self.inner.bits() { | ||
| 513 | TimerBits::Bits16 => { | ||
| 514 | Transfer::new_write( | ||
| 515 | dma, | ||
| 516 | req, | ||
| 517 | duty, | ||
| 518 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16, | ||
| 519 | dma_transfer_option, | ||
| 520 | ) | ||
| 521 | .await | ||
| 522 | } | ||
| 523 | #[cfg(not(any(stm32l0)))] | ||
| 524 | TimerBits::Bits32 => { | ||
| 525 | #[cfg(not(any(bdma, gpdma)))] | ||
| 526 | panic!("unsupported timer bits"); | ||
| 527 | |||
| 528 | #[cfg(any(bdma, gpdma))] | ||
| 529 | Transfer::new_write( | ||
| 530 | dma, | ||
| 531 | req, | ||
| 532 | duty, | ||
| 533 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32, | ||
| 534 | dma_transfer_option, | ||
| 535 | ) | ||
| 536 | .await | ||
| 537 | } | ||
| 538 | }; | ||
| 539 | }; | ||
| 540 | |||
| 541 | // restore output compare state | ||
| 542 | if !original_enable_state { | ||
| 543 | self.channel(cc_channel).disable(); | ||
| 544 | } | 517 | } |
| 518 | }; | ||
| 519 | }; | ||
| 520 | |||
| 521 | // restore output compare state | ||
| 522 | if !original_enable_state { | ||
| 523 | self.channel(cc_channel).disable(); | ||
| 524 | } | ||
| 545 | 525 | ||
| 546 | self.channel(cc_channel).set_duty_cycle(original_duty_state); | 526 | self.channel(cc_channel).set_duty_cycle(original_duty_state); |
| 547 | 527 | ||
| 548 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, | 528 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, |
| 549 | // this can almost always trigger a DMA FIFO error. | 529 | // this can almost always trigger a DMA FIFO error. |
| 550 | // | 530 | // |
| 551 | // optional TODO: | 531 | // optional TODO: |
| 552 | // clean FEIF after disable UDE | 532 | // clean FEIF after disable UDE |
| 553 | if !original_cc_dma_enabled { | 533 | if !original_cc_dma_enabled { |
| 554 | self.inner.set_cc_dma_enable_state(cc_channel, false); | 534 | self.inner.set_cc_dma_enable_state(cc_channel, false); |
| 555 | } | 535 | } |
| 556 | 536 | ||
| 557 | if !original_cc_dma_on_update { | 537 | if !original_cc_dma_on_update { |
| 558 | self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) | 538 | self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) |
| 559 | } | ||
| 560 | } | ||
| 561 | } | 539 | } |
| 562 | }; | 540 | } |
| 563 | } | 541 | } |
| 564 | 542 | ||
| 565 | impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1); | ||
| 566 | impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2); | ||
| 567 | impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3); | ||
| 568 | impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4); | ||
| 569 | |||
| 570 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { | 543 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { |
| 571 | type Error = core::convert::Infallible; | 544 | type Error = core::convert::Infallible; |
| 572 | } | 545 | } |
diff --git a/embassy-stm32/src/tsc/acquisition_banks.rs b/embassy-stm32/src/tsc/acquisition_banks.rs index 6791ef6c1..7d6442b48 100644 --- a/embassy-stm32/src/tsc/acquisition_banks.rs +++ b/embassy-stm32/src/tsc/acquisition_banks.rs | |||
| @@ -32,7 +32,7 @@ impl AcquisitionBankPins { | |||
| 32 | /// Returns an iterator over the pins in this acquisition bank. | 32 | /// Returns an iterator over the pins in this acquisition bank. |
| 33 | /// | 33 | /// |
| 34 | /// This method allows for easy traversal of all configured pins in the bank. | 34 | /// This method allows for easy traversal of all configured pins in the bank. |
| 35 | pub fn iter(&self) -> AcquisitionBankPinsIterator { | 35 | pub fn iter(&self) -> AcquisitionBankPinsIterator<'_> { |
| 36 | AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) | 36 | AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) |
| 37 | } | 37 | } |
| 38 | } | 38 | } |
| @@ -90,7 +90,7 @@ impl<'a> Iterator for AcquisitionBankPinsIterator<'a> { | |||
| 90 | 90 | ||
| 91 | impl AcquisitionBankPins { | 91 | impl AcquisitionBankPins { |
| 92 | /// Returns an iterator over the available pins in the bank | 92 | /// Returns an iterator over the available pins in the bank |
| 93 | pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator { | 93 | pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator<'_> { |
| 94 | AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) | 94 | AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) |
| 95 | } | 95 | } |
| 96 | } | 96 | } |
| @@ -107,7 +107,7 @@ pub struct AcquisitionBank { | |||
| 107 | 107 | ||
| 108 | impl AcquisitionBank { | 108 | impl AcquisitionBank { |
| 109 | /// Returns an iterator over the available pins in the bank. | 109 | /// Returns an iterator over the available pins in the bank. |
| 110 | pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator { | 110 | pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator<'_> { |
| 111 | self.pins.pins_iterator() | 111 | self.pins.pins_iterator() |
| 112 | } | 112 | } |
| 113 | 113 | ||
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 73ab46404..729440c46 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs | |||
| @@ -87,6 +87,8 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { | |||
| 87 | 87 | ||
| 88 | r.cr1().modify(|w| { | 88 | r.cr1().modify(|w| { |
| 89 | w.set_tcie(false); | 89 | w.set_tcie(false); |
| 90 | // Reenable receiver for half-duplex if it was disabled | ||
| 91 | w.set_re(true); | ||
| 90 | }); | 92 | }); |
| 91 | 93 | ||
| 92 | state.tx_done.store(true, Ordering::Release); | 94 | state.tx_done.store(true, Ordering::Release); |
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index b3f8bc00c..5bece6d66 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -550,6 +550,20 @@ impl<'d, M: Mode> UartTx<'d, M> { | |||
| 550 | reconfigure(self.info, self.kernel_clock, config) | 550 | reconfigure(self.info, self.kernel_clock, config) |
| 551 | } | 551 | } |
| 552 | 552 | ||
| 553 | /// Write a single u8 if there is tx empty, otherwise return WouldBlock | ||
| 554 | pub(crate) fn nb_write(&mut self, byte: u8) -> Result<(), nb::Error<Error>> { | ||
| 555 | let r = self.info.regs; | ||
| 556 | let sr = sr(r).read(); | ||
| 557 | if sr.txe() { | ||
| 558 | unsafe { | ||
| 559 | tdr(r).write_volatile(byte); | ||
| 560 | } | ||
| 561 | Ok(()) | ||
| 562 | } else { | ||
| 563 | Err(nb::Error::WouldBlock) | ||
| 564 | } | ||
| 565 | } | ||
| 566 | |||
| 553 | /// Perform a blocking UART write | 567 | /// Perform a blocking UART write |
| 554 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 568 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 555 | let r = self.info.regs; | 569 | let r = self.info.regs; |
| @@ -637,7 +651,7 @@ pub fn send_break(regs: &Regs) { | |||
| 637 | /// In case of readback, keep Receiver enabled | 651 | /// In case of readback, keep Receiver enabled |
| 638 | fn half_duplex_set_rx_tx_before_write(r: &Regs, enable_readback: bool) { | 652 | fn half_duplex_set_rx_tx_before_write(r: &Regs, enable_readback: bool) { |
| 639 | let mut cr1 = r.cr1().read(); | 653 | let mut cr1 = r.cr1().read(); |
| 640 | if r.cr3().read().hdsel() && !cr1.te() { | 654 | if r.cr3().read().hdsel() { |
| 641 | cr1.set_te(true); | 655 | cr1.set_te(true); |
| 642 | cr1.set_re(enable_readback); | 656 | cr1.set_re(enable_readback); |
| 643 | r.cr1().write_value(cr1); | 657 | r.cr1().write_value(cr1); |
| @@ -1864,7 +1878,7 @@ impl<'d, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, M> { | |||
| 1864 | 1878 | ||
| 1865 | impl<'d, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, M> { | 1879 | impl<'d, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, M> { |
| 1866 | fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { | 1880 | fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { |
| 1867 | self.blocking_write(&[char]).map_err(nb::Error::Other) | 1881 | self.nb_write(char) |
| 1868 | } | 1882 | } |
| 1869 | 1883 | ||
| 1870 | fn flush(&mut self) -> nb::Result<(), Self::Error> { | 1884 | fn flush(&mut self) -> nb::Result<(), Self::Error> { |
diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index ae5963420..692897b59 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs | |||
| @@ -15,7 +15,7 @@ fn common_init<T: Instance>() { | |||
| 15 | let freq = T::frequency(); | 15 | let freq = T::frequency(); |
| 16 | 16 | ||
| 17 | // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally | 17 | // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally |
| 18 | #[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs)))] | 18 | #[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs)))] |
| 19 | if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) { | 19 | if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) { |
| 20 | panic!( | 20 | panic!( |
| 21 | "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.", | 21 | "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.", |
| @@ -25,7 +25,7 @@ fn common_init<T: Instance>() { | |||
| 25 | // Check frequency is within the 0.25% tolerance allowed by the spec. | 25 | // Check frequency is within the 0.25% tolerance allowed by the spec. |
| 26 | // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user | 26 | // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user |
| 27 | // has tight clock restrictions due to something else (like audio). | 27 | // has tight clock restrictions due to something else (like audio). |
| 28 | #[cfg(not(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs))))] | 28 | #[cfg(not(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))] |
| 29 | if freq.0.abs_diff(48_000_000) > 120_000 { | 29 | if freq.0.abs_diff(48_000_000) > 120_000 { |
| 30 | panic!( | 30 | panic!( |
| 31 | "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", | 31 | "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", |
| @@ -102,6 +102,30 @@ fn common_init<T: Instance>() { | |||
| 102 | } | 102 | } |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | #[cfg(stm32wba)] | ||
| 106 | { | ||
| 107 | // Enable USB power | ||
| 108 | critical_section::with(|_| { | ||
| 109 | crate::pac::PWR.svmcr().modify(|w| { | ||
| 110 | w.set_usv(crate::pac::pwr::vals::Usv::B_0X1); | ||
| 111 | // w.set_uvmen(true); | ||
| 112 | }) | ||
| 113 | }); | ||
| 114 | |||
| 115 | // Wait for USB power to stabilize | ||
| 116 | while !crate::pac::PWR.vosr().read().vdd11usbrdy() {} | ||
| 117 | |||
| 118 | // Now set up transceiver power if it's a OTG-HS | ||
| 119 | #[cfg(peri_usb_otg_hs)] | ||
| 120 | { | ||
| 121 | crate::pac::PWR.vosr().modify(|w| { | ||
| 122 | w.set_usbpwren(true); | ||
| 123 | w.set_usbboosten(true); | ||
| 124 | }); | ||
| 125 | while !crate::pac::PWR.vosr().read().usbboostrdy() {} | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 105 | T::Interrupt::unpend(); | 129 | T::Interrupt::unpend(); |
| 106 | unsafe { T::Interrupt::enable() }; | 130 | unsafe { T::Interrupt::enable() }; |
| 107 | 131 | ||
diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 590d1a427..b074cfa1b 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs | |||
| @@ -105,7 +105,7 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 105 | config: Config, | 105 | config: Config, |
| 106 | ) -> Self { | 106 | ) -> Self { |
| 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(all(stm32u5, 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 | _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 111 | _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 111 | _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| @@ -231,19 +231,23 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { | |||
| 231 | fn alloc_endpoint_in( | 231 | fn alloc_endpoint_in( |
| 232 | &mut self, | 232 | &mut self, |
| 233 | ep_type: EndpointType, | 233 | ep_type: EndpointType, |
| 234 | ep_addr: Option<EndpointAddress>, | ||
| 234 | max_packet_size: u16, | 235 | max_packet_size: u16, |
| 235 | interval_ms: u8, | 236 | interval_ms: u8, |
| 236 | ) -> Result<Self::EndpointIn, EndpointAllocError> { | 237 | ) -> Result<Self::EndpointIn, EndpointAllocError> { |
| 237 | self.inner.alloc_endpoint_in(ep_type, max_packet_size, interval_ms) | 238 | self.inner |
| 239 | .alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms) | ||
| 238 | } | 240 | } |
| 239 | 241 | ||
| 240 | fn alloc_endpoint_out( | 242 | fn alloc_endpoint_out( |
| 241 | &mut self, | 243 | &mut self, |
| 242 | ep_type: EndpointType, | 244 | ep_type: EndpointType, |
| 245 | ep_addr: Option<EndpointAddress>, | ||
| 243 | max_packet_size: u16, | 246 | max_packet_size: u16, |
| 244 | interval_ms: u8, | 247 | interval_ms: u8, |
| 245 | ) -> Result<Self::EndpointOut, EndpointAllocError> { | 248 | ) -> Result<Self::EndpointOut, EndpointAllocError> { |
| 246 | self.inner.alloc_endpoint_out(ep_type, max_packet_size, interval_ms) | 249 | self.inner |
| 250 | .alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms) | ||
| 247 | } | 251 | } |
| 248 | 252 | ||
| 249 | fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { | 253 | fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { |
| @@ -323,6 +327,20 @@ impl<'d, T: Instance> Bus<'d, T> { | |||
| 323 | }); | 327 | }); |
| 324 | } | 328 | } |
| 325 | 329 | ||
| 330 | #[cfg(all(stm32wba, peri_usb_otg_hs))] | ||
| 331 | { | ||
| 332 | crate::pac::SYSCFG.otghsphycr().modify(|w| { | ||
| 333 | w.set_en(true); | ||
| 334 | }); | ||
| 335 | |||
| 336 | critical_section::with(|_| { | ||
| 337 | crate::pac::RCC.ahb2enr().modify(|w| { | ||
| 338 | w.set_usb_otg_hsen(true); | ||
| 339 | w.set_otghsphyen(true); | ||
| 340 | }); | ||
| 341 | }); | ||
| 342 | } | ||
| 343 | |||
| 326 | let r = T::regs(); | 344 | let r = T::regs(); |
| 327 | let core_id = r.cid().read().0; | 345 | let core_id = r.cid().read().0; |
| 328 | trace!("Core id {:08x}", core_id); | 346 | trace!("Core id {:08x}", core_id); |
| @@ -464,6 +482,7 @@ foreach_interrupt!( | |||
| 464 | stm32f7, | 482 | stm32f7, |
| 465 | stm32l4, | 483 | stm32l4, |
| 466 | stm32u5, | 484 | stm32u5, |
| 485 | stm32wba, | ||
| 467 | ))] { | 486 | ))] { |
| 468 | const FIFO_DEPTH_WORDS: u16 = 320; | 487 | const FIFO_DEPTH_WORDS: u16 = 320; |
| 469 | const ENDPOINT_COUNT: usize = 6; | 488 | const ENDPOINT_COUNT: usize = 6; |
| @@ -473,7 +492,7 @@ foreach_interrupt!( | |||
| 473 | } else if #[cfg(any(stm32h7, stm32h7rs))] { | 492 | } else if #[cfg(any(stm32h7, stm32h7rs))] { |
| 474 | const FIFO_DEPTH_WORDS: u16 = 1024; | 493 | const FIFO_DEPTH_WORDS: u16 = 1024; |
| 475 | const ENDPOINT_COUNT: usize = 9; | 494 | const ENDPOINT_COUNT: usize = 9; |
| 476 | } else if #[cfg(stm32u5)] { | 495 | } else if #[cfg(any(stm32wba, stm32u5))] { |
| 477 | const FIFO_DEPTH_WORDS: u16 = 320; | 496 | const FIFO_DEPTH_WORDS: u16 = 320; |
| 478 | const ENDPOINT_COUNT: usize = 6; | 497 | const ENDPOINT_COUNT: usize = 6; |
| 479 | } else { | 498 | } else { |
| @@ -523,7 +542,7 @@ foreach_interrupt!( | |||
| 523 | ))] { | 542 | ))] { |
| 524 | const FIFO_DEPTH_WORDS: u16 = 1024; | 543 | const FIFO_DEPTH_WORDS: u16 = 1024; |
| 525 | const ENDPOINT_COUNT: usize = 9; | 544 | const ENDPOINT_COUNT: usize = 9; |
| 526 | } else if #[cfg(stm32u5)] { | 545 | } else if #[cfg(any(stm32u5, stm32wba))] { |
| 527 | const FIFO_DEPTH_WORDS: u16 = 1024; | 546 | const FIFO_DEPTH_WORDS: u16 = 1024; |
| 528 | const ENDPOINT_COUNT: usize = 9; | 547 | const ENDPOINT_COUNT: usize = 9; |
| 529 | } else { | 548 | } else { |
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 3e8e74a1f..92c1601cc 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs | |||
| @@ -359,9 +359,38 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 359 | addr | 359 | addr |
| 360 | } | 360 | } |
| 361 | 361 | ||
| 362 | fn is_endpoint_available<D: Dir>(&self, index: usize, ep_type: EndpointType) -> bool { | ||
| 363 | if index == 0 && ep_type != EndpointType::Control { | ||
| 364 | return false; // EP0 is reserved for control | ||
| 365 | } | ||
| 366 | |||
| 367 | let ep = match self.alloc.get(index) { | ||
| 368 | Some(ep) => ep, | ||
| 369 | None => return false, | ||
| 370 | }; | ||
| 371 | |||
| 372 | let used = ep.used_out || ep.used_in; | ||
| 373 | |||
| 374 | if used && ep.ep_type == EndpointType::Isochronous { | ||
| 375 | // Isochronous endpoints are always double-buffered. | ||
| 376 | // Their corresponding endpoint/channel registers are forced to be unidirectional. | ||
| 377 | // Do not reuse this index. | ||
| 378 | // FIXME: Bulk endpoints can be double buffered, but are not in the current implementation. | ||
| 379 | return false; | ||
| 380 | } | ||
| 381 | |||
| 382 | let used_dir = match D::dir() { | ||
| 383 | Direction::Out => ep.used_out, | ||
| 384 | Direction::In => ep.used_in, | ||
| 385 | }; | ||
| 386 | |||
| 387 | !used || (ep.ep_type == ep_type && !used_dir) | ||
| 388 | } | ||
| 389 | |||
| 362 | fn alloc_endpoint<D: Dir>( | 390 | fn alloc_endpoint<D: Dir>( |
| 363 | &mut self, | 391 | &mut self, |
| 364 | ep_type: EndpointType, | 392 | ep_type: EndpointType, |
| 393 | ep_addr: Option<EndpointAddress>, | ||
| 365 | max_packet_size: u16, | 394 | max_packet_size: u16, |
| 366 | interval_ms: u8, | 395 | interval_ms: u8, |
| 367 | ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { | 396 | ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { |
| @@ -373,28 +402,17 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 373 | D::dir() | 402 | D::dir() |
| 374 | ); | 403 | ); |
| 375 | 404 | ||
| 376 | let index = self.alloc.iter_mut().enumerate().find(|(i, ep)| { | 405 | let index = if let Some(addr) = ep_addr { |
| 377 | if *i == 0 && ep_type != EndpointType::Control { | 406 | // Use the specified endpoint address |
| 378 | return false; // reserved for control pipe | 407 | self.is_endpoint_available::<D>(addr.index(), ep_type) |
| 379 | } | 408 | .then_some(addr.index()) |
| 380 | let used = ep.used_out || ep.used_in; | 409 | } else { |
| 381 | if used && (ep.ep_type == EndpointType::Isochronous) { | 410 | // Find any available endpoint |
| 382 | // Isochronous endpoints are always double-buffered. | 411 | (0..self.alloc.len()).find(|&i| self.is_endpoint_available::<D>(i, ep_type)) |
| 383 | // Their corresponding endpoint/channel registers are forced to be unidirectional. | 412 | }; |
| 384 | // Do not reuse this index. | ||
| 385 | // FIXME: Bulk endpoints can be double buffered, but are not in the current implementation. | ||
| 386 | return false; | ||
| 387 | } | ||
| 388 | |||
| 389 | let used_dir = match D::dir() { | ||
| 390 | Direction::Out => ep.used_out, | ||
| 391 | Direction::In => ep.used_in, | ||
| 392 | }; | ||
| 393 | !used || (ep.ep_type == ep_type && !used_dir) | ||
| 394 | }); | ||
| 395 | 413 | ||
| 396 | let (index, ep) = match index { | 414 | let (index, ep) = match index { |
| 397 | Some(x) => x, | 415 | Some(i) => (i, &mut self.alloc[i]), |
| 398 | None => return Err(EndpointAllocError), | 416 | None => return Err(EndpointAllocError), |
| 399 | }; | 417 | }; |
| 400 | 418 | ||
| @@ -479,27 +497,29 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { | |||
| 479 | fn alloc_endpoint_in( | 497 | fn alloc_endpoint_in( |
| 480 | &mut self, | 498 | &mut self, |
| 481 | ep_type: EndpointType, | 499 | ep_type: EndpointType, |
| 500 | ep_addr: Option<EndpointAddress>, | ||
| 482 | max_packet_size: u16, | 501 | max_packet_size: u16, |
| 483 | interval_ms: u8, | 502 | interval_ms: u8, |
| 484 | ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { | 503 | ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { |
| 485 | self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | 504 | self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) |
| 486 | } | 505 | } |
| 487 | 506 | ||
| 488 | fn alloc_endpoint_out( | 507 | fn alloc_endpoint_out( |
| 489 | &mut self, | 508 | &mut self, |
| 490 | ep_type: EndpointType, | 509 | ep_type: EndpointType, |
| 510 | ep_addr: Option<EndpointAddress>, | ||
| 491 | max_packet_size: u16, | 511 | max_packet_size: u16, |
| 492 | interval_ms: u8, | 512 | interval_ms: u8, |
| 493 | ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { | 513 | ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { |
| 494 | self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | 514 | self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) |
| 495 | } | 515 | } |
| 496 | 516 | ||
| 497 | fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { | 517 | fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { |
| 498 | let ep_out = self | 518 | let ep_out = self |
| 499 | .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) | 519 | .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0) |
| 500 | .unwrap(); | 520 | .unwrap(); |
| 501 | let ep_in = self | 521 | let ep_in = self |
| 502 | .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) | 522 | .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0) |
| 503 | .unwrap(); | 523 | .unwrap(); |
| 504 | assert_eq!(ep_out.info.addr.index(), 0); | 524 | assert_eq!(ep_out.info.addr.index(), 0); |
| 505 | assert_eq!(ep_in.info.addr.index(), 0); | 525 | assert_eq!(ep_in.info.addr.index(), 0); |
diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 99962f9f6..9e5c39f5e 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml | |||
| @@ -28,7 +28,7 @@ defmt = { version = "1.0.1", optional = true } | |||
| 28 | log = { version = "0.4.14", optional = true } | 28 | log = { version = "0.4.14", optional = true } |
| 29 | 29 | ||
| 30 | futures-sink = { version = "0.3", default-features = false, features = [] } | 30 | futures-sink = { version = "0.3", default-features = false, features = [] } |
| 31 | futures-util = { version = "0.3.17", default-features = false } | 31 | futures-core = { version = "0.3.31", default-features = false } |
| 32 | critical-section = "1.1" | 32 | critical-section = "1.1" |
| 33 | heapless = "0.8" | 33 | heapless = "0.8" |
| 34 | cfg-if = "1.0.0" | 34 | cfg-if = "1.0.0" |
| @@ -43,3 +43,4 @@ futures-util = { version = "0.3.17", features = [ "channel", "sink" ] } | |||
| 43 | # Enable critical-section implementation for std, for tests | 43 | # Enable critical-section implementation for std, for tests |
| 44 | critical-section = { version = "1.1", features = ["std"] } | 44 | critical-section = { version = "1.1", features = ["std"] } |
| 45 | static_cell = { version = "2" } | 45 | static_cell = { version = "2" } |
| 46 | trybuild = "1.0.105" | ||
diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 856551417..a0e39fcb5 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs | |||
| @@ -17,6 +17,31 @@ | |||
| 17 | //! messages that it can store, and if this limit is reached, trying to send | 17 | //! messages that it can store, and if this limit is reached, trying to send |
| 18 | //! another message will result in an error being returned. | 18 | //! another message will result in an error being returned. |
| 19 | //! | 19 | //! |
| 20 | //! # Example: Message passing between task and interrupt handler | ||
| 21 | //! | ||
| 22 | //! ```rust | ||
| 23 | //! use embassy_sync::channel::Channel; | ||
| 24 | //! use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 25 | //! | ||
| 26 | //! static SHARED_CHANNEL: Channel<CriticalSectionRawMutex, u32, 8> = Channel::new(); | ||
| 27 | //! | ||
| 28 | //! fn my_interrupt_handler() { | ||
| 29 | //! // Do some work.. | ||
| 30 | //! // ... | ||
| 31 | //! if let Err(e) = SHARED_CHANNEL.sender().try_send(42) { | ||
| 32 | //! // Channel is full.. | ||
| 33 | //! } | ||
| 34 | //! } | ||
| 35 | //! | ||
| 36 | //! async fn my_async_task() { | ||
| 37 | //! // ... | ||
| 38 | //! let receiver = SHARED_CHANNEL.receiver(); | ||
| 39 | //! loop { | ||
| 40 | //! let data_from_interrupt = receiver.receive().await; | ||
| 41 | //! // Do something with the data. | ||
| 42 | //! } | ||
| 43 | //! } | ||
| 44 | //! ``` | ||
| 20 | 45 | ||
| 21 | use core::cell::RefCell; | 46 | use core::cell::RefCell; |
| 22 | use core::future::Future; | 47 | use core::future::Future; |
| @@ -443,7 +468,7 @@ where | |||
| 443 | } | 468 | } |
| 444 | } | 469 | } |
| 445 | 470 | ||
| 446 | impl<'ch, M, T, const N: usize> futures_util::Stream for Receiver<'ch, M, T, N> | 471 | impl<'ch, M, T, const N: usize> futures_core::Stream for Receiver<'ch, M, T, N> |
| 447 | where | 472 | where |
| 448 | M: RawMutex, | 473 | M: RawMutex, |
| 449 | { | 474 | { |
| @@ -962,7 +987,7 @@ where | |||
| 962 | } | 987 | } |
| 963 | } | 988 | } |
| 964 | 989 | ||
| 965 | impl<M, T, const N: usize> futures_util::Stream for Channel<M, T, N> | 990 | impl<M, T, const N: usize> futures_core::Stream for Channel<M, T, N> |
| 966 | where | 991 | where |
| 967 | M: RawMutex, | 992 | M: RawMutex, |
| 968 | { | 993 | { |
diff --git a/embassy-sync/src/lazy_lock.rs b/embassy-sync/src/lazy_lock.rs index 18e3c2019..f1bd88b61 100644 --- a/embassy-sync/src/lazy_lock.rs +++ b/embassy-sync/src/lazy_lock.rs | |||
| @@ -31,7 +31,12 @@ union Data<T, F> { | |||
| 31 | f: ManuallyDrop<F>, | 31 | f: ManuallyDrop<F>, |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | unsafe impl<T, F> Sync for LazyLock<T, F> {} | 34 | unsafe impl<T, F> Sync for LazyLock<T, F> |
| 35 | where | ||
| 36 | T: Sync, | ||
| 37 | F: Sync, | ||
| 38 | { | ||
| 39 | } | ||
| 35 | 40 | ||
| 36 | impl<T, F: FnOnce() -> T> LazyLock<T, F> { | 41 | impl<T, F: FnOnce() -> T> LazyLock<T, F> { |
| 37 | /// Create a new uninitialized `StaticLock`. | 42 | /// Create a new uninitialized `StaticLock`. |
diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 7528a9f68..8496f34bf 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs | |||
| @@ -23,7 +23,7 @@ struct State { | |||
| 23 | 23 | ||
| 24 | /// Async mutex. | 24 | /// Async mutex. |
| 25 | /// | 25 | /// |
| 26 | /// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex). | 26 | /// The mutex is generic over a blocking [`RawMutex`]. |
| 27 | /// The raw mutex is used to guard access to the internal "is locked" flag. It | 27 | /// The raw mutex is used to guard access to the internal "is locked" flag. It |
| 28 | /// is held for very short periods only, while locking and unlocking. It is *not* held | 28 | /// is held for very short periods only, while locking and unlocking. It is *not* held |
| 29 | /// for the entire time the async Mutex is locked. | 29 | /// for the entire time the async Mutex is locked. |
diff --git a/embassy-sync/src/once_lock.rs b/embassy-sync/src/once_lock.rs index cd05b986d..1e848685a 100644 --- a/embassy-sync/src/once_lock.rs +++ b/embassy-sync/src/once_lock.rs | |||
| @@ -42,7 +42,7 @@ pub struct OnceLock<T> { | |||
| 42 | data: Cell<MaybeUninit<T>>, | 42 | data: Cell<MaybeUninit<T>>, |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | unsafe impl<T> Sync for OnceLock<T> {} | 45 | unsafe impl<T> Sync for OnceLock<T> where T: Sync {} |
| 46 | 46 | ||
| 47 | impl<T> OnceLock<T> { | 47 | impl<T> OnceLock<T> { |
| 48 | /// Create a new uninitialized `OnceLock`. | 48 | /// Create a new uninitialized `OnceLock`. |
diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 2598652d2..df3b28b45 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs | |||
| @@ -152,7 +152,7 @@ where | |||
| 152 | 152 | ||
| 153 | impl<'p, M, const N: usize> Unpin for ReadFuture<'p, M, N> where M: RawMutex {} | 153 | impl<'p, M, const N: usize> Unpin for ReadFuture<'p, M, N> where M: RawMutex {} |
| 154 | 154 | ||
| 155 | /// Future returned by [`Pipe::fill_buf`] and [`Reader::fill_buf`]. | 155 | /// Future returned by [`Reader::fill_buf`]. |
| 156 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 156 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 157 | pub struct FillBufFuture<'p, M, const N: usize> | 157 | pub struct FillBufFuture<'p, M, const N: usize> |
| 158 | where | 158 | where |
| @@ -587,7 +587,7 @@ where | |||
| 587 | } | 587 | } |
| 588 | } | 588 | } |
| 589 | 589 | ||
| 590 | /// Write-only access to a [`DynamicPipe`]. | 590 | /// Write-only access to the dynamic pipe. |
| 591 | pub struct DynamicWriter<'p> { | 591 | pub struct DynamicWriter<'p> { |
| 592 | pipe: &'p dyn DynamicPipe, | 592 | pipe: &'p dyn DynamicPipe, |
| 593 | } | 593 | } |
| @@ -657,7 +657,7 @@ where | |||
| 657 | } | 657 | } |
| 658 | } | 658 | } |
| 659 | 659 | ||
| 660 | /// Read-only access to a [`DynamicPipe`]. | 660 | /// Read-only access to a dynamic pipe. |
| 661 | pub struct DynamicReader<'p> { | 661 | pub struct DynamicReader<'p> { |
| 662 | pipe: &'p dyn DynamicPipe, | 662 | pipe: &'p dyn DynamicPipe, |
| 663 | } | 663 | } |
| @@ -742,7 +742,7 @@ where | |||
| 742 | } | 742 | } |
| 743 | } | 743 | } |
| 744 | 744 | ||
| 745 | /// Future returned by [`DynamicPipe::fill_buf`] and [`DynamicReader::fill_buf`]. | 745 | /// Future returned by [`DynamicReader::fill_buf`]. |
| 746 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 746 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 747 | pub struct DynamicFillBufFuture<'p> { | 747 | pub struct DynamicFillBufFuture<'p> { |
| 748 | pipe: Option<&'p dyn DynamicPipe>, | 748 | pipe: Option<&'p dyn DynamicPipe>, |
diff --git a/embassy-sync/src/priority_channel.rs b/embassy-sync/src/priority_channel.rs index 623c52993..715a20e86 100644 --- a/embassy-sync/src/priority_channel.rs +++ b/embassy-sync/src/priority_channel.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | //! A queue for sending values between asynchronous tasks. | 1 | //! A queue for sending values between asynchronous tasks. |
| 2 | //! | 2 | //! |
| 3 | //! Similar to a [`Channel`](crate::channel::Channel), however [`PriorityChannel`] sifts higher priority items to the front of the queue. | 3 | //! Similar to a [`Channel`](crate::channel::Channel), however [`PriorityChannel`] sifts higher priority items to the front of the queue. |
| 4 | //! Priority is determined by the `Ord` trait. Priority behavior is determined by the [`Kind`](heapless::binary_heap::Kind) parameter of the channel. | 4 | //! Priority is determined by the `Ord` trait. Priority behavior is determined by the [`Kind`] parameter of the channel. |
| 5 | 5 | ||
| 6 | use core::cell::RefCell; | 6 | use core::cell::RefCell; |
| 7 | use core::future::Future; | 7 | use core::future::Future; |
| @@ -473,7 +473,7 @@ where | |||
| 473 | /// received from the channel. | 473 | /// received from the channel. |
| 474 | /// | 474 | /// |
| 475 | /// Sent data may be reordered based on their priority within the channel. | 475 | /// Sent data may be reordered based on their priority within the channel. |
| 476 | /// For example, in a [`Max`](heapless::binary_heap::Max) [`PriorityChannel`] | 476 | /// For example, in a [`Max`] [`PriorityChannel`] |
| 477 | /// containing `u32`'s, data sent in the following order `[1, 2, 3]` will be received as `[3, 2, 1]`. | 477 | /// containing `u32`'s, data sent in the following order `[1, 2, 3]` will be received as `[3, 2, 1]`. |
| 478 | pub struct PriorityChannel<M, T, K, const N: usize> | 478 | pub struct PriorityChannel<M, T, K, const N: usize> |
| 479 | where | 479 | where |
diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index 606efff0a..9206b9383 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs | |||
| @@ -88,7 +88,7 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi | |||
| 88 | /// Create a new subscriber. It will only receive messages that are published after its creation. | 88 | /// Create a new subscriber. It will only receive messages that are published after its creation. |
| 89 | /// | 89 | /// |
| 90 | /// If there are no subscriber slots left, an error will be returned. | 90 | /// If there are no subscriber slots left, an error will be returned. |
| 91 | pub fn subscriber(&self) -> Result<Subscriber<M, T, CAP, SUBS, PUBS>, Error> { | 91 | pub fn subscriber(&self) -> Result<Subscriber<'_, M, T, CAP, SUBS, PUBS>, Error> { |
| 92 | self.inner.lock(|inner| { | 92 | self.inner.lock(|inner| { |
| 93 | let mut s = inner.borrow_mut(); | 93 | let mut s = inner.borrow_mut(); |
| 94 | 94 | ||
| @@ -120,7 +120,7 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi | |||
| 120 | /// Create a new publisher | 120 | /// Create a new publisher |
| 121 | /// | 121 | /// |
| 122 | /// If there are no publisher slots left, an error will be returned. | 122 | /// If there are no publisher slots left, an error will be returned. |
| 123 | pub fn publisher(&self) -> Result<Publisher<M, T, CAP, SUBS, PUBS>, Error> { | 123 | pub fn publisher(&self) -> Result<Publisher<'_, M, T, CAP, SUBS, PUBS>, Error> { |
| 124 | self.inner.lock(|inner| { | 124 | self.inner.lock(|inner| { |
| 125 | let mut s = inner.borrow_mut(); | 125 | let mut s = inner.borrow_mut(); |
| 126 | 126 | ||
| @@ -151,13 +151,13 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi | |||
| 151 | 151 | ||
| 152 | /// Create a new publisher that can only send immediate messages. | 152 | /// Create a new publisher that can only send immediate messages. |
| 153 | /// This kind of publisher does not take up a publisher slot. | 153 | /// This kind of publisher does not take up a publisher slot. |
| 154 | pub fn immediate_publisher(&self) -> ImmediatePublisher<M, T, CAP, SUBS, PUBS> { | 154 | pub fn immediate_publisher(&self) -> ImmediatePublisher<'_, M, T, CAP, SUBS, PUBS> { |
| 155 | ImmediatePublisher(ImmediatePub::new(self)) | 155 | ImmediatePublisher(ImmediatePub::new(self)) |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | /// Create a new publisher that can only send immediate messages. | 158 | /// Create a new publisher that can only send immediate messages. |
| 159 | /// This kind of publisher does not take up a publisher slot. | 159 | /// This kind of publisher does not take up a publisher slot. |
| 160 | pub fn dyn_immediate_publisher(&self) -> DynImmediatePublisher<T> { | 160 | pub fn dyn_immediate_publisher(&self) -> DynImmediatePublisher<'_, T> { |
| 161 | DynImmediatePublisher(ImmediatePub::new(self)) | 161 | DynImmediatePublisher(ImmediatePub::new(self)) |
| 162 | } | 162 | } |
| 163 | 163 | ||
diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs index 7a1ab66de..52a52f926 100644 --- a/embassy-sync/src/pubsub/publisher.rs +++ b/embassy-sync/src/pubsub/publisher.rs | |||
| @@ -75,7 +75,7 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Pub<'a, PSB, T> { | |||
| 75 | self.channel.is_full() | 75 | self.channel.is_full() |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | /// Create a [`futures::Sink`] adapter for this publisher. | 78 | /// Create a [`futures_sink::Sink`] adapter for this publisher. |
| 79 | #[inline] | 79 | #[inline] |
| 80 | pub const fn sink(&self) -> PubSink<'a, '_, PSB, T> { | 80 | pub const fn sink(&self) -> PubSink<'a, '_, PSB, T> { |
| 81 | PubSink { publ: self, fut: None } | 81 | PubSink { publ: self, fut: None } |
diff --git a/embassy-sync/src/pubsub/subscriber.rs b/embassy-sync/src/pubsub/subscriber.rs index 6ad660cb3..649382cf1 100644 --- a/embassy-sync/src/pubsub/subscriber.rs +++ b/embassy-sync/src/pubsub/subscriber.rs | |||
| @@ -115,7 +115,7 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Unpin for Sub<'a, PSB, T> {} | |||
| 115 | 115 | ||
| 116 | /// Warning: The stream implementation ignores lag results and returns all messages. | 116 | /// Warning: The stream implementation ignores lag results and returns all messages. |
| 117 | /// This might miss some messages without you knowing it. | 117 | /// This might miss some messages without you knowing it. |
| 118 | impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> futures_util::Stream for Sub<'a, PSB, T> { | 118 | impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> futures_core::Stream for Sub<'a, PSB, T> { |
| 119 | type Item = T; | 119 | type Item = T; |
| 120 | 120 | ||
| 121 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { | 121 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { |
diff --git a/embassy-sync/tests/ui.rs b/embassy-sync/tests/ui.rs new file mode 100644 index 000000000..e8b1080d8 --- /dev/null +++ b/embassy-sync/tests/ui.rs | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #[cfg(not(miri))] | ||
| 2 | #[test] | ||
| 3 | fn ui() { | ||
| 4 | let t = trybuild::TestCases::new(); | ||
| 5 | |||
| 6 | // These test cases should fail to compile since OnceLock and LazyLock should not unconditionally implement sync | ||
| 7 | // for all types. These tests are regression tests against the following issues: | ||
| 8 | // * https://github.com/embassy-rs/embassy/issues/4307 | ||
| 9 | // * https://github.com/embassy-rs/embassy/issues/3904 | ||
| 10 | t.compile_fail("tests/ui/sync_impl/lazy_lock_function.rs"); | ||
| 11 | t.compile_fail("tests/ui/sync_impl/lazy_lock_type.rs"); | ||
| 12 | t.compile_fail("tests/ui/sync_impl/once_lock.rs"); | ||
| 13 | } | ||
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs new file mode 100644 index 000000000..35f5587c0 --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | use embassy_sync::lazy_lock::LazyLock; | ||
| 2 | |||
| 3 | fn main() { | ||
| 4 | let x = 128u8; | ||
| 5 | let x_ptr: *const u8 = core::ptr::addr_of!(x); | ||
| 6 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) }; | ||
| 7 | |||
| 8 | check_sync(LazyLock::new(closure_capturing_non_sync_variable)); | ||
| 9 | } | ||
| 10 | |||
| 11 | fn check_sync<T: Sync>(_lazy_lock: T) {} | ||
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr new file mode 100644 index 000000000..daf79ad28 --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | error[E0277]: `*const u8` cannot be shared between threads safely | ||
| 2 | --> tests/ui/sync_impl/lazy_lock_function.rs:8:16 | ||
| 3 | | | ||
| 4 | 6 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) }; | ||
| 5 | | -- within this `{closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}` | ||
| 6 | 7 | | ||
| 7 | 8 | check_sync(LazyLock::new(closure_capturing_non_sync_variable)); | ||
| 8 | | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const u8` cannot be shared between threads safely | ||
| 9 | | | | ||
| 10 | | required by a bound introduced by this call | ||
| 11 | | | ||
| 12 | = help: within `{closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}`, the trait `Sync` is not implemented for `*const u8` | ||
| 13 | = note: required because it appears within the type `&*const u8` | ||
| 14 | note: required because it's used within this closure | ||
| 15 | --> tests/ui/sync_impl/lazy_lock_function.rs:6:47 | ||
| 16 | | | ||
| 17 | 6 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) }; | ||
| 18 | | ^^ | ||
| 19 | = note: required for `embassy_sync::lazy_lock::LazyLock<u8, {closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}>` to implement `Sync` | ||
| 20 | note: required by a bound in `check_sync` | ||
| 21 | --> tests/ui/sync_impl/lazy_lock_function.rs:11:18 | ||
| 22 | | | ||
| 23 | 11 | fn check_sync<T: Sync>(_lazy_lock: T) {} | ||
| 24 | | ^^^^ required by this bound in `check_sync` | ||
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs new file mode 100644 index 000000000..4e1383143 --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | use embassy_sync::lazy_lock::LazyLock; | ||
| 2 | |||
| 3 | // *mut u8 is not Sync, so LazyLock should not implement Sync for this type. This should fail to compile. | ||
| 4 | static GLOBAL: LazyLock<*mut u8> = LazyLock::new(|| core::ptr::null_mut()); | ||
| 5 | |||
| 6 | fn main() {} | ||
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr new file mode 100644 index 000000000..1ccc54c7a --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | error[E0277]: `*mut u8` cannot be shared between threads safely | ||
| 2 | --> tests/ui/sync_impl/lazy_lock_type.rs:4:16 | ||
| 3 | | | ||
| 4 | 4 | static GLOBAL: LazyLock<*mut u8> = LazyLock::new(|| core::ptr::null_mut()); | ||
| 5 | | ^^^^^^^^^^^^^^^^^ `*mut u8` cannot be shared between threads safely | ||
| 6 | | | ||
| 7 | = help: the trait `Sync` is not implemented for `*mut u8` | ||
| 8 | = note: required for `embassy_sync::lazy_lock::LazyLock<*mut u8>` to implement `Sync` | ||
| 9 | = note: shared static variables must have a type that implements `Sync` | ||
diff --git a/embassy-sync/tests/ui/sync_impl/once_lock.rs b/embassy-sync/tests/ui/sync_impl/once_lock.rs new file mode 100644 index 000000000..8f50d583b --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/once_lock.rs | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | use embassy_sync::once_lock::OnceLock; | ||
| 2 | |||
| 3 | // *mut u8 is not Sync, so OnceLock should not implement Sync for this type. This should fail to compile. | ||
| 4 | static GLOBAL: OnceLock<*mut u8> = OnceLock::new(); | ||
| 5 | |||
| 6 | fn main() {} | ||
diff --git a/embassy-sync/tests/ui/sync_impl/once_lock.stderr b/embassy-sync/tests/ui/sync_impl/once_lock.stderr new file mode 100644 index 000000000..e2419f844 --- /dev/null +++ b/embassy-sync/tests/ui/sync_impl/once_lock.stderr | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | error[E0277]: `*mut u8` cannot be shared between threads safely | ||
| 2 | --> tests/ui/sync_impl/once_lock.rs:4:16 | ||
| 3 | | | ||
| 4 | 4 | static GLOBAL: OnceLock<*mut u8> = OnceLock::new(); | ||
| 5 | | ^^^^^^^^^^^^^^^^^ `*mut u8` cannot be shared between threads safely | ||
| 6 | | | ||
| 7 | = help: the trait `Sync` is not implemented for `*mut u8` | ||
| 8 | = note: required for `embassy_sync::once_lock::OnceLock<*mut u8>` to implement `Sync` | ||
| 9 | = note: shared static variables must have a type that implements `Sync` | ||
diff --git a/embassy-time-driver/CHANGELOG.md b/embassy-time-driver/CHANGELOG.md index 744b0f648..b61a10bf6 100644 --- a/embassy-time-driver/CHANGELOG.md +++ b/embassy-time-driver/CHANGELOG.md | |||
| @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 7 | 7 | ||
| 8 | <!-- next-header --> | ||
| 9 | ## Unreleased - ReleaseDate | ||
| 10 | |||
| 11 | - Allow inlining on time driver boundary | ||
| 12 | - add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK` | ||
| 13 | |||
| 8 | ## 0.2.0 - 2025-01-02 | 14 | ## 0.2.0 - 2025-01-02 |
| 9 | 15 | ||
| 10 | - The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed. | 16 | - The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed. |
diff --git a/embassy-time-driver/release.toml b/embassy-time-driver/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-time-driver/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md index 09e951ce4..a0c1abe8d 100644 --- a/embassy-time/CHANGELOG.md +++ b/embassy-time/CHANGELOG.md | |||
| @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 7 | 7 | ||
| 8 | <!-- next-header --> | ||
| 9 | ## Unreleased - ReleaseDate | ||
| 10 | |||
| 11 | - Allow inlining on time driver boundary | ||
| 12 | - Add `saturating_add` and `saturating_sub` to `Instant` | ||
| 13 | - Add `Instant::try_from_*` constructor functions | ||
| 14 | - Add `Duration::try_from_*` constructor functions | ||
| 15 | - Don't select `critical-section` impl for `std` | ||
| 16 | - Manually implement the future for `with_timeout` | ||
| 17 | - Add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK` | ||
| 18 | |||
| 8 | ## 0.4.0 - 2025-01-02 | 19 | ## 0.4.0 - 2025-01-02 |
| 9 | 20 | ||
| 10 | - `embassy-time-driver` updated from v0.1 to v0.2. | 21 | - `embassy-time-driver` updated from v0.1 to v0.2. |
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 76983d880..2284906b0 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml | |||
| @@ -427,7 +427,7 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } | |||
| 427 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 427 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 428 | embedded-hal-async = { version = "1.0" } | 428 | embedded-hal-async = { version = "1.0" } |
| 429 | 429 | ||
| 430 | futures-util = { version = "0.3.17", default-features = false } | 430 | futures-core = { version = "0.3.31", default-features = false } |
| 431 | critical-section = "1.1" | 431 | critical-section = "1.1" |
| 432 | cfg-if = "1.0.0" | 432 | cfg-if = "1.0.0" |
| 433 | 433 | ||
diff --git a/embassy-time/release.toml b/embassy-time/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-time/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index d1162eadd..d3f1e1621 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs | |||
| @@ -2,8 +2,8 @@ use core::future::{poll_fn, Future}; | |||
| 2 | use core::pin::Pin; | 2 | use core::pin::Pin; |
| 3 | use core::task::{Context, Poll}; | 3 | use core::task::{Context, Poll}; |
| 4 | 4 | ||
| 5 | use futures_util::stream::FusedStream; | 5 | use futures_core::stream::FusedStream; |
| 6 | use futures_util::Stream; | 6 | use futures_core::Stream; |
| 7 | 7 | ||
| 8 | use crate::{Duration, Instant}; | 8 | use crate::{Duration, Instant}; |
| 9 | 9 | ||
diff --git a/embassy-usb-dfu/Cargo.toml b/embassy-usb-dfu/Cargo.toml index 8b2467bca..011046ba4 100644 --- a/embassy-usb-dfu/Cargo.toml +++ b/embassy-usb-dfu/Cargo.toml | |||
| @@ -31,11 +31,11 @@ log = { version = "0.4.17", optional = true } | |||
| 31 | 31 | ||
| 32 | bitflags = "2.4.1" | 32 | bitflags = "2.4.1" |
| 33 | cortex-m = { version = "0.7.7", features = ["inline-asm"], optional = true } | 33 | cortex-m = { version = "0.7.7", features = ["inline-asm"], optional = true } |
| 34 | embassy-boot = { version = "0.4.0", path = "../embassy-boot" } | 34 | embassy-boot = { version = "0.5.0", path = "../embassy-boot" } |
| 35 | embassy-futures = { version = "0.1.1", path = "../embassy-futures" } | 35 | embassy-futures = { version = "0.1.1", path = "../embassy-futures" } |
| 36 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 36 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 37 | embassy-time = { version = "0.4.0", path = "../embassy-time" } | 37 | embassy-time = { version = "0.4.0", path = "../embassy-time" } |
| 38 | embassy-usb = { version = "0.4.0", path = "../embassy-usb", default-features = false } | 38 | embassy-usb = { version = "0.5.0", path = "../embassy-usb", default-features = false } |
| 39 | embedded-storage = { version = "0.3.1" } | 39 | embedded-storage = { version = "0.3.1" } |
| 40 | esp32c3-hal = { version = "0.13.0", optional = true, default-features = false } | 40 | esp32c3-hal = { version = "0.13.0", optional = true, default-features = false } |
| 41 | 41 | ||
diff --git a/embassy-usb-driver/CHANGELOG.md b/embassy-usb-driver/CHANGELOG.md new file mode 100644 index 000000000..15875e087 --- /dev/null +++ b/embassy-usb-driver/CHANGELOG.md | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # Changelog for embassy-usb-driver | ||
| 2 | |||
| 3 | All notable changes to this project will be documented in this file. | ||
| 4 | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
| 7 | |||
| 8 | <!-- next-header --> | ||
| 9 | ## Unreleased - ReleaseDate | ||
| 10 | |||
| 11 | ## 0.2.0 - 2025-07-16 | ||
| 12 | |||
| 13 | - Make USB endpoint allocator methods accept an optional `EndpointAddress`. | ||
| 14 | |||
| 15 | ## 0.1.1 - 2025-07-15 | ||
| 16 | |||
| 17 | - Add `embedded_io_async::Error` implementation for `EndpointError` ([#4176](https://github.com/embassy-rs/embassy/pull/4176)) | ||
diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml index e40421649..de69bf694 100644 --- a/embassy-usb-driver/Cargo.toml +++ b/embassy-usb-driver/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-usb-driver" | 2 | name = "embassy-usb-driver" |
| 3 | version = "0.1.0" | 3 | version = "0.2.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "Driver trait for `embassy-usb`, an async USB device stack for embedded devices." | 6 | description = "Driver trait for `embassy-usb`, an async USB device stack for embedded devices." |
diff --git a/embassy-usb-driver/release.toml b/embassy-usb-driver/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-usb-driver/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index d204e4d85..99616f1ec 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs | |||
| @@ -136,6 +136,7 @@ pub trait Driver<'a> { | |||
| 136 | fn alloc_endpoint_out( | 136 | fn alloc_endpoint_out( |
| 137 | &mut self, | 137 | &mut self, |
| 138 | ep_type: EndpointType, | 138 | ep_type: EndpointType, |
| 139 | ep_addr: Option<EndpointAddress>, | ||
| 139 | max_packet_size: u16, | 140 | max_packet_size: u16, |
| 140 | interval_ms: u8, | 141 | interval_ms: u8, |
| 141 | ) -> Result<Self::EndpointOut, EndpointAllocError>; | 142 | ) -> Result<Self::EndpointOut, EndpointAllocError>; |
| @@ -153,6 +154,7 @@ pub trait Driver<'a> { | |||
| 153 | fn alloc_endpoint_in( | 154 | fn alloc_endpoint_in( |
| 154 | &mut self, | 155 | &mut self, |
| 155 | ep_type: EndpointType, | 156 | ep_type: EndpointType, |
| 157 | ep_addr: Option<EndpointAddress>, | ||
| 156 | max_packet_size: u16, | 158 | max_packet_size: u16, |
| 157 | interval_ms: u8, | 159 | interval_ms: u8, |
| 158 | ) -> Result<Self::EndpointIn, EndpointAllocError>; | 160 | ) -> Result<Self::EndpointIn, EndpointAllocError>; |
diff --git a/embassy-usb-logger/CHANGELOG.md b/embassy-usb-logger/CHANGELOG.md index 86a9fb032..79ea25839 100644 --- a/embassy-usb-logger/CHANGELOG.md +++ b/embassy-usb-logger/CHANGELOG.md | |||
| @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 7 | 7 | ||
| 8 | ## Unreleased | 8 | ## Unreleased |
| 9 | 9 | ||
| 10 | ## 0.5.0 - 2025-07-22 | ||
| 11 | |||
| 12 | - Update `embassy-usb` to 0.5.0 | ||
| 13 | |||
| 10 | ## 0.4.0 - 2025-01-15 | 14 | ## 0.4.0 - 2025-01-15 |
| 11 | 15 | ||
| 12 | - Update `embassy-usb` to 0.4.0 | 16 | - Update `embassy-usb` to 0.4.0 |
diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index 6b8e9af47..68b11ad8a 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-usb-logger" | 2 | name = "embassy-usb-logger" |
| 3 | version = "0.4.0" | 3 | version = "0.5.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "`log` implementation for USB serial using `embassy-usb`." | 6 | description = "`log` implementation for USB serial using `embassy-usb`." |
| @@ -15,7 +15,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-l | |||
| 15 | target = "thumbv7em-none-eabi" | 15 | target = "thumbv7em-none-eabi" |
| 16 | 16 | ||
| 17 | [dependencies] | 17 | [dependencies] |
| 18 | embassy-usb = { version = "0.4.0", path = "../embassy-usb" } | 18 | embassy-usb = { version = "0.5.0", path = "../embassy-usb" } |
| 19 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 19 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 20 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 20 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 21 | log = "0.4" | 21 | log = "0.4" |
diff --git a/embassy-usb-synopsys-otg/CHANGELOG.md b/embassy-usb-synopsys-otg/CHANGELOG.md index 293363d9a..9913ee533 100644 --- a/embassy-usb-synopsys-otg/CHANGELOG.md +++ b/embassy-usb-synopsys-otg/CHANGELOG.md | |||
| @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 7 | 7 | ||
| 8 | ## Unreleased | 8 | ## Unreleased |
| 9 | 9 | ||
| 10 | ## 0.3.0 - 2025-07-22 | ||
| 11 | |||
| 12 | - Bump `embassy-usb-driver` to v0.2.0 | ||
| 13 | |||
| 10 | ## 0.2.0 - 2024-12-06 | 14 | ## 0.2.0 - 2024-12-06 |
| 11 | 15 | ||
| 12 | - Fix corruption in CONTROL OUT transfers (and remove `quirk_setup_late_cnak`) | 16 | - Fix corruption in CONTROL OUT transfers (and remove `quirk_setup_late_cnak`) |
diff --git a/embassy-usb-synopsys-otg/Cargo.toml b/embassy-usb-synopsys-otg/Cargo.toml index 6252feaef..78cce24de 100644 --- a/embassy-usb-synopsys-otg/Cargo.toml +++ b/embassy-usb-synopsys-otg/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-usb-synopsys-otg" | 2 | name = "embassy-usb-synopsys-otg" |
| 3 | version = "0.2.0" | 3 | version = "0.3.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "`embassy-usb-driver` implementation for Synopsys OTG USB controllers" | 6 | description = "`embassy-usb-driver` implementation for Synopsys OTG USB controllers" |
| @@ -19,7 +19,7 @@ target = "thumbv7em-none-eabi" | |||
| 19 | critical-section = "1.1" | 19 | critical-section = "1.1" |
| 20 | 20 | ||
| 21 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 21 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 22 | embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } | 22 | embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } |
| 23 | 23 | ||
| 24 | defmt = { version = "1.0.1", optional = true } | 24 | defmt = { version = "1.0.1", optional = true } |
| 25 | log = { version = "0.4.14", optional = true } | 25 | log = { version = "0.4.14", optional = true } |
diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index fc4428b54..9d74c046d 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs | |||
| @@ -345,6 +345,7 @@ impl<'d, const MAX_EP_COUNT: usize> Driver<'d, MAX_EP_COUNT> { | |||
| 345 | fn alloc_endpoint<D: Dir>( | 345 | fn alloc_endpoint<D: Dir>( |
| 346 | &mut self, | 346 | &mut self, |
| 347 | ep_type: EndpointType, | 347 | ep_type: EndpointType, |
| 348 | ep_addr: Option<EndpointAddress>, | ||
| 348 | max_packet_size: u16, | 349 | max_packet_size: u16, |
| 349 | interval_ms: u8, | 350 | interval_ms: u8, |
| 350 | ) -> Result<Endpoint<'d, D>, EndpointAllocError> { | 351 | ) -> Result<Endpoint<'d, D>, EndpointAllocError> { |
| @@ -379,15 +380,31 @@ impl<'d, const MAX_EP_COUNT: usize> Driver<'d, MAX_EP_COUNT> { | |||
| 379 | Direction::In => &mut self.ep_in[..self.instance.endpoint_count], | 380 | Direction::In => &mut self.ep_in[..self.instance.endpoint_count], |
| 380 | }; | 381 | }; |
| 381 | 382 | ||
| 382 | // Find free endpoint slot | 383 | // Find endpoint slot |
| 383 | let slot = eps.iter_mut().enumerate().find(|(i, ep)| { | 384 | let slot = if let Some(addr) = ep_addr { |
| 384 | if *i == 0 && ep_type != EndpointType::Control { | 385 | // Use the specified endpoint address |
| 385 | // reserved for control pipe | 386 | let requested_index = addr.index(); |
| 386 | false | 387 | if requested_index >= self.instance.endpoint_count { |
| 387 | } else { | 388 | return Err(EndpointAllocError); |
| 388 | ep.is_none() | ||
| 389 | } | 389 | } |
| 390 | }); | 390 | if requested_index == 0 && ep_type != EndpointType::Control { |
| 391 | return Err(EndpointAllocError); // EP0 is reserved for control | ||
| 392 | } | ||
| 393 | if eps[requested_index].is_some() { | ||
| 394 | return Err(EndpointAllocError); // Already allocated | ||
| 395 | } | ||
| 396 | Some((requested_index, &mut eps[requested_index])) | ||
| 397 | } else { | ||
| 398 | // Find any free endpoint slot | ||
| 399 | eps.iter_mut().enumerate().find(|(i, ep)| { | ||
| 400 | if *i == 0 && ep_type != EndpointType::Control { | ||
| 401 | // reserved for control pipe | ||
| 402 | false | ||
| 403 | } else { | ||
| 404 | ep.is_none() | ||
| 405 | } | ||
| 406 | }) | ||
| 407 | }; | ||
| 391 | 408 | ||
| 392 | let index = match slot { | 409 | let index = match slot { |
| 393 | Some((index, ep)) => { | 410 | Some((index, ep)) => { |
| @@ -438,27 +455,29 @@ impl<'d, const MAX_EP_COUNT: usize> embassy_usb_driver::Driver<'d> for Driver<'d | |||
| 438 | fn alloc_endpoint_in( | 455 | fn alloc_endpoint_in( |
| 439 | &mut self, | 456 | &mut self, |
| 440 | ep_type: EndpointType, | 457 | ep_type: EndpointType, |
| 458 | ep_addr: Option<EndpointAddress>, | ||
| 441 | max_packet_size: u16, | 459 | max_packet_size: u16, |
| 442 | interval_ms: u8, | 460 | interval_ms: u8, |
| 443 | ) -> Result<Self::EndpointIn, EndpointAllocError> { | 461 | ) -> Result<Self::EndpointIn, EndpointAllocError> { |
| 444 | self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | 462 | self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) |
| 445 | } | 463 | } |
| 446 | 464 | ||
| 447 | fn alloc_endpoint_out( | 465 | fn alloc_endpoint_out( |
| 448 | &mut self, | 466 | &mut self, |
| 449 | ep_type: EndpointType, | 467 | ep_type: EndpointType, |
| 468 | ep_addr: Option<EndpointAddress>, | ||
| 450 | max_packet_size: u16, | 469 | max_packet_size: u16, |
| 451 | interval_ms: u8, | 470 | interval_ms: u8, |
| 452 | ) -> Result<Self::EndpointOut, EndpointAllocError> { | 471 | ) -> Result<Self::EndpointOut, EndpointAllocError> { |
| 453 | self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | 472 | self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms) |
| 454 | } | 473 | } |
| 455 | 474 | ||
| 456 | fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { | 475 | fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { |
| 457 | let ep_out = self | 476 | let ep_out = self |
| 458 | .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) | 477 | .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0) |
| 459 | .unwrap(); | 478 | .unwrap(); |
| 460 | let ep_in = self | 479 | let ep_in = self |
| 461 | .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) | 480 | .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0) |
| 462 | .unwrap(); | 481 | .unwrap(); |
| 463 | assert_eq!(ep_out.info.addr.index(), 0); | 482 | assert_eq!(ep_out.info.addr.index(), 0); |
| 464 | assert_eq!(ep_in.info.addr.index(), 0); | 483 | assert_eq!(ep_in.info.addr.index(), 0); |
| @@ -1210,10 +1229,23 @@ impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> { | |||
| 1210 | }); | 1229 | }); |
| 1211 | 1230 | ||
| 1212 | // Write data to FIFO | 1231 | // Write data to FIFO |
| 1213 | for chunk in buf.chunks(4) { | 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(|| { | ||
| 1214 | let mut tmp = [0u8; 4]; | 1236 | let mut tmp = [0u8; 4]; |
| 1215 | tmp[0..chunk.len()].copy_from_slice(chunk); | 1237 | tmp[0..rem.len()].copy_from_slice(rem); |
| 1216 | self.regs.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))); | 1238 | u32::from_ne_bytes(tmp) |
| 1239 | }); | ||
| 1240 | |||
| 1241 | let fifo = self.regs.fifo(index); | ||
| 1242 | for chunk in chunks { | ||
| 1243 | let val = u32::from_ne_bytes(chunk.try_into().unwrap()); | ||
| 1244 | fifo.write_value(regs::Fifo(val)); | ||
| 1245 | } | ||
| 1246 | // Write any last chunk | ||
| 1247 | if let Some(val) = last_chunk { | ||
| 1248 | fifo.write_value(regs::Fifo(val)); | ||
| 1217 | } | 1249 | } |
| 1218 | }); | 1250 | }); |
| 1219 | 1251 | ||
diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md index 76fafed31..3ee75e226 100644 --- a/embassy-usb/CHANGELOG.md +++ b/embassy-usb/CHANGELOG.md | |||
| @@ -5,7 +5,18 @@ All notable changes to this project will be documented in this file. | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), |
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 7 | 7 | ||
| 8 | ## Unreleased | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | ||
| 10 | |||
| 11 | ## 0.5.0 - 2025-07-16 | ||
| 12 | |||
| 13 | - `UAC1`: unmute by default ([#3992](https://github.com/embassy-rs/embassy/pull/3992)) | ||
| 14 | - `cdc_acm`: `State::new` is now `const` ([#4000](https://github.com/embassy-rs/embassy/pull/4000)) | ||
| 15 | - Add support for CMSIS-DAP v2 USB class ([#4107](https://github.com/embassy-rs/embassy/pull/4107)) | ||
| 16 | - Reduce `UsbDevice` builder logs to `trace` ([#4130](https://github.com/embassy-rs/embassy/pull/4130)) | ||
| 17 | - Implement `embedded-io-async` traits for USB CDC ACM ([#4176](https://github.com/embassy-rs/embassy/pull/4176)) | ||
| 18 | - Update `embassy-sync` to v0.7.0 | ||
| 19 | - Fix CDC ACM BufferedReceiver buffer calculation | ||
| 9 | 20 | ||
| 10 | ## 0.4.0 - 2025-01-15 | 21 | ## 0.4.0 - 2025-01-15 |
| 11 | 22 | ||
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 31fd1c1e0..4743fde65 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-usb" | 2 | name = "embassy-usb" |
| 3 | version = "0.4.0" | 3 | version = "0.5.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "Async USB device stack for embedded devices in Rust." | 6 | description = "Async USB device stack for embedded devices in Rust." |
| @@ -47,9 +47,9 @@ max-handler-count-8 = [] | |||
| 47 | 47 | ||
| 48 | [dependencies] | 48 | [dependencies] |
| 49 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 49 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 50 | embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } | 50 | embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } |
| 51 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } | 51 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 52 | embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } | 52 | embassy-net-driver-channel = { version = "0.3.1", path = "../embassy-net-driver-channel" } |
| 53 | 53 | ||
| 54 | defmt = { version = "1", optional = true } | 54 | defmt = { version = "1", optional = true } |
| 55 | log = { version = "0.4.14", optional = true } | 55 | log = { version = "0.4.14", optional = true } |
diff --git a/embassy-usb/release.toml b/embassy-usb/release.toml new file mode 100644 index 000000000..fb6feaf21 --- /dev/null +++ b/embassy-usb/release.toml | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pre-release-replacements = [ | ||
| 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, | ||
| 3 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, | ||
| 4 | {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1}, | ||
| 5 | ] | ||
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 6c4b3f9a4..8d7abe46c 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs | |||
| @@ -2,7 +2,7 @@ use heapless::Vec; | |||
| 2 | 2 | ||
| 3 | use crate::config::MAX_HANDLER_COUNT; | 3 | use crate::config::MAX_HANDLER_COUNT; |
| 4 | use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType}; | 4 | use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType}; |
| 5 | use crate::driver::{Driver, Endpoint, EndpointInfo, EndpointType}; | 5 | use crate::driver::{Driver, Endpoint, EndpointAddress, EndpointInfo, EndpointType}; |
| 6 | use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; | 6 | use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; |
| 7 | use crate::types::{InterfaceNumber, StringIndex}; | 7 | use crate::types::{InterfaceNumber, StringIndex}; |
| 8 | use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; | 8 | use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; |
| @@ -465,11 +465,17 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 465 | /// Allocate an IN endpoint, without writing its descriptor. | 465 | /// Allocate an IN endpoint, without writing its descriptor. |
| 466 | /// | 466 | /// |
| 467 | /// Used for granular control over the order of endpoint and descriptor creation. | 467 | /// Used for granular control over the order of endpoint and descriptor creation. |
| 468 | pub fn alloc_endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { | 468 | pub fn alloc_endpoint_in( |
| 469 | &mut self, | ||
| 470 | ep_type: EndpointType, | ||
| 471 | ep_addr: Option<EndpointAddress>, | ||
| 472 | max_packet_size: u16, | ||
| 473 | interval_ms: u8, | ||
| 474 | ) -> D::EndpointIn { | ||
| 469 | let ep = self | 475 | let ep = self |
| 470 | .builder | 476 | .builder |
| 471 | .driver | 477 | .driver |
| 472 | .alloc_endpoint_in(ep_type, max_packet_size, interval_ms) | 478 | .alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms) |
| 473 | .expect("alloc_endpoint_in failed"); | 479 | .expect("alloc_endpoint_in failed"); |
| 474 | 480 | ||
| 475 | ep | 481 | ep |
| @@ -478,13 +484,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 478 | fn endpoint_in( | 484 | fn endpoint_in( |
| 479 | &mut self, | 485 | &mut self, |
| 480 | ep_type: EndpointType, | 486 | ep_type: EndpointType, |
| 487 | ep_addr: Option<EndpointAddress>, | ||
| 481 | max_packet_size: u16, | 488 | max_packet_size: u16, |
| 482 | interval_ms: u8, | 489 | interval_ms: u8, |
| 483 | synchronization_type: SynchronizationType, | 490 | synchronization_type: SynchronizationType, |
| 484 | usage_type: UsageType, | 491 | usage_type: UsageType, |
| 485 | extra_fields: &[u8], | 492 | extra_fields: &[u8], |
| 486 | ) -> D::EndpointIn { | 493 | ) -> D::EndpointIn { |
| 487 | let ep = self.alloc_endpoint_in(ep_type, max_packet_size, interval_ms); | 494 | let ep = self.alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms); |
| 488 | self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); | 495 | self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); |
| 489 | 496 | ||
| 490 | ep | 497 | ep |
| @@ -496,13 +503,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 496 | pub fn alloc_endpoint_out( | 503 | pub fn alloc_endpoint_out( |
| 497 | &mut self, | 504 | &mut self, |
| 498 | ep_type: EndpointType, | 505 | ep_type: EndpointType, |
| 506 | ep_addr: Option<EndpointAddress>, | ||
| 499 | max_packet_size: u16, | 507 | max_packet_size: u16, |
| 500 | interval_ms: u8, | 508 | interval_ms: u8, |
| 501 | ) -> D::EndpointOut { | 509 | ) -> D::EndpointOut { |
| 502 | let ep = self | 510 | let ep = self |
| 503 | .builder | 511 | .builder |
| 504 | .driver | 512 | .driver |
| 505 | .alloc_endpoint_out(ep_type, max_packet_size, interval_ms) | 513 | .alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms) |
| 506 | .expect("alloc_endpoint_out failed"); | 514 | .expect("alloc_endpoint_out failed"); |
| 507 | 515 | ||
| 508 | ep | 516 | ep |
| @@ -511,13 +519,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 511 | fn endpoint_out( | 519 | fn endpoint_out( |
| 512 | &mut self, | 520 | &mut self, |
| 513 | ep_type: EndpointType, | 521 | ep_type: EndpointType, |
| 522 | ep_addr: Option<EndpointAddress>, | ||
| 514 | max_packet_size: u16, | 523 | max_packet_size: u16, |
| 515 | interval_ms: u8, | 524 | interval_ms: u8, |
| 516 | synchronization_type: SynchronizationType, | 525 | synchronization_type: SynchronizationType, |
| 517 | usage_type: UsageType, | 526 | usage_type: UsageType, |
| 518 | extra_fields: &[u8], | 527 | extra_fields: &[u8], |
| 519 | ) -> D::EndpointOut { | 528 | ) -> D::EndpointOut { |
| 520 | let ep = self.alloc_endpoint_out(ep_type, max_packet_size, interval_ms); | 529 | let ep = self.alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms); |
| 521 | self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); | 530 | self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); |
| 522 | 531 | ||
| 523 | ep | 532 | ep |
| @@ -527,9 +536,10 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 527 | /// | 536 | /// |
| 528 | /// Descriptors are written in the order builder functions are called. Note that some | 537 | /// Descriptors are written in the order builder functions are called. Note that some |
| 529 | /// classes care about the order. | 538 | /// classes care about the order. |
| 530 | pub fn endpoint_bulk_in(&mut self, max_packet_size: u16) -> D::EndpointIn { | 539 | pub fn endpoint_bulk_in(&mut self, ep_addr: Option<EndpointAddress>, max_packet_size: u16) -> D::EndpointIn { |
| 531 | self.endpoint_in( | 540 | self.endpoint_in( |
| 532 | EndpointType::Bulk, | 541 | EndpointType::Bulk, |
| 542 | ep_addr, | ||
| 533 | max_packet_size, | 543 | max_packet_size, |
| 534 | 0, | 544 | 0, |
| 535 | SynchronizationType::NoSynchronization, | 545 | SynchronizationType::NoSynchronization, |
| @@ -542,9 +552,10 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 542 | /// | 552 | /// |
| 543 | /// Descriptors are written in the order builder functions are called. Note that some | 553 | /// Descriptors are written in the order builder functions are called. Note that some |
| 544 | /// classes care about the order. | 554 | /// classes care about the order. |
| 545 | pub fn endpoint_bulk_out(&mut self, max_packet_size: u16) -> D::EndpointOut { | 555 | pub fn endpoint_bulk_out(&mut self, ep_addr: Option<EndpointAddress>, max_packet_size: u16) -> D::EndpointOut { |
| 546 | self.endpoint_out( | 556 | self.endpoint_out( |
| 547 | EndpointType::Bulk, | 557 | EndpointType::Bulk, |
| 558 | ep_addr, | ||
| 548 | max_packet_size, | 559 | max_packet_size, |
| 549 | 0, | 560 | 0, |
| 550 | SynchronizationType::NoSynchronization, | 561 | SynchronizationType::NoSynchronization, |
| @@ -557,9 +568,15 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 557 | /// | 568 | /// |
| 558 | /// Descriptors are written in the order builder functions are called. Note that some | 569 | /// Descriptors are written in the order builder functions are called. Note that some |
| 559 | /// classes care about the order. | 570 | /// classes care about the order. |
| 560 | pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { | 571 | pub fn endpoint_interrupt_in( |
| 572 | &mut self, | ||
| 573 | ep_addr: Option<EndpointAddress>, | ||
| 574 | max_packet_size: u16, | ||
| 575 | interval_ms: u8, | ||
| 576 | ) -> D::EndpointIn { | ||
| 561 | self.endpoint_in( | 577 | self.endpoint_in( |
| 562 | EndpointType::Interrupt, | 578 | EndpointType::Interrupt, |
| 579 | ep_addr, | ||
| 563 | max_packet_size, | 580 | max_packet_size, |
| 564 | interval_ms, | 581 | interval_ms, |
| 565 | SynchronizationType::NoSynchronization, | 582 | SynchronizationType::NoSynchronization, |
| @@ -569,9 +586,15 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 569 | } | 586 | } |
| 570 | 587 | ||
| 571 | /// Allocate a INTERRUPT OUT endpoint and write its descriptor. | 588 | /// Allocate a INTERRUPT OUT endpoint and write its descriptor. |
| 572 | pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { | 589 | pub fn endpoint_interrupt_out( |
| 590 | &mut self, | ||
| 591 | ep_addr: Option<EndpointAddress>, | ||
| 592 | max_packet_size: u16, | ||
| 593 | interval_ms: u8, | ||
| 594 | ) -> D::EndpointOut { | ||
| 573 | self.endpoint_out( | 595 | self.endpoint_out( |
| 574 | EndpointType::Interrupt, | 596 | EndpointType::Interrupt, |
| 597 | ep_addr, | ||
| 575 | max_packet_size, | 598 | max_packet_size, |
| 576 | interval_ms, | 599 | interval_ms, |
| 577 | SynchronizationType::NoSynchronization, | 600 | SynchronizationType::NoSynchronization, |
| @@ -586,6 +609,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 586 | /// classes care about the order. | 609 | /// classes care about the order. |
| 587 | pub fn endpoint_isochronous_in( | 610 | pub fn endpoint_isochronous_in( |
| 588 | &mut self, | 611 | &mut self, |
| 612 | ep_addr: Option<EndpointAddress>, | ||
| 589 | max_packet_size: u16, | 613 | max_packet_size: u16, |
| 590 | interval_ms: u8, | 614 | interval_ms: u8, |
| 591 | synchronization_type: SynchronizationType, | 615 | synchronization_type: SynchronizationType, |
| @@ -594,6 +618,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 594 | ) -> D::EndpointIn { | 618 | ) -> D::EndpointIn { |
| 595 | self.endpoint_in( | 619 | self.endpoint_in( |
| 596 | EndpointType::Isochronous, | 620 | EndpointType::Isochronous, |
| 621 | ep_addr, | ||
| 597 | max_packet_size, | 622 | max_packet_size, |
| 598 | interval_ms, | 623 | interval_ms, |
| 599 | synchronization_type, | 624 | synchronization_type, |
| @@ -605,6 +630,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 605 | /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. | 630 | /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. |
| 606 | pub fn endpoint_isochronous_out( | 631 | pub fn endpoint_isochronous_out( |
| 607 | &mut self, | 632 | &mut self, |
| 633 | ep_addr: Option<EndpointAddress>, | ||
| 608 | max_packet_size: u16, | 634 | max_packet_size: u16, |
| 609 | interval_ms: u8, | 635 | interval_ms: u8, |
| 610 | synchronization_type: SynchronizationType, | 636 | synchronization_type: SynchronizationType, |
| @@ -613,6 +639,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 613 | ) -> D::EndpointOut { | 639 | ) -> D::EndpointOut { |
| 614 | self.endpoint_out( | 640 | self.endpoint_out( |
| 615 | EndpointType::Isochronous, | 641 | EndpointType::Isochronous, |
| 642 | ep_addr, | ||
| 616 | max_packet_size, | 643 | max_packet_size, |
| 617 | interval_ms, | 644 | interval_ms, |
| 618 | synchronization_type, | 645 | synchronization_type, |
diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 732a433f8..0a1a5e64f 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs | |||
| @@ -254,14 +254,14 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | |||
| 254 | ], | 254 | ], |
| 255 | ); | 255 | ); |
| 256 | 256 | ||
| 257 | let comm_ep = alt.endpoint_interrupt_in(8, 255); | 257 | let comm_ep = alt.endpoint_interrupt_in(None, 8, 255); |
| 258 | 258 | ||
| 259 | // Data interface | 259 | // Data interface |
| 260 | let mut iface = func.interface(); | 260 | let mut iface = func.interface(); |
| 261 | let data_if = iface.interface_number(); | 261 | let data_if = iface.interface_number(); |
| 262 | let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE, None); | 262 | let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE, None); |
| 263 | let read_ep = alt.endpoint_bulk_out(max_packet_size); | 263 | let read_ep = alt.endpoint_bulk_out(None, max_packet_size); |
| 264 | let write_ep = alt.endpoint_bulk_in(max_packet_size); | 264 | let write_ep = alt.endpoint_bulk_in(None, max_packet_size); |
| 265 | 265 | ||
| 266 | drop(func); | 266 | drop(func); |
| 267 | 267 | ||
| @@ -501,7 +501,7 @@ impl<'d, D: Driver<'d>> BufferedReceiver<'d, D> { | |||
| 501 | fn read_from_buffer(&mut self, buf: &mut [u8]) -> usize { | 501 | fn read_from_buffer(&mut self, buf: &mut [u8]) -> usize { |
| 502 | let available = &self.buffer[self.start..self.end]; | 502 | let available = &self.buffer[self.start..self.end]; |
| 503 | let len = core::cmp::min(available.len(), buf.len()); | 503 | let len = core::cmp::min(available.len(), buf.len()); |
| 504 | buf[..len].copy_from_slice(&self.buffer[..len]); | 504 | buf[..len].copy_from_slice(&available[..len]); |
| 505 | self.start += len; | 505 | self.start += len; |
| 506 | len | 506 | len |
| 507 | } | 507 | } |
diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index 09d923d2a..3af853091 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs | |||
| @@ -313,15 +313,15 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | |||
| 313 | ], | 313 | ], |
| 314 | ); | 314 | ); |
| 315 | 315 | ||
| 316 | let comm_ep = alt.endpoint_interrupt_in(8, 255); | 316 | let comm_ep = alt.endpoint_interrupt_in(None, 8, 255); |
| 317 | 317 | ||
| 318 | // Data interface | 318 | // Data interface |
| 319 | let mut iface = func.interface(); | 319 | let mut iface = func.interface(); |
| 320 | let data_if = iface.interface_number(); | 320 | let data_if = iface.interface_number(); |
| 321 | let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); | 321 | let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); |
| 322 | let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); | 322 | let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); |
| 323 | let read_ep = alt.endpoint_bulk_out(max_packet_size); | 323 | let read_ep = alt.endpoint_bulk_out(None, max_packet_size); |
| 324 | let write_ep = alt.endpoint_bulk_in(max_packet_size); | 324 | let write_ep = alt.endpoint_bulk_in(None, max_packet_size); |
| 325 | 325 | ||
| 326 | drop(func); | 326 | drop(func); |
| 327 | 327 | ||
diff --git a/embassy-usb/src/class/cmsis_dap_v2.rs b/embassy-usb/src/class/cmsis_dap_v2.rs index a94e3ddb7..a9fd9cdf0 100644 --- a/embassy-usb/src/class/cmsis_dap_v2.rs +++ b/embassy-usb/src/class/cmsis_dap_v2.rs | |||
| @@ -61,10 +61,10 @@ impl<'d, D: Driver<'d>> CmsisDapV2Class<'d, D> { | |||
| 61 | )); | 61 | )); |
| 62 | let mut interface = function.interface(); | 62 | let mut interface = function.interface(); |
| 63 | let mut alt = interface.alt_setting(0xFF, 0, 0, Some(iface_string)); | 63 | let mut alt = interface.alt_setting(0xFF, 0, 0, Some(iface_string)); |
| 64 | let read_ep = alt.endpoint_bulk_out(max_packet_size); | 64 | let read_ep = alt.endpoint_bulk_out(None, max_packet_size); |
| 65 | let write_ep = alt.endpoint_bulk_in(max_packet_size); | 65 | let write_ep = alt.endpoint_bulk_in(None, max_packet_size); |
| 66 | let trace_ep = if trace { | 66 | let trace_ep = if trace { |
| 67 | Some(alt.endpoint_bulk_in(max_packet_size)) | 67 | Some(alt.endpoint_bulk_in(None, max_packet_size)) |
| 68 | } else { | 68 | } else { |
| 69 | None | 69 | None |
| 70 | }; | 70 | }; |
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 6d9e0aced..182e1f83f 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs | |||
| @@ -133,9 +133,9 @@ fn build<'d, D: Driver<'d>>( | |||
| 133 | ], | 133 | ], |
| 134 | ); | 134 | ); |
| 135 | 135 | ||
| 136 | let ep_in = alt.endpoint_interrupt_in(config.max_packet_size, config.poll_ms); | 136 | let ep_in = alt.endpoint_interrupt_in(None, config.max_packet_size, config.poll_ms); |
| 137 | let ep_out = if with_out_endpoint { | 137 | let ep_out = if with_out_endpoint { |
| 138 | Some(alt.endpoint_interrupt_out(config.max_packet_size, config.poll_ms)) | 138 | Some(alt.endpoint_interrupt_out(None, config.max_packet_size, config.poll_ms)) |
| 139 | } else { | 139 | } else { |
| 140 | None | 140 | None |
| 141 | }; | 141 | }; |
diff --git a/embassy-usb/src/class/midi.rs b/embassy-usb/src/class/midi.rs index 52a96f278..1d152ca44 100644 --- a/embassy-usb/src/class/midi.rs +++ b/embassy-usb/src/class/midi.rs | |||
| @@ -129,14 +129,14 @@ impl<'d, D: Driver<'d>> MidiClass<'d, D> { | |||
| 129 | for i in 0..n_out_jacks { | 129 | for i in 0..n_out_jacks { |
| 130 | endpoint_data[2 + i as usize] = in_jack_id_emb(i); | 130 | endpoint_data[2 + i as usize] = in_jack_id_emb(i); |
| 131 | } | 131 | } |
| 132 | let read_ep = alt.endpoint_bulk_out(max_packet_size); | 132 | let read_ep = alt.endpoint_bulk_out(None, max_packet_size); |
| 133 | alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_out_jacks as usize]); | 133 | alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_out_jacks as usize]); |
| 134 | 134 | ||
| 135 | endpoint_data[1] = n_in_jacks; | 135 | endpoint_data[1] = n_in_jacks; |
| 136 | for i in 0..n_in_jacks { | 136 | for i in 0..n_in_jacks { |
| 137 | endpoint_data[2 + i as usize] = out_jack_id_emb(i); | 137 | endpoint_data[2 + i as usize] = out_jack_id_emb(i); |
| 138 | } | 138 | } |
| 139 | let write_ep = alt.endpoint_bulk_in(max_packet_size); | 139 | let write_ep = alt.endpoint_bulk_in(None, max_packet_size); |
| 140 | alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_in_jacks as usize]); | 140 | alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_in_jacks as usize]); |
| 141 | 141 | ||
| 142 | MidiClass { read_ep, write_ep } | 142 | MidiClass { read_ep, write_ep } |
diff --git a/embassy-usb/src/class/uac1/speaker.rs b/embassy-usb/src/class/uac1/speaker.rs index 1ff29088c..9565e2a25 100644 --- a/embassy-usb/src/class/uac1/speaker.rs +++ b/embassy-usb/src/class/uac1/speaker.rs | |||
| @@ -268,9 +268,10 @@ impl<'d, D: Driver<'d>> Speaker<'d, D> { | |||
| 268 | 268 | ||
| 269 | alt.descriptor(CS_INTERFACE, &format_descriptor); | 269 | alt.descriptor(CS_INTERFACE, &format_descriptor); |
| 270 | 270 | ||
| 271 | let streaming_endpoint = alt.alloc_endpoint_out(EndpointType::Isochronous, max_packet_size, 1); | 271 | let streaming_endpoint = alt.alloc_endpoint_out(EndpointType::Isochronous, None, max_packet_size, 1); |
| 272 | let feedback_endpoint = alt.alloc_endpoint_in( | 272 | let feedback_endpoint = alt.alloc_endpoint_in( |
| 273 | EndpointType::Isochronous, | 273 | EndpointType::Isochronous, |
| 274 | None, | ||
| 274 | 4, // Feedback packets are 24 bit (10.14 format). | 275 | 4, // Feedback packets are 24 bit (10.14 format). |
| 275 | 1, | 276 | 1, |
| 276 | ); | 277 | ); |
diff --git a/embassy-usb/src/class/web_usb.rs b/embassy-usb/src/class/web_usb.rs index 405944f14..154b219ca 100644 --- a/embassy-usb/src/class/web_usb.rs +++ b/embassy-usb/src/class/web_usb.rs | |||
| @@ -84,7 +84,7 @@ impl<'d> Control<'d> { | |||
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | impl<'d> Handler for Control<'d> { | 86 | impl<'d> Handler for Control<'d> { |
| 87 | fn control_in(&mut self, req: Request, _data: &mut [u8]) -> Option<InResponse> { | 87 | fn control_in(&mut self, req: Request, _data: &mut [u8]) -> Option<InResponse<'_>> { |
| 88 | let landing_value = if self.landing_url.is_some() { 1 } else { 0 }; | 88 | let landing_value = if self.landing_url.is_some() { 1 } else { 0 }; |
| 89 | if req.request_type == RequestType::Vendor | 89 | if req.request_type == RequestType::Vendor |
| 90 | && req.recipient == Recipient::Device | 90 | && req.recipient == Recipient::Device |
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 4d633e8a8..37183df97 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml | |||
| @@ -8,10 +8,10 @@ license = "MIT OR Apache-2.0" | |||
| 8 | embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } | 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } |
| 11 | embassy-nrf = { version = "0.3.1", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } | 11 | embassy-nrf = { version = "0.5.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } |
| 12 | embassy-boot = { version = "0.4.0", path = "../../../../embassy-boot", features = [] } | 12 | embassy-boot = { version = "0.5.0", path = "../../../../embassy-boot", features = [] } |
| 13 | embassy-boot-nrf = { version = "0.4.0", path = "../../../../embassy-boot-nrf", features = [] } | 13 | embassy-boot-nrf = { version = "0.6.0", path = "../../../../embassy-boot-nrf", features = [] } |
| 14 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } | 14 | embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } |
| 15 | 15 | ||
| 16 | defmt = { version = "1.0.1", optional = true } | 16 | defmt = { version = "1.0.1", optional = true } |
| 17 | defmt-rtt = { version = "1.0.0", optional = true } | 17 | defmt-rtt = { version = "1.0.0", optional = true } |
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index be283fb27..e5568f6bb 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml | |||
| @@ -8,9 +8,9 @@ license = "MIT OR Apache-2.0" | |||
| 8 | embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } | 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } |
| 11 | embassy-rp = { version = "0.4.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } | 11 | embassy-rp = { version = "0.6.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } |
| 12 | embassy-boot-rp = { version = "0.5.0", path = "../../../../embassy-boot-rp", features = [] } | 12 | embassy-boot-rp = { version = "0.6.0", path = "../../../../embassy-boot-rp", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = "1.0.1" | 15 | defmt = "1.0.1" |
| 16 | defmt-rtt = "1.0.0" | 16 | defmt-rtt = "1.0.0" |
diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index ede0c07da..e6d7b3d4f 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs | |||
| @@ -54,7 +54,7 @@ async fn main(_s: Spawner) { | |||
| 54 | for chunk in APP_B.chunks(4096) { | 54 | for chunk in APP_B.chunks(4096) { |
| 55 | buf.0[..chunk.len()].copy_from_slice(chunk); | 55 | buf.0[..chunk.len()].copy_from_slice(chunk); |
| 56 | defmt::info!("writing block at offset {}", offset); | 56 | defmt::info!("writing block at offset {}", offset); |
| 57 | writer.write(offset, &buf.0[..]).unwrap(); | 57 | writer.write(offset, &buf.0[..chunk.len()]).unwrap(); |
| 58 | offset += chunk.len() as u32; | 58 | offset += chunk.len() as u32; |
| 59 | } | 59 | } |
| 60 | watchdog.feed(); | 60 | watchdog.feed(); |
diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index b3466e288..be8b7bff1 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml | |||
| @@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } | |||
| 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32" } | 12 | embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32" } |
| 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "1.0.1", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "1.0.0", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 72dbded5f..2b0175a0c 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml | |||
| @@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } | |||
| 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti", "single-bank"] } | 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti", "single-bank"] } |
| 12 | embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "1.0.1", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "1.0.0", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 57fb8312c..3c88f4241 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml | |||
| @@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } | |||
| 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "1.0.1", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "1.0.0", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 7dbbba138..b4e7e090a 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml | |||
| @@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } | |||
| 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } | 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } |
| 12 | embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "1.0.1", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "1.0.0", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 9549b2048..394578e1a 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml | |||
| @@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } | |||
| 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "1.0.1", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "1.0.0", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 03daeb0bc..abe0451fd 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml | |||
| @@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } | |||
| 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "1.0.1", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "1.0.0", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index e582628aa..bc4681f79 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml | |||
| @@ -9,9 +9,9 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } | |||
| 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } |
| 14 | embassy-usb = { version = "0.4.0", path = "../../../../embassy-usb" } | 14 | embassy-usb = { version = "0.5.0", path = "../../../../embassy-usb" } |
| 15 | embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] } | 15 | embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] } |
| 16 | 16 | ||
| 17 | defmt = { version = "1.0.1", optional = true } | 17 | defmt = { version = "1.0.1", optional = true } |
diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 3ed04b472..0552d109a 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml | |||
| @@ -9,8 +9,8 @@ embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" } | |||
| 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "1.0.1", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "1.0.0", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
diff --git a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml index 0bb93b12e..d101e6ace 100644 --- a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml +++ b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml | |||
| @@ -18,7 +18,7 @@ embedded-storage = "0.3.1" | |||
| 18 | embedded-storage-async = "0.4.0" | 18 | embedded-storage-async = "0.4.0" |
| 19 | cfg-if = "1.0.0" | 19 | cfg-if = "1.0.0" |
| 20 | embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["dfu", "cortex-m"] } | 20 | embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["dfu", "cortex-m"] } |
| 21 | embassy-usb = { version = "0.4.0", path = "../../../../embassy-usb", default-features = false } | 21 | embassy-usb = { version = "0.5.0", path = "../../../../embassy-usb", default-features = false } |
| 22 | embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" } | 22 | embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" } |
| 23 | 23 | ||
| 24 | [features] | 24 | [features] |
diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index 7f81e9c7f..1724a22d4 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml | |||
| @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["rt"] } | 9 | embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55", "rt", "defmt"] } |
| 10 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } | 10 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } |
| 11 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 11 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] } |
diff --git a/examples/mimxrt1011/.cargo/config.toml b/examples/mimxrt1011/.cargo/config.toml new file mode 100644 index 000000000..12f4b27b2 --- /dev/null +++ b/examples/mimxrt1011/.cargo/config.toml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | [target.thumbv7em-none-eabihf] | ||
| 2 | runner = 'probe-rs run --chip MIMXRT1010' | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv7em-none-eabihf" # Cortex-M7 | ||
| 6 | |||
| 7 | [env] | ||
| 8 | DEFMT_LOG = "trace" | ||
diff --git a/examples/mimxrt1011/Cargo.toml b/examples/mimxrt1011/Cargo.toml new file mode 100644 index 000000000..cf4e4c163 --- /dev/null +++ b/examples/mimxrt1011/Cargo.toml | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | [package] | ||
| 2 | name = "embassy-imxrt1011-examples" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2021" | ||
| 5 | license = "MIT or Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | cortex-m = { version = "0.7.7", features = ["inline-asm", "critical-section-single-core"] } | ||
| 9 | cortex-m-rt = "0.7.3" | ||
| 10 | defmt = "1.0.1" | ||
| 11 | defmt-rtt = "1.0.0" | ||
| 12 | |||
| 13 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | ||
| 14 | embassy-futures = { version = "0.1.1", path = "../../embassy-futures" } | ||
| 15 | embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["defmt", "mimxrt1011", "unstable-pac", "time-driver-pit"] } | ||
| 16 | embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", ] } # "defmt-timestamp-uptime" # RT1011 hard faults currently with this enabled. | ||
| 17 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 18 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 19 | embedded-hal-async = "1.0.0" | ||
| 20 | |||
| 21 | imxrt-boot-gen = { version = "0.3.4", features = ["imxrt1010"] } | ||
| 22 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 23 | panic-semihosting = "0.6.0" | ||
| 24 | |||
| 25 | [build-dependencies] | ||
| 26 | imxrt-rt = { version = "0.1.7", features = ["device"] } | ||
| 27 | |||
| 28 | [profile.release] | ||
| 29 | debug = 2 | ||
diff --git a/examples/mimxrt1011/build.rs b/examples/mimxrt1011/build.rs new file mode 100644 index 000000000..99e172aba --- /dev/null +++ b/examples/mimxrt1011/build.rs | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | use imxrt_rt::{Family, RuntimeBuilder}; | ||
| 2 | |||
| 3 | fn main() { | ||
| 4 | // The IMXRT1010-EVK technically has 128M of flash, but we only ever use 8MB so that the examples | ||
| 5 | // will build fine on the Adafruit Metro M7 boards. | ||
| 6 | RuntimeBuilder::from_flexspi(Family::Imxrt1010, 8 * 1024 * 1024) | ||
| 7 | .build() | ||
| 8 | .unwrap(); | ||
| 9 | |||
| 10 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 11 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 12 | // Not link.x, as imxrt-rt needs to do some special things | ||
| 13 | println!("cargo:rustc-link-arg-bins=-Timxrt-link.x"); | ||
| 14 | } | ||
diff --git a/examples/mimxrt1011/src/bin/blinky.rs b/examples/mimxrt1011/src/bin/blinky.rs new file mode 100644 index 000000000..a5d5de6b3 --- /dev/null +++ b/examples/mimxrt1011/src/bin/blinky.rs | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | //! This example works on the following boards: | ||
| 2 | //! - IMXRT1010-EVK | ||
| 3 | //! - Adafruit Metro M7 (with microSD or with AirLift), requires an external button | ||
| 4 | //! - Makerdiary iMX RT1011 Nano Kit (TODO: currently untested, please change this) | ||
| 5 | //! | ||
| 6 | //! Although beware you will need to change the GPIO pins being used (scroll down). | ||
| 7 | |||
| 8 | #![no_std] | ||
| 9 | #![no_main] | ||
| 10 | |||
| 11 | use defmt::info; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_nxp::gpio::{Level, Output}; | ||
| 14 | use embassy_time::Timer; | ||
| 15 | // Must include `embassy_imxrt1011_examples` to ensure the FCB gets linked. | ||
| 16 | use {defmt_rtt as _, embassy_imxrt1011_examples as _, panic_probe as _}; | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) -> ! { | ||
| 20 | let p = embassy_nxp::init(Default::default()); | ||
| 21 | info!("Hello world!"); | ||
| 22 | |||
| 23 | /* Pick the pins to use depending on your board. */ | ||
| 24 | |||
| 25 | // IMXRT1010-EVK | ||
| 26 | // | ||
| 27 | // LED (D25) | ||
| 28 | let led = p.GPIO_11; | ||
| 29 | |||
| 30 | // Adafruit Metro M7 (both microSD and AirLift variants) | ||
| 31 | // | ||
| 32 | // The LED is connected to D13 on the board. | ||
| 33 | // let led = p.GPIO_03; | ||
| 34 | |||
| 35 | // Makerdiary iMX RT1011 Nano Kit | ||
| 36 | // | ||
| 37 | // LED0 | ||
| 38 | // let led = p.GPIO_SD_04; | ||
| 39 | |||
| 40 | let mut led = Output::new(led, Level::Low); | ||
| 41 | |||
| 42 | loop { | ||
| 43 | Timer::after_millis(500).await; | ||
| 44 | |||
| 45 | info!("Toggle"); | ||
| 46 | led.toggle(); | ||
| 47 | } | ||
| 48 | } | ||
diff --git a/examples/mimxrt1011/src/bin/button.rs b/examples/mimxrt1011/src/bin/button.rs new file mode 100644 index 000000000..e63d7171d --- /dev/null +++ b/examples/mimxrt1011/src/bin/button.rs | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | //! This example works on the following boards: | ||
| 2 | //! - IMXRT1010-EVK | ||
| 3 | //! - Adafruit Metro M7 (with microSD or with AirLift), requires an external button | ||
| 4 | //! - Makerdiary iMX RT1011 Nano Kit (TODO: currently untested, please change this) | ||
| 5 | //! | ||
| 6 | //! Although beware you will need to change the GPIO pins being used (scroll down). | ||
| 7 | |||
| 8 | #![no_std] | ||
| 9 | #![no_main] | ||
| 10 | |||
| 11 | use defmt::info; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_nxp::gpio::{Input, Level, Output, Pull}; | ||
| 14 | // Must include `embassy_imxrt1011_examples` to ensure the FCB gets linked. | ||
| 15 | use {defmt_rtt as _, embassy_imxrt1011_examples as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) -> ! { | ||
| 19 | let p = embassy_nxp::init(Default::default()); | ||
| 20 | info!("Hello world!"); | ||
| 21 | |||
| 22 | /* Pick the pins to use depending on your board. */ | ||
| 23 | |||
| 24 | // IMXRT1010-EVK | ||
| 25 | // | ||
| 26 | // LED (D25) and user button (SW4) | ||
| 27 | let (led, button) = (p.GPIO_11, p.GPIO_SD_05); | ||
| 28 | |||
| 29 | // Adafruit Metro M7 (both microSD and AirLift variants) | ||
| 30 | // | ||
| 31 | // The LED is connected to D13 on the board. | ||
| 32 | // | ||
| 33 | // In particular the Metro M7 has no board user buttons, so you will need to connect a button. | ||
| 34 | // Any other GPIO pin can be used. GPIO_04 is used for example since it is on pin D12. | ||
| 35 | // let (led, button) = (p.GPIO_03, p.GPIO_04); | ||
| 36 | |||
| 37 | // Makerdiary iMX RT1011 Nano Kit | ||
| 38 | // | ||
| 39 | // LED0 and user button. | ||
| 40 | // let (led, button) = (p.GPIO_SD_04, p.GPIO_SD_03); | ||
| 41 | |||
| 42 | let mut button = Input::new(button, Pull::Up100K); | ||
| 43 | let mut led = Output::new(led, Level::Low); | ||
| 44 | led.set_high(); | ||
| 45 | |||
| 46 | loop { | ||
| 47 | button.wait_for_falling_edge().await; | ||
| 48 | |||
| 49 | info!("Toggled"); | ||
| 50 | led.toggle(); | ||
| 51 | |||
| 52 | // The RT1010EVK has a 100 nF debouncing capacitor which results in false positive events | ||
| 53 | // when listening for a falling edge in a loop, wait for the rising edge and then wait for | ||
| 54 | // stabilization. | ||
| 55 | button.wait_for_rising_edge().await; | ||
| 56 | |||
| 57 | // Stabilization. | ||
| 58 | for _ in 0..100_000 { | ||
| 59 | cortex_m::asm::nop(); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
diff --git a/examples/mimxrt1011/src/lib.rs b/examples/mimxrt1011/src/lib.rs new file mode 100644 index 000000000..f0391ef57 --- /dev/null +++ b/examples/mimxrt1011/src/lib.rs | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | //! FlexSPI configuration block (FCB) for iMXRT1011 boards. | ||
| 2 | //! | ||
| 3 | //! This is a generic FCB that should work with most QSPI flash. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | |||
| 7 | use imxrt_boot_gen::flexspi; | ||
| 8 | use imxrt_boot_gen::flexspi::opcodes::sdr::*; | ||
| 9 | use imxrt_boot_gen::flexspi::{ | ||
| 10 | ColumnAddressWidth, Command, DeviceModeConfiguration, FlashPadType, Instr, LookupTable, Pads, | ||
| 11 | ReadSampleClockSource, Sequence, SequenceBuilder, SerialClockFrequency, SerialFlashRegion, | ||
| 12 | WaitTimeConfigurationCommands, | ||
| 13 | }; | ||
| 14 | use imxrt_boot_gen::serial_flash::nor; | ||
| 15 | |||
| 16 | /// While the IMXRT1010-EVK and Makerdiary iMX RT1011 Nano Kit have 128MBit of flash we limit to 64Mbit | ||
| 17 | /// to allow the Metro M7 boards to use the same FCB configuration. | ||
| 18 | const DENSITY_BITS: u32 = 64 * 1024 * 1024; | ||
| 19 | const DENSITY_BYTES: u32 = DENSITY_BITS / 8; | ||
| 20 | |||
| 21 | const SEQ_READ: Sequence = SequenceBuilder::new() | ||
| 22 | .instr(Instr::new(CMD, Pads::One, 0xEB)) | ||
| 23 | .instr(Instr::new(RADDR, Pads::Four, 0x18)) | ||
| 24 | .instr(Instr::new(DUMMY, Pads::Four, 0x06)) | ||
| 25 | .instr(Instr::new(READ, Pads::Four, 0x04)) | ||
| 26 | .build(); | ||
| 27 | |||
| 28 | const SEQ_READ_STATUS: Sequence = SequenceBuilder::new() | ||
| 29 | .instr(Instr::new(CMD, Pads::One, 0x05)) | ||
| 30 | .instr(Instr::new(READ, Pads::One, 0x01)) | ||
| 31 | .build(); | ||
| 32 | |||
| 33 | const SEQ_WRITE_ENABLE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x06)).build(); | ||
| 34 | |||
| 35 | const SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new() | ||
| 36 | .instr(Instr::new(CMD, Pads::One, 0x20)) | ||
| 37 | .instr(Instr::new(RADDR, Pads::One, 0x18)) | ||
| 38 | .build(); | ||
| 39 | |||
| 40 | const SEQ_PAGE_PROGRAM: Sequence = SequenceBuilder::new() | ||
| 41 | .instr(Instr::new(CMD, Pads::One, 0x02)) | ||
| 42 | .instr(Instr::new(RADDR, Pads::One, 0x18)) | ||
| 43 | .instr(Instr::new(WRITE, Pads::One, 0x04)) | ||
| 44 | .build(); | ||
| 45 | |||
| 46 | const SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x60)).build(); | ||
| 47 | |||
| 48 | const LUT: LookupTable = LookupTable::new() | ||
| 49 | .command(Command::Read, SEQ_READ) | ||
| 50 | .command(Command::ReadStatus, SEQ_READ_STATUS) | ||
| 51 | .command(Command::WriteEnable, SEQ_WRITE_ENABLE) | ||
| 52 | .command(Command::EraseSector, SEQ_ERASE_SECTOR) | ||
| 53 | .command(Command::PageProgram, SEQ_PAGE_PROGRAM) | ||
| 54 | .command(Command::ChipErase, SEQ_CHIP_ERASE); | ||
| 55 | |||
| 56 | const COMMON_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LUT) | ||
| 57 | .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad) | ||
| 58 | .cs_hold_time(0x03) | ||
| 59 | .cs_setup_time(0x03) | ||
| 60 | .column_address_width(ColumnAddressWidth::OtherDevices) | ||
| 61 | .device_mode_configuration(DeviceModeConfiguration::Disabled) | ||
| 62 | .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable()) | ||
| 63 | .flash_size(SerialFlashRegion::A1, DENSITY_BYTES) | ||
| 64 | .serial_clk_freq(SerialClockFrequency::MHz120) | ||
| 65 | .serial_flash_pad_type(FlashPadType::Quad); | ||
| 66 | |||
| 67 | pub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock = | ||
| 68 | nor::ConfigurationBlock::new(COMMON_CONFIGURATION_BLOCK) | ||
| 69 | .page_size(256) | ||
| 70 | .sector_size(4096) | ||
| 71 | .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30); | ||
| 72 | |||
| 73 | #[unsafe(no_mangle)] | ||
| 74 | #[cfg_attr(all(target_arch = "arm", target_os = "none"), link_section = ".fcb")] | ||
| 75 | pub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK; | ||
diff --git a/examples/mimxrt1062-evk/.cargo/config.toml b/examples/mimxrt1062-evk/.cargo/config.toml new file mode 100644 index 000000000..ca4c606dc --- /dev/null +++ b/examples/mimxrt1062-evk/.cargo/config.toml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | [target.thumbv7em-none-eabihf] | ||
| 2 | runner = 'probe-rs run --chip MIMXRT1060' | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv7em-none-eabihf" # Cortex-M7 | ||
| 6 | |||
| 7 | [env] | ||
| 8 | DEFMT_LOG = "trace" | ||
diff --git a/examples/mimxrt1062-evk/Cargo.toml b/examples/mimxrt1062-evk/Cargo.toml new file mode 100644 index 000000000..430a26b41 --- /dev/null +++ b/examples/mimxrt1062-evk/Cargo.toml | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | [package] | ||
| 2 | name = "embassy-imxrt1062-evk-examples" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2021" | ||
| 5 | license = "MIT or Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | cortex-m = { version = "0.7.7", features = ["inline-asm", "critical-section-single-core"] } | ||
| 9 | cortex-m-rt = "0.7.3" | ||
| 10 | defmt = "1.0.1" | ||
| 11 | defmt-rtt = "1.0.0" | ||
| 12 | |||
| 13 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | ||
| 14 | embassy-futures = { version = "0.1.1", path = "../../embassy-futures" } | ||
| 15 | embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["defmt", "mimxrt1062", "unstable-pac", "time-driver-pit"] } | ||
| 16 | embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", ] } # "defmt-timestamp-uptime" | ||
| 17 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 18 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 19 | embedded-hal-async = "1.0.0" | ||
| 20 | |||
| 21 | imxrt-boot-gen = { version = "0.3.4", features = ["imxrt1060"] } | ||
| 22 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 23 | panic-semihosting = "0.6.0" | ||
| 24 | |||
| 25 | [build-dependencies] | ||
| 26 | imxrt-rt = { version = "0.1.7", features = ["device"] } | ||
| 27 | |||
| 28 | [profile.release] | ||
| 29 | debug = 2 | ||
diff --git a/examples/mimxrt1062-evk/build.rs b/examples/mimxrt1062-evk/build.rs new file mode 100644 index 000000000..e0e0d547e --- /dev/null +++ b/examples/mimxrt1062-evk/build.rs | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | use imxrt_rt::{Family, RuntimeBuilder}; | ||
| 2 | |||
| 3 | fn main() { | ||
| 4 | RuntimeBuilder::from_flexspi(Family::Imxrt1060, 8 * 1024 * 1024) | ||
| 5 | .build() | ||
| 6 | .unwrap(); | ||
| 7 | |||
| 8 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 9 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 10 | // Not link.x, as imxrt-rt needs to do some special things | ||
| 11 | println!("cargo:rustc-link-arg-bins=-Timxrt-link.x"); | ||
| 12 | } | ||
diff --git a/examples/mimxrt1062-evk/src/bin/blinky.rs b/examples/mimxrt1062-evk/src/bin/blinky.rs new file mode 100644 index 000000000..b6d90d94d --- /dev/null +++ b/examples/mimxrt1062-evk/src/bin/blinky.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nxp::gpio::{Level, Output}; | ||
| 7 | use embassy_time::Timer; | ||
| 8 | // Must include `embassy_imxrt1062_evk_examples` to ensure the FCB gets linked. | ||
| 9 | use {defmt_rtt as _, embassy_imxrt1062_evk_examples as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) -> ! { | ||
| 13 | let p = embassy_nxp::init(Default::default()); | ||
| 14 | info!("Hello world!"); | ||
| 15 | |||
| 16 | let led = p.GPIO_AD_B0_08; | ||
| 17 | let mut led = Output::new(led, Level::Low); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | Timer::after_millis(500).await; | ||
| 21 | |||
| 22 | info!("Toggle"); | ||
| 23 | led.toggle(); | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/mimxrt1062-evk/src/bin/button.rs b/examples/mimxrt1062-evk/src/bin/button.rs new file mode 100644 index 000000000..d60fa3dac --- /dev/null +++ b/examples/mimxrt1062-evk/src/bin/button.rs | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nxp::gpio::{Input, Level, Output, Pull}; | ||
| 7 | use {defmt_rtt as _, embassy_imxrt1062_evk_examples as _, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) -> ! { | ||
| 11 | let p = embassy_nxp::init(Default::default()); | ||
| 12 | info!("Hello world!"); | ||
| 13 | |||
| 14 | // User LED (D8) | ||
| 15 | let led = p.GPIO_AD_B0_08; | ||
| 16 | // User button (SW5) | ||
| 17 | let button = p.WAKEUP; | ||
| 18 | let mut button = Input::new(button, Pull::Up100K); | ||
| 19 | let mut led = Output::new(led, Level::Low); | ||
| 20 | led.set_high(); | ||
| 21 | |||
| 22 | loop { | ||
| 23 | button.wait_for_falling_edge().await; | ||
| 24 | |||
| 25 | info!("Toggled"); | ||
| 26 | led.toggle(); | ||
| 27 | |||
| 28 | // Software debounce. | ||
| 29 | button.wait_for_rising_edge().await; | ||
| 30 | |||
| 31 | // Stabilization. | ||
| 32 | for _ in 0..100_000 { | ||
| 33 | cortex_m::asm::nop(); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | } | ||
diff --git a/examples/mimxrt1062-evk/src/lib.rs b/examples/mimxrt1062-evk/src/lib.rs new file mode 100644 index 000000000..3f99f9db3 --- /dev/null +++ b/examples/mimxrt1062-evk/src/lib.rs | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | //! FlexSPI configuration block (FCB) for the iMXRT1060-EVK | ||
| 2 | //! | ||
| 3 | //! This uses IS25WP QuadSPI flash. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | |||
| 7 | use imxrt_boot_gen::flexspi::opcodes::sdr::*; | ||
| 8 | use imxrt_boot_gen::flexspi::{self, FlashPadType, ReadSampleClockSource, SerialClockFrequency, SerialFlashRegion, *}; | ||
| 9 | use imxrt_boot_gen::serial_flash::*; | ||
| 10 | pub use nor::ConfigurationBlock; | ||
| 11 | |||
| 12 | const SEQ_READ: Sequence = SequenceBuilder::new() | ||
| 13 | .instr(Instr::new(CMD, Pads::One, 0xEB)) | ||
| 14 | .instr(Instr::new(RADDR, Pads::Four, 0x18)) | ||
| 15 | .instr(Instr::new(DUMMY, Pads::Four, 0x06)) | ||
| 16 | .instr(Instr::new(READ, Pads::Four, 0x04)) | ||
| 17 | .build(); | ||
| 18 | const SEQ_READ_STATUS: Sequence = SequenceBuilder::new() | ||
| 19 | .instr(Instr::new(CMD, Pads::One, 0x05)) | ||
| 20 | .instr(Instr::new(READ, Pads::One, 0x04)) | ||
| 21 | .build(); | ||
| 22 | const SEQ_WRITE_ENABLE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x06)).build(); | ||
| 23 | const SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new() | ||
| 24 | .instr(Instr::new(CMD, Pads::One, 0x20)) | ||
| 25 | .instr(Instr::new(RADDR, Pads::One, 0x18)) | ||
| 26 | .build(); | ||
| 27 | const SEQ_PAGE_PROGRAM: Sequence = SequenceBuilder::new() | ||
| 28 | .instr(Instr::new(CMD, Pads::One, 0x02)) | ||
| 29 | .instr(Instr::new(RADDR, Pads::One, 0x18)) | ||
| 30 | .instr(Instr::new(WRITE, Pads::One, 0x04)) | ||
| 31 | .build(); | ||
| 32 | const SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x60)).build(); | ||
| 33 | |||
| 34 | const LUT: LookupTable = LookupTable::new() | ||
| 35 | .command(Command::Read, SEQ_READ) | ||
| 36 | .command(Command::ReadStatus, SEQ_READ_STATUS) | ||
| 37 | .command(Command::WriteEnable, SEQ_WRITE_ENABLE) | ||
| 38 | .command(Command::EraseSector, SEQ_ERASE_SECTOR) | ||
| 39 | .command(Command::PageProgram, SEQ_PAGE_PROGRAM) | ||
| 40 | .command(Command::ChipErase, SEQ_CHIP_ERASE); | ||
| 41 | |||
| 42 | const COMMON_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LUT) | ||
| 43 | .version(Version::new(1, 4, 0)) | ||
| 44 | .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad) | ||
| 45 | .cs_hold_time(3) | ||
| 46 | .cs_setup_time(3) | ||
| 47 | .controller_misc_options(0x10) | ||
| 48 | .serial_flash_pad_type(FlashPadType::Quad) | ||
| 49 | .serial_clk_freq(SerialClockFrequency::MHz133) | ||
| 50 | .flash_size(SerialFlashRegion::A1, 8 * 1024 * 1024); | ||
| 51 | |||
| 52 | pub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock = | ||
| 53 | nor::ConfigurationBlock::new(COMMON_CONFIGURATION_BLOCK) | ||
| 54 | .page_size(256) | ||
| 55 | .sector_size(4096) | ||
| 56 | .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30); | ||
| 57 | |||
| 58 | #[no_mangle] | ||
| 59 | #[cfg_attr(all(target_arch = "arm", target_os = "none"), link_section = ".fcb")] | ||
| 60 | pub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK; | ||
diff --git a/examples/mspm0g3507/.cargo/config.toml b/examples/mspm0g3507/.cargo/config.toml index 34c720cdd..e711afaf2 100644 --- a/examples/mspm0g3507/.cargo/config.toml +++ b/examples/mspm0g3507/.cargo/config.toml | |||
| @@ -6,4 +6,4 @@ runner = "probe-rs run --chip MSPM0G3507 --protocol=swd" | |||
| 6 | target = "thumbv6m-none-eabi" | 6 | target = "thumbv6m-none-eabi" |
| 7 | 7 | ||
| 8 | [env] | 8 | [env] |
| 9 | DEFMT_LOG = "debug" | 9 | DEFMT_LOG = "trace" |
diff --git a/examples/mspm0g3507/Cargo.toml b/examples/mspm0g3507/Cargo.toml index b6621c9c5..cc40b3109 100644 --- a/examples/mspm0g3507/Cargo.toml +++ b/examples/mspm0g3507/Cargo.toml | |||
| @@ -17,5 +17,7 @@ defmt-rtt = "1.0.0" | |||
| 17 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | 17 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 18 | panic-semihosting = "0.6.0" | 18 | panic-semihosting = "0.6.0" |
| 19 | 19 | ||
| 20 | embedded-io-async = "0.6.1" | ||
| 21 | |||
| 20 | [profile.release] | 22 | [profile.release] |
| 21 | debug = 2 | 23 | debug = 2 |
diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index d9e8ca2f9..f1c40192d 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml | |||
| @@ -18,7 +18,7 @@ log = [ | |||
| 18 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync" } | 18 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync" } |
| 19 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] } | 19 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] } |
| 20 | embassy-time = { version = "0.4.0", path = "../../embassy-time" } | 20 | embassy-time = { version = "0.4.0", path = "../../embassy-time" } |
| 21 | embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 21 | embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 22 | 22 | ||
| 23 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 23 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 24 | cortex-m-rt = "0.7.0" | 24 | cortex-m-rt = "0.7.0" |
diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index 91f78737f..a21d7f6ce 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml | |||
| @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 8 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 9 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } | 10 | embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } |
| 11 | 11 | ||
| 12 | defmt = "1.0.1" | 12 | defmt = "1.0.1" |
| 13 | defmt-rtt = "1.0.0" | 13 | defmt-rtt = "1.0.0" |
diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index 87da89efe..baa873f22 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml | |||
| @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | |||
| 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 10 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 12 | embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 13 | 13 | ||
| 14 | defmt = "1.0.1" | 14 | defmt = "1.0.1" |
| 15 | defmt-rtt = "1.0.0" | 15 | defmt-rtt = "1.0.0" |
diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index afd269f72..3e499e9bc 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml | |||
| @@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | |||
| 11 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 11 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-time-queue-utils = { version = "0.1", path = "../../embassy-time-queue-utils", features = ["generic-queue-8"] } | 13 | embassy-time-queue-utils = { version = "0.1", path = "../../embassy-time-queue-utils", features = ["generic-queue-8"] } |
| 14 | embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 14 | embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 15 | 15 | ||
| 16 | defmt = "1.0.1" | 16 | defmt = "1.0.1" |
| 17 | defmt-rtt = "1.0.0" | 17 | defmt-rtt = "1.0.0" |
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 4140e49d2..d2baebf8e 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml | |||
| @@ -9,9 +9,9 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | |||
| 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 10 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 12 | embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } |
| 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } | 15 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } |
| 16 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | 16 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } |
| 17 | embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } | 17 | embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } |
diff --git a/examples/nrf52840/src/bin/self_spawn_current_executor.rs b/examples/nrf52840/src/bin/self_spawn_current_executor.rs index ec9569a64..ddb40dc53 100644 --- a/examples/nrf52840/src/bin/self_spawn_current_executor.rs +++ b/examples/nrf52840/src/bin/self_spawn_current_executor.rs | |||
| @@ -10,7 +10,8 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 10 | async fn my_task(n: u32) { | 10 | async fn my_task(n: u32) { |
| 11 | Timer::after_secs(1).await; | 11 | Timer::after_secs(1).await; |
| 12 | info!("Spawning self! {}", n); | 12 | info!("Spawning self! {}", n); |
| 13 | unwrap!(Spawner::for_current_executor().await.spawn(my_task(n + 1))); | 13 | let spawner = unsafe { Spawner::for_current_executor().await }; |
| 14 | unwrap!(spawner.spawn(my_task(n + 1))); | ||
| 14 | } | 15 | } |
| 15 | 16 | ||
| 16 | #[embassy_executor::main] | 17 | #[embassy_executor::main] |
diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index dc4fba4fd..bdebd5386 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml | |||
| @@ -9,9 +9,9 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | |||
| 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 10 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 12 | embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } |
| 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embedded-io-async = { version = "0.6.1" } | 15 | embedded-io-async = { version = "0.6.1" } |
| 16 | 16 | ||
| 17 | defmt = "1.0.1" | 17 | defmt = "1.0.1" |
diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml index 4b229d06d..27d5babee 100644 --- a/examples/nrf54l15/Cargo.toml +++ b/examples/nrf54l15/Cargo.toml | |||
| @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 8 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 9 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 10 | embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 11 | 11 | ||
| 12 | defmt = "1.0.1" | 12 | defmt = "1.0.1" |
| 13 | defmt-rtt = "1.0.0" | 13 | defmt-rtt = "1.0.0" |
diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index a083aa5e7..2a492b595 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml | |||
| @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 8 | embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 9 | embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 10 | embassy-nrf = { version = "0.5.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 11 | 11 | ||
| 12 | defmt = "1.0.1" | 12 | defmt = "1.0.1" |
| 13 | defmt-rtt = "1.0.0" | 13 | defmt-rtt = "1.0.0" |
diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index ae98631ef..62ef3e4ce 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml | |||
| @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 8 | embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 9 | embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 10 | embassy-nrf = { version = "0.5.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 11 | 11 | ||
| 12 | defmt = "1.0.1" | 12 | defmt = "1.0.1" |
| 13 | defmt-rtt = "1.0.0" | 13 | defmt-rtt = "1.0.0" |
diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index 25aedf624..c896afdbe 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml | |||
| @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 8 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 9 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 10 | embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 11 | embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } | 11 | embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } |
| 12 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } | 12 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } |
| 13 | 13 | ||
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c8a132a5e..eefd69315 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml | |||
| @@ -6,18 +6,18 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal", features = ["defmt"] } | 9 | embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal", features = ["defmt"] } |
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-rp = { version = "0.4.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } | 13 | embassy-rp = { version = "0.6.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } |
| 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } | 15 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } |
| 16 | embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } | 16 | embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } |
| 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 18 | embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } | 18 | embassy-usb-logger = { version = "0.5.0", path = "../../embassy-usb-logger" } |
| 19 | cyw43 = { version = "0.3.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } | 19 | cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } |
| 20 | cyw43-pio = { version = "0.4.0", path = "../../cyw43-pio", features = ["defmt"] } | 20 | cyw43-pio = { version = "0.5.1", path = "../../cyw43-pio", features = ["defmt"] } |
| 21 | 21 | ||
| 22 | defmt = "1.0.1" | 22 | defmt = "1.0.1" |
| 23 | defmt-rtt = "1.0.0" | 23 | defmt-rtt = "1.0.0" |
| @@ -58,11 +58,5 @@ rand = { version = "0.9.0", default-features = false } | |||
| 58 | embedded-sdmmc = "0.7.0" | 58 | embedded-sdmmc = "0.7.0" |
| 59 | 59 | ||
| 60 | [profile.release] | 60 | [profile.release] |
| 61 | debug = 2 | 61 | # Enable generation of debug symbols even on release builds |
| 62 | lto = true | 62 | debug = true |
| 63 | opt-level = 'z' | ||
| 64 | |||
| 65 | [profile.dev] | ||
| 66 | debug = 2 | ||
| 67 | lto = true | ||
| 68 | opt-level = "z" | ||
diff --git a/examples/rp/src/bin/usb_raw_bulk.rs b/examples/rp/src/bin/usb_raw_bulk.rs index 103269791..0747901d1 100644 --- a/examples/rp/src/bin/usb_raw_bulk.rs +++ b/examples/rp/src/bin/usb_raw_bulk.rs | |||
| @@ -96,8 +96,8 @@ async fn main(_spawner: Spawner) { | |||
| 96 | let mut function = builder.function(0xFF, 0, 0); | 96 | let mut function = builder.function(0xFF, 0, 0); |
| 97 | let mut interface = function.interface(); | 97 | let mut interface = function.interface(); |
| 98 | let mut alt = interface.alt_setting(0xFF, 0, 0, None); | 98 | let mut alt = interface.alt_setting(0xFF, 0, 0, None); |
| 99 | let mut read_ep = alt.endpoint_bulk_out(64); | 99 | let mut read_ep = alt.endpoint_bulk_out(None, 64); |
| 100 | let mut write_ep = alt.endpoint_bulk_in(64); | 100 | let mut write_ep = alt.endpoint_bulk_in(None, 64); |
| 101 | drop(function); | 101 | drop(function); |
| 102 | 102 | ||
| 103 | // Build the builder. | 103 | // Build the builder. |
diff --git a/examples/rp/src/bin/usb_webusb.rs b/examples/rp/src/bin/usb_webusb.rs index a5dc94d5b..5cecb92f0 100644 --- a/examples/rp/src/bin/usb_webusb.rs +++ b/examples/rp/src/bin/usb_webusb.rs | |||
| @@ -125,8 +125,8 @@ impl<'d, D: Driver<'d>> WebEndpoints<'d, D> { | |||
| 125 | let mut iface = func.interface(); | 125 | let mut iface = func.interface(); |
| 126 | let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None); | 126 | let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None); |
| 127 | 127 | ||
| 128 | let write_ep = alt.endpoint_bulk_in(config.max_packet_size); | 128 | let write_ep = alt.endpoint_bulk_in(None, config.max_packet_size); |
| 129 | let read_ep = alt.endpoint_bulk_out(config.max_packet_size); | 129 | let read_ep = alt.endpoint_bulk_out(None, config.max_packet_size); |
| 130 | 130 | ||
| 131 | WebEndpoints { write_ep, read_ep } | 131 | WebEndpoints { write_ep, read_ep } |
| 132 | } | 132 | } |
diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index c81b79ae1..4d3dc77b5 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml | |||
| @@ -6,18 +6,18 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal", features = ["defmt"] } | 9 | embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal", features = ["defmt"] } |
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-rp = { version = "0.4.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } | 13 | embassy-rp = { version = "0.6.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } |
| 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } | 15 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } |
| 16 | embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } | 16 | embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } |
| 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 18 | embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } | 18 | embassy-usb-logger = { version = "0.5.0", path = "../../embassy-usb-logger" } |
| 19 | cyw43 = { version = "0.3.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } | 19 | cyw43 = { version = "0.4.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } |
| 20 | cyw43-pio = { version = "0.4.0", path = "../../cyw43-pio", features = ["defmt"] } | 20 | cyw43-pio = { version = "0.5.1", path = "../../cyw43-pio", features = ["defmt"] } |
| 21 | 21 | ||
| 22 | defmt = "1.0.1" | 22 | defmt = "1.0.1" |
| 23 | defmt-rtt = "1.0.0" | 23 | defmt-rtt = "1.0.0" |
| @@ -58,8 +58,5 @@ log = "0.4" | |||
| 58 | embedded-sdmmc = "0.7.0" | 58 | embedded-sdmmc = "0.7.0" |
| 59 | 59 | ||
| 60 | [profile.release] | 60 | [profile.release] |
| 61 | debug = 2 | 61 | # Enable generation of debug symbols even on release builds |
| 62 | 62 | debug = true | |
| 63 | [profile.dev] | ||
| 64 | lto = true | ||
| 65 | opt-level = "z" | ||
diff --git a/examples/rp235x/memory.x b/examples/rp235x/memory.x index c803896f6..4382e2065 100644 --- a/examples/rp235x/memory.x +++ b/examples/rp235x/memory.x | |||
| @@ -17,8 +17,8 @@ MEMORY { | |||
| 17 | * of access times. | 17 | * of access times. |
| 18 | * Example: Separate stacks for core0 and core1. | 18 | * Example: Separate stacks for core0 and core1. |
| 19 | */ | 19 | */ |
| 20 | SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K | 20 | SRAM8 : ORIGIN = 0x20080000, LENGTH = 4K |
| 21 | SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K | 21 | SRAM9 : ORIGIN = 0x20081000, LENGTH = 4K |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | SECTIONS { | 24 | SECTIONS { |
diff --git a/examples/rp235x/src/bin/blinky_wifi.rs b/examples/rp235x/src/bin/blinky_wifi.rs index 8c352ebc4..ef6057a1c 100644 --- a/examples/rp235x/src/bin/blinky_wifi.rs +++ b/examples/rp235x/src/bin/blinky_wifi.rs | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #![no_std] | 5 | #![no_std] |
| 6 | #![no_main] | 6 | #![no_main] |
| 7 | 7 | ||
| 8 | use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; | 8 | use cyw43_pio::{PioSpi, RM2_CLOCK_DIVIDER}; |
| 9 | use defmt::*; | 9 | use defmt::*; |
| 10 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 11 | use embassy_rp::bind_interrupts; | 11 | use embassy_rp::bind_interrupts; |
| @@ -58,7 +58,9 @@ async fn main(spawner: Spawner) { | |||
| 58 | let spi = PioSpi::new( | 58 | let spi = PioSpi::new( |
| 59 | &mut pio.common, | 59 | &mut pio.common, |
| 60 | pio.sm0, | 60 | pio.sm0, |
| 61 | DEFAULT_CLOCK_DIVIDER, | 61 | // SPI communication won't work if the speed is too high, so we use a divider larger than `DEFAULT_CLOCK_DIVIDER`. |
| 62 | // See: https://github.com/embassy-rs/embassy/issues/3960. | ||
| 63 | RM2_CLOCK_DIVIDER, | ||
| 62 | pio.irq0, | 64 | pio.irq0, |
| 63 | cs, | 65 | cs, |
| 64 | p.PIN_24, | 66 | p.PIN_24, |
diff --git a/examples/rp235x/src/bin/usb_webusb.rs b/examples/rp235x/src/bin/usb_webusb.rs index 75d28c853..a68163b61 100644 --- a/examples/rp235x/src/bin/usb_webusb.rs +++ b/examples/rp235x/src/bin/usb_webusb.rs | |||
| @@ -125,8 +125,8 @@ impl<'d, D: Driver<'d>> WebEndpoints<'d, D> { | |||
| 125 | let mut iface = func.interface(); | 125 | let mut iface = func.interface(); |
| 126 | let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None); | 126 | let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None); |
| 127 | 127 | ||
| 128 | let write_ep = alt.endpoint_bulk_in(config.max_packet_size); | 128 | let write_ep = alt.endpoint_bulk_in(None, config.max_packet_size); |
| 129 | let read_ep = alt.endpoint_bulk_out(config.max_packet_size); | 129 | let read_ep = alt.endpoint_bulk_out(None, config.max_packet_size); |
| 130 | 130 | ||
| 131 | WebEndpoints { write_ep, read_ep } | 131 | WebEndpoints { write_ep, read_ep } |
| 132 | } | 132 | } |
diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 4cf07cef4..70a7b0895 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml | |||
| @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32c031c6 to your chip name, if necessary. | 8 | # Change stm32c031c6 to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } | 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti", "chrono"] } |
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| @@ -19,6 +19,7 @@ cortex-m-rt = "0.7.0" | |||
| 19 | embedded-hal = "0.2.6" | 19 | embedded-hal = "0.2.6" |
| 20 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | 20 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 21 | heapless = { version = "0.8", default-features = false } | 21 | heapless = { version = "0.8", default-features = false } |
| 22 | chrono = { version = "^0.4", default-features = false} | ||
| 22 | 23 | ||
| 23 | [profile.release] | 24 | [profile.release] |
| 24 | debug = 2 | 25 | debug = 2 |
diff --git a/examples/stm32c0/src/bin/rtc.rs b/examples/stm32c0/src/bin/rtc.rs new file mode 100644 index 000000000..82d8a37ba --- /dev/null +++ b/examples/stm32c0/src/bin/rtc.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use chrono::{NaiveDate, NaiveDateTime}; | ||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::rtc::{Rtc, RtcConfig}; | ||
| 8 | use embassy_stm32::Config; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let config = Config::default(); | ||
| 15 | let p = embassy_stm32::init(config); | ||
| 16 | |||
| 17 | info!("Hello World!"); | ||
| 18 | |||
| 19 | let now = NaiveDate::from_ymd_opt(2020, 5, 15) | ||
| 20 | .unwrap() | ||
| 21 | .and_hms_opt(10, 30, 15) | ||
| 22 | .unwrap(); | ||
| 23 | |||
| 24 | let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); | ||
| 25 | |||
| 26 | rtc.set_datetime(now.into()).expect("datetime not set"); | ||
| 27 | |||
| 28 | loop { | ||
| 29 | let now: NaiveDateTime = rtc.now().unwrap().into(); | ||
| 30 | |||
| 31 | info!("{}", now.and_utc().timestamp()); | ||
| 32 | |||
| 33 | Timer::after_millis(1000).await; | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/examples/stm32f0/src/bin/adc-watchdog.rs b/examples/stm32f0/src/bin/adc-watchdog.rs new file mode 100644 index 000000000..ff98aac8e --- /dev/null +++ b/examples/stm32f0/src/bin/adc-watchdog.rs | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::adc::{self, Adc, WatchdogChannels}; | ||
| 7 | use embassy_stm32::bind_interrupts; | ||
| 8 | use embassy_stm32::peripherals::ADC1; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | ADC1_COMP => adc::InterruptHandler<ADC1>; | ||
| 13 | }); | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_stm32::init(Default::default()); | ||
| 18 | info!("ADC watchdog example"); | ||
| 19 | |||
| 20 | let mut adc = Adc::new(p.ADC1, Irqs); | ||
| 21 | let pin = p.PC1; | ||
| 22 | |||
| 23 | loop { | ||
| 24 | // Wait for pin to go high | ||
| 25 | adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0, 0x07F); | ||
| 26 | let v_high = adc.monitor_watchdog().await; | ||
| 27 | info!("ADC sample is high {}", v_high); | ||
| 28 | |||
| 29 | // Wait for pin to go low | ||
| 30 | adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0x01f, 0xFFF); | ||
| 31 | let v_low = adc.monitor_watchdog().await; | ||
| 32 | info!("ADC sample is low {}", v_low); | ||
| 33 | } | ||
| 34 | } | ||
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 261733305..01f4fd84a 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml | |||
| @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ | |||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 15 | 15 | ||
| 16 | defmt = "1.0.1" | 16 | defmt = "1.0.1" |
diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs index 6fe8e0b50..84811fb95 100644 --- a/examples/stm32f1/src/bin/input_capture.rs +++ b/examples/stm32f1/src/bin/input_capture.rs | |||
| @@ -39,7 +39,7 @@ async fn main(spawner: Spawner) { | |||
| 39 | 39 | ||
| 40 | unwrap!(spawner.spawn(blinky(p.PC13))); | 40 | unwrap!(spawner.spawn(blinky(p.PC13))); |
| 41 | 41 | ||
| 42 | let ch3 = CapturePin::new_ch3(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 = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); |
| 44 | 44 | ||
| 45 | loop { | 45 | loop { |
diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index afbef3edb..aa6a11ff8 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs | |||
| @@ -38,7 +38,7 @@ async fn main(spawner: Spawner) { | |||
| 38 | 38 | ||
| 39 | unwrap!(spawner.spawn(blinky(p.PC13))); | 39 | unwrap!(spawner.spawn(blinky(p.PC13))); |
| 40 | 40 | ||
| 41 | let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(10)); | 41 | let mut pwm_input = PwmInput::new_ch1(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/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index f675b0be1..ab7d6b255 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml | |||
| @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ | |||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 15 | 15 | ||
| 16 | defmt = "1.0.1" | 16 | defmt = "1.0.1" |
diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index b47a81e1b..6595804e1 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml | |||
| @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de | |||
| 9 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } |
| 12 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 14 | 14 | ||
| 15 | defmt = "1.0.1" | 15 | defmt = "1.0.1" |
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index edab9ea00..80c43f2ab 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml | |||
| @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [" | |||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt" ] } | 13 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt" ] } |
| 14 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } | 14 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } |
| 15 | embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } | 15 | embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
diff --git a/examples/stm32f4/src/bin/input_capture.rs b/examples/stm32f4/src/bin/input_capture.rs index fe5e2bdfc..e15b4d26e 100644 --- a/examples/stm32f4/src/bin/input_capture.rs +++ b/examples/stm32f4/src/bin/input_capture.rs | |||
| @@ -39,7 +39,7 @@ async fn main(spawner: Spawner) { | |||
| 39 | 39 | ||
| 40 | unwrap!(spawner.spawn(blinky(p.PB2))); | 40 | unwrap!(spawner.spawn(blinky(p.PB2))); |
| 41 | 41 | ||
| 42 | let ch3 = CapturePin::new_ch3(p.PB10, Pull::None); | 42 | let ch3 = CapturePin::new(p.PB10, Pull::None); |
| 43 | let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); | 43 | let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); |
| 44 | 44 | ||
| 45 | loop { | 45 | loop { |
diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 04811162b..e385842f0 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs | |||
| @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { | |||
| 14 | let p = embassy_stm32::init(Default::default()); | 14 | let p = embassy_stm32::init(Default::default()); |
| 15 | info!("Hello World!"); | 15 | info!("Hello World!"); |
| 16 | 16 | ||
| 17 | let ch1_pin = PwmPin::new_ch1(p.PE9, OutputType::PushPull); | 17 | let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); |
| 18 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); | 18 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); |
| 19 | let mut ch1 = pwm.ch1(); | 19 | let mut ch1 = pwm.ch1(); |
| 20 | ch1.enable(); | 20 | ch1.enable(); |
diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index 161f43c48..c981f1a76 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs | |||
| @@ -16,8 +16,8 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let p = embassy_stm32::init(Default::default()); | 16 | let p = embassy_stm32::init(Default::default()); |
| 17 | info!("Hello World!"); | 17 | info!("Hello World!"); |
| 18 | 18 | ||
| 19 | let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); | 19 | let ch1 = PwmPin::new(p.PE9, OutputType::PushPull); |
| 20 | let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); | 20 | let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull); |
| 21 | let mut pwm = ComplementaryPwm::new( | 21 | let mut pwm = ComplementaryPwm::new( |
| 22 | p.TIM1, | 22 | p.TIM1, |
| 23 | Some(ch1), | 23 | Some(ch1), |
diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs index 465cbe4f5..74167cbf2 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f4/src/bin/pwm_input.rs | |||
| @@ -38,7 +38,7 @@ async fn main(spawner: Spawner) { | |||
| 38 | 38 | ||
| 39 | unwrap!(spawner.spawn(blinky(p.PB2))); | 39 | unwrap!(spawner.spawn(blinky(p.PB2))); |
| 40 | 40 | ||
| 41 | let mut pwm_input = PwmInput::new(p.TIM3, p.PA6, Pull::None, khz(10)); | 41 | let mut pwm_input = PwmInput::new_ch1(p.TIM3, p.PA6, Pull::None, khz(10)); |
| 42 | pwm_input.enable(); | 42 | pwm_input.enable(); |
| 43 | 43 | ||
| 44 | loop { | 44 | loop { |
diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index ca924e181..5153e1cfd 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs | |||
| @@ -50,7 +50,7 @@ async fn main(_spawner: Spawner) { | |||
| 50 | 50 | ||
| 51 | let mut ws2812_pwm = SimplePwm::new( | 51 | let mut ws2812_pwm = SimplePwm::new( |
| 52 | dp.TIM3, | 52 | dp.TIM3, |
| 53 | Some(PwmPin::new_ch1(dp.PB4, OutputType::PushPull)), | 53 | Some(PwmPin::new(dp.PB4, OutputType::PushPull)), |
| 54 | None, | 54 | None, |
| 55 | None, | 55 | None, |
| 56 | None, | 56 | None, |
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index c5801ea90..5995211d2 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml | |||
| @@ -12,7 +12,7 @@ embassy-executor = { version = "0.7.0", path = "../../embassy-executor", feature | |||
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } |
| 14 | embedded-io-async = { version = "0.6.1" } | 14 | embedded-io-async = { version = "0.6.1" } |
| 15 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 15 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 17 | 17 | ||
| 18 | defmt = "1.0.1" | 18 | defmt = "1.0.1" |
diff --git a/examples/stm32f7/src/bin/cryp.rs b/examples/stm32f7/src/bin/cryp.rs index 235853cb9..a31e9b4f2 100644 --- a/examples/stm32f7/src/bin/cryp.rs +++ b/examples/stm32f7/src/bin/cryp.rs | |||
| @@ -68,7 +68,9 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 68 | ); | 68 | ); |
| 69 | 69 | ||
| 70 | // Decrypt in software using AES-GCM 128-bit | 70 | // Decrypt in software using AES-GCM 128-bit |
| 71 | let _ = cipher.decrypt_in_place(&iv.into(), aad.into(), &mut payload_vec); | 71 | cipher |
| 72 | .decrypt_in_place(&iv.into(), aad.into(), &mut payload_vec) | ||
| 73 | .unwrap(); | ||
| 72 | 74 | ||
| 73 | let sw_end_time = Instant::now(); | 75 | let sw_end_time = Instant::now(); |
| 74 | let sw_execution_time = sw_end_time - sw_start_time; | 76 | let sw_execution_time = sw_end_time - sw_start_time; |
diff --git a/examples/stm32f7/src/bin/qspi.rs b/examples/stm32f7/src/bin/qspi.rs index bd3287964..ab29ddeff 100644 --- a/examples/stm32f7/src/bin/qspi.rs +++ b/examples/stm32f7/src/bin/qspi.rs | |||
| @@ -279,6 +279,7 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 279 | prescaler: 16, | 279 | prescaler: 16, |
| 280 | cs_high_time: ChipSelectHighTime::_1Cycle, | 280 | cs_high_time: ChipSelectHighTime::_1Cycle, |
| 281 | fifo_threshold: FIFOThresholdLevel::_16Bytes, | 281 | fifo_threshold: FIFOThresholdLevel::_16Bytes, |
| 282 | sample_shifting: SampleShifting::None, | ||
| 282 | }; | 283 | }; |
| 283 | let driver = Qspi::new_bank1( | 284 | let driver = Qspi::new_bank1( |
| 284 | 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, |
diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index bf1e7250e..1c290fcfa 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml | |||
| @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ | |||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } | 13 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 15 | 15 | ||
| 16 | defmt = "1.0.1" | 16 | defmt = "1.0.1" |
diff --git a/examples/stm32g0/src/bin/hf_timer.rs b/examples/stm32g0/src/bin/hf_timer.rs index dfb6e0edc..705905f01 100644 --- a/examples/stm32g0/src/bin/hf_timer.rs +++ b/examples/stm32g0/src/bin/hf_timer.rs | |||
| @@ -37,8 +37,8 @@ async fn main(_spawner: Spawner) { | |||
| 37 | } | 37 | } |
| 38 | let p = embassy_stm32::init(config); | 38 | let p = embassy_stm32::init(config); |
| 39 | 39 | ||
| 40 | let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); | 40 | let ch1 = PwmPin::new(p.PA8, OutputType::PushPull); |
| 41 | let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); | 41 | let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull); |
| 42 | 42 | ||
| 43 | let mut pwm = ComplementaryPwm::new( | 43 | let mut pwm = ComplementaryPwm::new( |
| 44 | p.TIM1, | 44 | p.TIM1, |
diff --git a/examples/stm32g0/src/bin/input_capture.rs b/examples/stm32g0/src/bin/input_capture.rs index 08df4e043..df339d541 100644 --- a/examples/stm32g0/src/bin/input_capture.rs +++ b/examples/stm32g0/src/bin/input_capture.rs | |||
| @@ -47,12 +47,12 @@ async fn main(spawner: Spawner) { | |||
| 47 | unwrap!(spawner.spawn(blinky(p.PB1))); | 47 | unwrap!(spawner.spawn(blinky(p.PB1))); |
| 48 | 48 | ||
| 49 | // Connect PB1 and PA8 with a 1k Ohm resistor | 49 | // Connect PB1 and PA8 with a 1k Ohm resistor |
| 50 | let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); | 50 | let ch1_pin = PwmPin::new(p.PA8, OutputType::PushPull); |
| 51 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); | 51 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); |
| 52 | pwm.ch1().enable(); | 52 | pwm.ch1().enable(); |
| 53 | pwm.ch1().set_duty_cycle(50); | 53 | pwm.ch1().set_duty_cycle(50); |
| 54 | 54 | ||
| 55 | let ch1 = CapturePin::new_ch1(p.PA0, Pull::None); | 55 | let ch1 = CapturePin::new(p.PA0, Pull::None); |
| 56 | let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default()); | 56 | let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default()); |
| 57 | 57 | ||
| 58 | let mut old_capture = 0; | 58 | let mut old_capture = 0; |
diff --git a/examples/stm32g0/src/bin/pwm_complementary.rs b/examples/stm32g0/src/bin/pwm_complementary.rs index 97b163c40..dbd9194c9 100644 --- a/examples/stm32g0/src/bin/pwm_complementary.rs +++ b/examples/stm32g0/src/bin/pwm_complementary.rs | |||
| @@ -26,10 +26,10 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 26 | async fn main(_spawner: Spawner) { | 26 | async fn main(_spawner: Spawner) { |
| 27 | let p = embassy_stm32::init(Default::default()); | 27 | let p = embassy_stm32::init(Default::default()); |
| 28 | 28 | ||
| 29 | let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); | 29 | let ch1 = PwmPin::new(p.PA8, OutputType::PushPull); |
| 30 | let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); | 30 | let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull); |
| 31 | let ch2 = PwmPin::new_ch2(p.PB3, OutputType::PushPull); | 31 | let ch2 = PwmPin::new(p.PB3, OutputType::PushPull); |
| 32 | let ch2n = ComplementaryPwmPin::new_ch2(p.PB0, OutputType::PushPull); | 32 | let ch2n = ComplementaryPwmPin::new(p.PB0, OutputType::PushPull); |
| 33 | 33 | ||
| 34 | let mut pwm = ComplementaryPwm::new( | 34 | let mut pwm = ComplementaryPwm::new( |
| 35 | p.TIM1, | 35 | p.TIM1, |
diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs index 9d6b5fe97..fd4f53f1e 100644 --- a/examples/stm32g0/src/bin/pwm_input.rs +++ b/examples/stm32g0/src/bin/pwm_input.rs | |||
| @@ -42,12 +42,12 @@ async fn main(spawner: Spawner) { | |||
| 42 | 42 | ||
| 43 | unwrap!(spawner.spawn(blinky(p.PB1))); | 43 | unwrap!(spawner.spawn(blinky(p.PB1))); |
| 44 | // Connect PA8 and PA6 with a 1k Ohm resistor | 44 | // Connect PA8 and PA6 with a 1k Ohm resistor |
| 45 | let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); | 45 | let ch1_pin = PwmPin::new(p.PA8, OutputType::PushPull); |
| 46 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); | 46 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); |
| 47 | pwm.ch1().set_duty_cycle_fraction(1, 4); | 47 | pwm.ch1().set_duty_cycle_fraction(1, 4); |
| 48 | pwm.ch1().enable(); | 48 | pwm.ch1().enable(); |
| 49 | 49 | ||
| 50 | let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); | 50 | let mut pwm_input = PwmInput::new_ch1(p.TIM2, p.PA0, Pull::None, khz(1000)); |
| 51 | pwm_input.enable(); | 51 | pwm_input.enable(); |
| 52 | 52 | ||
| 53 | loop { | 53 | loop { |
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 3d2c2aa7d..a503420e5 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml | |||
| @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ | |||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 15 | usbd-hid = "0.8.1" | 15 | usbd-hid = "0.8.1" |
| 16 | 16 | ||
diff --git a/examples/stm32g4/src/bin/i2c_slave.rs b/examples/stm32g4/src/bin/i2c_slave.rs new file mode 100644 index 000000000..a723a0e18 --- /dev/null +++ b/examples/stm32g4/src/bin/i2c_slave.rs | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | //! This example shows how to use an stm32 as both a master and a slave. | ||
| 2 | #![no_std] | ||
| 3 | #![no_main] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::i2c::{Address, OwnAddresses, SlaveCommandKind}; | ||
| 8 | use embassy_stm32::mode::Async; | ||
| 9 | use embassy_stm32::time::Hertz; | ||
| 10 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 11 | use embassy_time::Timer; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>; | ||
| 16 | I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; | ||
| 17 | I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; | ||
| 18 | I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | const DEV_ADDR: u8 = 0x42; | ||
| 22 | |||
| 23 | #[embassy_executor::task] | ||
| 24 | async fn device_task(mut dev: i2c::I2c<'static, Async, i2c::MultiMaster>) -> ! { | ||
| 25 | info!("Device start"); | ||
| 26 | |||
| 27 | let mut state = 0; | ||
| 28 | |||
| 29 | loop { | ||
| 30 | let mut buf = [0u8; 128]; | ||
| 31 | match dev.listen().await { | ||
| 32 | Ok(i2c::SlaveCommand { | ||
| 33 | kind: SlaveCommandKind::Read, | ||
| 34 | address: Address::SevenBit(DEV_ADDR), | ||
| 35 | }) => match dev.respond_to_read(&[state]).await { | ||
| 36 | Ok(i2c::SendStatus::LeftoverBytes(x)) => info!("tried to write {} extra bytes", x), | ||
| 37 | Ok(i2c::SendStatus::Done) => {} | ||
| 38 | Err(e) => error!("error while responding {}", e), | ||
| 39 | }, | ||
| 40 | Ok(i2c::SlaveCommand { | ||
| 41 | kind: SlaveCommandKind::Write, | ||
| 42 | address: Address::SevenBit(DEV_ADDR), | ||
| 43 | }) => match dev.respond_to_write(&mut buf).await { | ||
| 44 | Ok(len) => { | ||
| 45 | info!("Device received write: {}", buf[..len]); | ||
| 46 | |||
| 47 | if match buf[0] { | ||
| 48 | // Set the state | ||
| 49 | 0xC2 => { | ||
| 50 | state = buf[1]; | ||
| 51 | true | ||
| 52 | } | ||
| 53 | // Reset State | ||
| 54 | 0xC8 => { | ||
| 55 | state = 0; | ||
| 56 | true | ||
| 57 | } | ||
| 58 | x => { | ||
| 59 | error!("Invalid Write Read {:x}", x); | ||
| 60 | false | ||
| 61 | } | ||
| 62 | } { | ||
| 63 | match dev.respond_to_read(&[state]).await { | ||
| 64 | Ok(read_status) => info!( | ||
| 65 | "This read is part of a write/read transaction. The response read status {}", | ||
| 66 | read_status | ||
| 67 | ), | ||
| 68 | Err(i2c::Error::Timeout) => { | ||
| 69 | info!("The device only performed a write and it not also do a read") | ||
| 70 | } | ||
| 71 | Err(e) => error!("error while responding {}", e), | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | Err(e) => error!("error while receiving {}", e), | ||
| 76 | }, | ||
| 77 | Ok(i2c::SlaveCommand { address, .. }) => { | ||
| 78 | defmt::unreachable!( | ||
| 79 | "The slave matched address: {}, which it was not configured for", | ||
| 80 | address | ||
| 81 | ); | ||
| 82 | } | ||
| 83 | Err(e) => error!("{}", e), | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | #[embassy_executor::task] | ||
| 89 | async fn controller_task(mut con: i2c::I2c<'static, Async, i2c::Master>) { | ||
| 90 | info!("Controller start"); | ||
| 91 | |||
| 92 | loop { | ||
| 93 | let mut resp_buff = [0u8; 1]; | ||
| 94 | for i in 0..10 { | ||
| 95 | match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await { | ||
| 96 | Ok(_) => { | ||
| 97 | info!("write_read response: {}", resp_buff); | ||
| 98 | defmt::assert_eq!(resp_buff[0], i); | ||
| 99 | } | ||
| 100 | Err(e) => error!("Error writing {}", e), | ||
| 101 | } | ||
| 102 | |||
| 103 | Timer::after_millis(100).await; | ||
| 104 | } | ||
| 105 | match con.read(DEV_ADDR, &mut resp_buff).await { | ||
| 106 | Ok(_) => { | ||
| 107 | info!("read response: {}", resp_buff); | ||
| 108 | // assert that the state is the last index that was written | ||
| 109 | defmt::assert_eq!(resp_buff[0], 9); | ||
| 110 | } | ||
| 111 | Err(e) => error!("Error writing {}", e), | ||
| 112 | } | ||
| 113 | match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await { | ||
| 114 | Ok(_) => { | ||
| 115 | info!("write_read response: {}", resp_buff); | ||
| 116 | // assert that the state has been reset | ||
| 117 | defmt::assert_eq!(resp_buff[0], 0); | ||
| 118 | } | ||
| 119 | Err(e) => error!("Error writing {}", e), | ||
| 120 | } | ||
| 121 | Timer::after_millis(100).await; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | #[embassy_executor::main] | ||
| 126 | async fn main(spawner: Spawner) { | ||
| 127 | let p = embassy_stm32::init(Default::default()); | ||
| 128 | info!("Hello World!"); | ||
| 129 | |||
| 130 | let speed = Hertz::khz(400); | ||
| 131 | let config = i2c::Config::default(); | ||
| 132 | |||
| 133 | let d_addr_config = i2c::SlaveAddrConfig { | ||
| 134 | addr: OwnAddresses::OA1(Address::SevenBit(DEV_ADDR)), | ||
| 135 | general_call: false, | ||
| 136 | }; | ||
| 137 | let d_sda = p.PA8; | ||
| 138 | let d_scl = p.PA9; | ||
| 139 | let device = i2c::I2c::new(p.I2C2, d_scl, d_sda, Irqs, p.DMA1_CH1, p.DMA1_CH2, speed, config) | ||
| 140 | .into_slave_multimaster(d_addr_config); | ||
| 141 | |||
| 142 | unwrap!(spawner.spawn(device_task(device))); | ||
| 143 | |||
| 144 | let c_sda = p.PB8; | ||
| 145 | let c_scl = p.PB7; | ||
| 146 | let controller = i2c::I2c::new(p.I2C1, c_sda, c_scl, Irqs, p.DMA1_CH3, p.DMA1_CH4, speed, config); | ||
| 147 | |||
| 148 | unwrap!(spawner.spawn(controller_task(controller))); | ||
| 149 | } | ||
diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index 6c965012c..5b4194927 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs | |||
| @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { | |||
| 14 | let p = embassy_stm32::init(Default::default()); | 14 | let p = embassy_stm32::init(Default::default()); |
| 15 | info!("Hello World!"); | 15 | info!("Hello World!"); |
| 16 | 16 | ||
| 17 | let ch1_pin = PwmPin::new_ch1(p.PC0, OutputType::PushPull); | 17 | let ch1_pin = PwmPin::new(p.PC0, OutputType::PushPull); |
| 18 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); | 18 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); |
| 19 | let mut ch1 = pwm.ch1(); | 19 | let mut ch1 = pwm.ch1(); |
| 20 | ch1.enable(); | 20 | ch1.enable(); |
diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index f3fda7ff3..0ac9016d4 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml | |||
| @@ -11,7 +11,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de | |||
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } | 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } |
| 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 16 | 16 | ||
| 17 | defmt = "1.0.1" | 17 | defmt = "1.0.1" |
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 27c59d980..9053289ea 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml | |||
| @@ -8,11 +8,11 @@ license = "MIT OR Apache-2.0" | |||
| 8 | # Change stm32h743bi to your chip name, if necessary. | 8 | # Change stm32h743bi to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } |
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } | 11 | embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } |
| 12 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 12 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 13 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 13 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 14 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } | 14 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } |
| 15 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 15 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 17 | 17 | ||
| 18 | defmt = "1.0.1" | 18 | defmt = "1.0.1" |
diff --git a/examples/stm32h7/src/bin/i2c_shared.rs b/examples/stm32h7/src/bin/i2c_shared.rs index 136b91eeb..655ff901f 100644 --- a/examples/stm32h7/src/bin/i2c_shared.rs +++ b/examples/stm32h7/src/bin/i2c_shared.rs | |||
| @@ -25,7 +25,7 @@ const SHTC3_WAKEUP: [u8; 2] = [0x35, 0x17]; | |||
| 25 | const SHTC3_MEASURE_RH_FIRST: [u8; 2] = [0x5c, 0x24]; | 25 | const SHTC3_MEASURE_RH_FIRST: [u8; 2] = [0x5c, 0x24]; |
| 26 | const SHTC3_SLEEP: [u8; 2] = [0xb0, 0x98]; | 26 | const SHTC3_SLEEP: [u8; 2] = [0xb0, 0x98]; |
| 27 | 27 | ||
| 28 | static I2C_BUS: StaticCell<NoopMutex<RefCell<I2c<'static, Async>>>> = StaticCell::new(); | 28 | static I2C_BUS: StaticCell<NoopMutex<RefCell<I2c<'static, Async, i2c::Master>>>> = StaticCell::new(); |
| 29 | 29 | ||
| 30 | bind_interrupts!(struct Irqs { | 30 | bind_interrupts!(struct Irqs { |
| 31 | I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; | 31 | I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; |
| @@ -33,7 +33,7 @@ bind_interrupts!(struct Irqs { | |||
| 33 | }); | 33 | }); |
| 34 | 34 | ||
| 35 | #[embassy_executor::task] | 35 | #[embassy_executor::task] |
| 36 | async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) { | 36 | async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async, i2c::Master>>) { |
| 37 | let mut data = [0u8; 2]; | 37 | let mut data = [0u8; 2]; |
| 38 | 38 | ||
| 39 | loop { | 39 | loop { |
| @@ -50,7 +50,7 @@ async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Asyn | |||
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | #[embassy_executor::task] | 52 | #[embassy_executor::task] |
| 53 | async fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) { | 53 | async fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async, i2c::Master>>) { |
| 54 | let mut data = [0u8; 6]; | 54 | let mut data = [0u8; 6]; |
| 55 | 55 | ||
| 56 | loop { | 56 | loop { |
diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index 8de31ea5b..12abb8693 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs | |||
| @@ -6,7 +6,7 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::gpio::{AfType, Flex, OutputType, Speed}; | 6 | use embassy_stm32::gpio::{AfType, Flex, OutputType, Speed}; |
| 7 | use embassy_stm32::time::{khz, Hertz}; | 7 | use embassy_stm32::time::{khz, Hertz}; |
| 8 | use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer}; | 8 | use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer}; |
| 9 | use embassy_stm32::timer::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance32bit4Channel}; | 9 | use embassy_stm32::timer::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance32bit4Channel, TimerPin}; |
| 10 | use embassy_stm32::{Config, Peri}; | 10 | use embassy_stm32::{Config, Peri}; |
| 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 _}; |
| @@ -67,10 +67,10 @@ pub struct SimplePwm32<'d, T: GeneralInstance32bit4Channel> { | |||
| 67 | impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> { | 67 | impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> { |
| 68 | pub fn new( | 68 | pub fn new( |
| 69 | tim: Peri<'d, T>, | 69 | tim: Peri<'d, T>, |
| 70 | ch1: Peri<'d, impl Channel1Pin<T>>, | 70 | ch1: Peri<'d, impl TimerPin<T, Ch1>>, |
| 71 | ch2: Peri<'d, impl Channel2Pin<T>>, | 71 | ch2: Peri<'d, impl TimerPin<T, Ch2>>, |
| 72 | ch3: Peri<'d, impl Channel3Pin<T>>, | 72 | ch3: Peri<'d, impl TimerPin<T, Ch3>>, |
| 73 | ch4: Peri<'d, impl Channel4Pin<T>>, | 73 | ch4: Peri<'d, impl TimerPin<T, Ch4>>, |
| 74 | freq: Hertz, | 74 | freq: Hertz, |
| 75 | ) -> Self { | 75 | ) -> Self { |
| 76 | let af1 = ch1.af_num(); | 76 | let af1 = ch1.af_num(); |
diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index a1c53fc3f..73b43be69 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs | |||
| @@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) { | |||
| 36 | let p = embassy_stm32::init(config); | 36 | let p = embassy_stm32::init(config); |
| 37 | info!("Hello World!"); | 37 | info!("Hello World!"); |
| 38 | 38 | ||
| 39 | let ch1_pin = PwmPin::new_ch1(p.PA6, OutputType::PushPull); | 39 | let ch1_pin = PwmPin::new(p.PA6, OutputType::PushPull); |
| 40 | let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default()); | 40 | let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default()); |
| 41 | let mut ch1 = pwm.ch1(); | 41 | let mut ch1 = pwm.ch1(); |
| 42 | ch1.enable(); | 42 | ch1.enable(); |
diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs index a04d7cb34..6d29e8a4d 100644 --- a/examples/stm32h723/src/bin/spdifrx.rs +++ b/examples/stm32h723/src/bin/spdifrx.rs | |||
| @@ -7,9 +7,9 @@ | |||
| 7 | 7 | ||
| 8 | use defmt::{info, trace}; | 8 | use defmt::{info, trace}; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_futures::select::{self, select, Either}; | 10 | use embassy_futures::select::{select, Either}; |
| 11 | use embassy_stm32::spdifrx::{self, Spdifrx}; | 11 | use embassy_stm32::spdifrx::{self, Spdifrx}; |
| 12 | use embassy_stm32::{bind_interrupts, peripherals, sai}; | 12 | use embassy_stm32::{bind_interrupts, peripherals, sai, Peri}; |
| 13 | use grounded::uninit::GroundedArrayCell; | 13 | use grounded::uninit::GroundedArrayCell; |
| 14 | use hal::sai::*; | 14 | use hal::sai::*; |
| 15 | use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _}; | 15 | use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _}; |
| @@ -25,10 +25,10 @@ const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks | |||
| 25 | 25 | ||
| 26 | // DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions | 26 | // DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions |
| 27 | #[unsafe(link_section = ".sram1")] | 27 | #[unsafe(link_section = ".sram1")] |
| 28 | static mut SPDIFRX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); | 28 | static SPDIFRX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); |
| 29 | 29 | ||
| 30 | #[unsafe(link_section = ".sram4")] | 30 | #[unsafe(link_section = ".sram4")] |
| 31 | static mut SAI_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); | 31 | static SAI_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); |
| 32 | 32 | ||
| 33 | #[embassy_executor::main] | 33 | #[embassy_executor::main] |
| 34 | async fn main(_spawner: Spawner) { | 34 | async fn main(_spawner: Spawner) { |
| @@ -144,9 +144,9 @@ async fn main(_spawner: Spawner) { | |||
| 144 | /// | 144 | /// |
| 145 | /// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect). | 145 | /// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect). |
| 146 | fn new_spdif_receiver<'d>( | 146 | fn new_spdif_receiver<'d>( |
| 147 | spdifrx: &'d mut peripherals::SPDIFRX1, | 147 | spdifrx: Peri<'d, peripherals::SPDIFRX1>, |
| 148 | input_pin: &'d mut peripherals::PD7, | 148 | input_pin: Peri<'d, peripherals::PD7>, |
| 149 | dma: &'d mut peripherals::DMA2_CH7, | 149 | dma: Peri<'d, peripherals::DMA2_CH7>, |
| 150 | buf: &'d mut [u32], | 150 | buf: &'d mut [u32], |
| 151 | ) -> Spdifrx<'d, peripherals::SPDIFRX1> { | 151 | ) -> Spdifrx<'d, peripherals::SPDIFRX1> { |
| 152 | Spdifrx::new(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf) | 152 | Spdifrx::new(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf) |
| @@ -156,11 +156,11 @@ fn new_spdif_receiver<'d>( | |||
| 156 | /// | 156 | /// |
| 157 | /// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun). | 157 | /// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun). |
| 158 | fn new_sai_transmitter<'d>( | 158 | fn new_sai_transmitter<'d>( |
| 159 | sai: &'d mut peripherals::SAI4, | 159 | sai: Peri<'d, peripherals::SAI4>, |
| 160 | sck: &'d mut peripherals::PD13, | 160 | sck: Peri<'d, peripherals::PD13>, |
| 161 | sd: &'d mut peripherals::PC1, | 161 | sd: Peri<'d, peripherals::PC1>, |
| 162 | fs: &'d mut peripherals::PD12, | 162 | fs: Peri<'d, peripherals::PD12>, |
| 163 | dma: &'d mut peripherals::BDMA_CH0, | 163 | dma: Peri<'d, peripherals::BDMA_CH0>, |
| 164 | buf: &'d mut [u32], | 164 | buf: &'d mut [u32], |
| 165 | ) -> Sai<'d, peripherals::SAI4, u32> { | 165 | ) -> Sai<'d, peripherals::SAI4, u32> { |
| 166 | let mut sai_config = hal::sai::Config::default(); | 166 | let mut sai_config = hal::sai::Config::default(); |
diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml index 8d23c346a..e4938e15b 100644 --- a/examples/stm32h735/Cargo.toml +++ b/examples/stm32h735/Cargo.toml | |||
| @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | 8 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } |
| 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } | 10 | embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
diff --git a/examples/stm32h742/Cargo.toml b/examples/stm32h742/Cargo.toml index 31eff4379..efa71e144 100644 --- a/examples/stm32h742/Cargo.toml +++ b/examples/stm32h742/Cargo.toml | |||
| @@ -34,7 +34,7 @@ embassy-net = { version = "0.7.0", path = "../../embassy-net", features = [ | |||
| 34 | "medium-ethernet", | 34 | "medium-ethernet", |
| 35 | ] } | 35 | ] } |
| 36 | embedded-io-async = { version = "0.6.1" } | 36 | embedded-io-async = { version = "0.6.1" } |
| 37 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = [ | 37 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = [ |
| 38 | "defmt", | 38 | "defmt", |
| 39 | ] } | 39 | ] } |
| 40 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 40 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
diff --git a/examples/stm32h742/src/bin/qspi.rs b/examples/stm32h742/src/bin/qspi.rs index aee07f3f2..50e37ec52 100644 --- a/examples/stm32h742/src/bin/qspi.rs +++ b/examples/stm32h742/src/bin/qspi.rs | |||
| @@ -272,6 +272,7 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 272 | prescaler: 16, | 272 | prescaler: 16, |
| 273 | cs_high_time: ChipSelectHighTime::_1Cycle, | 273 | cs_high_time: ChipSelectHighTime::_1Cycle, |
| 274 | fifo_threshold: FIFOThresholdLevel::_16Bytes, | 274 | fifo_threshold: FIFOThresholdLevel::_16Bytes, |
| 275 | sample_shifting: SampleShifting::None, | ||
| 275 | }; | 276 | }; |
| 276 | 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); |
| 277 | let mut flash = FlashMemory::new(driver); | 278 | let mut flash = FlashMemory::new(driver); |
diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 71bd50d60..eaaeb1ac0 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml | |||
| @@ -8,11 +8,11 @@ license = "MIT OR Apache-2.0" | |||
| 8 | # Change stm32h755zi-cm4 to your chip name, if necessary. | 8 | # Change stm32h755zi-cm4 to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } |
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } | 11 | embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } |
| 12 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 12 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 13 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 13 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 14 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } | 14 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } |
| 15 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 15 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 17 | 17 | ||
| 18 | defmt = "1.0.1" | 18 | defmt = "1.0.1" |
diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 8e960932a..e7fc05948 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml | |||
| @@ -8,11 +8,11 @@ license = "MIT OR Apache-2.0" | |||
| 8 | # Change stm32h743bi to your chip name, if necessary. | 8 | # Change stm32h743bi to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } | 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } |
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } | 11 | embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } |
| 12 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 12 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 13 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 13 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 14 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } | 14 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } |
| 15 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 15 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 17 | 17 | ||
| 18 | defmt = "1.0.1" | 18 | defmt = "1.0.1" |
diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml index 72f86e0cf..bfa7f9202 100644 --- a/examples/stm32h7b0/Cargo.toml +++ b/examples/stm32h7b0/Cargo.toml | |||
| @@ -7,11 +7,11 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | 8 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } |
| 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } | 10 | embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } | 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } |
| 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 16 | 16 | ||
| 17 | defmt = "1.0.1" | 17 | defmt = "1.0.1" |
diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml index 5f1ce8dfc..9794c6dbd 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml | |||
| @@ -11,7 +11,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de | |||
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "medium-ethernet", "medium-ip", "proto-ipv4"] } | 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "medium-ethernet", "medium-ip", "proto-ipv4"] } |
| 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 16 | 16 | ||
| 17 | defmt = "1.0.1" | 17 | defmt = "1.0.1" |
diff --git a/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs index 88d914180..59045ca2e 100644 --- a/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs +++ b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs | |||
| @@ -3,7 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | //! For Nucleo STM32H7S3L8 MB1737, has MX25UW25645GXDI00 | 4 | //! For Nucleo STM32H7S3L8 MB1737, has MX25UW25645GXDI00 |
| 5 | //! | 5 | //! |
| 6 | //! TODO: Currently this only uses single SPI, pending flash chip documentation for octo SPI. | 6 | |
| 7 | use core::cmp::min; | ||
| 7 | 8 | ||
| 8 | use defmt::info; | 9 | use defmt::info; |
| 9 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| @@ -52,14 +53,16 @@ async fn main(_spawner: Spawner) { | |||
| 52 | fifo_threshold: FIFOThresholdLevel::_4Bytes, | 53 | fifo_threshold: FIFOThresholdLevel::_4Bytes, |
| 53 | memory_type: MemoryType::Macronix, | 54 | memory_type: MemoryType::Macronix, |
| 54 | delay_hold_quarter_cycle: true, | 55 | delay_hold_quarter_cycle: true, |
| 55 | // memory_type: MemoryType::Micron, | ||
| 56 | // delay_hold_quarter_cycle: false, | ||
| 57 | device_size: MemorySize::_32MiB, | 56 | device_size: MemorySize::_32MiB, |
| 58 | chip_select_high_time: ChipSelectHighTime::_2Cycle, | 57 | chip_select_high_time: ChipSelectHighTime::_2Cycle, |
| 59 | free_running_clock: false, | 58 | free_running_clock: false, |
| 60 | clock_mode: false, | 59 | clock_mode: false, |
| 61 | wrap_size: WrapSize::None, | 60 | wrap_size: WrapSize::None, |
| 62 | // 300mhz / (4+1) = 60mhz. Unsure the limit, need to find a MX25UW25645GXDI00 datasheet. | 61 | // 300 MHz clock / (3 + 1) = 75 MHz. This is above the max for READ instructions so the |
| 62 | // FAST READ must be used. The nucleo board's flash can run at up to 133 MHz in SPI mode | ||
| 63 | // and 200 MHz in OPI mode. This clock prescaler must be even otherwise the clock will not | ||
| 64 | // have symmetric high and low times. | ||
| 65 | // The clock can also be fed by one of the PLLs to allow for more flexible clock rates. | ||
| 63 | clock_prescaler: 3, | 66 | clock_prescaler: 3, |
| 64 | sample_shifting: false, | 67 | sample_shifting: false, |
| 65 | chip_select_boundary: 0, | 68 | chip_select_boundary: 0, |
| @@ -71,28 +74,41 @@ async fn main(_spawner: Spawner) { | |||
| 71 | 74 | ||
| 72 | // Not necessary, but recommended if using XIP | 75 | // Not necessary, but recommended if using XIP |
| 73 | cor.SCB.enable_icache(); | 76 | cor.SCB.enable_icache(); |
| 77 | // Note: Enabling data cache can cause issues with DMA transfers. | ||
| 74 | cor.SCB.enable_dcache(&mut cor.CPUID); | 78 | cor.SCB.enable_dcache(&mut cor.CPUID); |
| 75 | 79 | ||
| 76 | let xspi = embassy_stm32::xspi::Xspi::new_blocking_xspi( | 80 | let xspi = embassy_stm32::xspi::Xspi::new_blocking_xspi( |
| 77 | p.XSPI2, p.PN6, p.PN2, p.PN3, p.PN4, p.PN5, p.PN8, p.PN9, p.PN10, p.PN11, p.PN1, spi_config, | 81 | p.XSPI2, p.PN6, p.PN2, p.PN3, p.PN4, p.PN5, p.PN8, p.PN9, p.PN10, p.PN11, p.PN1, spi_config, |
| 78 | ); | 82 | ); |
| 79 | 83 | ||
| 80 | let mut flash = FlashMemory::new(xspi).await; | 84 | let mut flash = SpiFlashMemory::new(xspi); |
| 81 | 85 | ||
| 82 | let flash_id = flash.read_id(); | 86 | let flash_id = flash.read_id(); |
| 83 | info!("FLASH ID: {=[u8]:x}", flash_id); | 87 | info!("FLASH ID: {=[u8]:x}", flash_id); |
| 84 | 88 | ||
| 85 | let mut wr_buf = [0u8; 8]; | 89 | // Erase the first sector |
| 86 | for i in 0..8 { | 90 | flash.erase_sector(0); |
| 87 | wr_buf[i] = 0x90 + i as u8; | 91 | |
| 88 | } | 92 | // Write some data into the flash. This writes more than one page to test that functionality. |
| 89 | let mut rd_buf = [0u8; 8]; | 93 | let mut wr_buf = [0u8; 512]; |
| 90 | flash.erase_sector(0).await; | 94 | let base_number: u8 = 0x90; |
| 91 | flash.write_memory(0, &wr_buf, true).await; | 95 | for i in 0..512 { |
| 92 | flash.read_memory(0, &mut rd_buf, true); | 96 | wr_buf[i] = base_number.wrapping_add(i as u8); |
| 93 | info!("WRITE BUF: {=[u8]:#X}", wr_buf); | 97 | } |
| 94 | info!("READ BUF: {=[u8]:#X}", rd_buf); | 98 | flash.write_memory(0, &wr_buf); |
| 95 | flash.enable_mm().await; | 99 | |
| 100 | // Read the data back and verify it. | ||
| 101 | let mut rd_buf = [0u8; 512]; | ||
| 102 | let start_time = embassy_time::Instant::now(); | ||
| 103 | flash.read_memory(0, &mut rd_buf); | ||
| 104 | let elapsed = start_time.elapsed(); | ||
| 105 | info!("Read 512 bytes in {} us in SPI mode", elapsed.as_micros()); | ||
| 106 | info!("WRITE BUF: {=[u8]:#X}", wr_buf[0..32]); | ||
| 107 | info!("READ BUF: {=[u8]:#X}", rd_buf[0..32]); | ||
| 108 | |||
| 109 | assert_eq!(wr_buf, rd_buf, "Read buffer does not match write buffer"); | ||
| 110 | |||
| 111 | flash.enable_mm(); | ||
| 96 | info!("Enabled memory mapped mode"); | 112 | info!("Enabled memory mapped mode"); |
| 97 | 113 | ||
| 98 | let first_u32 = unsafe { *(0x70000000 as *const u32) }; | 114 | let first_u32 = unsafe { *(0x70000000 as *const u32) }; |
| @@ -103,10 +119,53 @@ async fn main(_spawner: Spawner) { | |||
| 103 | assert_eq!(second_u32, 0x97969594); | 119 | assert_eq!(second_u32, 0x97969594); |
| 104 | info!("second_u32 {:08x}", first_u32); | 120 | info!("second_u32 {:08x}", first_u32); |
| 105 | 121 | ||
| 106 | flash.disable_mm().await; | 122 | flash.disable_mm(); |
| 107 | info!("Disabled memory mapped mode"); | 123 | info!("Disabled memory mapped mode"); |
| 108 | 124 | ||
| 125 | let flash_id = flash.read_id(); | ||
| 126 | info!("FLASH ID: {=[u8]:x}", flash_id); | ||
| 127 | |||
| 128 | let mut flash = flash.into_octo(); | ||
| 129 | |||
| 130 | Timer::after_millis(100).await; | ||
| 131 | |||
| 132 | let flash_id = flash.read_id(); | ||
| 133 | info!("FLASH ID in OPI mode: {=[u8]:x}", flash_id); | ||
| 134 | |||
| 135 | flash.erase_sector(0); | ||
| 136 | |||
| 137 | let mut rd_buf = [0u8; 512]; | ||
| 138 | flash.read_memory(0, &mut rd_buf); | ||
| 139 | info!("READ BUF after erase: {=[u8]:#X}", rd_buf[0..32]); | ||
| 140 | |||
| 141 | assert_eq!(rd_buf, [0xFF; 512], "Read buffer is not all 0xFF after erase"); | ||
| 142 | |||
| 143 | flash.write_memory(0, &wr_buf); | ||
| 144 | let start = embassy_time::Instant::now(); | ||
| 145 | flash.read_memory(0, &mut rd_buf); | ||
| 146 | let elapsed = start.elapsed(); | ||
| 147 | info!("Read 512 bytes in {} us in OPI mode", elapsed.as_micros()); | ||
| 148 | info!("READ BUF after write: {=[u8]:#X}", rd_buf[0..32]); | ||
| 149 | assert_eq!(wr_buf, rd_buf, "Read buffer does not match write buffer in OPI mode"); | ||
| 150 | |||
| 151 | flash.enable_mm(); | ||
| 152 | info!("Enabled memory mapped mode in OPI mode"); | ||
| 153 | let first_u32 = unsafe { *(0x70000000 as *const u32) }; | ||
| 154 | assert_eq!(first_u32, 0x93929190); | ||
| 155 | info!("first_u32 {:08x}", first_u32); | ||
| 156 | let second_u32 = unsafe { *(0x70000004 as *const u32) }; | ||
| 157 | assert_eq!(second_u32, 0x97969594); | ||
| 158 | info!("second_u32 {:08x}", first_u32); | ||
| 159 | flash.disable_mm(); | ||
| 160 | info!("Disabled memory mapped mode in OPI mode"); | ||
| 161 | |||
| 162 | // Reset back to SPI mode | ||
| 163 | let mut flash = flash.into_spi(); | ||
| 164 | let flash_id = flash.read_id(); | ||
| 165 | info!("FLASH ID back in SPI mode: {=[u8]:x}", flash_id); | ||
| 166 | |||
| 109 | info!("DONE"); | 167 | info!("DONE"); |
| 168 | |||
| 110 | // Output pin PE3 | 169 | // Output pin PE3 |
| 111 | let mut led = Output::new(p.PE3, Level::Low, Speed::Low); | 170 | let mut led = Output::new(p.PE3, Level::Low, Speed::Low); |
| 112 | 171 | ||
| @@ -116,80 +175,268 @@ async fn main(_spawner: Spawner) { | |||
| 116 | } | 175 | } |
| 117 | } | 176 | } |
| 118 | 177 | ||
| 119 | const MEMORY_PAGE_SIZE: usize = 8; | 178 | const MEMORY_PAGE_SIZE: usize = 256; |
| 120 | |||
| 121 | const CMD_READ: u8 = 0x0B; | ||
| 122 | const _CMD_QUAD_READ: u8 = 0x6B; | ||
| 123 | |||
| 124 | const CMD_WRITE_PG: u8 = 0x02; | ||
| 125 | const _CMD_QUAD_WRITE_PG: u8 = 0x32; | ||
| 126 | |||
| 127 | const CMD_READ_ID: u8 = 0x9F; | ||
| 128 | const CMD_READ_ID_OCTO: u16 = 0x9F60; | ||
| 129 | 179 | ||
| 130 | const CMD_ENABLE_RESET: u8 = 0x66; | 180 | /// Implementation of access to flash chip using SPI. |
| 131 | const CMD_RESET: u8 = 0x99; | 181 | /// |
| 132 | 182 | /// Chip commands are hardcoded as it depends on used chip. | |
| 133 | const CMD_WRITE_ENABLE: u8 = 0x06; | 183 | /// This targets a MX25UW25645GXDI00. |
| 134 | 184 | pub struct SpiFlashMemory<I: Instance> { | |
| 135 | const CMD_CHIP_ERASE: u8 = 0xC7; | 185 | xspi: Xspi<'static, I, Blocking>, |
| 136 | const CMD_SECTOR_ERASE: u8 = 0x20; | 186 | } |
| 137 | const CMD_BLOCK_ERASE_32K: u8 = 0x52; | ||
| 138 | const CMD_BLOCK_ERASE_64K: u8 = 0xD8; | ||
| 139 | |||
| 140 | const CMD_READ_SR: u8 = 0x05; | ||
| 141 | const CMD_READ_CR: u8 = 0x35; | ||
| 142 | |||
| 143 | const CMD_WRITE_SR: u8 = 0x01; | ||
| 144 | const CMD_WRITE_CR: u8 = 0x31; | ||
| 145 | 187 | ||
| 146 | /// Implementation of access to flash chip. | 188 | /// Implementation of access to flash chip using Octo SPI. |
| 147 | /// | 189 | /// |
| 148 | /// Chip commands are hardcoded as it depends on used chip. | 190 | /// Chip commands are hardcoded as it depends on used chip. |
| 149 | /// This targets a MX25UW25645GXDI00. | 191 | /// This targets a MX25UW25645GXDI00. |
| 150 | pub struct FlashMemory<I: Instance> { | 192 | pub struct OpiFlashMemory<I: Instance> { |
| 151 | xspi: Xspi<'static, I, Blocking>, | 193 | xspi: Xspi<'static, I, Blocking>, |
| 152 | } | 194 | } |
| 153 | 195 | ||
| 154 | impl<I: Instance> FlashMemory<I> { | 196 | /// SPI mode commands for MX25UW25645G flash memory |
| 155 | pub async fn new(xspi: Xspi<'static, I, Blocking>) -> Self { | 197 | #[allow(dead_code)] |
| 156 | let mut memory = Self { xspi }; | 198 | #[repr(u8)] |
| 199 | enum SpiCommand { | ||
| 200 | // Array access commands | ||
| 201 | /// Read data bytes using 3-byte address (up to 50 MHz) | ||
| 202 | Read3B = 0x03, | ||
| 203 | /// Fast read data bytes using 3-byte address with 8 dummy cycles (up to 133 MHz) | ||
| 204 | FastRead3B = 0x0B, | ||
| 205 | /// Program 1-256 bytes of data using 3-byte address | ||
| 206 | PageProgram3B = 0x02, | ||
| 207 | /// Erase 4KB sector using 3-byte address | ||
| 208 | SectorErase3B = 0x20, | ||
| 209 | /// Erase 64KB block using 3-byte address | ||
| 210 | BlockErase3B = 0xD8, | ||
| 211 | /// Read data bytes using 4-byte address (up to 50 MHz) | ||
| 212 | Read4B = 0x13, | ||
| 213 | /// Fast read data bytes using 4-byte address with 8 dummy cycles (up to 133 MHz) | ||
| 214 | FastRead4B = 0x0C, | ||
| 215 | /// Program 1-256 bytes of data using 4-byte address | ||
| 216 | PageProgram4B = 0x12, | ||
| 217 | /// Erase 4KB sector using 4-byte address | ||
| 218 | SectorErase4B = 0x21, | ||
| 219 | /// Erase 64KB block using 4-byte address | ||
| 220 | BlockErase4B = 0xDC, | ||
| 221 | /// Erase entire chip (only if no blocks are protected) | ||
| 222 | ChipErase = 0x60, | ||
| 223 | |||
| 224 | // Write Buffer Access commands | ||
| 225 | /// Read data from the 256-byte page buffer | ||
| 226 | ReadBuffer = 0x25, | ||
| 227 | /// Initialize write-to-buffer sequence, clears buffer and writes initial data | ||
| 228 | WriteBufferInitial = 0x22, | ||
| 229 | /// Continue writing data to buffer (used between WRBI and WRCF) | ||
| 230 | WriteBufferContinue = 0x24, | ||
| 231 | /// Confirm write operation, programs buffer contents to flash array | ||
| 232 | WriteBufferConfirm = 0x31, | ||
| 233 | |||
| 234 | // Device operation commands | ||
| 235 | /// Set Write Enable Latch (WEL) bit, required before write/program/erase operations | ||
| 236 | WriteEnable = 0x06, | ||
| 237 | /// Clear Write Enable Latch (WEL) bit | ||
| 238 | WriteDisable = 0x04, | ||
| 239 | /// Select write protection mode (BP mode or Advanced Sector Protection) | ||
| 240 | WriteProtectSelection = 0x68, | ||
| 241 | /// Suspend ongoing program or erase operation to allow read access | ||
| 242 | ProgramEraseSuspend = 0xB0, | ||
| 243 | /// Resume suspended program or erase operation | ||
| 244 | ProgramEraseResume = 0x30, | ||
| 245 | /// Enter deep power-down mode for minimum power consumption | ||
| 246 | DeepPowerDown = 0xB9, | ||
| 247 | /// Exit deep power-down mode and return to standby | ||
| 248 | ReleaseFromDeepPowerDown = 0xAB, | ||
| 249 | /// No operation, can terminate Reset Enable command | ||
| 250 | NoOperation = 0x00, | ||
| 251 | /// Enable reset operation (must precede Reset Memory command) | ||
| 252 | ResetEnable = 0x66, | ||
| 253 | /// Reset device to power-on state (requires prior Reset Enable) | ||
| 254 | ResetMemory = 0x99, | ||
| 255 | /// Protect all sectors using Dynamic Protection Bits (DPB) | ||
| 256 | GangBlockLock = 0x7E, | ||
| 257 | /// Unprotect all sectors by clearing Dynamic Protection Bits (DPB) | ||
| 258 | GangBlockUnlock = 0x98, | ||
| 259 | |||
| 260 | // Register Access commands | ||
| 261 | /// Read 3-byte device identification (manufacturer ID + device ID) | ||
| 262 | ReadIdentification = 0x9F, | ||
| 263 | /// Read Serial Flash Discoverable Parameters (SFDP) table | ||
| 264 | ReadSFDP = 0x5A, | ||
| 265 | /// Read 8-bit Status Register (WIP, WEL, BP bits, etc.) | ||
| 266 | ReadStatusRegister = 0x05, | ||
| 267 | /// Read 8-bit Configuration Register (ODS, TB, PBE bits) | ||
| 268 | ReadConfigurationRegister = 0x15, | ||
| 269 | /// Write Status and/or Configuration Register (1-2 bytes) | ||
| 270 | WriteStatusConfigurationRegister = 0x01, | ||
| 271 | /// Read Configuration Register 2 from specified 4-byte address | ||
| 272 | ReadConfigurationRegister2 = 0x71, | ||
| 273 | /// Write Configuration Register 2 to specified 4-byte address | ||
| 274 | WriteConfigurationRegister2 = 0x72, | ||
| 275 | /// Read 8-bit Security Register (protection status, suspend bits) | ||
| 276 | ReadSecurityRegister = 0x2B, | ||
| 277 | /// Write Security Register to set customer lock-down bit | ||
| 278 | WriteSecurityRegister = 0x2F, | ||
| 279 | /// Read 32-bit Fast Boot Register (boot address and configuration) | ||
| 280 | ReadFastBootRegister = 0x16, | ||
| 281 | /// Write 32-bit Fast Boot Register | ||
| 282 | WriteFastBootRegister = 0x17, | ||
| 283 | /// Erase Fast Boot Register (disable fast boot feature) | ||
| 284 | EraseFastBootRegister = 0x18, | ||
| 285 | /// Set burst/wrap length for read operations (16/32/64 bytes) | ||
| 286 | SetBurstLength = 0xC0, | ||
| 287 | /// Enter 8K-bit secured OTP mode for programming unique identifiers | ||
| 288 | EnterSecuredOTP = 0xB1, | ||
| 289 | /// Exit secured OTP mode and return to main array access | ||
| 290 | ExitSecuredOTP = 0xC1, | ||
| 291 | /// Write Lock Register to control SPB protection mode | ||
| 292 | WriteLockRegister = 0x2C, | ||
| 293 | /// Read Lock Register status | ||
| 294 | ReadLockRegister = 0x2D, | ||
| 295 | /// Program Solid Protection Bit (SPB) for specified sector/block | ||
| 296 | WriteSPB = 0xE3, | ||
| 297 | /// Erase all Solid Protection Bits (SPB) | ||
| 298 | EraseSPB = 0xE4, | ||
| 299 | /// Read Solid Protection Bit (SPB) status for specified sector/block | ||
| 300 | ReadSPB = 0xE2, | ||
| 301 | /// Write Dynamic Protection Bit (DPB) for specified sector | ||
| 302 | WriteDPB = 0xE1, | ||
| 303 | /// Read Dynamic Protection Bit (DPB) status for specified sector | ||
| 304 | ReadDPB = 0xE0, | ||
| 305 | /// Read 64-bit password register (only in Solid Protection mode) | ||
| 306 | ReadPassword = 0x27, | ||
| 307 | /// Write 64-bit password register | ||
| 308 | WritePassword = 0x28, | ||
| 309 | /// Unlock SPB operations using 64-bit password | ||
| 310 | PasswordUnlock = 0x29, | ||
| 311 | } | ||
| 157 | 312 | ||
| 158 | memory.reset_memory().await; | 313 | /// OPI mode commands for MX25UW25645G flash memory |
| 159 | memory.enable_octo(); | 314 | #[allow(dead_code)] |
| 160 | memory | 315 | #[repr(u16)] |
| 161 | } | 316 | enum OpiCommand { |
| 317 | // Array access commands | ||
| 318 | /// Read data using 8 I/O lines in STR mode with configurable dummy cycles (up to 200 MHz) | ||
| 319 | OctaRead = 0xEC13, | ||
| 320 | /// Read data using 8 I/O lines in DTR mode with configurable dummy cycles (up to 200 MHz) | ||
| 321 | OctaDTRRead = 0xEE11, | ||
| 322 | /// Program 1-256 bytes using 4-byte address and 8 I/O lines | ||
| 323 | PageProgram4B = 0x12ED, | ||
| 324 | /// Erase 4KB sector using 4-byte address | ||
| 325 | SectorErase4B = 0x21DE, | ||
| 326 | /// Erase 64KB block using 4-byte address | ||
| 327 | BlockErase4B = 0xDC23, | ||
| 328 | /// Erase entire chip (only if no blocks are protected) | ||
| 329 | ChipErase = 0x609F, | ||
| 330 | |||
| 331 | // Write Buffer Access commands | ||
| 332 | /// Read data from the 256-byte page buffer using 4-byte address | ||
| 333 | ReadBuffer = 0x25DA, | ||
| 334 | /// Initialize interruptible write-to-buffer sequence with 4-byte address | ||
| 335 | WriteBufferInitial = 0x22DD, | ||
| 336 | /// Continue writing data to buffer during interruptible sequence | ||
| 337 | WriteBufferContinue = 0x24DB, | ||
| 338 | /// Confirm and execute write operation from buffer to flash array | ||
| 339 | WriteBufferConfirm = 0x31CE, | ||
| 340 | |||
| 341 | // Device operation commands | ||
| 342 | /// Set Write Enable Latch (WEL) bit, required before write/program/erase operations | ||
| 343 | WriteEnable = 0x06F9, | ||
| 344 | /// Clear Write Enable Latch (WEL) bit, aborts write-to-buffer sequence | ||
| 345 | WriteDisable = 0x04FB, | ||
| 346 | /// Select write protection mode (BP mode or Advanced Sector Protection) - OTP bit | ||
| 347 | WriteProtectSelection = 0x6897, | ||
| 348 | /// Suspend ongoing program or erase operation to allow read from other banks | ||
| 349 | ProgramEraseSuspend = 0xB04F, | ||
| 350 | /// Resume suspended program or erase operation | ||
| 351 | ProgramEraseResume = 0x30CF, | ||
| 352 | /// Enter deep power-down mode for minimum power consumption | ||
| 353 | DeepPowerDown = 0xB946, | ||
| 354 | /// Exit deep power-down mode and return to standby | ||
| 355 | ReleaseFromDeepPowerDown = 0xAB54, | ||
| 356 | /// No operation, can terminate Reset Enable command | ||
| 357 | NoOperation = 0x00FF, | ||
| 358 | /// Enable reset operation (must precede Reset Memory command) | ||
| 359 | ResetEnable = 0x6699, | ||
| 360 | /// Reset device to power-on state, clears volatile settings | ||
| 361 | ResetMemory = 0x9966, | ||
| 362 | /// Protect all sectors using Dynamic Protection Bits (DPB) | ||
| 363 | GangBlockLock = 0x7E81, | ||
| 364 | /// Unprotect all sectors by clearing Dynamic Protection Bits (DPB) | ||
| 365 | GangBlockUnlock = 0x9867, | ||
| 366 | |||
| 367 | // Register Access commands | ||
| 368 | /// Read 3-byte device identification with 4-byte dummy address | ||
| 369 | ReadIdentification = 0x9F60, | ||
| 370 | /// Read Serial Flash Discoverable Parameters (SFDP) table with 4-byte address | ||
| 371 | ReadSFDP = 0x5AA5, | ||
| 372 | /// Read 8-bit Status Register with 4-byte dummy address | ||
| 373 | ReadStatusRegister = 0x05FA, | ||
| 374 | /// Read 8-bit Configuration Register with specific address (00000001h) | ||
| 375 | ReadConfigurationRegister = 0x15EA, | ||
| 376 | /// Write 8-bit Status Register with specific address (00000000h) or Configuration Register with address (00000001h) | ||
| 377 | WriteStatusConfigurationRegister = 0x01FE, | ||
| 378 | /// Read Configuration Register 2 from specified 4-byte address | ||
| 379 | ReadConfigurationRegister2 = 0x718E, | ||
| 380 | /// Write Configuration Register 2 to specified 4-byte address | ||
| 381 | WriteConfigurationRegister2 = 0x728D, | ||
| 382 | /// Read 8-bit Security Register with 4-byte dummy address | ||
| 383 | ReadSecurityRegister = 0x2BD4, | ||
| 384 | /// Write Security Register to set customer lock-down bit | ||
| 385 | WriteSecurityRegister = 0x2FD0, | ||
| 386 | /// Set burst/wrap length for read operations with 4-byte dummy address | ||
| 387 | SetBurstLength = 0xC03F, | ||
| 388 | /// Read 32-bit Fast Boot Register with 4-byte dummy address | ||
| 389 | ReadFastBootRegister = 0x16E9, | ||
| 390 | /// Write 32-bit Fast Boot Register with 4-byte dummy address | ||
| 391 | WriteFastBootRegister = 0x17E8, | ||
| 392 | /// Erase Fast Boot Register (disable fast boot feature) | ||
| 393 | EraseFastBootRegister = 0x18E7, | ||
| 394 | /// Enter 8K-bit secured OTP mode for programming unique identifiers | ||
| 395 | EnterSecuredOTP = 0xB14E, | ||
| 396 | /// Exit secured OTP mode and return to main array access | ||
| 397 | ExitSecuredOTP = 0xC13E, | ||
| 398 | /// Write Lock Register to control SPB protection mode with 4-byte dummy address | ||
| 399 | WriteLockRegister = 0x2CD3, | ||
| 400 | /// Read Lock Register status with 4-byte dummy address | ||
| 401 | ReadLockRegister = 0x2DD2, | ||
| 402 | /// Program Solid Protection Bit (SPB) for specified 4-byte address | ||
| 403 | WriteSPB = 0xE31C, | ||
| 404 | /// Erase all Solid Protection Bits (SPB) | ||
| 405 | EraseSPB = 0xE41B, | ||
| 406 | /// Read Solid Protection Bit (SPB) status for specified 4-byte address | ||
| 407 | ReadSPB = 0xE21D, | ||
| 408 | /// Write Dynamic Protection Bit (DPB) for specified 4-byte address | ||
| 409 | WriteDPB = 0xE11E, | ||
| 410 | /// Read Dynamic Protection Bit (DPB) status for specified 4-byte address | ||
| 411 | ReadDPB = 0xE01F, | ||
| 412 | /// Read 64-bit password register with 4-byte dummy address and 20 dummy cycles | ||
| 413 | ReadPassword = 0x27D8, | ||
| 414 | /// Write 64-bit password register with 4-byte dummy address | ||
| 415 | WritePassword = 0x28D7, | ||
| 416 | /// Unlock SPB operations using 64-bit password with 4-byte dummy address | ||
| 417 | PasswordUnlock = 0x29D6, | ||
| 418 | } | ||
| 162 | 419 | ||
| 163 | async fn qpi_mode(&mut self) { | 420 | impl<I: Instance> SpiFlashMemory<I> { |
| 164 | // Enter qpi mode | 421 | pub fn new(xspi: Xspi<'static, I, Blocking>) -> Self { |
| 165 | self.exec_command(0x38).await; | 422 | let mut memory = Self { xspi }; |
| 166 | 423 | ||
| 167 | // Set read param | 424 | memory.reset_memory(); |
| 168 | let transaction = TransferConfig { | 425 | memory |
| 169 | iwidth: XspiWidth::QUAD, | ||
| 170 | dwidth: XspiWidth::QUAD, | ||
| 171 | instruction: Some(0xC0), | ||
| 172 | ..Default::default() | ||
| 173 | }; | ||
| 174 | self.enable_write().await; | ||
| 175 | self.xspi.blocking_write(&[0x30_u8], transaction).unwrap(); | ||
| 176 | self.wait_write_finish(); | ||
| 177 | } | 426 | } |
| 178 | 427 | ||
| 179 | pub async fn disable_mm(&mut self) { | 428 | pub fn disable_mm(&mut self) { |
| 180 | self.xspi.disable_memory_mapped_mode(); | 429 | self.xspi.disable_memory_mapped_mode(); |
| 181 | } | 430 | } |
| 182 | 431 | ||
| 183 | pub async fn enable_mm(&mut self) { | 432 | pub fn enable_mm(&mut self) { |
| 184 | self.qpi_mode().await; | ||
| 185 | |||
| 186 | let read_config = TransferConfig { | 433 | let read_config = TransferConfig { |
| 187 | iwidth: XspiWidth::SING, | 434 | iwidth: XspiWidth::SING, |
| 188 | isize: AddressSize::_8bit, | 435 | isize: AddressSize::_8bit, |
| 189 | adwidth: XspiWidth::SING, | 436 | adwidth: XspiWidth::SING, |
| 190 | adsize: AddressSize::_24bit, | 437 | adsize: AddressSize::_32bit, |
| 191 | dwidth: XspiWidth::SING, | 438 | dwidth: XspiWidth::SING, |
| 192 | instruction: Some(CMD_READ as u32), | 439 | instruction: Some(SpiCommand::FastRead4B as u32), |
| 193 | dummy: DummyCycles::_8, | 440 | dummy: DummyCycles::_8, |
| 194 | ..Default::default() | 441 | ..Default::default() |
| 195 | }; | 442 | }; |
| @@ -198,42 +445,28 @@ impl<I: Instance> FlashMemory<I> { | |||
| 198 | iwidth: XspiWidth::SING, | 445 | iwidth: XspiWidth::SING, |
| 199 | isize: AddressSize::_8bit, | 446 | isize: AddressSize::_8bit, |
| 200 | adwidth: XspiWidth::SING, | 447 | adwidth: XspiWidth::SING, |
| 201 | adsize: AddressSize::_24bit, | 448 | adsize: AddressSize::_32bit, |
| 202 | dwidth: XspiWidth::SING, | 449 | dwidth: XspiWidth::SING, |
| 203 | instruction: Some(CMD_WRITE_PG as u32), | 450 | instruction: Some(SpiCommand::PageProgram4B as u32), |
| 204 | dummy: DummyCycles::_0, | 451 | dummy: DummyCycles::_0, |
| 205 | ..Default::default() | 452 | ..Default::default() |
| 206 | }; | 453 | }; |
| 207 | self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap(); | 454 | self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap(); |
| 208 | } | 455 | } |
| 209 | 456 | ||
| 210 | fn enable_octo(&mut self) { | 457 | fn into_octo(mut self) -> OpiFlashMemory<I> { |
| 211 | let cr = self.read_cr(); | 458 | self.enable_opi_mode(); |
| 212 | // info!("Read cr: {:x}", cr); | 459 | OpiFlashMemory { xspi: self.xspi } |
| 213 | self.write_cr(cr | 0x02); | ||
| 214 | // info!("Read cr after writing: {:x}", cr); | ||
| 215 | } | 460 | } |
| 216 | 461 | ||
| 217 | pub fn disable_octo(&mut self) { | 462 | fn enable_opi_mode(&mut self) { |
| 218 | let cr = self.read_cr(); | 463 | let cr2_0 = self.read_cr2(0); |
| 219 | self.write_cr(cr & (!(0x02))); | 464 | info!("Read CR2 at 0x0: {:x}", cr2_0); |
| 220 | } | 465 | self.enable_write(); |
| 221 | 466 | self.write_cr2(0, cr2_0 | 0x01); // Set bit 0 to enable octo SPI in STR | |
| 222 | async fn exec_command_4(&mut self, cmd: u8) { | ||
| 223 | let transaction = TransferConfig { | ||
| 224 | iwidth: XspiWidth::QUAD, | ||
| 225 | adwidth: XspiWidth::NONE, | ||
| 226 | // adsize: AddressSize::_24bit, | ||
| 227 | dwidth: XspiWidth::NONE, | ||
| 228 | instruction: Some(cmd as u32), | ||
| 229 | address: None, | ||
| 230 | dummy: DummyCycles::_0, | ||
| 231 | ..Default::default() | ||
| 232 | }; | ||
| 233 | self.xspi.blocking_command(&transaction).unwrap(); | ||
| 234 | } | 467 | } |
| 235 | 468 | ||
| 236 | async fn exec_command(&mut self, cmd: u8) { | 469 | fn exec_command(&mut self, cmd: u8) { |
| 237 | let transaction = TransferConfig { | 470 | let transaction = TransferConfig { |
| 238 | iwidth: XspiWidth::SING, | 471 | iwidth: XspiWidth::SING, |
| 239 | adwidth: XspiWidth::NONE, | 472 | adwidth: XspiWidth::NONE, |
| @@ -248,16 +481,14 @@ impl<I: Instance> FlashMemory<I> { | |||
| 248 | self.xspi.blocking_command(&transaction).unwrap(); | 481 | self.xspi.blocking_command(&transaction).unwrap(); |
| 249 | } | 482 | } |
| 250 | 483 | ||
| 251 | pub async fn reset_memory(&mut self) { | 484 | pub fn reset_memory(&mut self) { |
| 252 | self.exec_command_4(CMD_ENABLE_RESET).await; | 485 | self.exec_command(SpiCommand::ResetEnable as u8); |
| 253 | self.exec_command_4(CMD_RESET).await; | 486 | self.exec_command(SpiCommand::ResetMemory as u8); |
| 254 | self.exec_command(CMD_ENABLE_RESET).await; | ||
| 255 | self.exec_command(CMD_RESET).await; | ||
| 256 | self.wait_write_finish(); | 487 | self.wait_write_finish(); |
| 257 | } | 488 | } |
| 258 | 489 | ||
| 259 | pub async fn enable_write(&mut self) { | 490 | pub fn enable_write(&mut self) { |
| 260 | self.exec_command(CMD_WRITE_ENABLE).await; | 491 | self.exec_command(SpiCommand::WriteEnable as u8); |
| 261 | } | 492 | } |
| 262 | 493 | ||
| 263 | pub fn read_id(&mut self) -> [u8; 3] { | 494 | pub fn read_id(&mut self) -> [u8; 3] { |
| @@ -266,92 +497,64 @@ impl<I: Instance> FlashMemory<I> { | |||
| 266 | iwidth: XspiWidth::SING, | 497 | iwidth: XspiWidth::SING, |
| 267 | isize: AddressSize::_8bit, | 498 | isize: AddressSize::_8bit, |
| 268 | adwidth: XspiWidth::NONE, | 499 | adwidth: XspiWidth::NONE, |
| 269 | // adsize: AddressSize::_24bit, | ||
| 270 | dwidth: XspiWidth::SING, | 500 | dwidth: XspiWidth::SING, |
| 271 | instruction: Some(CMD_READ_ID as u32), | 501 | instruction: Some(SpiCommand::ReadIdentification as u32), |
| 272 | ..Default::default() | 502 | ..Default::default() |
| 273 | }; | 503 | }; |
| 274 | // info!("Reading id: 0x{:X}", transaction.instruction); | ||
| 275 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); | 504 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); |
| 276 | buffer | 505 | buffer |
| 277 | } | 506 | } |
| 278 | 507 | ||
| 279 | pub fn read_id_8(&mut self) -> [u8; 3] { | 508 | pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8]) { |
| 280 | let mut buffer = [0; 3]; | ||
| 281 | let transaction: TransferConfig = TransferConfig { | ||
| 282 | iwidth: XspiWidth::OCTO, | ||
| 283 | isize: AddressSize::_16bit, | ||
| 284 | adwidth: XspiWidth::OCTO, | ||
| 285 | address: Some(0), | ||
| 286 | adsize: AddressSize::_32bit, | ||
| 287 | dwidth: XspiWidth::OCTO, | ||
| 288 | instruction: Some(CMD_READ_ID_OCTO as u32), | ||
| 289 | dummy: DummyCycles::_4, | ||
| 290 | ..Default::default() | ||
| 291 | }; | ||
| 292 | info!("Reading id: {:#X}", transaction.instruction); | ||
| 293 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 294 | buffer | ||
| 295 | } | ||
| 296 | |||
| 297 | pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) { | ||
| 298 | let transaction = TransferConfig { | 509 | let transaction = TransferConfig { |
| 299 | iwidth: XspiWidth::SING, | 510 | iwidth: XspiWidth::SING, |
| 300 | adwidth: XspiWidth::SING, | 511 | adwidth: XspiWidth::SING, |
| 301 | adsize: AddressSize::_24bit, | 512 | adsize: AddressSize::_32bit, |
| 302 | dwidth: XspiWidth::SING, | 513 | dwidth: XspiWidth::SING, |
| 303 | instruction: Some(CMD_READ as u32), | 514 | instruction: Some(SpiCommand::FastRead4B as u32), |
| 304 | dummy: DummyCycles::_8, | 515 | dummy: DummyCycles::_8, |
| 305 | // dwidth: XspiWidth::QUAD, | ||
| 306 | // instruction: Some(CMD_QUAD_READ as u32), | ||
| 307 | // dummy: DummyCycles::_8, | ||
| 308 | address: Some(addr), | 516 | address: Some(addr), |
| 309 | ..Default::default() | 517 | ..Default::default() |
| 310 | }; | 518 | }; |
| 311 | if use_dma { | 519 | |
| 312 | self.xspi.blocking_read(buffer, transaction).unwrap(); | 520 | self.xspi.blocking_read(buffer, transaction).unwrap(); |
| 313 | } else { | ||
| 314 | self.xspi.blocking_read(buffer, transaction).unwrap(); | ||
| 315 | } | ||
| 316 | } | 521 | } |
| 317 | 522 | ||
| 318 | fn wait_write_finish(&mut self) { | 523 | fn wait_write_finish(&mut self) { |
| 319 | while (self.read_sr() & 0x01) != 0 {} | 524 | while (self.read_sr() & 0x01) != 0 {} |
| 320 | } | 525 | } |
| 321 | 526 | ||
| 322 | async fn perform_erase(&mut self, addr: u32, cmd: u8) { | 527 | fn perform_erase(&mut self, addr: u32, cmd: u8) { |
| 323 | let transaction = TransferConfig { | 528 | let transaction = TransferConfig { |
| 324 | iwidth: XspiWidth::SING, | 529 | iwidth: XspiWidth::SING, |
| 325 | adwidth: XspiWidth::SING, | 530 | adwidth: XspiWidth::SING, |
| 326 | adsize: AddressSize::_24bit, | 531 | adsize: AddressSize::_32bit, |
| 327 | dwidth: XspiWidth::NONE, | 532 | dwidth: XspiWidth::NONE, |
| 328 | instruction: Some(cmd as u32), | 533 | instruction: Some(cmd as u32), |
| 329 | address: Some(addr), | 534 | address: Some(addr), |
| 330 | dummy: DummyCycles::_0, | 535 | dummy: DummyCycles::_0, |
| 331 | ..Default::default() | 536 | ..Default::default() |
| 332 | }; | 537 | }; |
| 333 | self.enable_write().await; | 538 | self.enable_write(); |
| 334 | self.xspi.blocking_command(&transaction).unwrap(); | 539 | self.xspi.blocking_command(&transaction).unwrap(); |
| 335 | self.wait_write_finish(); | 540 | self.wait_write_finish(); |
| 336 | } | 541 | } |
| 337 | 542 | ||
| 338 | pub async fn erase_sector(&mut self, addr: u32) { | 543 | pub fn erase_sector(&mut self, addr: u32) { |
| 339 | self.perform_erase(addr, CMD_SECTOR_ERASE).await; | 544 | self.perform_erase(addr, SpiCommand::SectorErase4B as u8); |
| 340 | } | 545 | } |
| 341 | 546 | ||
| 342 | pub async fn erase_block_32k(&mut self, addr: u32) { | 547 | pub fn erase_block_64k(&mut self, addr: u32) { |
| 343 | self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await; | 548 | self.perform_erase(addr, SpiCommand::BlockErase4B as u8); |
| 344 | } | 549 | } |
| 345 | 550 | ||
| 346 | pub async fn erase_block_64k(&mut self, addr: u32) { | 551 | pub fn erase_chip(&mut self) { |
| 347 | self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await; | 552 | self.enable_write(); |
| 348 | } | 553 | self.exec_command(SpiCommand::ChipErase as u8); |
| 349 | 554 | self.wait_write_finish(); | |
| 350 | pub async fn erase_chip(&mut self) { | ||
| 351 | self.exec_command(CMD_CHIP_ERASE).await; | ||
| 352 | } | 555 | } |
| 353 | 556 | ||
| 354 | async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { | 557 | fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize) { |
| 355 | assert!( | 558 | assert!( |
| 356 | (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, | 559 | (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, |
| 357 | "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", | 560 | "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", |
| @@ -361,48 +564,43 @@ impl<I: Instance> FlashMemory<I> { | |||
| 361 | 564 | ||
| 362 | let transaction = TransferConfig { | 565 | let transaction = TransferConfig { |
| 363 | iwidth: XspiWidth::SING, | 566 | iwidth: XspiWidth::SING, |
| 364 | adsize: AddressSize::_24bit, | 567 | adsize: AddressSize::_32bit, |
| 365 | adwidth: XspiWidth::SING, | 568 | adwidth: XspiWidth::SING, |
| 366 | dwidth: XspiWidth::SING, | 569 | dwidth: XspiWidth::SING, |
| 367 | instruction: Some(CMD_WRITE_PG as u32), | 570 | instruction: Some(SpiCommand::PageProgram4B as u32), |
| 368 | // dwidth: XspiWidth::QUAD, | ||
| 369 | // instruction: Some(CMD_QUAD_WRITE_PG as u32), | ||
| 370 | address: Some(addr), | 571 | address: Some(addr), |
| 371 | dummy: DummyCycles::_0, | 572 | dummy: DummyCycles::_0, |
| 372 | ..Default::default() | 573 | ..Default::default() |
| 373 | }; | 574 | }; |
| 374 | self.enable_write().await; | 575 | self.enable_write(); |
| 375 | if use_dma { | 576 | self.xspi.blocking_write(buffer, transaction).unwrap(); |
| 376 | self.xspi.blocking_write(buffer, transaction).unwrap(); | ||
| 377 | } else { | ||
| 378 | self.xspi.blocking_write(buffer, transaction).unwrap(); | ||
| 379 | } | ||
| 380 | self.wait_write_finish(); | 577 | self.wait_write_finish(); |
| 381 | } | 578 | } |
| 382 | 579 | ||
| 383 | pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { | 580 | pub fn write_memory(&mut self, addr: u32, buffer: &[u8]) { |
| 384 | let mut left = buffer.len(); | 581 | let mut left = buffer.len(); |
| 385 | let mut place = addr; | 582 | let mut place = addr; |
| 386 | let mut chunk_start = 0; | 583 | let mut chunk_start = 0; |
| 387 | 584 | ||
| 388 | while left > 0 { | 585 | while left > 0 { |
| 389 | let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; | 586 | let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; |
| 390 | let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; | 587 | let chunk_size = min(max_chunk_size, left); |
| 391 | let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; | 588 | let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; |
| 392 | self.write_page(place, chunk, chunk_size, use_dma).await; | 589 | self.write_page(place, chunk, chunk_size); |
| 393 | place += chunk_size as u32; | 590 | place += chunk_size as u32; |
| 394 | left -= chunk_size; | 591 | left -= chunk_size; |
| 395 | chunk_start += chunk_size; | 592 | chunk_start += chunk_size; |
| 396 | } | 593 | } |
| 397 | } | 594 | } |
| 398 | 595 | ||
| 596 | // Note: read_register cannot be used to read the configuration register 2 since there is an | ||
| 597 | // address required for that read. | ||
| 399 | fn read_register(&mut self, cmd: u8) -> u8 { | 598 | fn read_register(&mut self, cmd: u8) -> u8 { |
| 400 | let mut buffer = [0; 1]; | 599 | let mut buffer = [0; 1]; |
| 401 | let transaction: TransferConfig = TransferConfig { | 600 | let transaction: TransferConfig = TransferConfig { |
| 402 | iwidth: XspiWidth::SING, | 601 | iwidth: XspiWidth::SING, |
| 403 | isize: AddressSize::_8bit, | 602 | isize: AddressSize::_8bit, |
| 404 | adwidth: XspiWidth::NONE, | 603 | adwidth: XspiWidth::NONE, |
| 405 | adsize: AddressSize::_24bit, | ||
| 406 | dwidth: XspiWidth::SING, | 604 | dwidth: XspiWidth::SING, |
| 407 | instruction: Some(cmd as u32), | 605 | instruction: Some(cmd as u32), |
| 408 | address: None, | 606 | address: None, |
| @@ -410,39 +608,345 @@ impl<I: Instance> FlashMemory<I> { | |||
| 410 | ..Default::default() | 608 | ..Default::default() |
| 411 | }; | 609 | }; |
| 412 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); | 610 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); |
| 413 | // info!("Read w25q64 register: 0x{:x}", buffer[0]); | ||
| 414 | buffer[0] | 611 | buffer[0] |
| 415 | } | 612 | } |
| 416 | 613 | ||
| 417 | fn write_register(&mut self, cmd: u8, value: u8) { | 614 | pub fn read_sr(&mut self) -> u8 { |
| 418 | let buffer = [value; 1]; | 615 | self.read_register(SpiCommand::ReadStatusRegister as u8) |
| 616 | } | ||
| 617 | |||
| 618 | pub fn read_cr(&mut self) -> u8 { | ||
| 619 | self.read_register(SpiCommand::ReadConfigurationRegister as u8) | ||
| 620 | } | ||
| 621 | |||
| 622 | pub fn write_sr_cr(&mut self, sr: u8, cr: u8) { | ||
| 623 | let buffer = [sr, cr]; | ||
| 419 | let transaction: TransferConfig = TransferConfig { | 624 | let transaction: TransferConfig = TransferConfig { |
| 420 | iwidth: XspiWidth::SING, | 625 | iwidth: XspiWidth::SING, |
| 421 | isize: AddressSize::_8bit, | 626 | isize: AddressSize::_8bit, |
| 422 | instruction: Some(cmd as u32), | 627 | instruction: Some(SpiCommand::WriteStatusConfigurationRegister as u32), |
| 423 | adsize: AddressSize::_24bit, | ||
| 424 | adwidth: XspiWidth::NONE, | 628 | adwidth: XspiWidth::NONE, |
| 425 | dwidth: XspiWidth::SING, | 629 | dwidth: XspiWidth::SING, |
| 426 | address: None, | 630 | address: None, |
| 427 | dummy: DummyCycles::_0, | 631 | dummy: DummyCycles::_0, |
| 428 | ..Default::default() | 632 | ..Default::default() |
| 429 | }; | 633 | }; |
| 634 | self.enable_write(); | ||
| 635 | self.xspi.blocking_write(&buffer, transaction).unwrap(); | ||
| 636 | self.wait_write_finish(); | ||
| 637 | } | ||
| 638 | |||
| 639 | pub fn read_cr2(&mut self, address: u32) -> u8 { | ||
| 640 | let mut buffer = [0; 1]; | ||
| 641 | let transaction: TransferConfig = TransferConfig { | ||
| 642 | iwidth: XspiWidth::SING, | ||
| 643 | isize: AddressSize::_8bit, | ||
| 644 | instruction: Some(SpiCommand::ReadConfigurationRegister2 as u32), | ||
| 645 | adsize: AddressSize::_32bit, | ||
| 646 | adwidth: XspiWidth::SING, | ||
| 647 | dwidth: XspiWidth::SING, | ||
| 648 | address: Some(address), | ||
| 649 | dummy: DummyCycles::_0, | ||
| 650 | ..Default::default() | ||
| 651 | }; | ||
| 652 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 653 | buffer[0] | ||
| 654 | } | ||
| 655 | |||
| 656 | pub fn write_cr2(&mut self, address: u32, value: u8) { | ||
| 657 | let buffer = [value; 1]; | ||
| 658 | let transaction: TransferConfig = TransferConfig { | ||
| 659 | iwidth: XspiWidth::SING, | ||
| 660 | isize: AddressSize::_8bit, | ||
| 661 | instruction: Some(SpiCommand::WriteConfigurationRegister2 as u32), | ||
| 662 | adsize: AddressSize::_32bit, | ||
| 663 | adwidth: XspiWidth::SING, | ||
| 664 | dwidth: XspiWidth::SING, | ||
| 665 | address: Some(address), | ||
| 666 | dummy: DummyCycles::_0, | ||
| 667 | ..Default::default() | ||
| 668 | }; | ||
| 430 | self.xspi.blocking_write(&buffer, transaction).unwrap(); | 669 | self.xspi.blocking_write(&buffer, transaction).unwrap(); |
| 670 | self.wait_write_finish(); | ||
| 671 | } | ||
| 672 | } | ||
| 673 | |||
| 674 | impl<I: Instance> OpiFlashMemory<I> { | ||
| 675 | pub fn into_spi(mut self) -> SpiFlashMemory<I> { | ||
| 676 | self.disable_opi_mode(); | ||
| 677 | SpiFlashMemory { xspi: self.xspi } | ||
| 678 | } | ||
| 679 | |||
| 680 | /// Disable OPI mode and return to SPI | ||
| 681 | pub fn disable_opi_mode(&mut self) { | ||
| 682 | // Clear SOPI and DOPI bits in CR2 volatile register | ||
| 683 | let cr2_0 = self.read_cr2(0x00000000); | ||
| 684 | self.write_cr2(0x00000000, cr2_0 & 0xFC); // Clear bits 0 and 1 | ||
| 685 | } | ||
| 686 | |||
| 687 | /// Enable memory-mapped mode for OPI | ||
| 688 | pub fn enable_mm(&mut self) { | ||
| 689 | let read_config = TransferConfig { | ||
| 690 | iwidth: XspiWidth::OCTO, | ||
| 691 | isize: AddressSize::_16bit, // 2-byte command for OPI | ||
| 692 | adwidth: XspiWidth::OCTO, | ||
| 693 | adsize: AddressSize::_32bit, | ||
| 694 | dwidth: XspiWidth::OCTO, | ||
| 695 | instruction: Some(OpiCommand::OctaRead as u32), | ||
| 696 | dummy: DummyCycles::_20, // Default dummy cycles for OPI | ||
| 697 | ..Default::default() | ||
| 698 | }; | ||
| 699 | |||
| 700 | let write_config = TransferConfig { | ||
| 701 | iwidth: XspiWidth::OCTO, | ||
| 702 | isize: AddressSize::_16bit, | ||
| 703 | adwidth: XspiWidth::OCTO, | ||
| 704 | adsize: AddressSize::_32bit, | ||
| 705 | dwidth: XspiWidth::OCTO, | ||
| 706 | instruction: Some(OpiCommand::PageProgram4B as u32), | ||
| 707 | dummy: DummyCycles::_0, | ||
| 708 | ..Default::default() | ||
| 709 | }; | ||
| 710 | |||
| 711 | self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap(); | ||
| 712 | } | ||
| 713 | |||
| 714 | pub fn disable_mm(&mut self) { | ||
| 715 | self.xspi.disable_memory_mapped_mode(); | ||
| 716 | } | ||
| 717 | |||
| 718 | /// Execute OPI command (2-byte command) | ||
| 719 | fn exec_command(&mut self, cmd: OpiCommand) { | ||
| 720 | let transaction = TransferConfig { | ||
| 721 | iwidth: XspiWidth::OCTO, | ||
| 722 | isize: AddressSize::_16bit, // 2-byte command | ||
| 723 | adwidth: XspiWidth::NONE, | ||
| 724 | dwidth: XspiWidth::NONE, | ||
| 725 | instruction: Some(cmd as u32), | ||
| 726 | address: None, | ||
| 727 | dummy: DummyCycles::_0, | ||
| 728 | ..Default::default() | ||
| 729 | }; | ||
| 730 | self.xspi.blocking_command(&transaction).unwrap(); | ||
| 731 | } | ||
| 732 | |||
| 733 | /// Reset memory using OPI commands | ||
| 734 | pub fn reset_memory(&mut self) { | ||
| 735 | self.exec_command(OpiCommand::ResetEnable); | ||
| 736 | self.exec_command(OpiCommand::ResetMemory); | ||
| 737 | self.wait_write_finish(); | ||
| 431 | } | 738 | } |
| 432 | 739 | ||
| 740 | /// Enable write using OPI command | ||
| 741 | pub fn enable_write(&mut self) { | ||
| 742 | self.exec_command(OpiCommand::WriteEnable); | ||
| 743 | } | ||
| 744 | |||
| 745 | /// Read device ID in OPI mode | ||
| 746 | pub fn read_id(&mut self) -> [u8; 3] { | ||
| 747 | let mut buffer = [0; 3]; | ||
| 748 | let transaction = TransferConfig { | ||
| 749 | iwidth: XspiWidth::OCTO, | ||
| 750 | isize: AddressSize::_16bit, | ||
| 751 | adwidth: XspiWidth::OCTO, | ||
| 752 | adsize: AddressSize::_32bit, | ||
| 753 | dwidth: XspiWidth::OCTO, | ||
| 754 | instruction: Some(OpiCommand::ReadIdentification as u32), | ||
| 755 | address: Some(0x00000000), // Dummy address required | ||
| 756 | dummy: DummyCycles::_4, | ||
| 757 | ..Default::default() | ||
| 758 | }; | ||
| 759 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 760 | buffer | ||
| 761 | } | ||
| 762 | |||
| 763 | /// Read memory using OPI mode | ||
| 764 | pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8]) { | ||
| 765 | let transaction = TransferConfig { | ||
| 766 | iwidth: XspiWidth::OCTO, | ||
| 767 | isize: AddressSize::_16bit, | ||
| 768 | adwidth: XspiWidth::OCTO, | ||
| 769 | adsize: AddressSize::_32bit, | ||
| 770 | dwidth: XspiWidth::OCTO, | ||
| 771 | instruction: Some(OpiCommand::OctaRead as u32), | ||
| 772 | address: Some(addr), | ||
| 773 | dummy: DummyCycles::_20, // Default for 200MHz operation | ||
| 774 | ..Default::default() | ||
| 775 | }; | ||
| 776 | self.xspi.blocking_read(buffer, transaction).unwrap(); | ||
| 777 | } | ||
| 778 | |||
| 779 | /// Wait for write completion using OPI status read | ||
| 780 | fn wait_write_finish(&mut self) { | ||
| 781 | while (self.read_sr() & 0x01) != 0 {} | ||
| 782 | } | ||
| 783 | |||
| 784 | /// Perform erase operation using OPI command | ||
| 785 | fn perform_erase(&mut self, addr: u32, cmd: OpiCommand) { | ||
| 786 | let transaction = TransferConfig { | ||
| 787 | iwidth: XspiWidth::OCTO, | ||
| 788 | isize: AddressSize::_16bit, | ||
| 789 | adwidth: XspiWidth::OCTO, | ||
| 790 | adsize: AddressSize::_32bit, | ||
| 791 | dwidth: XspiWidth::NONE, | ||
| 792 | instruction: Some(cmd as u32), | ||
| 793 | address: Some(addr), | ||
| 794 | dummy: DummyCycles::_0, | ||
| 795 | ..Default::default() | ||
| 796 | }; | ||
| 797 | self.enable_write(); | ||
| 798 | self.xspi.blocking_command(&transaction).unwrap(); | ||
| 799 | self.wait_write_finish(); | ||
| 800 | } | ||
| 801 | |||
| 802 | /// Erase 4KB sector using OPI | ||
| 803 | pub fn erase_sector(&mut self, addr: u32) { | ||
| 804 | self.perform_erase(addr, OpiCommand::SectorErase4B); | ||
| 805 | } | ||
| 806 | |||
| 807 | /// Erase 64KB block using OPI | ||
| 808 | pub fn erase_block_64k(&mut self, addr: u32) { | ||
| 809 | self.perform_erase(addr, OpiCommand::BlockErase4B); | ||
| 810 | } | ||
| 811 | |||
| 812 | /// Erase entire chip using OPI | ||
| 813 | pub fn erase_chip(&mut self) { | ||
| 814 | self.enable_write(); | ||
| 815 | self.exec_command(OpiCommand::ChipErase); | ||
| 816 | self.wait_write_finish(); | ||
| 817 | } | ||
| 818 | |||
| 819 | /// Write single page using OPI | ||
| 820 | fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize) { | ||
| 821 | assert!( | ||
| 822 | (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, | ||
| 823 | "write_page(): page write length exceeds page boundary (len = {}, addr = {:X})", | ||
| 824 | len, | ||
| 825 | addr | ||
| 826 | ); | ||
| 827 | |||
| 828 | let transaction = TransferConfig { | ||
| 829 | iwidth: XspiWidth::OCTO, | ||
| 830 | isize: AddressSize::_16bit, | ||
| 831 | adwidth: XspiWidth::OCTO, | ||
| 832 | adsize: AddressSize::_32bit, | ||
| 833 | dwidth: XspiWidth::OCTO, | ||
| 834 | instruction: Some(OpiCommand::PageProgram4B as u32), | ||
| 835 | address: Some(addr), | ||
| 836 | dummy: DummyCycles::_0, | ||
| 837 | ..Default::default() | ||
| 838 | }; | ||
| 839 | self.enable_write(); | ||
| 840 | self.xspi.blocking_write(buffer, transaction).unwrap(); | ||
| 841 | self.wait_write_finish(); | ||
| 842 | } | ||
| 843 | |||
| 844 | /// Write memory using OPI (handles page boundaries) | ||
| 845 | pub fn write_memory(&mut self, addr: u32, buffer: &[u8]) { | ||
| 846 | let mut left = buffer.len(); | ||
| 847 | let mut place = addr; | ||
| 848 | let mut chunk_start = 0; | ||
| 849 | |||
| 850 | while left > 0 { | ||
| 851 | let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; | ||
| 852 | let chunk_size = min(max_chunk_size, left); | ||
| 853 | let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; | ||
| 854 | self.write_page(place, chunk, chunk_size); | ||
| 855 | place += chunk_size as u32; | ||
| 856 | left -= chunk_size; | ||
| 857 | chunk_start += chunk_size; | ||
| 858 | } | ||
| 859 | } | ||
| 860 | |||
| 861 | /// Read register using OPI mode | ||
| 862 | fn read_register(&mut self, cmd: OpiCommand, dummy_addr: u32, dummy_cycles: DummyCycles) -> u8 { | ||
| 863 | let mut buffer = [0; 1]; | ||
| 864 | let transaction = TransferConfig { | ||
| 865 | iwidth: XspiWidth::OCTO, | ||
| 866 | isize: AddressSize::_16bit, | ||
| 867 | adwidth: XspiWidth::OCTO, | ||
| 868 | adsize: AddressSize::_32bit, | ||
| 869 | dwidth: XspiWidth::OCTO, | ||
| 870 | instruction: Some(cmd as u32), | ||
| 871 | address: Some(dummy_addr), | ||
| 872 | dummy: dummy_cycles, | ||
| 873 | ..Default::default() | ||
| 874 | }; | ||
| 875 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 876 | buffer[0] | ||
| 877 | } | ||
| 878 | |||
| 879 | /// Read Status Register using OPI | ||
| 433 | pub fn read_sr(&mut self) -> u8 { | 880 | pub fn read_sr(&mut self) -> u8 { |
| 434 | self.read_register(CMD_READ_SR) | 881 | self.read_register( |
| 882 | OpiCommand::ReadStatusRegister, | ||
| 883 | 0x00000000, // Dummy address | ||
| 884 | DummyCycles::_4, | ||
| 885 | ) | ||
| 435 | } | 886 | } |
| 436 | 887 | ||
| 888 | /// Read Configuration Register using OPI | ||
| 437 | pub fn read_cr(&mut self) -> u8 { | 889 | pub fn read_cr(&mut self) -> u8 { |
| 438 | self.read_register(CMD_READ_CR) | 890 | self.read_register( |
| 891 | OpiCommand::ReadConfigurationRegister, | ||
| 892 | 0x00000001, // Address for CR | ||
| 893 | DummyCycles::_4, | ||
| 894 | ) | ||
| 439 | } | 895 | } |
| 440 | 896 | ||
| 441 | pub fn write_sr(&mut self, value: u8) { | 897 | /// Write Status/Configuration Register using OPI |
| 442 | self.write_register(CMD_WRITE_SR, value); | 898 | pub fn write_sr_cr(&mut self, sr: u8, cr: u8) { |
| 899 | let transaction = TransferConfig { | ||
| 900 | iwidth: XspiWidth::OCTO, | ||
| 901 | isize: AddressSize::_16bit, | ||
| 902 | adwidth: XspiWidth::OCTO, | ||
| 903 | adsize: AddressSize::_32bit, | ||
| 904 | dwidth: XspiWidth::OCTO, | ||
| 905 | instruction: Some(OpiCommand::WriteStatusConfigurationRegister as u32), | ||
| 906 | address: Some(0x00000000), | ||
| 907 | dummy: DummyCycles::_0, | ||
| 908 | ..Default::default() | ||
| 909 | }; | ||
| 910 | |||
| 911 | self.enable_write(); | ||
| 912 | self.xspi.blocking_write(&[sr, cr], transaction).unwrap(); | ||
| 913 | self.wait_write_finish(); | ||
| 914 | } | ||
| 915 | |||
| 916 | /// Read Configuration Register 2 using OPI | ||
| 917 | pub fn read_cr2(&mut self, address: u32) -> u8 { | ||
| 918 | let mut buffer = [0; 1]; | ||
| 919 | let transaction = TransferConfig { | ||
| 920 | iwidth: XspiWidth::OCTO, | ||
| 921 | isize: AddressSize::_16bit, | ||
| 922 | adwidth: XspiWidth::OCTO, | ||
| 923 | adsize: AddressSize::_32bit, | ||
| 924 | dwidth: XspiWidth::OCTO, | ||
| 925 | instruction: Some(OpiCommand::ReadConfigurationRegister2 as u32), | ||
| 926 | address: Some(address), | ||
| 927 | dummy: DummyCycles::_4, | ||
| 928 | ..Default::default() | ||
| 929 | }; | ||
| 930 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 931 | buffer[0] | ||
| 443 | } | 932 | } |
| 444 | 933 | ||
| 445 | pub fn write_cr(&mut self, value: u8) { | 934 | /// Write Configuration Register 2 using OPI |
| 446 | self.write_register(CMD_WRITE_CR, value); | 935 | pub fn write_cr2(&mut self, address: u32, value: u8) { |
| 936 | let transaction = TransferConfig { | ||
| 937 | iwidth: XspiWidth::OCTO, | ||
| 938 | isize: AddressSize::_16bit, | ||
| 939 | adwidth: XspiWidth::OCTO, | ||
| 940 | adsize: AddressSize::_32bit, | ||
| 941 | dwidth: XspiWidth::OCTO, | ||
| 942 | instruction: Some(OpiCommand::WriteConfigurationRegister2 as u32), | ||
| 943 | address: Some(address), | ||
| 944 | dummy: DummyCycles::_0, | ||
| 945 | ..Default::default() | ||
| 946 | }; | ||
| 947 | |||
| 948 | self.enable_write(); | ||
| 949 | self.xspi.blocking_write(&[value], transaction).unwrap(); | ||
| 950 | self.wait_write_finish(); | ||
| 447 | } | 951 | } |
| 448 | } | 952 | } |
diff --git a/examples/stm32l0/src/bin/dds.rs b/examples/stm32l0/src/bin/dds.rs index a54b28a93..eaa7a61a8 100644 --- a/examples/stm32l0/src/bin/dds.rs +++ b/examples/stm32l0/src/bin/dds.rs | |||
| @@ -11,7 +11,7 @@ use embassy_stm32::rcc::*; | |||
| 11 | use embassy_stm32::time::hz; | 11 | use embassy_stm32::time::hz; |
| 12 | use embassy_stm32::timer::low_level::{Timer as LLTimer, *}; | 12 | use embassy_stm32::timer::low_level::{Timer as LLTimer, *}; |
| 13 | use embassy_stm32::timer::simple_pwm::PwmPin; | 13 | use embassy_stm32::timer::simple_pwm::PwmPin; |
| 14 | use embassy_stm32::timer::Channel; | 14 | use embassy_stm32::timer::{Ch3, Channel}; |
| 15 | use embassy_stm32::{interrupt, pac, Config}; | 15 | use embassy_stm32::{interrupt, pac, Config}; |
| 16 | use panic_probe as _; | 16 | use panic_probe as _; |
| 17 | 17 | ||
| @@ -70,7 +70,7 @@ async fn main(_spawner: Spawner) { | |||
| 70 | let p = embassy_stm32::init(config); | 70 | let p = embassy_stm32::init(config); |
| 71 | 71 | ||
| 72 | // setup PWM pin in AF mode | 72 | // setup PWM pin in AF mode |
| 73 | let _ch3 = PwmPin::new_ch3(p.PA2, OutputType::PushPull); | 73 | let _ch3 = PwmPin::<_, Ch3>::new(p.PA2, OutputType::PushPull); |
| 74 | 74 | ||
| 75 | // initialize timer | 75 | // initialize timer |
| 76 | // we cannot use SimplePWM here because the Time is privately encapsulated | 76 | // we cannot use SimplePWM here because the Time is privately encapsulated |
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index a0a7916a7..5d5d401f6 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml | |||
| @@ -9,7 +9,7 @@ embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["de | |||
| 9 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 9 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 10 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } | 11 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } |
| 12 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 14 | 14 | ||
| 15 | defmt = "1.0.1" | 15 | defmt = "1.0.1" |
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 7da09e5b0..a6b4efcf8 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml | |||
| @@ -10,8 +10,8 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ | |||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } |
| 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } |
| 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-net-adin1110 = { version = "0.3.0", path = "../../embassy-net-adin1110" } | 15 | embassy-net-adin1110 = { version = "0.3.0", path = "../../embassy-net-adin1110" } |
| 16 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } | 16 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } |
| 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 354ac90b2..dc90a3b85 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs | |||
| @@ -59,7 +59,7 @@ pub type SpeSpiCs = ExclusiveDevice<SpeSpi, Output<'static>, Delay>; | |||
| 59 | pub type SpeInt = exti::ExtiInput<'static>; | 59 | pub type SpeInt = exti::ExtiInput<'static>; |
| 60 | pub type SpeRst = Output<'static>; | 60 | pub type SpeRst = Output<'static>; |
| 61 | pub type Adin1110T = ADIN1110<SpeSpiCs>; | 61 | pub type Adin1110T = ADIN1110<SpeSpiCs>; |
| 62 | pub type TempSensI2c = I2c<'static, Async>; | 62 | pub type TempSensI2c = I2c<'static, Async, i2c::Master>; |
| 63 | 63 | ||
| 64 | static TEMP: AtomicI32 = AtomicI32::new(0); | 64 | static TEMP: AtomicI32 = AtomicI32::new(0); |
| 65 | 65 | ||
diff --git a/examples/stm32l432/src/bin/qspi_mmap.rs b/examples/stm32l432/src/bin/qspi_mmap.rs index 86a20eb3d..075458fe5 100644 --- a/examples/stm32l432/src/bin/qspi_mmap.rs +++ b/examples/stm32l432/src/bin/qspi_mmap.rs | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | use defmt::info; | 7 | use defmt::info; |
| 8 | use embassy_stm32::mode; | 8 | use embassy_stm32::mode; |
| 9 | use embassy_stm32::qspi::enums::{ | 9 | use embassy_stm32::qspi::enums::{ |
| 10 | AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth, | 10 | AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth, SampleShifting, |
| 11 | }; | 11 | }; |
| 12 | use embassy_stm32::qspi::{self, Instance, TransferConfig}; | 12 | use embassy_stm32::qspi::{self, Instance, TransferConfig}; |
| 13 | pub struct FlashMemory<I: Instance> { | 13 | pub struct FlashMemory<I: Instance> { |
| @@ -252,6 +252,7 @@ async fn main(_spawner: Spawner) { | |||
| 252 | prescaler: 200, | 252 | prescaler: 200, |
| 253 | cs_high_time: ChipSelectHighTime::_1Cycle, | 253 | cs_high_time: ChipSelectHighTime::_1Cycle, |
| 254 | fifo_threshold: FIFOThresholdLevel::_16Bytes, | 254 | fifo_threshold: FIFOThresholdLevel::_16Bytes, |
| 255 | sample_shifting: SampleShifting::None, | ||
| 255 | }; | 256 | }; |
| 256 | 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); |
| 257 | let mut flash = FlashMemory::new(driver); | 258 | let mut flash = FlashMemory::new(driver); |
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 3ea3bcd5c..1379d963c 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml | |||
| @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ | |||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 14 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } |
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 16 | usbd-hid = "0.8.1" | 16 | usbd-hid = "0.8.1" |
diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 3aa45dc79..612d12ac2 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml | |||
| @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ | |||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } | 13 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 15 | 15 | ||
| 16 | defmt = "1.0.1" | 16 | defmt = "1.0.1" |
diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 777d3ed4c..f92e85852 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml | |||
| @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [" | |||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 15 | 15 | ||
| 16 | defmt = "1.0.1" | 16 | defmt = "1.0.1" |
diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index 041dc0cf5..9864fa026 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs | |||
| @@ -27,6 +27,7 @@ use embassy_stm32_wpan::hci::vendor::event::{self, AttributeHandle, VendorEvent} | |||
| 27 | use embassy_stm32_wpan::hci::{BdAddr, Event}; | 27 | use embassy_stm32_wpan::hci::{BdAddr, Event}; |
| 28 | use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; | 28 | use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; |
| 29 | use embassy_stm32_wpan::sub::ble::Ble; | 29 | use embassy_stm32_wpan::sub::ble::Ble; |
| 30 | use embassy_stm32_wpan::sub::mm; | ||
| 30 | use embassy_stm32_wpan::TlMbox; | 31 | use embassy_stm32_wpan::TlMbox; |
| 31 | use {defmt_rtt as _, panic_probe as _}; | 32 | use {defmt_rtt as _, panic_probe as _}; |
| 32 | 33 | ||
| @@ -38,7 +39,7 @@ bind_interrupts!(struct Irqs{ | |||
| 38 | const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; | 39 | const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; |
| 39 | 40 | ||
| 40 | #[embassy_executor::main] | 41 | #[embassy_executor::main] |
| 41 | async fn main(_spawner: Spawner) { | 42 | async fn main(spawner: Spawner) { |
| 42 | /* | 43 | /* |
| 43 | How to make this work: | 44 | How to make this work: |
| 44 | 45 | ||
| @@ -70,6 +71,7 @@ async fn main(_spawner: Spawner) { | |||
| 70 | let config = Config::default(); | 71 | let config = Config::default(); |
| 71 | let mut mbox = TlMbox::init(p.IPCC, Irqs, config); | 72 | let mut mbox = TlMbox::init(p.IPCC, Irqs, config); |
| 72 | 73 | ||
| 74 | spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); | ||
| 73 | let sys_event = mbox.sys_subsystem.read().await; | 75 | let sys_event = mbox.sys_subsystem.read().await; |
| 74 | info!("sys event: {}", sys_event.payload()); | 76 | info!("sys event: {}", sys_event.payload()); |
| 75 | 77 | ||
| @@ -221,6 +223,11 @@ async fn main(_spawner: Spawner) { | |||
| 221 | } | 223 | } |
| 222 | } | 224 | } |
| 223 | 225 | ||
| 226 | #[embassy_executor::task] | ||
| 227 | async fn run_mm_queue(memory_manager: mm::MemoryManager) { | ||
| 228 | memory_manager.run_queue().await; | ||
| 229 | } | ||
| 230 | |||
| 224 | fn get_bd_addr() -> BdAddr { | 231 | fn get_bd_addr() -> BdAddr { |
| 225 | let mut bytes = [0u8; 6]; | 232 | let mut bytes = [0u8; 6]; |
| 226 | 233 | ||
diff --git a/examples/stm32wba/src/bin/adc.rs b/examples/stm32wba/src/bin/adc.rs new file mode 100644 index 000000000..a9651d57e --- /dev/null +++ b/examples/stm32wba/src/bin/adc.rs | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_stm32::adc::{adc4, AdcChannel}; | ||
| 6 | use {defmt_rtt as _, panic_probe as _}; | ||
| 7 | |||
| 8 | #[embassy_executor::main] | ||
| 9 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 10 | let config = embassy_stm32::Config::default(); | ||
| 11 | |||
| 12 | let mut p = embassy_stm32::init(config); | ||
| 13 | |||
| 14 | // **** ADC4 init **** | ||
| 15 | let mut adc4 = adc4::Adc4::new(p.ADC4); | ||
| 16 | let mut adc4_pin1 = p.PA0; // A4 | ||
| 17 | let mut adc4_pin2 = p.PA1; // A5 | ||
| 18 | adc4.set_resolution(adc4::Resolution::BITS12); | ||
| 19 | adc4.set_averaging(adc4::Averaging::Samples256); | ||
| 20 | adc4.set_sample_time(adc4::SampleTime::CYCLES1_5); | ||
| 21 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); | ||
| 22 | |||
| 23 | // **** ADC4 blocking read **** | ||
| 24 | let raw: u16 = adc4.blocking_read(&mut adc4_pin1); | ||
| 25 | let volt: f32 = 3.0 * raw as f32 / max4 as f32; | ||
| 26 | info!("Read adc4 pin 1 {}", volt); | ||
| 27 | |||
| 28 | let raw: u16 = adc4.blocking_read(&mut adc4_pin2); | ||
| 29 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; | ||
| 30 | info!("Read adc4 pin 2 {}", volt); | ||
| 31 | |||
| 32 | // **** ADC4 async read **** | ||
| 33 | let mut degraded41 = adc4_pin1.degrade_adc(); | ||
| 34 | let mut degraded42 = adc4_pin2.degrade_adc(); | ||
| 35 | let mut measurements = [0u16; 2]; | ||
| 36 | |||
| 37 | // The channels must be in ascending order and can't repeat for ADC4 | ||
| 38 | adc4.read( | ||
| 39 | p.GPDMA1_CH1.reborrow(), | ||
| 40 | [&mut degraded42, &mut degraded41].into_iter(), | ||
| 41 | &mut measurements, | ||
| 42 | ) | ||
| 43 | .await | ||
| 44 | .unwrap(); | ||
| 45 | let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; | ||
| 46 | let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; | ||
| 47 | info!("Async read 4 pin 1 {}", volt1); | ||
| 48 | info!("Async read 4 pin 2 {}", volt2); | ||
| 49 | } | ||
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 5ecd77443..1c5d9c07a 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml | |||
| @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [" | |||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = "1.0.1" | 15 | defmt = "1.0.1" |
| 16 | defmt-rtt = "1.0.0" | 16 | defmt-rtt = "1.0.0" |
diff --git a/release/bump-dependency.sh b/release/bump-dependency.sh index 07511d229..97f73cddc 100755 --- a/release/bump-dependency.sh +++ b/release/bump-dependency.sh | |||
| @@ -8,4 +8,4 @@ | |||
| 8 | # | 8 | # |
| 9 | CRATE=$1 | 9 | CRATE=$1 |
| 10 | TARGET_VER=$2 | 10 | TARGET_VER=$2 |
| 11 | find . -name "Cargo.toml" | xargs sed -rie "s/($CRATE = \{.*version = \")[0-9]+.[0-9]+.?[0-9]*(\".*)/\1$TARGET_VER\2/g" | 11 | find . -name "Cargo.toml" | xargs sed -ri "s/($CRATE = \{.*version = \")[0-9]+.[0-9]+.?[0-9]*(\".*)/\1$TARGET_VER\2/g" |
diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml index e75ea40cc..411cc6946 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | [toolchain] | 1 | [toolchain] |
| 2 | channel = "nightly-2025-03-12" | 2 | channel = "nightly-2025-06-29" |
| 3 | components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] | 3 | components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] |
| 4 | targets = [ | 4 | targets = [ |
| 5 | "thumbv7em-none-eabi", | 5 | "thumbv7em-none-eabi", |
diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 870904c3a..e24864037 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | [toolchain] | 1 | [toolchain] |
| 2 | channel = "1.85" | 2 | channel = "1.88" |
| 3 | components = [ "rust-src", "rustfmt", "llvm-tools" ] | 3 | components = [ "rust-src", "rustfmt", "llvm-tools" ] |
| 4 | targets = [ | 4 | targets = [ |
| 5 | "thumbv7em-none-eabi", | 5 | "thumbv7em-none-eabi", |
diff --git a/tests/mspm0/Cargo.toml b/tests/mspm0/Cargo.toml index 298522d09..5ba3e586b 100644 --- a/tests/mspm0/Cargo.toml +++ b/tests/mspm0/Cargo.toml | |||
| @@ -6,15 +6,17 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [features] | 7 | [features] |
| 8 | mspm0g3507 = [ "embassy-mspm0/mspm0g3507pm" ] | 8 | mspm0g3507 = [ "embassy-mspm0/mspm0g3507pm" ] |
| 9 | mspm0g3519 = [ "embassy-mspm0/mspm0g3519pz" ] | ||
| 9 | 10 | ||
| 10 | [dependencies] | 11 | [dependencies] |
| 11 | teleprobe-meta = "1.1" | 12 | teleprobe-meta = "1.1" |
| 12 | 13 | ||
| 13 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = [ "defmt" ] } | 14 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = [ "defmt" ] } |
| 14 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = [ "arch-cortex-m", "executor-thread", "defmt" ] } | 15 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = [ "arch-cortex-m", "executor-thread", "defmt" ] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 15 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt" ] } | 17 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt" ] } |
| 16 | embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = [ "rt", "defmt", "unstable-pac", "time-driver-any" ] } | 18 | embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = [ "rt", "defmt", "unstable-pac", "time-driver-any" ] } |
| 17 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal/"} | 19 | embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal/"} |
| 18 | 20 | ||
| 19 | defmt = "1.0.1" | 21 | defmt = "1.0.1" |
| 20 | defmt-rtt = "1.0.0" | 22 | defmt-rtt = "1.0.0" |
| @@ -23,6 +25,8 @@ cortex-m = { version = "0.7.6", features = [ "inline-asm", "critical-section-sin | |||
| 23 | cortex-m-rt = "0.7.0" | 25 | cortex-m-rt = "0.7.0" |
| 24 | embedded-hal = { package = "embedded-hal", version = "1.0" } | 26 | embedded-hal = { package = "embedded-hal", version = "1.0" } |
| 25 | embedded-hal-async = { version = "1.0" } | 27 | embedded-hal-async = { version = "1.0" } |
| 28 | embedded-io = { version = "0.6.1", features = ["defmt-03"] } | ||
| 29 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | ||
| 26 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | 30 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 27 | static_cell = "2" | 31 | static_cell = "2" |
| 28 | portable-atomic = { version = "1.5", features = ["critical-section"] } | 32 | portable-atomic = { version = "1.5", features = ["critical-section"] } |
diff --git a/tests/mspm0/build.rs b/tests/mspm0/build.rs index 0b58fb9e9..43a9ac04f 100644 --- a/tests/mspm0/build.rs +++ b/tests/mspm0/build.rs | |||
| @@ -8,6 +8,9 @@ fn main() -> Result<(), Box<dyn Error>> { | |||
| 8 | #[cfg(feature = "mspm0g3507")] | 8 | #[cfg(feature = "mspm0g3507")] |
| 9 | let memory_x = include_bytes!("memory_g3507.x"); | 9 | let memory_x = include_bytes!("memory_g3507.x"); |
| 10 | 10 | ||
| 11 | #[cfg(feature = "mspm0g3519")] | ||
| 12 | let memory_x = include_bytes!("memory_g3519.x"); | ||
| 13 | |||
| 11 | fs::write(out.join("memory.x"), memory_x).unwrap(); | 14 | fs::write(out.join("memory.x"), memory_x).unwrap(); |
| 12 | 15 | ||
| 13 | println!("cargo:rustc-link-search={}", out.display()); | 16 | println!("cargo:rustc-link-search={}", out.display()); |
diff --git a/tests/mspm0/memory_g3519.x b/tests/mspm0/memory_g3519.x new file mode 100644 index 000000000..d62f10360 --- /dev/null +++ b/tests/mspm0/memory_g3519.x | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 128K | ||
| 4 | /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */ | ||
| 5 | RAM : ORIGIN = 0x20200000, LENGTH = 64K | ||
| 6 | } | ||
diff --git a/tests/mspm0/src/bin/dma.rs b/tests/mspm0/src/bin/dma.rs new file mode 100644 index 000000000..6fd973a18 --- /dev/null +++ b/tests/mspm0/src/bin/dma.rs | |||
| @@ -0,0 +1,503 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | #[cfg(feature = "mspm0g3507")] | ||
| 5 | teleprobe_meta::target!(b"lp-mspm0g3507"); | ||
| 6 | |||
| 7 | #[cfg(feature = "mspm0g3519")] | ||
| 8 | teleprobe_meta::target!(b"lp-mspm0g3519"); | ||
| 9 | |||
| 10 | use core::slice; | ||
| 11 | |||
| 12 | use defmt::{assert, assert_eq, *}; | ||
| 13 | use embassy_executor::Spawner; | ||
| 14 | use embassy_mspm0::dma::{Channel, Transfer, TransferMode, TransferOptions, Word}; | ||
| 15 | use embassy_mspm0::Peri; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | let mut p = embassy_mspm0::init(Default::default()); | ||
| 21 | info!("Hello World!"); | ||
| 22 | |||
| 23 | { | ||
| 24 | info!("Single u8 read (blocking)"); | ||
| 25 | single_read(p.DMA_CH0.reborrow(), 0x41_u8); | ||
| 26 | |||
| 27 | info!("Single u16 read (blocking)"); | ||
| 28 | single_read(p.DMA_CH0.reborrow(), 0xFF41_u16); | ||
| 29 | |||
| 30 | info!("Single u32 read (blocking)"); | ||
| 31 | single_read(p.DMA_CH0.reborrow(), 0xFFEE_FF41_u32); | ||
| 32 | |||
| 33 | info!("Single u64 read (blocking)"); | ||
| 34 | single_read(p.DMA_CH0.reborrow(), 0x0011_2233_FFEE_FF41_u64); | ||
| 35 | } | ||
| 36 | |||
| 37 | // Widening transfers | ||
| 38 | { | ||
| 39 | info!("Single u8 read to u16"); | ||
| 40 | widening_single_read::<u8, u16>(p.DMA_CH0.reborrow(), 0x41); | ||
| 41 | |||
| 42 | info!("Single u8 read to u32"); | ||
| 43 | widening_single_read::<u8, u32>(p.DMA_CH0.reborrow(), 0x43); | ||
| 44 | |||
| 45 | info!("Single u8 read to u64"); | ||
| 46 | widening_single_read::<u8, u64>(p.DMA_CH0.reborrow(), 0x47); | ||
| 47 | |||
| 48 | info!("Single u16 read to u32"); | ||
| 49 | widening_single_read::<u16, u32>(p.DMA_CH0.reborrow(), 0xAE43); | ||
| 50 | |||
| 51 | info!("Single u16 read to u64"); | ||
| 52 | widening_single_read::<u16, u64>(p.DMA_CH0.reborrow(), 0xAF47); | ||
| 53 | |||
| 54 | info!("Single u32 read to u64"); | ||
| 55 | widening_single_read::<u32, u64>(p.DMA_CH0.reborrow(), 0xDEAD_AF47); | ||
| 56 | } | ||
| 57 | |||
| 58 | // Narrowing transfers. | ||
| 59 | { | ||
| 60 | info!("Single u16 read to u8"); | ||
| 61 | narrowing_single_read::<u16, u8>(p.DMA_CH0.reborrow(), 0x4142); | ||
| 62 | |||
| 63 | info!("Single u32 read to u8"); | ||
| 64 | narrowing_single_read::<u32, u8>(p.DMA_CH0.reborrow(), 0x4142_2414); | ||
| 65 | |||
| 66 | info!("Single u64 read to u8"); | ||
| 67 | narrowing_single_read::<u64, u8>(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776); | ||
| 68 | |||
| 69 | info!("Single u32 read to u16"); | ||
| 70 | narrowing_single_read::<u32, u16>(p.DMA_CH0.reborrow(), 0x4142_2414); | ||
| 71 | |||
| 72 | info!("Single u64 read to u16"); | ||
| 73 | narrowing_single_read::<u64, u16>(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776); | ||
| 74 | |||
| 75 | info!("Single u64 read to u32"); | ||
| 76 | narrowing_single_read::<u64, u32>(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776); | ||
| 77 | } | ||
| 78 | |||
| 79 | { | ||
| 80 | info!("Single u8 read (async)"); | ||
| 81 | async_single_read(p.DMA_CH0.reborrow(), 0x42_u8).await; | ||
| 82 | |||
| 83 | info!("Single u16 read (async)"); | ||
| 84 | async_single_read(p.DMA_CH0.reborrow(), 0xAE42_u16).await; | ||
| 85 | |||
| 86 | info!("Single u32 read (async)"); | ||
| 87 | async_single_read(p.DMA_CH0.reborrow(), 0xFE44_1500_u32).await; | ||
| 88 | |||
| 89 | info!("Single u64 read (async)"); | ||
| 90 | async_single_read(p.DMA_CH0.reborrow(), 0x8F7F_6F5F_4F3F_2F1F_u64).await; | ||
| 91 | } | ||
| 92 | |||
| 93 | { | ||
| 94 | info!("Multiple u8 reads (blocking)"); | ||
| 95 | block_read::<_, 16>(p.DMA_CH0.reborrow(), 0x98_u8); | ||
| 96 | |||
| 97 | info!("Multiple u16 reads (blocking)"); | ||
| 98 | block_read::<_, 2>(p.DMA_CH0.reborrow(), 0x9801_u16); | ||
| 99 | |||
| 100 | info!("Multiple u32 reads (blocking)"); | ||
| 101 | block_read::<_, 4>(p.DMA_CH0.reborrow(), 0x9821_9801_u32); | ||
| 102 | |||
| 103 | info!("Multiple u64 reads (blocking)"); | ||
| 104 | block_read::<_, 4>(p.DMA_CH0.reborrow(), 0xABCD_EF01_2345_6789_u64); | ||
| 105 | } | ||
| 106 | |||
| 107 | { | ||
| 108 | info!("Multiple u8 reads (async)"); | ||
| 109 | async_block_read::<_, 8>(p.DMA_CH0.reborrow(), 0x86_u8).await; | ||
| 110 | |||
| 111 | info!("Multiple u16 reads (async)"); | ||
| 112 | async_block_read::<_, 6>(p.DMA_CH0.reborrow(), 0x7777_u16).await; | ||
| 113 | |||
| 114 | info!("Multiple u32 reads (async)"); | ||
| 115 | async_block_read::<_, 3>(p.DMA_CH0.reborrow(), 0xA5A5_A5A5_u32).await; | ||
| 116 | |||
| 117 | info!("Multiple u64 reads (async)"); | ||
| 118 | async_block_read::<_, 14>(p.DMA_CH0.reborrow(), 0x5A5A_5A5A_A5A5_A5A5_u64).await; | ||
| 119 | } | ||
| 120 | |||
| 121 | // Intentionally skip testing multiple reads in single transfer mode. | ||
| 122 | // | ||
| 123 | // If the destination length is greater than 1 and single transfer mode is used then two transfers | ||
| 124 | // are performed in a trigger. Similarly with any other length of destination above 2, only 2 transfers | ||
| 125 | // are performed. Issuing another trigger (resume) results in no further progress. More than likely | ||
| 126 | // the test does not work due to some combination of a hardware bug and the datasheet being unclear | ||
| 127 | // regarding what ends a software trigger. | ||
| 128 | // | ||
| 129 | // However this case works fine with a hardware trigger (such as the ADC hardware trigger). | ||
| 130 | |||
| 131 | { | ||
| 132 | info!("Single u8 write (blocking)"); | ||
| 133 | single_write(p.DMA_CH0.reborrow(), 0x41_u8); | ||
| 134 | |||
| 135 | info!("Single u16 write (blocking)"); | ||
| 136 | single_write(p.DMA_CH0.reborrow(), 0x4142_u16); | ||
| 137 | |||
| 138 | info!("Single u32 write (blocking)"); | ||
| 139 | single_write(p.DMA_CH0.reborrow(), 0x4142_4344_u32); | ||
| 140 | |||
| 141 | info!("Single u64 write (blocking)"); | ||
| 142 | single_write(p.DMA_CH0.reborrow(), 0x4142_4344_4546_4748_u64); | ||
| 143 | } | ||
| 144 | |||
| 145 | { | ||
| 146 | info!("Single u8 write (async)"); | ||
| 147 | async_single_write(p.DMA_CH0.reborrow(), 0xAA_u8).await; | ||
| 148 | |||
| 149 | info!("Single u16 write (async)"); | ||
| 150 | async_single_write(p.DMA_CH0.reborrow(), 0xBBBB_u16).await; | ||
| 151 | |||
| 152 | info!("Single u32 write (async)"); | ||
| 153 | async_single_write(p.DMA_CH0.reborrow(), 0xCCCC_CCCC_u32).await; | ||
| 154 | |||
| 155 | info!("Single u64 write (async)"); | ||
| 156 | async_single_write(p.DMA_CH0.reborrow(), 0xDDDD_DDDD_DDDD_DDDD_u64).await; | ||
| 157 | } | ||
| 158 | |||
| 159 | { | ||
| 160 | info!("Multiple u8 writes (blocking)"); | ||
| 161 | block_write(p.DMA_CH0.reborrow(), &[0xFF_u8, 0x7F, 0x3F, 0x1F]); | ||
| 162 | |||
| 163 | info!("Multiple u16 writes (blocking)"); | ||
| 164 | block_write(p.DMA_CH0.reborrow(), &[0xFFFF_u16, 0xFF7F, 0xFF3F, 0xFF1F]); | ||
| 165 | |||
| 166 | info!("Multiple u32 writes (blocking)"); | ||
| 167 | block_write( | ||
| 168 | p.DMA_CH0.reborrow(), | ||
| 169 | &[0xFF00_00FF_u32, 0xFF00_007F, 0x0000_FF3F, 0xFF1F_0000], | ||
| 170 | ); | ||
| 171 | |||
| 172 | info!("Multiple u64 writes (blocking)"); | ||
| 173 | block_write( | ||
| 174 | p.DMA_CH0.reborrow(), | ||
| 175 | &[ | ||
| 176 | 0xFF00_0000_0000_00FF_u64, | ||
| 177 | 0x0000_FF00_007F_0000, | ||
| 178 | 0x0000_FF3F_0000_0000, | ||
| 179 | 0xFF1F_0000_1111_837A, | ||
| 180 | ], | ||
| 181 | ); | ||
| 182 | } | ||
| 183 | |||
| 184 | { | ||
| 185 | info!("Multiple u8 writes (async)"); | ||
| 186 | async_block_write(p.DMA_CH0.reborrow(), &[0u8, 1, 2, 3]).await; | ||
| 187 | |||
| 188 | info!("Multiple u16 writes (async)"); | ||
| 189 | async_block_write(p.DMA_CH0.reborrow(), &[0x9801u16, 0x9802, 0x9803, 0x9800, 0x9000]).await; | ||
| 190 | |||
| 191 | info!("Multiple u32 writes (async)"); | ||
| 192 | async_block_write(p.DMA_CH0.reborrow(), &[0x9801_ABCDu32, 0xFFAC_9802, 0xDEAD_9803]).await; | ||
| 193 | |||
| 194 | info!("Multiple u64 writes (async)"); | ||
| 195 | async_block_write( | ||
| 196 | p.DMA_CH0.reborrow(), | ||
| 197 | &[ | ||
| 198 | 0xA55A_1111_3333_5555_u64, | ||
| 199 | 0x1111_A55A_3333_5555, | ||
| 200 | 0x5555_A55A_3333_1111, | ||
| 201 | 0x01234_5678_89AB_CDEF, | ||
| 202 | ], | ||
| 203 | ) | ||
| 204 | .await; | ||
| 205 | } | ||
| 206 | |||
| 207 | // TODO: Mixed byte and word transfers. | ||
| 208 | |||
| 209 | info!("Test OK"); | ||
| 210 | cortex_m::asm::bkpt(); | ||
| 211 | } | ||
| 212 | |||
| 213 | fn single_read<W: Word + Copy + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, mut src: W) { | ||
| 214 | let options = TransferOptions::default(); | ||
| 215 | let mut dst = W::default(); | ||
| 216 | |||
| 217 | // SAFETY: src and dst outlive the transfer. | ||
| 218 | let transfer = unsafe { | ||
| 219 | unwrap!(Transfer::new_read( | ||
| 220 | channel.reborrow(), | ||
| 221 | Transfer::SOFTWARE_TRIGGER, | ||
| 222 | &mut src, | ||
| 223 | slice::from_mut(&mut dst), | ||
| 224 | options, | ||
| 225 | )) | ||
| 226 | }; | ||
| 227 | transfer.blocking_wait(); | ||
| 228 | |||
| 229 | assert_eq!(src, dst); | ||
| 230 | } | ||
| 231 | |||
| 232 | async fn async_single_read<W: Word + Copy + Default + Eq + defmt::Format>( | ||
| 233 | mut channel: Peri<'_, impl Channel>, | ||
| 234 | mut src: W, | ||
| 235 | ) { | ||
| 236 | let options = TransferOptions::default(); | ||
| 237 | let mut dst = W::default(); | ||
| 238 | |||
| 239 | // SAFETY: src and dst outlive the transfer. | ||
| 240 | let transfer = unsafe { | ||
| 241 | unwrap!(Transfer::new_read( | ||
| 242 | channel.reborrow(), | ||
| 243 | Transfer::SOFTWARE_TRIGGER, | ||
| 244 | &mut src, | ||
| 245 | slice::from_mut(&mut dst), | ||
| 246 | options, | ||
| 247 | )) | ||
| 248 | }; | ||
| 249 | transfer.await; | ||
| 250 | |||
| 251 | assert_eq!(src, dst); | ||
| 252 | } | ||
| 253 | |||
| 254 | fn block_read<W: Word + Copy + Default + Eq + defmt::Format, const N: usize>( | ||
| 255 | mut channel: Peri<'_, impl Channel>, | ||
| 256 | mut src: W, | ||
| 257 | ) { | ||
| 258 | let mut options = TransferOptions::default(); | ||
| 259 | // Complete the entire transfer. | ||
| 260 | options.mode = TransferMode::Block; | ||
| 261 | |||
| 262 | let mut dst = [W::default(); N]; | ||
| 263 | |||
| 264 | // SAFETY: src and dst outlive the transfer. | ||
| 265 | let transfer = unsafe { | ||
| 266 | unwrap!(Transfer::new_read( | ||
| 267 | channel.reborrow(), | ||
| 268 | Transfer::SOFTWARE_TRIGGER, | ||
| 269 | &mut src, | ||
| 270 | &mut dst[..], | ||
| 271 | options, | ||
| 272 | )) | ||
| 273 | }; | ||
| 274 | transfer.blocking_wait(); | ||
| 275 | |||
| 276 | assert_eq!(dst, [src; N]); | ||
| 277 | } | ||
| 278 | |||
| 279 | async fn async_block_read<W: Word + Copy + Default + Eq + defmt::Format, const N: usize>( | ||
| 280 | mut channel: Peri<'_, impl Channel>, | ||
| 281 | mut src: W, | ||
| 282 | ) { | ||
| 283 | let mut options = TransferOptions::default(); | ||
| 284 | // Complete the entire transfer. | ||
| 285 | options.mode = TransferMode::Block; | ||
| 286 | |||
| 287 | let mut dst = [W::default(); N]; | ||
| 288 | |||
| 289 | // SAFETY: src and dst outlive the transfer. | ||
| 290 | let transfer = unsafe { | ||
| 291 | unwrap!(Transfer::new_read( | ||
| 292 | channel.reborrow(), | ||
| 293 | Transfer::SOFTWARE_TRIGGER, | ||
| 294 | &mut src, | ||
| 295 | &mut dst[..], | ||
| 296 | options, | ||
| 297 | )) | ||
| 298 | }; | ||
| 299 | transfer.await; | ||
| 300 | |||
| 301 | assert_eq!(dst, [src; N]); | ||
| 302 | } | ||
| 303 | |||
| 304 | fn single_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: W) { | ||
| 305 | let options = TransferOptions::default(); | ||
| 306 | let mut dst = W::default(); | ||
| 307 | |||
| 308 | // SAFETY: src and dst outlive the transfer. | ||
| 309 | let transfer = unsafe { | ||
| 310 | unwrap!(Transfer::new_write( | ||
| 311 | channel.reborrow(), | ||
| 312 | Transfer::SOFTWARE_TRIGGER, | ||
| 313 | slice::from_ref(&src), | ||
| 314 | &mut dst, | ||
| 315 | options, | ||
| 316 | )) | ||
| 317 | }; | ||
| 318 | transfer.blocking_wait(); | ||
| 319 | |||
| 320 | assert_eq!(src, dst); | ||
| 321 | } | ||
| 322 | |||
| 323 | async fn async_single_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: W) { | ||
| 324 | let options = TransferOptions::default(); | ||
| 325 | let mut dst = W::default(); | ||
| 326 | |||
| 327 | // SAFETY: src and dst outlive the transfer. | ||
| 328 | let transfer = unsafe { | ||
| 329 | unwrap!(Transfer::new_write( | ||
| 330 | channel.reborrow(), | ||
| 331 | Transfer::SOFTWARE_TRIGGER, | ||
| 332 | slice::from_ref(&src), | ||
| 333 | &mut dst, | ||
| 334 | options, | ||
| 335 | )) | ||
| 336 | }; | ||
| 337 | transfer.await; | ||
| 338 | |||
| 339 | assert_eq!(src, dst); | ||
| 340 | } | ||
| 341 | |||
| 342 | fn block_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: &[W]) { | ||
| 343 | let mut options = TransferOptions::default(); | ||
| 344 | // Complete the entire transfer. | ||
| 345 | options.mode = TransferMode::Block; | ||
| 346 | |||
| 347 | let mut dst = W::default(); | ||
| 348 | |||
| 349 | // Starting from 1 because a zero length transfer does nothing. | ||
| 350 | for i in 1..src.len() { | ||
| 351 | info!("-> {} write(s)", i); | ||
| 352 | |||
| 353 | // SAFETY: src and dst outlive the transfer. | ||
| 354 | let transfer = unsafe { | ||
| 355 | unwrap!(Transfer::new_write( | ||
| 356 | channel.reborrow(), | ||
| 357 | Transfer::SOFTWARE_TRIGGER, | ||
| 358 | &src[..i], | ||
| 359 | &mut dst, | ||
| 360 | options, | ||
| 361 | )) | ||
| 362 | }; | ||
| 363 | transfer.blocking_wait(); | ||
| 364 | |||
| 365 | // The result will be the last value written. | ||
| 366 | assert_eq!(dst, src[i - 1]); | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | async fn async_block_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: &[W]) { | ||
| 371 | let mut options = TransferOptions::default(); | ||
| 372 | // Complete the entire transfer. | ||
| 373 | options.mode = TransferMode::Block; | ||
| 374 | |||
| 375 | let mut dst = W::default(); | ||
| 376 | |||
| 377 | // Starting from 1 because a zero length transfer does nothing. | ||
| 378 | for i in 1..src.len() { | ||
| 379 | info!("-> {} write(s)", i); | ||
| 380 | // SAFETY: src and dst outlive the transfer. | ||
| 381 | let transfer = unsafe { | ||
| 382 | unwrap!(Transfer::new_write( | ||
| 383 | channel.reborrow(), | ||
| 384 | Transfer::SOFTWARE_TRIGGER, | ||
| 385 | &src[..i], | ||
| 386 | &mut dst, | ||
| 387 | options, | ||
| 388 | )) | ||
| 389 | }; | ||
| 390 | transfer.await; | ||
| 391 | |||
| 392 | // The result will be the last value written. | ||
| 393 | assert_eq!(dst, src[i - 1]); | ||
| 394 | } | ||
| 395 | } | ||
| 396 | |||
| 397 | /// [`single_read`], but testing when the destination is wider than the source. | ||
| 398 | /// | ||
| 399 | /// The MSPM0 DMA states that the upper bytes when the destination is longer than the source are zeroed. | ||
| 400 | /// This matches the behavior in Rust for all unsigned integer types. | ||
| 401 | fn widening_single_read<SW, DW>(mut channel: Peri<'_, impl Channel>, mut src: SW) | ||
| 402 | where | ||
| 403 | SW: Word + Copy + Default + Eq + defmt::Format, | ||
| 404 | DW: Word + Copy + Default + Eq + defmt::Format + From<SW>, | ||
| 405 | { | ||
| 406 | assert!( | ||
| 407 | DW::size() > SW::size(), | ||
| 408 | "This test only works when the destination is larger than the source" | ||
| 409 | ); | ||
| 410 | |||
| 411 | let options = TransferOptions::default(); | ||
| 412 | let mut dst = DW::default(); | ||
| 413 | |||
| 414 | // SAFETY: src and dst outlive the transfer. | ||
| 415 | let transfer = unsafe { | ||
| 416 | unwrap!(Transfer::new_read( | ||
| 417 | channel.reborrow(), | ||
| 418 | Transfer::SOFTWARE_TRIGGER, | ||
| 419 | &mut src, | ||
| 420 | slice::from_mut(&mut dst), | ||
| 421 | options, | ||
| 422 | )) | ||
| 423 | }; | ||
| 424 | transfer.blocking_wait(); | ||
| 425 | |||
| 426 | assert_eq!(DW::from(src), dst); | ||
| 427 | } | ||
| 428 | |||
| 429 | /// [`single_read`], but testing when the destination is narrower than the source. | ||
| 430 | /// | ||
| 431 | /// The MSPM0 DMA states that the upper bytes when the source is longer than the destination are dropped. | ||
| 432 | /// This matches the behavior in Rust for all unsigned integer types. | ||
| 433 | fn narrowing_single_read<SW, DW>(mut channel: Peri<'_, impl Channel>, mut src: SW) | ||
| 434 | where | ||
| 435 | SW: Word + Copy + Default + Eq + defmt::Format + From<DW>, | ||
| 436 | DW: Word + Copy + Default + Eq + defmt::Format + Narrow<SW>, | ||
| 437 | { | ||
| 438 | assert!( | ||
| 439 | SW::size() > DW::size(), | ||
| 440 | "This test only works when the source is larger than the destination" | ||
| 441 | ); | ||
| 442 | |||
| 443 | let options = TransferOptions::default(); | ||
| 444 | let mut dst = DW::default(); | ||
| 445 | |||
| 446 | // SAFETY: src and dst outlive the transfer. | ||
| 447 | let transfer = unsafe { | ||
| 448 | unwrap!(Transfer::new_read( | ||
| 449 | channel.reborrow(), | ||
| 450 | Transfer::SOFTWARE_TRIGGER, | ||
| 451 | &mut src, | ||
| 452 | slice::from_mut(&mut dst), | ||
| 453 | options, | ||
| 454 | )) | ||
| 455 | }; | ||
| 456 | transfer.blocking_wait(); | ||
| 457 | |||
| 458 | // The expected value is the source value masked by the maximum destination value. | ||
| 459 | // This is effectively `src as DW as SW` to drop the upper byte(s). | ||
| 460 | let expect = SW::from(DW::narrow(src)); | ||
| 461 | assert_eq!(expect, dst.into()); | ||
| 462 | } | ||
| 463 | |||
| 464 | /// A pseudo `as` trait to allow downcasting integer types (TryFrom could fail). | ||
| 465 | trait Narrow<T> { | ||
| 466 | fn narrow(value: T) -> Self; | ||
| 467 | } | ||
| 468 | |||
| 469 | impl Narrow<u16> for u8 { | ||
| 470 | fn narrow(value: u16) -> Self { | ||
| 471 | value as u8 | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | impl Narrow<u32> for u8 { | ||
| 476 | fn narrow(value: u32) -> Self { | ||
| 477 | value as u8 | ||
| 478 | } | ||
| 479 | } | ||
| 480 | |||
| 481 | impl Narrow<u64> for u8 { | ||
| 482 | fn narrow(value: u64) -> Self { | ||
| 483 | value as u8 | ||
| 484 | } | ||
| 485 | } | ||
| 486 | |||
| 487 | impl Narrow<u32> for u16 { | ||
| 488 | fn narrow(value: u32) -> Self { | ||
| 489 | value as u16 | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | impl Narrow<u64> for u16 { | ||
| 494 | fn narrow(value: u64) -> Self { | ||
| 495 | value as u16 | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | impl Narrow<u64> for u32 { | ||
| 500 | fn narrow(value: u64) -> Self { | ||
| 501 | value as u32 | ||
| 502 | } | ||
| 503 | } | ||
diff --git a/tests/mspm0/src/bin/uart.rs b/tests/mspm0/src/bin/uart.rs index 458129d44..916ce0d4b 100644 --- a/tests/mspm0/src/bin/uart.rs +++ b/tests/mspm0/src/bin/uart.rs | |||
| @@ -4,6 +4,9 @@ | |||
| 4 | #[cfg(feature = "mspm0g3507")] | 4 | #[cfg(feature = "mspm0g3507")] |
| 5 | teleprobe_meta::target!(b"lp-mspm0g3507"); | 5 | teleprobe_meta::target!(b"lp-mspm0g3507"); |
| 6 | 6 | ||
| 7 | #[cfg(feature = "mspm0g3519")] | ||
| 8 | teleprobe_meta::target!(b"lp-mspm0g3519"); | ||
| 9 | |||
| 7 | use defmt::{assert_eq, unwrap, *}; | 10 | use defmt::{assert_eq, unwrap, *}; |
| 8 | use embassy_executor::Spawner; | 11 | use embassy_executor::Spawner; |
| 9 | use embassy_mspm0::mode::Blocking; | 12 | use embassy_mspm0::mode::Blocking; |
| @@ -23,7 +26,7 @@ async fn main(_spawner: Spawner) { | |||
| 23 | 26 | ||
| 24 | // TODO: Allow creating a looped-back UART (so pins are not needed). | 27 | // TODO: Allow creating a looped-back UART (so pins are not needed). |
| 25 | // Do not select default UART since the virtual COM port is attached to UART0. | 28 | // Do not select default UART since the virtual COM port is attached to UART0. |
| 26 | #[cfg(feature = "mspm0g3507")] | 29 | #[cfg(any(feature = "mspm0g3507", feature = "mspm0g3519"))] |
| 27 | let (mut tx, mut rx, mut uart) = (p.PA8, p.PA9, p.UART1); | 30 | let (mut tx, mut rx, mut uart) = (p.PA8, p.PA9, p.UART1); |
| 28 | 31 | ||
| 29 | const MFCLK_BUAD_RATES: &[u32] = &[1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200]; | 32 | const MFCLK_BUAD_RATES: &[u32] = &[1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200]; |
diff --git a/tests/mspm0/src/bin/uart_buffered.rs b/tests/mspm0/src/bin/uart_buffered.rs new file mode 100644 index 000000000..135ac1287 --- /dev/null +++ b/tests/mspm0/src/bin/uart_buffered.rs | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | #[cfg(feature = "mspm0g3507")] | ||
| 5 | teleprobe_meta::target!(b"lp-mspm0g3507"); | ||
| 6 | |||
| 7 | use defmt::{assert_eq, unwrap, *}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_mspm0::uart::{BufferedInterruptHandler, BufferedUart, Config}; | ||
| 10 | use embassy_mspm0::{bind_interrupts, peripherals}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | bind_interrupts!(struct Irqs { | ||
| 14 | UART1 => BufferedInterruptHandler<peripherals::UART1>; | ||
| 15 | }); | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_mspm0::init(Default::default()); | ||
| 20 | info!("Hello World!"); | ||
| 21 | |||
| 22 | // TODO: Allow creating a looped-back UART (so pins are not needed). | ||
| 23 | // Do not select default UART since the virtual COM port is attached to UART0. | ||
| 24 | #[cfg(any(feature = "mspm0g3507"))] | ||
| 25 | let (mut tx, mut rx, mut uart) = (p.PA8, p.PA9, p.UART1); | ||
| 26 | |||
| 27 | { | ||
| 28 | use embedded_io_async::{Read, Write}; | ||
| 29 | |||
| 30 | let mut config = Config::default(); | ||
| 31 | config.loop_back_enable = true; | ||
| 32 | config.fifo_enable = false; | ||
| 33 | |||
| 34 | let tx_buf = &mut [0u8; 16]; | ||
| 35 | let rx_buf = &mut [0u8; 16]; | ||
| 36 | let mut uart = unwrap!(BufferedUart::new( | ||
| 37 | uart.reborrow(), | ||
| 38 | tx.reborrow(), | ||
| 39 | rx.reborrow(), | ||
| 40 | Irqs, | ||
| 41 | tx_buf, | ||
| 42 | rx_buf, | ||
| 43 | config | ||
| 44 | )); | ||
| 45 | |||
| 46 | let mut buf = [0; 16]; | ||
| 47 | for (j, b) in buf.iter_mut().enumerate() { | ||
| 48 | *b = j as u8; | ||
| 49 | } | ||
| 50 | |||
| 51 | unwrap!(uart.write_all(&buf).await); | ||
| 52 | unwrap!(uart.flush().await); | ||
| 53 | |||
| 54 | unwrap!(uart.read_exact(&mut buf).await); | ||
| 55 | for (j, b) in buf.iter().enumerate() { | ||
| 56 | assert_eq!(*b, j as u8); | ||
| 57 | } | ||
| 58 | |||
| 59 | // Buffer is unclogged, should be able to write again. | ||
| 60 | unwrap!(uart.write_all(&buf).await); | ||
| 61 | unwrap!(uart.flush().await); | ||
| 62 | |||
| 63 | unwrap!(uart.read_exact(&mut buf).await); | ||
| 64 | for (j, b) in buf.iter().enumerate() { | ||
| 65 | assert_eq!(*b, j as u8); | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | info!("Blocking buffered"); | ||
| 70 | { | ||
| 71 | use embedded_io::{Read, Write}; | ||
| 72 | |||
| 73 | let mut config = Config::default(); | ||
| 74 | config.loop_back_enable = true; | ||
| 75 | config.fifo_enable = false; | ||
| 76 | |||
| 77 | let tx_buf = &mut [0u8; 16]; | ||
| 78 | let rx_buf = &mut [0u8; 16]; | ||
| 79 | let mut uart = unwrap!(BufferedUart::new( | ||
| 80 | uart.reborrow(), | ||
| 81 | tx.reborrow(), | ||
| 82 | rx.reborrow(), | ||
| 83 | Irqs, | ||
| 84 | tx_buf, | ||
| 85 | rx_buf, | ||
| 86 | config | ||
| 87 | )); | ||
| 88 | |||
| 89 | let mut buf = [0; 16]; | ||
| 90 | |||
| 91 | for (j, b) in buf.iter_mut().enumerate() { | ||
| 92 | *b = j as u8; | ||
| 93 | } | ||
| 94 | |||
| 95 | unwrap!(uart.write_all(&buf)); | ||
| 96 | unwrap!(uart.blocking_flush()); | ||
| 97 | unwrap!(uart.read_exact(&mut buf)); | ||
| 98 | |||
| 99 | for (j, b) in buf.iter().enumerate() { | ||
| 100 | assert_eq!(*b, j as u8); | ||
| 101 | } | ||
| 102 | |||
| 103 | // Buffer is unclogged, should be able to write again. | ||
| 104 | unwrap!(uart.write_all(&buf)); | ||
| 105 | unwrap!(uart.blocking_flush()); | ||
| 106 | unwrap!(uart.read_exact(&mut buf)); | ||
| 107 | |||
| 108 | for (j, b) in buf.iter().enumerate() { | ||
| 109 | assert_eq!(*b, j as u8, "at {}", j); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | info!("Test OK"); | ||
| 114 | cortex_m::asm::bkpt(); | ||
| 115 | } | ||
diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 30c4223b7..b167c589e 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml | |||
| @@ -11,7 +11,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | |||
| 11 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt", ] } | 11 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt", ] } |
| 12 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 12 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 13 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 13 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 14 | embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 14 | embassy-nrf = { version = "0.5.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 15 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | 15 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } |
| 16 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } | 16 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } |
| 17 | embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } | 17 | embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } |
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 2be37f525..298955fe3 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml | |||
| @@ -15,11 +15,11 @@ teleprobe-meta = "1.1" | |||
| 15 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | 15 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 16 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 16 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 17 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", ] } | 17 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", ] } |
| 18 | embassy-rp = { version = "0.4.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } | 18 | embassy-rp = { version = "0.6.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } |
| 19 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 19 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 20 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } | 20 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } |
| 21 | embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } | 21 | embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } |
| 22 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal/"} | 22 | embassy-embedded-hal = { version = "0.3.1", path = "../../embassy-embedded-hal/"} |
| 23 | cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } | 23 | cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } |
| 24 | cyw43-pio = { path = "../../cyw43-pio", features = ["defmt"] } | 24 | cyw43-pio = { path = "../../cyw43-pio", features = ["defmt"] } |
| 25 | perf-client = { path = "../perf-client" } | 25 | perf-client = { path = "../perf-client" } |
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 8d10f6593..7c32c0ce1 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml | |||
| @@ -7,6 +7,7 @@ autobins = false | |||
| 7 | 7 | ||
| 8 | [features] | 8 | [features] |
| 9 | stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] | 9 | stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] |
| 10 | stm32c071rb = ["embassy-stm32/stm32c071rb", "cm0", "not-gpdma"] | ||
| 10 | stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma"] | 11 | stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma"] |
| 11 | stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "eth", "rng"] | 12 | stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "eth", "rng"] |
| 12 | stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] | 13 | stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] |
diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 722671bf1..556d77a20 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs | |||
| @@ -12,6 +12,7 @@ fn main() -> Result<(), Box<dyn Error>> { | |||
| 12 | // too little RAM to run from RAM. | 12 | // too little RAM to run from RAM. |
| 13 | feature = "stm32f103c8", // 20 kb | 13 | feature = "stm32f103c8", // 20 kb |
| 14 | feature = "stm32c031c6", // 6 kb | 14 | feature = "stm32c031c6", // 6 kb |
| 15 | feature = "stm32c071rb", // 24 kb | ||
| 15 | feature = "stm32l073rz", // 20 kb | 16 | feature = "stm32l073rz", // 20 kb |
| 16 | feature = "stm32h503rb", // 32 kb | 17 | feature = "stm32h503rb", // 32 kb |
| 17 | // no VTOR, so interrupts can't work when running from RAM | 18 | // no VTOR, so interrupts can't work when running from RAM |
diff --git a/tests/stm32/src/bin/cryp.rs b/tests/stm32/src/bin/cryp.rs index 028775ac8..f54c99cc3 100644 --- a/tests/stm32/src/bin/cryp.rs +++ b/tests/stm32/src/bin/cryp.rs | |||
| @@ -72,7 +72,7 @@ async fn main(_spawner: Spawner) { | |||
| 72 | defmt::assert!(encrypt_tag == payload_vec[ciphertext.len()..ciphertext.len() + encrypt_tag.len()]); | 72 | defmt::assert!(encrypt_tag == payload_vec[ciphertext.len()..ciphertext.len() + encrypt_tag.len()]); |
| 73 | 73 | ||
| 74 | // Decrypt in software using AES-GCM 128-bit | 74 | // Decrypt in software using AES-GCM 128-bit |
| 75 | let _ = cipher.decrypt_in_place(&iv.into(), &aad, &mut payload_vec); | 75 | cipher.decrypt_in_place(&iv.into(), &aad, &mut payload_vec).unwrap(); |
| 76 | 76 | ||
| 77 | info!("Test OK"); | 77 | info!("Test OK"); |
| 78 | cortex_m::asm::bkpt(); | 78 | cortex_m::asm::bkpt(); |
diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs index 34a53a725..9f9c526e1 100644 --- a/tests/stm32/src/bin/sdmmc.rs +++ b/tests/stm32/src/bin/sdmmc.rs | |||
| @@ -95,6 +95,9 @@ async fn main(_spawner: Spawner) { | |||
| 95 | 95 | ||
| 96 | drop(s); | 96 | drop(s); |
| 97 | 97 | ||
| 98 | // FIXME: this hangs on Rust 1.86 and higher. | ||
| 99 | // I haven't been able to figure out why. | ||
| 100 | /* | ||
| 98 | // ======== Try 1bit. ============== | 101 | // ======== Try 1bit. ============== |
| 99 | info!("initializing in 1-bit mode..."); | 102 | info!("initializing in 1-bit mode..."); |
| 100 | let mut s = Sdmmc::new_1bit( | 103 | let mut s = Sdmmc::new_1bit( |
| @@ -151,6 +154,7 @@ async fn main(_spawner: Spawner) { | |||
| 151 | assert_eq!(&blocks, &patterns); | 154 | assert_eq!(&blocks, &patterns); |
| 152 | 155 | ||
| 153 | drop(s); | 156 | drop(s); |
| 157 | */ | ||
| 154 | 158 | ||
| 155 | info!("Test OK"); | 159 | info!("Test OK"); |
| 156 | cortex_m::asm::bkpt(); | 160 | cortex_m::asm::bkpt(); |
diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 829f2cff0..a4d8048ce 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs | |||
| @@ -34,6 +34,8 @@ teleprobe_meta::target!(b"nucleo-stm32u5a5zj"); | |||
| 34 | teleprobe_meta::target!(b"nucleo-stm32h563zi"); | 34 | teleprobe_meta::target!(b"nucleo-stm32h563zi"); |
| 35 | #[cfg(feature = "stm32c031c6")] | 35 | #[cfg(feature = "stm32c031c6")] |
| 36 | teleprobe_meta::target!(b"nucleo-stm32c031c6"); | 36 | teleprobe_meta::target!(b"nucleo-stm32c031c6"); |
| 37 | #[cfg(feature = "stm32c071rb")] | ||
| 38 | teleprobe_meta::target!(b"nucleo-stm32c071rb"); | ||
| 37 | #[cfg(feature = "stm32l073rz")] | 39 | #[cfg(feature = "stm32l073rz")] |
| 38 | teleprobe_meta::target!(b"nucleo-stm32l073rz"); | 40 | teleprobe_meta::target!(b"nucleo-stm32l073rz"); |
| 39 | #[cfg(feature = "stm32l152re")] | 41 | #[cfg(feature = "stm32l152re")] |
| @@ -186,6 +188,12 @@ define_peris!( | |||
| 186 | SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, | 188 | SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, |
| 187 | @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;}, | 189 | @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;}, |
| 188 | ); | 190 | ); |
| 191 | #[cfg(feature = "stm32c071rb")] | ||
| 192 | define_peris!( | ||
| 193 | UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, | ||
| 194 | SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, | ||
| 195 | @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;}, | ||
| 196 | ); | ||
| 189 | #[cfg(feature = "stm32l496zg")] | 197 | #[cfg(feature = "stm32l496zg")] |
| 190 | define_peris!( | 198 | define_peris!( |
| 191 | UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3, | 199 | UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3, |
| @@ -271,7 +279,7 @@ pub fn config() -> Config { | |||
| 271 | #[allow(unused_mut)] | 279 | #[allow(unused_mut)] |
| 272 | let mut config = Config::default(); | 280 | let mut config = Config::default(); |
| 273 | 281 | ||
| 274 | #[cfg(feature = "stm32c031c6")] | 282 | #[cfg(any(feature = "stm32c031c6", feature = "stm32c071rb"))] |
| 275 | { | 283 | { |
| 276 | config.rcc.hsi = Some(Hsi { | 284 | config.rcc.hsi = Some(Hsi { |
| 277 | sys_div: HsiSysDiv::DIV1, // 48Mhz | 285 | sys_div: HsiSysDiv::DIV1, // 48Mhz |
