aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2025-09-17 11:52:36 +0200
committerGitHub <[email protected]>2025-09-17 11:52:36 +0200
commit12cb85c54c97e8d8081611018c065be2957171c5 (patch)
tree71e84122a1703c99dee227a9f2e76f919fe41163
parent40f5161c326166380b1af19170e7db15644b92b1 (diff)
parent90f6497959adf6f0f0a65f1c53be0bd6b0e3f1a7 (diff)
Merge branch 'main' into usb_dfu_reset
-rw-r--r--.gitattributes7
-rwxr-xr-x.github/ci/build-nightly.sh3
-rwxr-xr-x.github/ci/build-xtensa.sh11
-rwxr-xr-x.github/ci/build.sh3
-rwxr-xr-x.github/ci/doc.sh68
-rw-r--r--.vscode/settings.json2
-rwxr-xr-xci-nightly.sh17
-rwxr-xr-xci-xtensa.sh28
-rwxr-xr-xci.sh353
-rw-r--r--docs/pages/embassy_in_the_wild.adoc4
-rw-r--r--embassy-executor-macros/Cargo.toml1
-rw-r--r--embassy-executor-macros/src/macros/main.rs2
-rw-r--r--embassy-executor/CHANGELOG.md10
-rw-r--r--embassy-executor/Cargo.toml71
-rw-r--r--embassy-executor/src/metadata.rs93
-rw-r--r--embassy-executor/src/raw/deadline.rs44
-rw-r--r--embassy-executor/src/raw/mod.rs8
-rw-r--r--embassy-executor/src/raw/run_queue.rs213
-rw-r--r--embassy-executor/src/raw/run_queue_atomics.rs88
-rw-r--r--embassy-executor/src/raw/run_queue_critical_section.rs74
-rw-r--r--embassy-mspm0/CHANGELOG.md4
-rw-r--r--embassy-mspm0/Cargo.toml22
-rw-r--r--embassy-mspm0/build.rs30
-rw-r--r--embassy-mspm0/src/adc.rs510
-rw-r--r--embassy-mspm0/src/gpio.rs14
-rw-r--r--embassy-mspm0/src/i2c.rs2
-rw-r--r--embassy-mspm0/src/lib.rs1
-rw-r--r--embassy-net-wiznet/CHANGELOG.md3
-rw-r--r--embassy-net-wiznet/src/chip/mod.rs4
-rw-r--r--embassy-net-wiznet/src/chip/w5100s.rs1
-rw-r--r--embassy-net-wiznet/src/chip/w5500.rs1
-rw-r--r--embassy-net-wiznet/src/chip/w6100.rs83
-rw-r--r--embassy-net-wiznet/src/device.rs2
-rw-r--r--embassy-net/Cargo.toml13
-rw-r--r--embassy-net/README.md1
-rw-r--r--embassy-nrf/CHANGELOG.md1
-rw-r--r--embassy-nrf/Cargo.toml7
-rw-r--r--embassy-nrf/README.md4
-rw-r--r--embassy-nrf/src/embassy_net_802154_driver.rs96
-rw-r--r--embassy-nrf/src/lib.rs11
-rw-r--r--embassy-nxp/CHANGELOG.md2
-rw-r--r--embassy-nxp/Cargo.toml13
-rw-r--r--embassy-nxp/src/chips/lpc55.rs2
-rw-r--r--embassy-nxp/src/gpio.rs2
-rw-r--r--embassy-nxp/src/gpio/lpc55.rs360
-rw-r--r--embassy-nxp/src/lib.rs10
-rw-r--r--embassy-nxp/src/pint.rs97
-rw-r--r--embassy-nxp/src/time_driver/rtc.rs76
-rw-r--r--embassy-nxp/src/usart.rs3
-rw-r--r--embassy-nxp/src/usart/lpc55.rs884
-rw-r--r--embassy-rp/CHANGELOG.md9
-rw-r--r--embassy-rp/src/lib.rs6
-rw-r--r--embassy-rp/src/pio/mod.rs86
-rw-r--r--embassy-rp/src/pio_programs/i2s.rs96
-rw-r--r--embassy-rp/src/pio_programs/mod.rs1
-rw-r--r--embassy-rp/src/pio_programs/onewire.rs45
-rw-r--r--embassy-rp/src/pio_programs/spi.rs474
-rw-r--r--embassy-rp/src/psram.rs682
-rw-r--r--embassy-rp/src/qmi_cs1.rs57
-rw-r--r--embassy-rp/src/rtc/datetime_no_deps.rs1
-rw-r--r--embassy-rp/src/rtc/filter.rs3
-rw-r--r--embassy-rp/src/rtc/mod.rs123
-rw-r--r--embassy-stm32/CHANGELOG.md20
-rw-r--r--embassy-stm32/Cargo.toml9
-rw-r--r--embassy-stm32/build.rs105
-rw-r--r--embassy-stm32/src/adc/adc4.rs3
-rw-r--r--embassy-stm32/src/adc/c0.rs3
-rw-r--r--embassy-stm32/src/adc/mod.rs68
-rw-r--r--embassy-stm32/src/adc/v1.rs6
-rw-r--r--embassy-stm32/src/adc/v3.rs225
-rw-r--r--embassy-stm32/src/adc/v4.rs3
-rw-r--r--embassy-stm32/src/can/bxcan/mod.rs18
-rw-r--r--embassy-stm32/src/can/fdcan.rs4
-rw-r--r--embassy-stm32/src/cryp/mod.rs3
-rw-r--r--embassy-stm32/src/dac/mod.rs57
-rw-r--r--embassy-stm32/src/dcmi.rs2
-rw-r--r--embassy-stm32/src/dma/dma_bdma.rs102
-rw-r--r--embassy-stm32/src/dma/gpdma.rs339
-rw-r--r--embassy-stm32/src/dma/gpdma/linked_list.rs267
-rw-r--r--embassy-stm32/src/dma/gpdma/mod.rs699
-rw-r--r--embassy-stm32/src/dma/gpdma/ringbuffered.rs332
-rw-r--r--embassy-stm32/src/dma/mod.rs7
-rw-r--r--embassy-stm32/src/dma/ringbuffer/mod.rs13
-rw-r--r--embassy-stm32/src/dsihost.rs2
-rw-r--r--embassy-stm32/src/eth/mod.rs32
-rw-r--r--embassy-stm32/src/eth/v1/mod.rs56
-rw-r--r--embassy-stm32/src/eth/v2/mod.rs2
-rw-r--r--embassy-stm32/src/fmc.rs2
-rw-r--r--embassy-stm32/src/gpio.rs31
-rw-r--r--embassy-stm32/src/hrtim/mod.rs10
-rw-r--r--embassy-stm32/src/hspi/mod.rs19
-rw-r--r--embassy-stm32/src/i2c/mod.rs16
-rw-r--r--embassy-stm32/src/i2c/v2.rs17
-rw-r--r--embassy-stm32/src/i2s.rs233
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/lptim/pwm.rs17
-rw-r--r--embassy-stm32/src/macros.rs120
-rw-r--r--embassy-stm32/src/ospi/enums.rs8
-rw-r--r--embassy-stm32/src/ospi/mod.rs188
-rw-r--r--embassy-stm32/src/qspi/enums.rs7
-rw-r--r--embassy-stm32/src/qspi/mod.rs56
-rw-r--r--embassy-stm32/src/rcc/mco.rs2
-rw-r--r--embassy-stm32/src/sai/mod.rs347
-rw-r--r--embassy-stm32/src/sdmmc/mod.rs76
-rw-r--r--embassy-stm32/src/spdifrx/mod.rs9
-rw-r--r--embassy-stm32/src/spi/mod.rs196
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs109
-rw-r--r--embassy-stm32/src/timer/input_capture.rs26
-rw-r--r--embassy-stm32/src/timer/mod.rs12
-rw-r--r--embassy-stm32/src/timer/one_pulse.rs70
-rw-r--r--embassy-stm32/src/timer/pwm_input.rs18
-rw-r--r--embassy-stm32/src/timer/qei.rs24
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs44
-rw-r--r--embassy-stm32/src/tsc/pin_groups.rs2
-rw-r--r--embassy-stm32/src/ucpd.rs4
-rw-r--r--embassy-stm32/src/usart/buffered.rs52
-rw-r--r--embassy-stm32/src/usart/mod.rs118
-rw-r--r--embassy-stm32/src/usart/ringbuffered.rs2
-rw-r--r--embassy-stm32/src/usb/otg.rs10
-rw-r--r--embassy-stm32/src/usb/usb.rs6
-rw-r--r--embassy-stm32/src/xspi/enums.rs8
-rw-r--r--embassy-stm32/src/xspi/mod.rs20
-rw-r--r--embassy-sync/CHANGELOG.md1
-rw-r--r--embassy-sync/Cargo.toml2
-rw-r--r--embassy-sync/src/mutex.rs4
-rw-r--r--embassy-time-queue-utils/Cargo.toml3
-rw-r--r--embassy-time/Cargo.toml2
-rw-r--r--embassy-usb-dfu/CHANGELOG.md1
-rw-r--r--embassy-usb-dfu/src/application.rs1
-rw-r--r--embassy-usb-dfu/src/dfu.rs1
-rw-r--r--embassy-usb-dfu/src/lib.rs14
-rw-r--r--embassy-usb-driver/CHANGELOG.md2
-rw-r--r--embassy-usb-driver/src/lib.rs30
-rw-r--r--embassy-usb-synopsys-otg/CHANGELOG.md2
-rw-r--r--embassy-usb-synopsys-otg/src/lib.rs30
-rw-r--r--examples/lpc55s69/Cargo.toml2
-rw-r--r--examples/mspm0c1104/Cargo.toml3
-rw-r--r--examples/mspm0g3507/src/bin/adc.rs39
-rw-r--r--examples/mspm0l1306/src/bin/adc.rs39
-rw-r--r--examples/nrf52840-edf/.cargo/config.toml9
-rw-r--r--examples/nrf52840-edf/Cargo.toml27
-rw-r--r--examples/nrf52840-edf/build.rs35
-rw-r--r--examples/nrf52840-edf/memory.x12
-rw-r--r--examples/nrf52840-edf/src/bin/basic.rs194
-rw-r--r--examples/nrf52840-rtic/src/bin/blinky.rs1
-rw-r--r--examples/nrf52840/Cargo.toml4
-rw-r--r--examples/nrf52840/src/bin/nfct.rs274
-rw-r--r--examples/nrf52840/src/bin/sixlowpan.rs120
-rw-r--r--examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs155
-rw-r--r--examples/rp/src/bin/pio_onewire.rs1
-rw-r--r--examples/rp/src/bin/pio_onewire_parasite.rs89
-rw-r--r--examples/rp/src/bin/pio_spi.rs48
-rw-r--r--examples/rp/src/bin/pio_spi_async.rs57
-rw-r--r--examples/rp/src/bin/rtc.rs8
-rw-r--r--examples/rp/src/bin/rtc_alarm.rs66
-rw-r--r--examples/rp235x/src/bin/pio_i2s_rx.rs81
-rw-r--r--examples/rp235x/src/bin/pio_onewire.rs103
-rw-r--r--examples/rp235x/src/bin/pio_onewire_parasite.rs89
-rw-r--r--examples/rp235x/src/bin/psram.rs49
-rw-r--r--examples/stm32f1/src/bin/input_capture.rs5
-rw-r--r--examples/stm32f1/src/bin/pwm_input.rs4
-rw-r--r--examples/stm32f7/src/bin/qspi.rs16
-rw-r--r--examples/stm32g0/src/bin/adc.rs4
-rw-r--r--examples/stm32g0/src/bin/adc_dma.rs4
-rw-r--r--examples/stm32g0/src/bin/adc_oversampling.rs19
-rw-r--r--examples/stm32h5/src/bin/sai.rs52
-rw-r--r--examples/stm32h7/src/bin/sai.rs72
-rw-r--r--examples/stm32h723/src/bin/spdifrx.rs2
-rw-r--r--examples/stm32h742/src/bin/qspi.rs16
-rw-r--r--examples/stm32l0/Cargo.toml2
-rw-r--r--examples/stm32l0/src/bin/usb_serial.rs95
-rw-r--r--examples/stm32l432/src/bin/qspi_mmap.rs16
-rw-r--r--examples/stm32wl/src/bin/adc.rs39
-rw-r--r--tests/rp/Cargo.toml6
-rw-r--r--tests/rp/src/bin/rtc.rs125
-rw-r--r--tests/stm32/Cargo.toml12
-rw-r--r--tests/stm32/src/bin/afio.rs1156
-rw-r--r--tests/stm32/src/common.rs2
178 files changed, 9129 insertions, 3316 deletions
diff --git a/.gitattributes b/.gitattributes
index a51376f0d..2850eb3b1 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -17,6 +17,13 @@
17 17
18.vscode/*.json linguist-language=JSON-with-Comments 18.vscode/*.json linguist-language=JSON-with-Comments
19 19
20# Configure changelog files to use union merge strategy
21# This prevents merge conflicts by automatically combining changes from both branches
22CHANGELOG.md merge=union
23changelog.md merge=union
24CHANGELOG.txt merge=union
25changelog.txt merge=union
26
20*.raw binary 27*.raw binary
21*.bin binary 28*.bin binary
22*.png binary 29*.png binary
diff --git a/.github/ci/build-nightly.sh b/.github/ci/build-nightly.sh
index 95cb4100c..2d7c4db3f 100755
--- a/.github/ci/build-nightly.sh
+++ b/.github/ci/build-nightly.sh
@@ -7,6 +7,7 @@ set -euo pipefail
7export RUSTUP_HOME=/ci/cache/rustup 7export RUSTUP_HOME=/ci/cache/rustup
8export CARGO_HOME=/ci/cache/cargo 8export CARGO_HOME=/ci/cache/cargo
9export CARGO_TARGET_DIR=/ci/cache/target 9export CARGO_TARGET_DIR=/ci/cache/target
10export PATH=$CARGO_HOME/bin:$PATH
10mv rust-toolchain-nightly.toml rust-toolchain.toml 11mv rust-toolchain-nightly.toml rust-toolchain.toml
11 12
12# needed for "dumb HTTP" transport support 13# needed for "dumb HTTP" transport support
@@ -22,6 +23,8 @@ fi
22hashtime restore /ci/cache/filetime.json || true 23hashtime restore /ci/cache/filetime.json || true
23hashtime save /ci/cache/filetime.json 24hashtime save /ci/cache/filetime.json
24 25
26cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe
27
25./ci-nightly.sh 28./ci-nightly.sh
26 29
27# Save lockfiles 30# Save lockfiles
diff --git a/.github/ci/build-xtensa.sh b/.github/ci/build-xtensa.sh
index 103575bc9..b6626639d 100755
--- a/.github/ci/build-xtensa.sh
+++ b/.github/ci/build-xtensa.sh
@@ -7,13 +7,14 @@ set -euo pipefail
7export RUSTUP_HOME=/ci/cache/rustup 7export RUSTUP_HOME=/ci/cache/rustup
8export CARGO_HOME=/ci/cache/cargo 8export CARGO_HOME=/ci/cache/cargo
9export CARGO_TARGET_DIR=/ci/cache/target 9export CARGO_TARGET_DIR=/ci/cache/target
10export PATH=$CARGO_HOME/bin:$PATH
10 11
11# needed for "dumb HTTP" transport support 12# needed for "dumb HTTP" transport support
12# used when pointing stm32-metapac to a CI-built one. 13# used when pointing stm32-metapac to a CI-built one.
13export CARGO_NET_GIT_FETCH_WITH_CLI=true 14export CARGO_NET_GIT_FETCH_WITH_CLI=true
14 15
15cargo install espup 16cargo install espup --locked
16/ci/cache/cargo/bin/espup install --toolchain-version 1.84.0.0 17espup install --toolchain-version 1.88.0.0
17 18
18# Restore lockfiles 19# Restore lockfiles
19if [ -f /ci/cache/lockfiles.tar ]; then 20if [ -f /ci/cache/lockfiles.tar ]; then
@@ -24,11 +25,7 @@ fi
24hashtime restore /ci/cache/filetime.json || true 25hashtime restore /ci/cache/filetime.json || true
25hashtime save /ci/cache/filetime.json 26hashtime save /ci/cache/filetime.json
26 27
27mkdir .cargo 28cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe
28cat > .cargo/config.toml<< EOF
29[unstable]
30build-std = ["alloc", "core"]
31EOF
32 29
33./ci-xtensa.sh 30./ci-xtensa.sh
34 31
diff --git a/.github/ci/build.sh b/.github/ci/build.sh
index 68a7c0c34..59bcefed6 100755
--- a/.github/ci/build.sh
+++ b/.github/ci/build.sh
@@ -7,6 +7,7 @@ set -euo pipefail
7export RUSTUP_HOME=/ci/cache/rustup 7export RUSTUP_HOME=/ci/cache/rustup
8export CARGO_HOME=/ci/cache/cargo 8export CARGO_HOME=/ci/cache/cargo
9export CARGO_TARGET_DIR=/ci/cache/target 9export CARGO_TARGET_DIR=/ci/cache/target
10export PATH=$CARGO_HOME/bin:$PATH
10if [ -f /ci/secrets/teleprobe-token.txt ]; then 11if [ -f /ci/secrets/teleprobe-token.txt ]; then
11 echo Got teleprobe token! 12 echo Got teleprobe token!
12 export TELEPROBE_HOST=https://teleprobe.embassy.dev 13 export TELEPROBE_HOST=https://teleprobe.embassy.dev
@@ -27,6 +28,8 @@ fi
27hashtime restore /ci/cache/filetime.json || true 28hashtime restore /ci/cache/filetime.json || true
28hashtime save /ci/cache/filetime.json 29hashtime save /ci/cache/filetime.json
29 30
31cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe
32
30./ci.sh 33./ci.sh
31 34
32# Save lockfiles 35# Save lockfiles
diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh
index ac96008d8..876c261a1 100755
--- a/.github/ci/doc.sh
+++ b/.github/ci/doc.sh
@@ -19,42 +19,42 @@ mv rust-toolchain-nightly.toml rust-toolchain.toml
19# which makes rustup very sad 19# which makes rustup very sad
20rustc --version > /dev/null 20rustc --version > /dev/null
21 21
22docserver-builder -i ./embassy-boot -o webroot/crates/embassy-boot/git.zup 22docserver build -i ./embassy-boot -o webroot/crates/embassy-boot/git.zup
23docserver-builder -i ./embassy-boot-nrf -o webroot/crates/embassy-boot-nrf/git.zup 23docserver build -i ./embassy-boot-nrf -o webroot/crates/embassy-boot-nrf/git.zup
24docserver-builder -i ./embassy-boot-rp -o webroot/crates/embassy-boot-rp/git.zup 24docserver build -i ./embassy-boot-rp -o webroot/crates/embassy-boot-rp/git.zup
25docserver-builder -i ./embassy-boot-stm32 -o webroot/crates/embassy-boot-stm32/git.zup 25docserver build -i ./embassy-boot-stm32 -o webroot/crates/embassy-boot-stm32/git.zup
26docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup 26docserver build -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup
27docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup 27docserver build -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup
28docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup 28docserver build -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
29docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup 29docserver build -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup
30docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup 30docserver build -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup
31docserver-builder -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup 31docserver build -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup
32docserver-builder -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup 32docserver build -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup
33docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup 33docserver build -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup
34docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup 34docserver build -i ./cyw43 -o webroot/crates/cyw43/git.zup
35docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup 35docserver build -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
36docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static 36docserver build -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
37 37
38docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup 38docserver build -i ./embassy-time -o webroot/crates/embassy-time/git.zup
39docserver-builder -i ./embassy-time-driver -o webroot/crates/embassy-time-driver/git.zup 39docserver build -i ./embassy-time-driver -o webroot/crates/embassy-time-driver/git.zup
40docserver-builder -i ./embassy-time-queue-utils -o webroot/crates/embassy-time-queue-utils/git.zup 40docserver build -i ./embassy-time-queue-utils -o webroot/crates/embassy-time-queue-utils/git.zup
41 41
42docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup 42docserver build -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup
43docserver-builder -i ./embassy-usb-dfu -o webroot/crates/embassy-usb-dfu/git.zup 43docserver build -i ./embassy-usb-dfu -o webroot/crates/embassy-usb-dfu/git.zup
44docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup 44docserver build -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup
45docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup 45docserver build -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup
46docserver-builder -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup 46docserver build -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup
47 47
48docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup 48docserver build -i ./embassy-net -o webroot/crates/embassy-net/git.zup
49docserver-builder -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup 49docserver build -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup
50docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup 50docserver build -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup
51docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup 51docserver build -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
52docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup 52docserver build -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup
53docserver-builder -i ./embassy-net-ppp -o webroot/crates/embassy-net-ppp/git.zup 53docserver build -i ./embassy-net-ppp -o webroot/crates/embassy-net-ppp/git.zup
54docserver-builder -i ./embassy-net-tuntap -o webroot/crates/embassy-net-tuntap/git.zup 54docserver build -i ./embassy-net-tuntap -o webroot/crates/embassy-net-tuntap/git.zup
55docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup 55docserver build -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup
56docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup 56docserver build -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
57docserver-builder -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup 57docserver build -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup
58 58
59export KUBECONFIG=/ci/secrets/kubeconfig.yml 59export KUBECONFIG=/ci/secrets/kubeconfig.yml
60POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) 60POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
@@ -65,6 +65,6 @@ kubectl cp webroot/static $POD:/data
65# so that it doesn't prevent other crates from getting docs updates when it breaks. 65# so that it doesn't prevent other crates from getting docs updates when it breaks.
66 66
67rm -rf webroot 67rm -rf webroot
68docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup 68docserver build -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
69POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) 69POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
70kubectl cp webroot/crates $POD:/data 70kubectl cp webroot/crates $POD:/data
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 6edd9312a..c504f3ccd 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -17,7 +17,7 @@
17 //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", 17 //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
18 "rust-analyzer.cargo.features": [ 18 "rust-analyzer.cargo.features": [
19 // Comment out these features when working on the examples. Most example crates do not have any cargo features. 19 // Comment out these features when working on the examples. Most example crates do not have any cargo features.
20 "stm32f446re", 20 "stm32f107rb",
21 "time-driver-any", 21 "time-driver-any",
22 "unstable-pac", 22 "unstable-pac",
23 "exti", 23 "exti",
diff --git a/ci-nightly.sh b/ci-nightly.sh
index d486d442c..afe9f534c 100755
--- a/ci-nightly.sh
+++ b/ci-nightly.sh
@@ -8,19 +8,4 @@ if [[ -z "${CARGO_TARGET_DIR}" ]]; then
8 export CARGO_TARGET_DIR=target_ci 8 export CARGO_TARGET_DIR=target_ci
9fi 9fi
10 10
11cargo batch \ 11cargo embassy-devtool build --group nightly
12 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly \
13 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \
14 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
15 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
16 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt \
17 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \
18 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \
19 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \
20 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \
21 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \
22 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \
23 --- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840-rtic \
24 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7a-none-eabi --features nightly,arch-cortex-ar,executor-thread \
25
26RUSTFLAGS="$RUSTFLAGS -C target-cpu=atmega328p" cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-none -Z build-std=core,alloc --features nightly,arch-avr,avr-device/atmega328p
diff --git a/ci-xtensa.sh b/ci-xtensa.sh
index 6c9807e98..0dd41a9ce 100755
--- a/ci-xtensa.sh
+++ b/ci-xtensa.sh
@@ -9,30 +9,4 @@ if [[ -z "${CARGO_TARGET_DIR}" ]]; then
9 export CARGO_TARGET_DIR=target_ci 9 export CARGO_TARGET_DIR=target_ci
10fi 10fi
11 11
12cargo batch \ 12cargo embassy-devtool build --group xtensa
13 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf \
14 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features log \
15 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt \
16 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \
17 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt,arch-spin,executor-thread \
18 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,arch-spin,executor-thread \
19 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s3-none-elf --features defmt,arch-spin,executor-thread \
20 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin \
21 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,rtos-trace \
22 --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread \
23 --- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \
24 --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,mock-driver \
25 --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target xtensa-esp32s2-none-elf \
26 --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target xtensa-esp32s2-none-elf --features generic-queue-8 \
27 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \
28 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \
29 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
30 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,dhcpv4-hostname \
31 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
32 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \
33 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \
34 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
35 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \
36 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \
37 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet \
38 --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \
diff --git a/ci.sh b/ci.sh
index a585a3ab8..6cc2a031d 100755
--- a/ci.sh
+++ b/ci.sh
@@ -10,352 +10,21 @@ if ! command -v cargo-batch &> /dev/null; then
10 exit 1 10 exit 1
11fi 11fi
12 12
13if ! command -v cargo-embassy-devtool &> /dev/null; then
14 echo "cargo-embassy-devtool could not be found. Install it with the following command:"
15 echo ""
16 echo " cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked"
17 echo ""
18 exit 1
19fi
20
13export RUSTFLAGS=-Dwarnings 21export RUSTFLAGS=-Dwarnings
14export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info 22export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
15if [[ -z "${CARGO_TARGET_DIR}" ]]; then 23if [[ -z "${CARGO_TARGET_DIR}" ]]; then
16 export CARGO_TARGET_DIR=target_ci 24 export CARGO_TARGET_DIR=target_ci
17fi 25fi
18 26
19TARGET=$(rustc -vV | sed -n 's|host: ||p') 27cargo embassy-devtool build
20
21BUILD_EXTRA=""
22if [ $TARGET = "x86_64-unknown-linux-gnu" ] || [ $TARGET = "aarch64-unknown-linux-gnu" ]; then
23 BUILD_EXTRA="--- build --release --manifest-path examples/std/Cargo.toml --target $TARGET --artifact-dir out/examples/std"
24fi
25
26# CI intentionally does not use -eabihf on thumbv7em to minimize dep compile time.
27cargo batch \
28 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi \
29 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \
30 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
31 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
32 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt \
33 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m \
34 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,metadata-name \
35 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,trace \
36 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,rtos-trace \
37 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,trace,rtos-trace \
38 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \
39 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \
40 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt \
41 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7a-none-eabi --features arch-cortex-ar,executor-thread \
42 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabi --features arch-cortex-ar,executor-thread \
43 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \
44 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \
45 --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \
46 --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi \
47 --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi --features time \
48 --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \
49 --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \
50 --- build --release --manifest-path embassy-time/Cargo.toml --features defmt,std \
51 --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target thumbv6m-none-eabi \
52 --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target thumbv6m-none-eabi --features generic-queue-8 \
53 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \
54 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \
55 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
56 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,dhcpv4-hostname \
57 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
58 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \
59 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \
60 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
61 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \
62 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \
63 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet \
64 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \
65 --- build --release --manifest-path embassy-imxrt/Cargo.toml --target thumbv8m.main-none-eabihf --features mimxrt633s,defmt,unstable-pac,time,time-driver-rtc \
66 --- build --release --manifest-path embassy-imxrt/Cargo.toml --target thumbv8m.main-none-eabihf --features mimxrt685s,defmt,unstable-pac,time,time-driver-rtc \
67 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,gpiote,time,time-driver-rtc1 \
68 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time,time-driver-rtc1 \
69 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time,time-driver-rtc1 \
70 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time,time-driver-rtc1 \
71 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52820,gpiote,time,time-driver-rtc1 \
72 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832,gpiote,time,time-driver-rtc1,reset-pin-as-gpio \
73 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833,gpiote,time,time-driver-rtc1,nfc-pins-as-gpio \
74 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-s,gpiote,time,time-driver-rtc1 \
75 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-ns,gpiote,time,time-driver-rtc1 \
76 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time,time-driver-rtc1 \
77 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time,time-driver-rtc1 \
78 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time,time-driver-rtc1 \
79 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf54l15-app-s,gpiote,time,time-driver-rtc1 \
80 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf54l15-app-ns,gpiote,time,time-driver-rtc1 \
81 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time \
82 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time-driver-rtc1 \
83 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time,time-driver-rtc1 \
84 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time \
85 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time-driver-rtc1 \
86 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time,time-driver-rtc1 \
87 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time \
88 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time-driver-rtc1 \
89 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time,time-driver-rtc1 \
90 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time,time-driver-rtc1 \
91 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time \
92 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,time \
93 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,defmt,rp2040 \
94 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,log,rp2040 \
95 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,intrinsics,rp2040 \
96 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,qspi-as-gpio,rp2040 \
97 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,defmt,rp235xa \
98 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,log,rp235xa \
99 --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,rp235xa,binary-info \
100 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,time \
101 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time-driver-any,time \
102 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time \
103 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time \
104 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,time \
105 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time-driver-any,time \
106 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time \
107 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time \
108 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti \
109 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt \
110 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,single-bank,defmt \
111 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb,defmt,exti,time-driver-any,time \
112 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c051f6,defmt,exti,time-driver-any,time \
113 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c091gb,defmt,exti,time-driver-any,time \
114 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c092rc,defmt,exti,time-driver-any,time \
115 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \
116 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \
117 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \
118 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030r8,defmt,exti,time-driver-any,time \
119 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f031k6,defmt,exti,time-driver-any,time \
120 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030rc,defmt,exti,time-driver-any,time \
121 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f070f6,defmt,exti,time-driver-any,time \
122 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f078vb,defmt,exti,time-driver-any,time \
123 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f042g4,defmt,exti,time-driver-any,time \
124 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f072c8,defmt,exti,time-driver-any,time \
125 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any \
126 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f405zg,defmt,exti,time-driver-any \
127 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f407zg,defmt,exti,time-driver-any \
128 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any,time \
129 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f405zg,defmt,exti,time-driver-any,time \
130 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f407zg,defmt,exti,time-driver-any,time \
131 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,time \
132 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,exti,time-driver-any,time \
133 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f412zg,defmt,exti,time-driver-any,time \
134 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f413vh,defmt,exti,time-driver-any,time \
135 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f415zg,defmt,exti,time-driver-any,time \
136 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f417zg,defmt,exti,time-driver-any,time \
137 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f423zh,defmt,exti,time-driver-any,time \
138 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f427zi,defmt,exti,time-driver-any,time \
139 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,time \
140 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f437zi,log,exti,time-driver-any,time \
141 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f439zi,defmt,exti,time-driver-any,time \
142 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446ze,defmt,exti,time-driver-any,time \
143 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f469zi,defmt,exti,time-driver-any,time \
144 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f479zi,defmt,exti,time-driver-any,time \
145 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f730i8,defmt,exti,time-driver-any,time \
146 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi,defmt,exti,time-driver-any,time \
147 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h735zg,defmt,exti,time-driver-any,time \
148 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time,split-pc2,split-pc3 \
149 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h725re,defmt,exti,time-driver-any,time \
150 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-any,time \
151 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-tim1,time \
152 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7r3z8,defmt,exti,time-driver-tim1,time \
153 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7r7a8,defmt,exti,time-driver-tim1,time \
154 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7s3a8,defmt,exti,time-driver-tim1,time \
155 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7s7z8,defmt,exti,time-driver-tim1,time \
156 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l431cb,defmt,exti,time-driver-any,time \
157 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \
158 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l422cb,defmt,exti,time-driver-any,time \
159 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb15cc,defmt,exti,time-driver-any,time \
160 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,time \
161 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l041f6,defmt,exti,time-driver-any,time \
162 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l051k8,defmt,exti,time-driver-any,time \
163 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073cz,defmt,exti,time-driver-any,low-power,time \
164 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,time \
165 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303c8,defmt,exti,time-driver-any,time \
166 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \
167 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \
168 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0b0ce,defmt,exti,time-driver-any,time \
169 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0c1ve,defmt,exti,time-driver-any,time \
170 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \
171 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,low-power,time \
172 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32wl54jc-cm0p,defmt,exti,time-driver-any,time \
173 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wle5jb,defmt,exti,time-driver-any,time \
174 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g431kb,defmt,exti,time-driver-any,time \
175 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,dual-bank,defmt,exti,time-driver-any,time \
176 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc,defmt,exti,time-driver-any,time \
177 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103re,defmt,exti,time-driver-any,time \
178 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f100c4,defmt,exti,time-driver-any,time \
179 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb,defmt,exti,time-driver-any,time \
180 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h523cc,defmt,exti,time-driver-any,time \
181 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \
182 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba50ke,defmt,exti,time-driver-any,time \
183 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba55ug,defmt,exti,time-driver-any,time \
184 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba62cg,defmt,exti,time-driver-any,time \
185 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba65ri,defmt,exti,time-driver-any,low-power,time \
186 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5f9zj,defmt,exti,time-driver-any,time \
187 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5g9nj,defmt,exti,time-driver-any,time \
188 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \
189 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg,defmt,exti,time-driver-any,low-power,time \
190 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \
191 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \
192 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \
193 --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf --features lpc55,defmt \
194 --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1011,rt,defmt,time-driver-pit \
195 --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1062,rt,defmt,time-driver-pit \
196 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \
197 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \
198 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \
199 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1306rhb,defmt,time-driver-any \
200 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l2228pn,defmt,time-driver-any \
201 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1345dgs28,defmt,time-driver-any \
202 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1106dgs28,defmt,time-driver-any \
203 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1228pm,defmt,time-driver-any \
204 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1107ycj,defmt,time-driver-any \
205 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3105rhb,defmt,time-driver-any \
206 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1505pt,defmt,time-driver-any \
207 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1519rhb,defmt,time-driver-any \
208 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\
209 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \
210 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
211 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs' \
212 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs' \
213 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs,bluetooth' \
214 --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs,bluetooth' \
215 --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040' \
216 --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040' \
217 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
218 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf5340-app-s \
219 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
220 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \
221 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns \
222 --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns \
223 --- build --release --manifest-path embassy-boot-rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/rp2040 \
224 --- build --release --manifest-path embassy-boot-stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32l496zg \
225 --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --no-default-features \
226 --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi \
227 --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features log \
228 --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features defmt \
229 --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features usbd-hid \
230 --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-interface-count-1 \
231 --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-interface-count-8 \
232 --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-handler-count-8 \
233 --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-handler-count-8 \
234 --- build --release --manifest-path docs/examples/basic/Cargo.toml --target thumbv7em-none-eabi \
235 --- build --release --manifest-path docs/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \
236 --- build --release --manifest-path docs/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \
237 --- build --release --manifest-path docs/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \
238 --- build --release --manifest-path docs/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \
239 --- build --release --manifest-path examples/nrf52810/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52810 \
240 --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840 \
241 --- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf5340 \
242 --- build --release --manifest-path examples/nrf54l15/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf54l15 \
243 --- build --release --manifest-path examples/nrf9160/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf9160 \
244 --- build --release --manifest-path examples/nrf9151/s/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf9151/s \
245 --- build --release --manifest-path examples/nrf9151/ns/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf9151/ns \
246 --- build --release --manifest-path examples/nrf51/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/nrf51 \
247 --- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/rp \
248 --- build --release --manifest-path examples/rp235x/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/rp235x \
249 --- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32f0 \
250 --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --artifact-dir out/examples/stm32f1 \
251 --- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --artifact-dir out/examples/stm32f2 \
252 --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f3 \
253 --- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f334 \
254 --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f4 \
255 --- build --release --manifest-path examples/stm32f469/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f469 \
256 --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f7 \
257 --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32c0 \
258 --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32g0 \
259 --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32g4 \
260 --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32h5 \
261 --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7 \
262 --- build --release --manifest-path examples/stm32h7b0/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7b0 \
263 --- build --release --manifest-path examples/stm32h723/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h723 \
264 --- build --release --manifest-path examples/stm32h735/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h735 \
265 --- build --release --manifest-path examples/stm32h755cm4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm4 \
266 --- build --release --manifest-path examples/stm32h755cm7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm7 \
267 --- build --release --manifest-path examples/stm32h7rs/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7rs \
268 --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32l0 \
269 --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --artifact-dir out/examples/stm32l1 \
270 --- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32l4 \
271 --- build --release --manifest-path examples/stm32l432/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32l432 \
272 --- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32l5 \
273 --- build --release --manifest-path examples/stm32u0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32u0 \
274 --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32u5 \
275 --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wb \
276 --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \
277 --- build --release --manifest-path examples/stm32wba6/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba6 \
278 --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \
279 --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \
280 --- build --release --manifest-path examples/mimxrt1011/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1011 \
281 --- build --release --manifest-path examples/mimxrt1062-evk/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1062-evk \
282 --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \
283 --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \
284 --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \
285 --- build --release --manifest-path examples/mspm0l2228/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l2228 \
286 --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --artifact-dir out/examples/boot/nrf52840 \
287 --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --artifact-dir out/examples/boot/nrf9160 \
288 --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns,skip-include --artifact-dir out/examples/boot/nrf9120 \
289 --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns,skip-include --artifact-dir out/examples/boot/nrf9151 \
290 --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns,skip-include --artifact-dir out/examples/boot/nrf9161 \
291 --- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --artifact-dir out/examples/boot/rp \
292 --- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32f3 \
293 --- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32f7 \
294 --- build --release --manifest-path examples/boot/application/stm32h7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32h7 \
295 --- build --release --manifest-path examples/boot/application/stm32l0/Cargo.toml --target thumbv6m-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32l0 \
296 --- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32l1 \
297 --- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32l4 \
298 --- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32wl \
299 --- build --release --manifest-path examples/boot/application/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/boot/stm32wb-dfu \
300 --- build --release --manifest-path examples/boot/application/stm32wba-dfu/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/boot/stm32wba-dfu \
301 --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
302 --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
303 --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \
304 --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns \
305 --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns \
306 --- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \
307 --- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32l496zg \
308 --- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wb55rg \
309 --- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wb55rg,verify \
310 --- build --release --manifest-path examples/boot/bootloader/stm32wba-dfu/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-stm32/stm32wba65ri,verify \
311 --- build --release --manifest-path examples/boot/bootloader/stm32-dual-bank/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32h743zi \
312 --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --artifact-dir out/examples/wasm \
313 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --artifact-dir out/tests/stm32f103c8 \
314 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --artifact-dir out/tests/stm32f429zi \
315 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446re --artifact-dir out/tests/stm32f446re \
316 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --artifact-dir out/tests/stm32g491re \
317 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --artifact-dir out/tests/stm32g071rb \
318 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --artifact-dir out/tests/stm32c031c6 \
319 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb --artifact-dir out/tests/stm32c071rb \
320 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --artifact-dir out/tests/stm32h755zi \
321 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --artifact-dir out/tests/stm32h753zi \
322 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --artifact-dir out/tests/stm32h7a3zi \
323 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --artifact-dir out/tests/stm32wb55rg \
324 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h563zi --artifact-dir out/tests/stm32h563zi \
325 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u585ai --artifact-dir out/tests/stm32u585ai \
326 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5a5zj --artifact-dir out/tests/stm32u5a5zj \
327 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba52cg --artifact-dir out/tests/stm32wba52cg \
328 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073rz --artifact-dir out/tests/stm32l073rz \
329 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l152re --artifact-dir out/tests/stm32l152re \
330 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4a6zg --artifact-dir out/tests/stm32l4a6zg \
331 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r5zi --artifact-dir out/tests/stm32l4r5zi \
332 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze --artifact-dir out/tests/stm32l552ze \
333 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f767zi --artifact-dir out/tests/stm32f767zi \
334 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f207zg --artifact-dir out/tests/stm32f207zg \
335 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --artifact-dir out/tests/stm32f303ze \
336 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --artifact-dir out/tests/stm32l496zg \
337 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --artifact-dir out/tests/stm32wl55jc \
338 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7s3l8 --artifact-dir out/tests/stm32h7s3l8 \
339 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f091rc --artifact-dir out/tests/stm32f091rc \
340 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb --artifact-dir out/tests/stm32h503rb \
341 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc --artifact-dir out/tests/stm32u083rc \
342 --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --features rp2040 --artifact-dir out/tests/rpi-pico \
343 --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv8m.main-none-eabihf --features rp235xb --artifact-dir out/tests/pimoroni-pico-plus-2 \
344 --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51422 --artifact-dir out/tests/nrf51422-dk \
345 --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832 --artifact-dir out/tests/nrf52832-dk \
346 --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833 --artifact-dir out/tests/nrf52833-dk \
347 --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840 --artifact-dir out/tests/nrf52840-dk \
348 --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340 --artifact-dir out/tests/nrf5340-dk \
349 --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160 --artifact-dir out/tests/nrf9160-dk \
350 --- build --release --manifest-path tests/mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507 --artifact-dir out/tests/mspm0g3507 \
351 --- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
352 $BUILD_EXTRA
353
354
355# MSPM0C1104 must be built seperately since cargo batch does not consider env vars set in `.cargo/config.toml`.
356# Since the target has 1KB of ram, we need to limit defmt's buffer size.
357DEFMT_RTT_BUFFER_SIZE="72" cargo batch \
358 --- build --release --manifest-path examples/mspm0c1104/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0c1104 \
359 28
360# temporarily disabled, these boards are dead. 29# temporarily disabled, these boards are dead.
361rm -rf out/tests/stm32f103c8 30rm -rf out/tests/stm32f103c8
@@ -397,8 +66,10 @@ rm out/tests/pimoroni-pico-plus-2/pwm
397rm out/tests/rpi-pico/pwm 66rm out/tests/rpi-pico/pwm
398rm out/tests/rpi-pico/cyw43-perf 67rm out/tests/rpi-pico/cyw43-perf
399 68
400# tests are implemented but the HIL test farm doesn't actually have this board yet 69# tests are implemented but the HIL test farm doesn't actually have these boards, yet
401rm -rf out/tests/stm32c071rb 70rm -rf out/tests/stm32c071rb
71rm -rf out/tests/stm32f100rd
72rm -rf out/tests/stm32f107vc
402 73
403if [[ -z "${TELEPROBE_TOKEN-}" ]]; then 74if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
404 echo No teleprobe token found, skipping running HIL tests 75 echo No teleprobe token found, skipping running HIL tests
diff --git a/docs/pages/embassy_in_the_wild.adoc b/docs/pages/embassy_in_the_wild.adoc
index 620794c31..cedbedada 100644
--- a/docs/pages/embassy_in_the_wild.adoc
+++ b/docs/pages/embassy_in_the_wild.adoc
@@ -2,6 +2,10 @@
2 2
3Here are known examples of real-world projects which make use of Embassy. Feel free to link:https://github.com/embassy-rs/embassy/blob/main/docs/pages/embassy_in_the_wild.adoc[add more]! 3Here are known examples of real-world projects which make use of Embassy. Feel free to link:https://github.com/embassy-rs/embassy/blob/main/docs/pages/embassy_in_the_wild.adoc[add more]!
4 4
5_newer entries at the top_
6
7* link:https://github.com/CarlKCarlK/clock[Embassy Clock: Layered, modular bare-metal clock with emulation]
8** A `no_std` Raspberry Pi Pico clock demonstrating layered Embassy tasks (Display->Blinker->Clock) for clean separation of multiplexing, blinking, and UI logic. Features single-button HH:MM/MM:SS time-set UI, heapless data structures, and a Renode emulator for hardware-free testing. See link:https://medium.com/@carlmkadie/how-rust-embassy-shine-on-embedded-devices-part-2-aad1adfccf72[this article] for details.
5* link:https://github.com/1-rafael-1/simple-robot[A simple tracked robot based on Raspberry Pi Pico 2] 9* link:https://github.com/1-rafael-1/simple-robot[A simple tracked robot based on Raspberry Pi Pico 2]
6** A hobbyist project building a tracked robot with basic autonomous and manual drive mode. 10** A hobbyist project building a tracked robot with basic autonomous and manual drive mode.
7* link:https://github.com/1-rafael-1/pi-pico-alarmclock-rust[A Raspberry Pi Pico W Alarmclock] 11* link:https://github.com/1-rafael-1/pi-pico-alarmclock-rust[A Raspberry Pi Pico W Alarmclock]
diff --git a/embassy-executor-macros/Cargo.toml b/embassy-executor-macros/Cargo.toml
index f803e6644..9c2b40d03 100644
--- a/embassy-executor-macros/Cargo.toml
+++ b/embassy-executor-macros/Cargo.toml
@@ -23,3 +23,4 @@ proc-macro = true
23 23
24[features] 24[features]
25nightly = [] 25nightly = []
26metadata-name = []
diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs
index f043ff7f6..dc470e51c 100644
--- a/embassy-executor-macros/src/macros/main.rs
+++ b/embassy-executor-macros/src/macros/main.rs
@@ -170,7 +170,7 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe
170 let f_body = f.body; 170 let f_body = f.body;
171 let out = &f.sig.output; 171 let out = &f.sig.output;
172 172
173 let name_main_task = if cfg!(feature = "rtos-trace") { 173 let name_main_task = if cfg!(feature = "metadata-name") {
174 quote!( 174 quote!(
175 main_task.metadata().set_name("main\0"); 175 main_task.metadata().set_name("main\0");
176 ) 176 )
diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md
index 3e6c180c6..6f079a11a 100644
--- a/embassy-executor/CHANGELOG.md
+++ b/embassy-executor/CHANGELOG.md
@@ -8,9 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Added new metadata API for tasks 11- Added new metadata API for tasks.
12- Named main task when rtos-trace feature is enabled. 12- Main task automatically gets a name of `main` when the `metadata-name` feature is enabled.
13- Upgraded rtos-trace 13- Upgraded rtos-trace
14- Added optional "highest priority" scheduling
15- Added optional "earliest deadline first" EDF scheduling
14 16
15## 0.9.1 - 2025-08-31 17## 0.9.1 - 2025-08-31
16 18
@@ -20,8 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
20 22
21- Added `extern "Rust" fn __embassy_time_queue_item_from_waker` 23- Added `extern "Rust" fn __embassy_time_queue_item_from_waker`
22- Removed `TaskRef::dangling` 24- Removed `TaskRef::dangling`
23- Added `embassy_time_queue_utils` as a dependency 25- Added `embassy-executor-timer-queue` as a dependency
24- Moved the `TimeQueueItem` struct and `timer-item-payload-size-*` features into embassy-time-queue-utils 26- Moved the `TimeQueueItem` struct and `timer-item-payload-size-*` features (as `timer-item-size-X-words`) into `embassy-executor-timer-queue`
25 27
26## 0.8.0 - 2025-07-31 28## 0.8.0 - 2025-07-31
27 29
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml
index dc423aba2..f6dce5c0e 100644
--- a/embassy-executor/Cargo.toml
+++ b/embassy-executor/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-executor" 2name = "embassy-executor"
3version = "0.9.0" 3version = "0.9.1"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "async/await executor designed for embedded usage" 6description = "async/await executor designed for embedded usage"
@@ -24,18 +24,49 @@ build = [
24 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread"]}, 24 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread"]},
25 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt"]}, 25 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt"]},
26 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"]}, 26 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"]},
27 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver"]},
28 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-priority"]},
29 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-priority", "scheduler-deadline"]},
30 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-deadline"]},
31 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-priority", "scheduler-deadline"]},
32 {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-deadline"]},
27 {target = "armv7a-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, 33 {target = "armv7a-none-eabi", features = ["arch-cortex-ar", "executor-thread"]},
28 {target = "armv7r-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, 34 {target = "armv7r-none-eabi", features = ["arch-cortex-ar", "executor-thread"]},
29 {target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]}, 35 {target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]},
30 {target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32"]}, 36 {target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32"]},
31 {target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32", "executor-thread"]}, 37 {target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32", "executor-thread"]},
38 # Nightly builds
39 {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly"]},
40 {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "log"]},
41 {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "defmt"]},
42 {group = "nightly", target = "thumbv6m-none-eabi", features = ["nightly", "defmt"]},
43 {group = "nightly", target = "thumbv6m-none-eabi", features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]},
44 {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m"]},
45 {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m", "executor-thread"]},
46 {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m", "executor-interrupt"]},
47 {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt"]},
48 {group = "nightly", target = "riscv32imac-unknown-none-elf", features = ["nightly", "arch-riscv32"]},
49 {group = "nightly", target = "riscv32imac-unknown-none-elf", features = ["nightly", "arch-riscv32", "executor-thread"]},
50 {group = "nightly", target = "armv7a-none-eabi", features = ["nightly", "arch-cortex-ar", "executor-thread"]},
51 {group = "nightly", target = "avr-none", features = ["nightly", "arch-avr", "avr-device/atmega328p"], build-std = ["core", "alloc"], env = { RUSTFLAGS = "-Ctarget-cpu=atmega328p" }},
52 # Xtensa builds
53 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = []},
54 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["log"]},
55 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt"]},
56 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt"]},
57 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "arch-spin", "executor-thread"]},
58 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt", "arch-spin", "executor-thread"]},
59 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s3-none-elf", features = ["defmt", "arch-spin", "executor-thread"]},
60 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["arch-spin"]},
61 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["arch-spin", "rtos-trace"]},
62 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["arch-spin", "executor-thread"]},
32] 63]
33 64
34 65
35[package.metadata.embassy_docs] 66[package.metadata.embassy_docs]
36src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" 67src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
37src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" 68src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
38features = ["defmt"] 69features = ["defmt", "scheduler-deadline", "scheduler-priority"]
39flavors = [ 70flavors = [
40 { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, 71 { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] },
41 { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, 72 { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] },
@@ -46,7 +77,7 @@ flavors = [
46[package.metadata.docs.rs] 77[package.metadata.docs.rs]
47default-target = "thumbv7em-none-eabi" 78default-target = "thumbv7em-none-eabi"
48targets = ["thumbv7em-none-eabi"] 79targets = ["thumbv7em-none-eabi"]
49features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"] 80features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "scheduler-deadline", "scheduler-priority", "embassy-time-driver"]
50 81
51[dependencies] 82[dependencies]
52defmt = { version = "1.0.1", optional = true } 83defmt = { version = "1.0.1", optional = true }
@@ -76,6 +107,11 @@ js-sys = { version = "0.3", optional = true }
76# arch-avr dependencies 107# arch-avr dependencies
77avr-device = { version = "0.7.0", optional = true } 108avr-device = { version = "0.7.0", optional = true }
78 109
110
111[dependencies.cordyceps]
112version = "0.3.4"
113features = ["no-cache-pad"]
114
79[dev-dependencies] 115[dev-dependencies]
80critical-section = { version = "1.1", features = ["std"] } 116critical-section = { version = "1.1", features = ["std"] }
81trybuild = "1.0" 117trybuild = "1.0"
@@ -112,7 +148,7 @@ arch-spin = ["_arch"]
112#! ### Metadata 148#! ### Metadata
113 149
114## Enable the `name` field in task metadata. 150## Enable the `name` field in task metadata.
115metadata-name = [] 151metadata-name = ["embassy-executor-macros/metadata-name"]
116 152
117#! ### Executor 153#! ### Executor
118 154
@@ -123,21 +159,16 @@ executor-interrupt = []
123## Enable tracing hooks 159## Enable tracing hooks
124trace = ["_any_trace"] 160trace = ["_any_trace"]
125## Enable support for rtos-trace framework 161## Enable support for rtos-trace framework
126rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"] 162rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "embassy-time-driver"]
127_any_trace = [] 163_any_trace = []
128 164
129#! ### Timer Item Payload Size 165## Enable "Earliest Deadline First" Scheduler, using soft-realtime "deadlines" to prioritize
130#! Sets the size of the payload for timer items, allowing integrated timer implementors to store 166## tasks based on the remaining time before their deadline. Adds some overhead.
131#! additional data in the timer item. The payload field will be aligned to this value as well. 167scheduler-deadline = []
132#! If these features are not defined, the timer item will contain no payload field. 168
133 169## Enable "Highest Priority First" Scheduler. Adds some overhead.
134_timer-item-payload = [] # A size was picked 170scheduler-priority = []
135 171
136## 1 bytes 172## Enable the embassy_time_driver dependency.
137timer-item-payload-size-1 = ["_timer-item-payload"] 173## This can unlock extra APIs, for example for the `sheduler-deadline`
138## 2 bytes 174embassy-time-driver = ["dep:embassy-time-driver"]
139timer-item-payload-size-2 = ["_timer-item-payload"]
140## 4 bytes
141timer-item-payload-size-4 = ["_timer-item-payload"]
142## 8 bytes
143timer-item-payload-size-8 = ["_timer-item-payload"]
diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs
index f92c9b37c..bc0df0f83 100644
--- a/embassy-executor/src/metadata.rs
+++ b/embassy-executor/src/metadata.rs
@@ -1,17 +1,25 @@
1#[cfg(feature = "metadata-name")] 1#[cfg(feature = "metadata-name")]
2use core::cell::Cell; 2use core::cell::Cell;
3use core::future::{poll_fn, Future}; 3use core::future::{poll_fn, Future};
4#[cfg(feature = "scheduler-priority")]
5use core::sync::atomic::{AtomicU8, Ordering};
4use core::task::Poll; 6use core::task::Poll;
5 7
6#[cfg(feature = "metadata-name")] 8#[cfg(feature = "metadata-name")]
7use critical_section::Mutex; 9use critical_section::Mutex;
8 10
9use crate::raw; 11use crate::raw;
12#[cfg(feature = "scheduler-deadline")]
13use crate::raw::Deadline;
10 14
11/// Metadata associated with a task. 15/// Metadata associated with a task.
12pub struct Metadata { 16pub struct Metadata {
13 #[cfg(feature = "metadata-name")] 17 #[cfg(feature = "metadata-name")]
14 name: Mutex<Cell<Option<&'static str>>>, 18 name: Mutex<Cell<Option<&'static str>>>,
19 #[cfg(feature = "scheduler-priority")]
20 priority: AtomicU8,
21 #[cfg(feature = "scheduler-deadline")]
22 deadline: raw::Deadline,
15} 23}
16 24
17impl Metadata { 25impl Metadata {
@@ -19,12 +27,26 @@ impl Metadata {
19 Self { 27 Self {
20 #[cfg(feature = "metadata-name")] 28 #[cfg(feature = "metadata-name")]
21 name: Mutex::new(Cell::new(None)), 29 name: Mutex::new(Cell::new(None)),
30 #[cfg(feature = "scheduler-priority")]
31 priority: AtomicU8::new(0),
32 // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This
33 // will be lazily initalized in `initialize_impl`
34 #[cfg(feature = "scheduler-deadline")]
35 deadline: raw::Deadline::new_unset(),
22 } 36 }
23 } 37 }
24 38
25 pub(crate) fn reset(&self) { 39 pub(crate) fn reset(&self) {
26 #[cfg(feature = "metadata-name")] 40 #[cfg(feature = "metadata-name")]
27 critical_section::with(|cs| self.name.borrow(cs).set(None)); 41 critical_section::with(|cs| self.name.borrow(cs).set(None));
42
43 #[cfg(feature = "scheduler-priority")]
44 self.set_priority(0);
45
46 // By default, deadlines are set to the maximum value, so that any task WITH
47 // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline
48 #[cfg(feature = "scheduler-deadline")]
49 self.unset_deadline();
28 } 50 }
29 51
30 /// Get the metadata for the current task. 52 /// Get the metadata for the current task.
@@ -52,4 +74,75 @@ impl Metadata {
52 pub fn set_name(&self, name: &'static str) { 74 pub fn set_name(&self, name: &'static str) {
53 critical_section::with(|cs| self.name.borrow(cs).set(Some(name))) 75 critical_section::with(|cs| self.name.borrow(cs).set(Some(name)))
54 } 76 }
77
78 /// Get this task's priority.
79 #[cfg(feature = "scheduler-priority")]
80 pub fn priority(&self) -> u8 {
81 self.priority.load(Ordering::Relaxed)
82 }
83
84 /// Set this task's priority.
85 #[cfg(feature = "scheduler-priority")]
86 pub fn set_priority(&self, priority: u8) {
87 self.priority.store(priority, Ordering::Relaxed)
88 }
89
90 /// Get this task's deadline.
91 #[cfg(feature = "scheduler-deadline")]
92 pub fn deadline(&self) -> u64 {
93 self.deadline.instant_ticks()
94 }
95
96 /// Set this task's deadline.
97 ///
98 /// This method does NOT check whether the deadline has already passed.
99 #[cfg(feature = "scheduler-deadline")]
100 pub fn set_deadline(&self, instant_ticks: u64) {
101 self.deadline.set(instant_ticks);
102 }
103
104 /// Remove this task's deadline.
105 /// This brings it back to the defaul where it's not scheduled ahead of other tasks.
106 #[cfg(feature = "scheduler-deadline")]
107 pub fn unset_deadline(&self) {
108 self.deadline.set(Deadline::UNSET_TICKS);
109 }
110
111 /// Set this task's deadline `duration_ticks` in the future from when
112 /// this future is polled. This deadline is saturated to the max tick value.
113 ///
114 /// Analogous to `Timer::after`.
115 #[cfg(all(feature = "scheduler-deadline", feature = "embassy-time-driver"))]
116 pub fn set_deadline_after(&self, duration_ticks: u64) {
117 let now = embassy_time_driver::now();
118
119 // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave
120 // it for now, we can probably make this wrapping_add for performance
121 // reasons later.
122 let deadline = now.saturating_add(duration_ticks);
123
124 self.set_deadline(deadline);
125 }
126
127 /// Set the this task's deadline `increment_ticks` from the previous deadline.
128 ///
129 /// This deadline is saturated to the max tick value.
130 ///
131 /// Note that by default (unless otherwise set), tasks start life with the deadline
132 /// not set, which means this method will have no effect.
133 ///
134 /// Analogous to one increment of `Ticker::every().next()`.
135 ///
136 /// Returns the deadline that was set.
137 #[cfg(feature = "scheduler-deadline")]
138 pub fn increment_deadline(&self, duration_ticks: u64) {
139 let last = self.deadline();
140
141 // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave
142 // it for now, we can probably make this wrapping_add for performance
143 // reasons later.
144 let deadline = last.saturating_add(duration_ticks);
145
146 self.set_deadline(deadline);
147 }
55} 148}
diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs
new file mode 100644
index 000000000..cc89fadb0
--- /dev/null
+++ b/embassy-executor/src/raw/deadline.rs
@@ -0,0 +1,44 @@
1use core::sync::atomic::{AtomicU32, Ordering};
2
3/// A type for interacting with the deadline of the current task
4///
5/// Requires the `scheduler-deadline` feature.
6///
7/// Note: Interacting with the deadline should be done locally in a task.
8/// In theory you could try to set or read the deadline from another task,
9/// but that will result in weird (though not unsound) behavior.
10pub(crate) struct Deadline {
11 instant_ticks_hi: AtomicU32,
12 instant_ticks_lo: AtomicU32,
13}
14
15impl Deadline {
16 pub(crate) const fn new(instant_ticks: u64) -> Self {
17 Self {
18 instant_ticks_hi: AtomicU32::new((instant_ticks >> 32) as u32),
19 instant_ticks_lo: AtomicU32::new(instant_ticks as u32),
20 }
21 }
22
23 pub(crate) const fn new_unset() -> Self {
24 Self::new(Self::UNSET_TICKS)
25 }
26
27 pub(crate) fn set(&self, instant_ticks: u64) {
28 self.instant_ticks_hi
29 .store((instant_ticks >> 32) as u32, Ordering::Relaxed);
30 self.instant_ticks_lo.store(instant_ticks as u32, Ordering::Relaxed);
31 }
32
33 /// Deadline value in ticks, same time base and ticks as `embassy-time`
34 pub(crate) fn instant_ticks(&self) -> u64 {
35 let hi = self.instant_ticks_hi.load(Ordering::Relaxed) as u64;
36 let lo = self.instant_ticks_lo.load(Ordering::Relaxed) as u64;
37
38 (hi << 32) | lo
39 }
40
41 /// Sentinel value representing an "unset" deadline, which has lower priority
42 /// than any other set deadline value
43 pub(crate) const UNSET_TICKS: u64 = u64::MAX;
44}
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index 4280c5750..9f36c60bc 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -7,8 +7,6 @@
7//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe 7//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe
8//! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_executor_macros::task) macro, which are fully safe. 8//! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_executor_macros::task) macro, which are fully safe.
9 9
10#[cfg_attr(target_has_atomic = "ptr", path = "run_queue_atomics.rs")]
11#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")]
12mod run_queue; 10mod run_queue;
13 11
14#[cfg_attr(all(cortex_m, target_has_atomic = "32"), path = "state_atomics_arm.rs")] 12#[cfg_attr(all(cortex_m, target_has_atomic = "32"), path = "state_atomics_arm.rs")]
@@ -28,6 +26,9 @@ pub(crate) mod util;
28#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] 26#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")]
29mod waker; 27mod waker;
30 28
29#[cfg(feature = "scheduler-deadline")]
30mod deadline;
31
31use core::future::Future; 32use core::future::Future;
32use core::marker::PhantomData; 33use core::marker::PhantomData;
33use core::mem; 34use core::mem;
@@ -38,6 +39,8 @@ use core::sync::atomic::AtomicPtr;
38use core::sync::atomic::Ordering; 39use core::sync::atomic::Ordering;
39use core::task::{Context, Poll, Waker}; 40use core::task::{Context, Poll, Waker};
40 41
42#[cfg(feature = "scheduler-deadline")]
43pub(crate) use deadline::Deadline;
41use embassy_executor_timer_queue::TimerQueueItem; 44use embassy_executor_timer_queue::TimerQueueItem;
42#[cfg(feature = "arch-avr")] 45#[cfg(feature = "arch-avr")]
43use portable_atomic::AtomicPtr; 46use portable_atomic::AtomicPtr;
@@ -96,6 +99,7 @@ extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static
96pub(crate) struct TaskHeader { 99pub(crate) struct TaskHeader {
97 pub(crate) state: State, 100 pub(crate) state: State,
98 pub(crate) run_queue_item: RunQueueItem, 101 pub(crate) run_queue_item: RunQueueItem,
102
99 pub(crate) executor: AtomicPtr<SyncExecutor>, 103 pub(crate) executor: AtomicPtr<SyncExecutor>,
100 poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>, 104 poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
101 105
diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs
new file mode 100644
index 000000000..b8b052310
--- /dev/null
+++ b/embassy-executor/src/raw/run_queue.rs
@@ -0,0 +1,213 @@
1use core::ptr::{addr_of_mut, NonNull};
2
3use cordyceps::sorted_list::Links;
4use cordyceps::Linked;
5#[cfg(any(feature = "scheduler-priority", feature = "scheduler-deadline"))]
6use cordyceps::SortedList;
7
8#[cfg(target_has_atomic = "ptr")]
9type TransferStack<T> = cordyceps::TransferStack<T>;
10
11#[cfg(not(target_has_atomic = "ptr"))]
12type TransferStack<T> = MutexTransferStack<T>;
13
14use super::{TaskHeader, TaskRef};
15
16/// Use `cordyceps::sorted_list::Links` as the singly linked list
17/// for RunQueueItems.
18pub(crate) type RunQueueItem = Links<TaskHeader>;
19
20/// Implements the `Linked` trait, allowing for singly linked list usage
21/// of any of cordyceps' `TransferStack` (used for the atomic runqueue),
22/// `SortedList` (used with the DRS scheduler), or `Stack`, which is
23/// popped atomically from the `TransferStack`.
24unsafe impl Linked<Links<TaskHeader>> for TaskHeader {
25 type Handle = TaskRef;
26
27 // Convert a TaskRef into a TaskHeader ptr
28 fn into_ptr(r: TaskRef) -> NonNull<TaskHeader> {
29 r.ptr
30 }
31
32 // Convert a TaskHeader into a TaskRef
33 unsafe fn from_ptr(ptr: NonNull<TaskHeader>) -> TaskRef {
34 TaskRef { ptr }
35 }
36
37 // Given a pointer to a TaskHeader, obtain a pointer to the Links structure,
38 // which can be used to traverse to other TaskHeader nodes in the linked list
39 unsafe fn links(ptr: NonNull<TaskHeader>) -> NonNull<Links<TaskHeader>> {
40 let ptr: *mut TaskHeader = ptr.as_ptr();
41 NonNull::new_unchecked(addr_of_mut!((*ptr).run_queue_item))
42 }
43}
44
45/// Atomic task queue using a very, very simple lock-free linked-list queue:
46///
47/// To enqueue a task, task.next is set to the old head, and head is atomically set to task.
48///
49/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with
50/// null. Then the batch is iterated following the next pointers until null is reached.
51///
52/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK
53/// for our purposes: it can't create fairness problems since the next batch won't run until the
54/// current batch is completely processed, so even if a task enqueues itself instantly (for example
55/// by waking its own waker) can't prevent other tasks from running.
56pub(crate) struct RunQueue {
57 stack: TransferStack<TaskHeader>,
58}
59
60impl RunQueue {
61 pub const fn new() -> Self {
62 Self {
63 stack: TransferStack::new(),
64 }
65 }
66
67 /// Enqueues an item. Returns true if the queue was empty.
68 ///
69 /// # Safety
70 ///
71 /// `item` must NOT be already enqueued in any queue.
72 #[inline(always)]
73 pub(crate) unsafe fn enqueue(&self, task: TaskRef, _tok: super::state::Token) -> bool {
74 self.stack.push_was_empty(
75 task,
76 #[cfg(not(target_has_atomic = "ptr"))]
77 _tok,
78 )
79 }
80
81 /// # Standard atomic runqueue
82 ///
83 /// Empty the queue, then call `on_task` for each task that was in the queue.
84 /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue
85 /// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
86 #[cfg(not(any(feature = "scheduler-priority", feature = "scheduler-deadline")))]
87 pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
88 let taken = self.stack.take_all();
89 for taskref in taken {
90 run_dequeue(&taskref);
91 on_task(taskref);
92 }
93 }
94
95 /// # Earliest Deadline First Scheduler
96 ///
97 /// This algorithm will loop until all enqueued tasks are processed.
98 ///
99 /// Before polling a task, all currently enqueued tasks will be popped from the
100 /// runqueue, and will be added to the working `sorted` list, a linked-list that
101 /// sorts tasks by their deadline, with nearest deadline items in the front, and
102 /// furthest deadline items in the back.
103 ///
104 /// After popping and sorting all pending tasks, the SOONEST task will be popped
105 /// from the front of the queue, and polled by calling `on_task` on it.
106 ///
107 /// This process will repeat until the local `sorted` queue AND the global
108 /// runqueue are both empty, at which point this function will return.
109 #[cfg(any(feature = "scheduler-priority", feature = "scheduler-deadline"))]
110 pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
111 let mut sorted = SortedList::<TaskHeader>::new_with_cmp(|lhs, rhs| {
112 // compare by priority first
113 #[cfg(feature = "scheduler-priority")]
114 {
115 let lp = lhs.metadata.priority();
116 let rp = rhs.metadata.priority();
117 if lp != rp {
118 return lp.cmp(&rp).reverse();
119 }
120 }
121 // compare deadlines in case of tie.
122 #[cfg(feature = "scheduler-deadline")]
123 {
124 let ld = lhs.metadata.deadline();
125 let rd = rhs.metadata.deadline();
126 if ld != rd {
127 return ld.cmp(&rd);
128 }
129 }
130 core::cmp::Ordering::Equal
131 });
132
133 loop {
134 // For each loop, grab any newly pended items
135 let taken = self.stack.take_all();
136
137 // Sort these into the list - this is potentially expensive! We do an
138 // insertion sort of new items, which iterates the linked list.
139 //
140 // Something on the order of `O(n * m)`, where `n` is the number
141 // of new tasks, and `m` is the number of already pending tasks.
142 sorted.extend(taken);
143
144 // Pop the task with the SOONEST deadline. If there are no tasks
145 // pending, then we are done.
146 let Some(taskref) = sorted.pop_front() else {
147 return;
148 };
149
150 // We got one task, mark it as dequeued, and process the task.
151 run_dequeue(&taskref);
152 on_task(taskref);
153 }
154 }
155}
156
157/// atomic state does not require a cs...
158#[cfg(target_has_atomic = "ptr")]
159#[inline(always)]
160fn run_dequeue(taskref: &TaskRef) {
161 taskref.header().state.run_dequeue();
162}
163
164/// ...while non-atomic state does
165#[cfg(not(target_has_atomic = "ptr"))]
166#[inline(always)]
167fn run_dequeue(taskref: &TaskRef) {
168 critical_section::with(|cs| {
169 taskref.header().state.run_dequeue(cs);
170 })
171}
172
173/// A wrapper type that acts like TransferStack by wrapping a normal Stack in a CS mutex
174#[cfg(not(target_has_atomic = "ptr"))]
175struct MutexTransferStack<T: Linked<cordyceps::stack::Links<T>>> {
176 inner: critical_section::Mutex<core::cell::UnsafeCell<cordyceps::Stack<T>>>,
177}
178
179#[cfg(not(target_has_atomic = "ptr"))]
180impl<T: Linked<cordyceps::stack::Links<T>>> MutexTransferStack<T> {
181 const fn new() -> Self {
182 Self {
183 inner: critical_section::Mutex::new(core::cell::UnsafeCell::new(cordyceps::Stack::new())),
184 }
185 }
186
187 /// Push an item to the transfer stack, returning whether the stack was previously empty
188 fn push_was_empty(&self, item: T::Handle, token: super::state::Token) -> bool {
189 // SAFETY: The critical-section mutex guarantees that there is no *concurrent* access
190 // for the lifetime of the token, but does NOT protect against re-entrant access.
191 // However, we never *return* the reference, nor do we recurse (or call another method
192 // like `take_all`) that could ever allow for re-entrant aliasing. Therefore, the
193 // presence of the critical section is sufficient to guarantee exclusive access to
194 // the `inner` field for the purposes of this function.
195 let inner = unsafe { &mut *self.inner.borrow(token).get() };
196 let is_empty = inner.is_empty();
197 inner.push(item);
198 is_empty
199 }
200
201 fn take_all(&self) -> cordyceps::Stack<T> {
202 critical_section::with(|cs| {
203 // SAFETY: The critical-section mutex guarantees that there is no *concurrent* access
204 // for the lifetime of the token, but does NOT protect against re-entrant access.
205 // However, we never *return* the reference, nor do we recurse (or call another method
206 // like `push_was_empty`) that could ever allow for re-entrant aliasing. Therefore, the
207 // presence of the critical section is sufficient to guarantee exclusive access to
208 // the `inner` field for the purposes of this function.
209 let inner = unsafe { &mut *self.inner.borrow(cs).get() };
210 inner.take_all()
211 })
212 }
213}
diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue_atomics.rs
deleted file mode 100644
index ce511d79a..000000000
--- a/embassy-executor/src/raw/run_queue_atomics.rs
+++ /dev/null
@@ -1,88 +0,0 @@
1use core::ptr;
2use core::ptr::NonNull;
3use core::sync::atomic::{AtomicPtr, Ordering};
4
5use super::{TaskHeader, TaskRef};
6use crate::raw::util::SyncUnsafeCell;
7
8pub(crate) struct RunQueueItem {
9 next: SyncUnsafeCell<Option<TaskRef>>,
10}
11
12impl RunQueueItem {
13 pub const fn new() -> Self {
14 Self {
15 next: SyncUnsafeCell::new(None),
16 }
17 }
18}
19
20/// Atomic task queue using a very, very simple lock-free linked-list queue:
21///
22/// To enqueue a task, task.next is set to the old head, and head is atomically set to task.
23///
24/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with
25/// null. Then the batch is iterated following the next pointers until null is reached.
26///
27/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK
28/// for our purposes: it can't create fairness problems since the next batch won't run until the
29/// current batch is completely processed, so even if a task enqueues itself instantly (for example
30/// by waking its own waker) can't prevent other tasks from running.
31pub(crate) struct RunQueue {
32 head: AtomicPtr<TaskHeader>,
33}
34
35impl RunQueue {
36 pub const fn new() -> Self {
37 Self {
38 head: AtomicPtr::new(ptr::null_mut()),
39 }
40 }
41
42 /// Enqueues an item. Returns true if the queue was empty.
43 ///
44 /// # Safety
45 ///
46 /// `item` must NOT be already enqueued in any queue.
47 #[inline(always)]
48 pub(crate) unsafe fn enqueue(&self, task: TaskRef, _: super::state::Token) -> bool {
49 let mut was_empty = false;
50
51 self.head
52 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
53 was_empty = prev.is_null();
54 unsafe {
55 // safety: the pointer is either null or valid
56 let prev = NonNull::new(prev).map(|ptr| TaskRef::from_ptr(ptr.as_ptr()));
57 // safety: there are no concurrent accesses to `next`
58 task.header().run_queue_item.next.set(prev);
59 }
60 Some(task.as_ptr() as *mut _)
61 })
62 .ok();
63
64 was_empty
65 }
66
67 /// Empty the queue, then call `on_task` for each task that was in the queue.
68 /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue
69 /// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
70 pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
71 // Atomically empty the queue.
72 let ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel);
73
74 // safety: the pointer is either null or valid
75 let mut next = unsafe { NonNull::new(ptr).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())) };
76
77 // Iterate the linked list of tasks that were previously in the queue.
78 while let Some(task) = next {
79 // If the task re-enqueues itself, the `next` pointer will get overwritten.
80 // Therefore, first read the next pointer, and only then process the task.
81 // safety: there are no concurrent accesses to `next`
82 next = unsafe { task.header().run_queue_item.next.get() };
83
84 task.header().state.run_dequeue();
85 on_task(task);
86 }
87 }
88}
diff --git a/embassy-executor/src/raw/run_queue_critical_section.rs b/embassy-executor/src/raw/run_queue_critical_section.rs
deleted file mode 100644
index 86c4085ed..000000000
--- a/embassy-executor/src/raw/run_queue_critical_section.rs
+++ /dev/null
@@ -1,74 +0,0 @@
1use core::cell::Cell;
2
3use critical_section::{CriticalSection, Mutex};
4
5use super::TaskRef;
6
7pub(crate) struct RunQueueItem {
8 next: Mutex<Cell<Option<TaskRef>>>,
9}
10
11impl RunQueueItem {
12 pub const fn new() -> Self {
13 Self {
14 next: Mutex::new(Cell::new(None)),
15 }
16 }
17}
18
19/// Atomic task queue using a very, very simple lock-free linked-list queue:
20///
21/// To enqueue a task, task.next is set to the old head, and head is atomically set to task.
22///
23/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with
24/// null. Then the batch is iterated following the next pointers until null is reached.
25///
26/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK
27/// for our purposes: it can't create fairness problems since the next batch won't run until the
28/// current batch is completely processed, so even if a task enqueues itself instantly (for example
29/// by waking its own waker) can't prevent other tasks from running.
30pub(crate) struct RunQueue {
31 head: Mutex<Cell<Option<TaskRef>>>,
32}
33
34impl RunQueue {
35 pub const fn new() -> Self {
36 Self {
37 head: Mutex::new(Cell::new(None)),
38 }
39 }
40
41 /// Enqueues an item. Returns true if the queue was empty.
42 ///
43 /// # Safety
44 ///
45 /// `item` must NOT be already enqueued in any queue.
46 #[inline(always)]
47 pub(crate) unsafe fn enqueue(&self, task: TaskRef, cs: CriticalSection<'_>) -> bool {
48 let prev = self.head.borrow(cs).replace(Some(task));
49 task.header().run_queue_item.next.borrow(cs).set(prev);
50
51 prev.is_none()
52 }
53
54 /// Empty the queue, then call `on_task` for each task that was in the queue.
55 /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue
56 /// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
57 pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
58 // Atomically empty the queue.
59 let mut next = critical_section::with(|cs| self.head.borrow(cs).take());
60
61 // Iterate the linked list of tasks that were previously in the queue.
62 while let Some(task) = next {
63 // If the task re-enqueues itself, the `next` pointer will get overwritten.
64 // Therefore, first read the next pointer, and only then process the task.
65
66 critical_section::with(|cs| {
67 next = task.header().run_queue_item.next.borrow(cs).get();
68 task.header().state.run_dequeue(cs);
69 });
70
71 on_task(task);
72 }
73 }
74}
diff --git a/embassy-mspm0/CHANGELOG.md b/embassy-mspm0/CHANGELOG.md
index eca0defd7..b846f21b1 100644
--- a/embassy-mspm0/CHANGELOG.md
+++ b/embassy-mspm0/CHANGELOG.md
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7 7
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- feat: Add I2C Controller (blocking & async) + examples for mspm0l1306, mspm0g3507 (tested MCUs) (#4435) 11- feat: Add I2C Controller (blocking & async) + examples for mspm0l1306, mspm0g3507 (tested MCUs) (#4435)
12- fix gpio interrupt not being set for mspm0l110x 12- fix gpio interrupt not being set for mspm0l110x
13- feat: Add window watchdog implementation based on WWDT0, WWDT1 peripherals (#4574) 13- feat: Add window watchdog implementation based on WWDT0, WWDT1 peripherals (#4574)
14- feat: Add MSPM0C1105/C1106 support
15- feat: Add adc implementation (#4646)
diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml
index 92f7a2655..1b32c4d43 100644
--- a/embassy-mspm0/Cargo.toml
+++ b/embassy-mspm0/Cargo.toml
@@ -69,7 +69,7 @@ cortex-m = "0.7.6"
69critical-section = "1.2.0" 69critical-section = "1.2.0"
70 70
71# mspm0-metapac = { version = "" } 71# mspm0-metapac = { version = "" }
72mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-fe17d879548757ca29821da66a1bebf2debd4846" } 72mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-d7bf3d01ac0780e716a45b0474234d39443dc5cf" }
73 73
74[build-dependencies] 74[build-dependencies]
75proc-macro2 = "1.0.94" 75proc-macro2 = "1.0.94"
@@ -77,7 +77,7 @@ quote = "1.0.40"
77cfg_aliases = "0.2.1" 77cfg_aliases = "0.2.1"
78 78
79# mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } 79# mspm0-metapac = { version = "", default-features = false, features = ["metadata"] }
80mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-fe17d879548757ca29821da66a1bebf2debd4846", default-features = false, features = ["metadata"] } 80mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-d7bf3d01ac0780e716a45b0474234d39443dc5cf", default-features = false, features = ["metadata"] }
81 81
82[features] 82[features]
83default = ["rt"] 83default = ["rt"]
@@ -159,6 +159,24 @@ mspm0c1104dsg = ["mspm0-metapac/mspm0c1104dsg"]
159mspm0c1104dyy = ["mspm0-metapac/mspm0c1104dyy"] 159mspm0c1104dyy = ["mspm0-metapac/mspm0c1104dyy"]
160mspm0c1104ruk = ["mspm0-metapac/mspm0c1104ruk"] 160mspm0c1104ruk = ["mspm0-metapac/mspm0c1104ruk"]
161mspm0c1104ycj = ["mspm0-metapac/mspm0c1104ycj"] 161mspm0c1104ycj = ["mspm0-metapac/mspm0c1104ycj"]
162mspm0c1105pt = ["mspm0-metapac/mspm0c1105pt"]
163mspm0c1105rgz = ["mspm0-metapac/mspm0c1105rgz"]
164mspm0c1105rhb = ["mspm0-metapac/mspm0c1105rhb"]
165mspm0c1105dgs32 = ["mspm0-metapac/mspm0c1105dgs32"]
166mspm0c1105dgs28 = ["mspm0-metapac/mspm0c1105dgs28"]
167mspm0c1105rge = ["mspm0-metapac/mspm0c1105rge"]
168mspm0c1105dgs20 = ["mspm0-metapac/mspm0c1105dgs20"]
169mspm0c1105ruk = ["mspm0-metapac/mspm0c1105ruk"]
170mspm0c1105zcm = ["mspm0-metapac/mspm0c1105zcm"]
171mspm0c1106pt = ["mspm0-metapac/mspm0c1106pt"]
172mspm0c1106rgz = ["mspm0-metapac/mspm0c1106rgz"]
173mspm0c1106rhb = ["mspm0-metapac/mspm0c1106rhb"]
174mspm0c1106dgs32 = ["mspm0-metapac/mspm0c1106dgs32"]
175mspm0c1106dgs28 = ["mspm0-metapac/mspm0c1106dgs28"]
176mspm0c1106rge = ["mspm0-metapac/mspm0c1106rge"]
177mspm0c1106dgs20 = ["mspm0-metapac/mspm0c1106dgs20"]
178mspm0c1106ruk = ["mspm0-metapac/mspm0c1106ruk"]
179mspm0c1106zcm = ["mspm0-metapac/mspm0c1106zcm"]
162mspm0g1105dgs28 = ["mspm0-metapac/mspm0g1105dgs28"] 180mspm0g1105dgs28 = ["mspm0-metapac/mspm0g1105dgs28"]
163mspm0g1105pm = ["mspm0-metapac/mspm0g1105pm"] 181mspm0g1105pm = ["mspm0-metapac/mspm0g1105pm"]
164mspm0g1105pt = ["mspm0-metapac/mspm0g1105pt"] 182mspm0g1105pt = ["mspm0-metapac/mspm0g1105pt"]
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs
index 256192f8b..d294bc422 100644
--- a/embassy-mspm0/build.rs
+++ b/embassy-mspm0/build.rs
@@ -68,6 +68,7 @@ fn generate_code() {
68 g.extend(generate_pin_trait_impls()); 68 g.extend(generate_pin_trait_impls());
69 g.extend(generate_groups()); 69 g.extend(generate_groups());
70 g.extend(generate_dma_channel_count()); 70 g.extend(generate_dma_channel_count());
71 g.extend(generate_adc_constants(&mut cfgs));
71 72
72 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 73 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
73 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); 74 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
@@ -79,10 +80,14 @@ fn get_chip_cfgs(chip_name: &str) -> Vec<String> {
79 let mut cfgs = Vec::new(); 80 let mut cfgs = Vec::new();
80 81
81 // GPIO on C110x is special as it does not belong to an interrupt group. 82 // GPIO on C110x is special as it does not belong to an interrupt group.
82 if chip_name.starts_with("mspm0c110") || chip_name.starts_with("msps003f") { 83 if chip_name.starts_with("mspm0c1103") || chip_name.starts_with("mspm0c1104") || chip_name.starts_with("msps003f") {
83 cfgs.push("mspm0c110x".to_string()); 84 cfgs.push("mspm0c110x".to_string());
84 } 85 }
85 86
87 if chip_name.starts_with("mspm0c1105") || chip_name.starts_with("mspm0c1106") {
88 cfgs.push("mspm0c1105_c1106".to_string());
89 }
90
86 // Family ranges (temporary until int groups are generated) 91 // Family ranges (temporary until int groups are generated)
87 // 92 //
88 // TODO: Remove this once int group stuff is generated. 93 // TODO: Remove this once int group stuff is generated.
@@ -216,6 +221,22 @@ fn generate_dma_channel_count() -> TokenStream {
216 quote! { pub const DMA_CHANNELS: usize = #count; } 221 quote! { pub const DMA_CHANNELS: usize = #count; }
217} 222}
218 223
224fn generate_adc_constants(cfgs: &mut CfgSet) -> TokenStream {
225 let vrsel = METADATA.adc_vrsel;
226 let memctl = METADATA.adc_memctl;
227
228 cfgs.declare("adc_neg_vref");
229 match vrsel {
230 3 => (),
231 5 => cfgs.enable("adc_neg_vref"),
232 _ => panic!("Unsupported ADC VRSEL value: {vrsel}"),
233 }
234 quote! {
235 pub const ADC_VRSEL: u8 = #vrsel;
236 pub const ADC_MEMCTL: u8 = #memctl;
237 }
238}
239
219#[derive(Debug, Clone)] 240#[derive(Debug, Clone)]
220struct Singleton { 241struct Singleton {
221 name: String, 242 name: String,
@@ -537,6 +558,8 @@ fn generate_interrupts() -> TokenStream {
537 pub fn enable_group_interrupts(_cs: critical_section::CriticalSection) { 558 pub fn enable_group_interrupts(_cs: critical_section::CriticalSection) {
538 use crate::interrupt::typelevel::Interrupt; 559 use crate::interrupt::typelevel::Interrupt;
539 560
561 // This is empty for C1105/6
562 #[allow(unused_unsafe)]
540 unsafe { 563 unsafe {
541 #(#group_interrupt_enables)* 564 #(#group_interrupt_enables)*
542 } 565 }
@@ -555,6 +578,7 @@ fn generate_peripheral_instances() -> TokenStream {
555 "uart" => Some(quote! { impl_uart_instance!(#peri); }), 578 "uart" => Some(quote! { impl_uart_instance!(#peri); }),
556 "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }), 579 "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }),
557 "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }), 580 "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }),
581 "adc" => Some(quote! { impl_adc_instance!(#peri); }),
558 _ => None, 582 _ => None,
559 }; 583 };
560 584
@@ -603,6 +627,10 @@ fn generate_pin_trait_impls() -> TokenStream {
603 ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }), 627 ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }),
604 ("i2c", "SDA") => Some(quote! { impl_i2c_sda_pin!(#peri, #pin_name, #pf); }), 628 ("i2c", "SDA") => Some(quote! { impl_i2c_sda_pin!(#peri, #pin_name, #pf); }),
605 ("i2c", "SCL") => Some(quote! { impl_i2c_scl_pin!(#peri, #pin_name, #pf); }), 629 ("i2c", "SCL") => Some(quote! { impl_i2c_scl_pin!(#peri, #pin_name, #pf); }),
630 ("adc", s) => {
631 let signal = s.parse::<u8>().unwrap();
632 Some(quote! { impl_adc_pin!(#peri, #pin_name, #signal); })
633 }
606 634
607 _ => None, 635 _ => None,
608 }; 636 };
diff --git a/embassy-mspm0/src/adc.rs b/embassy-mspm0/src/adc.rs
new file mode 100644
index 000000000..5b93e9a6e
--- /dev/null
+++ b/embassy-mspm0/src/adc.rs
@@ -0,0 +1,510 @@
1#![macro_use]
2
3use core::future::poll_fn;
4use core::marker::PhantomData;
5use core::task::Poll;
6
7use embassy_hal_internal::{impl_peripheral, PeripheralType};
8use embassy_sync::waitqueue::AtomicWaker;
9
10use crate::interrupt::{Interrupt, InterruptExt};
11use crate::mode::{Async, Blocking, Mode};
12use crate::pac::adc::{vals, Adc as Regs};
13use crate::{interrupt, Peri};
14
15/// Interrupt handler.
16pub struct InterruptHandler<T: Instance> {
17 _phantom: PhantomData<T>,
18}
19
20impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
21 unsafe fn on_interrupt() {
22 // Mis is cleared upon reading iidx
23 let iidx = T::info().regs.cpu_int(0).iidx().read().stat();
24 // TODO: Running in sequence mode, we get an interrupt per finished result. It would be
25 // nice to wake up only after all results are finished.
26 if vals::CpuIntIidxStat::MEMRESIFG0 <= iidx && iidx <= vals::CpuIntIidxStat::MEMRESIFG23 {
27 T::state().waker.wake();
28 }
29 }
30}
31
32// Constants from the metapac crate
33const ADC_VRSEL: u8 = crate::_generated::ADC_VRSEL;
34const ADC_MEMCTL: u8 = crate::_generated::ADC_MEMCTL;
35
36#[derive(Clone, Copy, PartialEq, Eq, Debug)]
37#[cfg_attr(feature = "defmt", derive(defmt::Format))]
38/// Conversion resolution of the ADC results.
39pub enum Resolution {
40 /// 12-bits resolution
41 BIT12,
42
43 /// 10-bits resolution
44 BIT10,
45
46 /// 8-bits resolution
47 BIT8,
48}
49
50impl Resolution {
51 /// Number of bits of the resolution.
52 pub fn bits(&self) -> u8 {
53 match self {
54 Resolution::BIT12 => 12,
55 Resolution::BIT10 => 10,
56 Resolution::BIT8 => 8,
57 }
58 }
59}
60
61#[derive(Clone, Copy, PartialEq, Eq, Debug)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63/// Reference voltage (Vref) selection for the ADC channels.
64pub enum Vrsel {
65 /// VDDA reference
66 VddaVssa = 0,
67
68 /// External reference from pin
69 ExtrefVrefm = 1,
70
71 /// Internal reference
72 IntrefVssa = 2,
73
74 /// VDDA and VREFM connected to VREF+ and VREF- of ADC
75 #[cfg(adc_neg_vref)]
76 VddaVrefm = 3,
77
78 /// INTREF and VREFM connected to VREF+ and VREF- of ADC
79 #[cfg(adc_neg_vref)]
80 IntrefVrefm = 4,
81}
82
83/// ADC configuration.
84#[derive(Copy, Clone)]
85#[non_exhaustive]
86pub struct Config {
87 /// Resolution of the ADC conversion. The number of bits used to represent an ADC measurement.
88 pub resolution: Resolution,
89 /// ADC voltage reference selection.
90 ///
91 /// This value is used when reading a single channel. When reading a sequence
92 /// the vr_select is provided per channel.
93 pub vr_select: Vrsel,
94 /// The sample time in number of ADC sample clock cycles.
95 pub sample_time: u16,
96}
97
98impl Default for Config {
99 fn default() -> Self {
100 Self {
101 resolution: Resolution::BIT12,
102 vr_select: Vrsel::VddaVssa,
103 sample_time: 50,
104 }
105 }
106}
107
108/// ADC (Analog to Digial Converter) Driver.
109pub struct Adc<'d, T: Instance, M: Mode> {
110 #[allow(unused)]
111 adc: crate::Peri<'d, T>,
112 info: &'static Info,
113 state: &'static State,
114 config: Config,
115 _phantom: PhantomData<M>,
116}
117
118impl<'d, T: Instance> Adc<'d, T, Blocking> {
119 /// A new blocking ADC driver instance.
120 pub fn new_blocking(peri: Peri<'d, T>, config: Config) -> Self {
121 let mut this = Self {
122 adc: peri,
123 info: T::info(),
124 state: T::state(),
125 config,
126 _phantom: PhantomData,
127 };
128 this.setup();
129 this
130 }
131}
132
133impl<'d, T: Instance> Adc<'d, T, Async> {
134 /// A new asynchronous ADC driver instance.
135 pub fn new_async(
136 peri: Peri<'d, T>,
137 config: Config,
138 _irqs: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
139 ) -> Self {
140 let mut this = Self {
141 adc: peri,
142 info: T::info(),
143 state: T::state(),
144 config,
145 _phantom: PhantomData,
146 };
147 this.setup();
148 unsafe {
149 this.info.interrupt.enable();
150 }
151 this
152 }
153}
154
155impl<'d, T: Instance, M: Mode> Adc<'d, T, M> {
156 const SINGLE_CHANNEL: u8 = 0;
157
158 fn setup(&mut self) {
159 let config = &self.config;
160 assert!(
161 (config.vr_select as u8) < ADC_VRSEL,
162 "Reference voltage selection out of bounds"
163 );
164 // Reset adc
165 self.info.regs.gprcm(0).rstctl().write(|reg| {
166 reg.set_resetstkyclr(true);
167 reg.set_resetassert(true);
168 reg.set_key(vals::ResetKey::KEY);
169 });
170
171 // Power up adc
172 self.info.regs.gprcm(0).pwren().modify(|reg| {
173 reg.set_enable(true);
174 reg.set_key(vals::PwrenKey::KEY);
175 });
176
177 // Wait for cycles similar to TI power setup
178 cortex_m::asm::delay(16);
179
180 // Set clock config
181 self.info.regs.gprcm(0).clkcfg().modify(|reg| {
182 reg.set_key(vals::ClkcfgKey::KEY);
183 reg.set_sampclk(vals::Sampclk::SYSOSC);
184 });
185 self.info.regs.ctl0().modify(|reg| {
186 reg.set_sclkdiv(vals::Sclkdiv::DIV_BY_4);
187 });
188 self.info.regs.clkfreq().modify(|reg| {
189 reg.set_frange(vals::Frange::RANGE24TO32);
190 });
191
192 // Init single conversion with software trigger and auto sampling
193 //
194 // We use sequence to support sequence operation in the future, but only set up a single
195 // channel
196 self.info.regs.ctl1().modify(|reg| {
197 reg.set_conseq(vals::Conseq::SEQUENCE);
198 reg.set_sampmode(vals::Sampmode::AUTO);
199 reg.set_trigsrc(vals::Trigsrc::SOFTWARE);
200 });
201 let res = match config.resolution {
202 Resolution::BIT12 => vals::Res::BIT_12,
203 Resolution::BIT10 => vals::Res::BIT_10,
204 Resolution::BIT8 => vals::Res::BIT_8,
205 };
206 self.info.regs.ctl2().modify(|reg| {
207 // Startadd detemines the channel used in single mode.
208 reg.set_startadd(Self::SINGLE_CHANNEL);
209 reg.set_endadd(Self::SINGLE_CHANNEL);
210 reg.set_res(res);
211 reg.set_df(false);
212 });
213
214 // Set the sample time used by all channels for now
215 self.info.regs.scomp0().modify(|reg| {
216 reg.set_val(config.sample_time);
217 });
218 }
219
220 fn setup_blocking_channel(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) {
221 channel.setup();
222
223 // CTL0.ENC must be 0 to write the MEMCTL register
224 while self.info.regs.ctl0().read().enc() {
225 // Wait until the ADC is not in conversion mode
226 }
227
228 // Conversion mem config
229 let vrsel = vals::Vrsel::from_bits(self.config.vr_select as u8);
230 self.info.regs.memctl(Self::SINGLE_CHANNEL as usize).modify(|reg| {
231 reg.set_chansel(channel.channel());
232 reg.set_vrsel(vrsel);
233 reg.set_stime(vals::Stime::SEL_SCOMP0);
234 reg.set_avgen(false);
235 reg.set_bcsen(false);
236 reg.set_trig(vals::Trig::AUTO_NEXT);
237 reg.set_wincomp(false);
238 });
239 self.info.regs.ctl2().modify(|reg| {
240 // Set end address to the number of used channels
241 reg.set_endadd(Self::SINGLE_CHANNEL);
242 });
243 }
244
245 fn enable_conversion(&mut self) {
246 // Enable conversion
247 self.info.regs.ctl0().modify(|reg| {
248 reg.set_enc(true);
249 });
250 }
251
252 fn start_conversion(&mut self) {
253 // Start conversion
254 self.info.regs.ctl1().modify(|reg| {
255 reg.set_sc(vals::Sc::START);
256 });
257 }
258
259 fn conversion_result(&mut self, channel_id: usize) -> u16 {
260 // Read result
261 self.info.regs.memres(channel_id).read().data()
262 }
263
264 /// Read one ADC channel in blocking mode using the config provided at initialization.
265 pub fn blocking_read(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) -> u16 {
266 self.setup_blocking_channel(channel);
267 self.enable_conversion();
268 self.start_conversion();
269
270 while self.info.regs.ctl0().read().enc() {}
271
272 self.conversion_result(Self::SINGLE_CHANNEL as usize)
273 }
274}
275
276impl<'d, T: Instance> Adc<'d, T, Async> {
277 /// Maximum length allowed for [`Self::read_sequence`].
278 pub const MAX_SEQUENCE_LEN: usize = ADC_MEMCTL as usize;
279
280 async fn wait_for_conversion(&self) {
281 let info = self.info;
282 let state = self.state;
283 poll_fn(move |cx| {
284 state.waker.register(cx.waker());
285
286 if !info.regs.ctl0().read().enc() {
287 Poll::Ready(())
288 } else {
289 Poll::Pending
290 }
291 })
292 .await;
293 }
294
295 fn setup_async_channel(&self, id: usize, channel: &Peri<'d, impl AdcChannel<T>>, vrsel: Vrsel) {
296 let vrsel = vals::Vrsel::from_bits(vrsel as u8);
297 // Conversion mem config
298 self.info.regs.memctl(id).modify(|reg| {
299 reg.set_chansel(channel.channel());
300 reg.set_vrsel(vrsel);
301 reg.set_stime(vals::Stime::SEL_SCOMP0);
302 reg.set_avgen(false);
303 reg.set_bcsen(false);
304 reg.set_trig(vals::Trig::AUTO_NEXT);
305 reg.set_wincomp(false);
306 });
307
308 // Clear interrupt status
309 self.info.regs.cpu_int(0).iclr().write(|reg| {
310 reg.set_memresifg(id, true);
311 });
312 // Enable interrupt
313 self.info.regs.cpu_int(0).imask().modify(|reg| {
314 reg.set_memresifg(id, true);
315 });
316 }
317
318 /// Read one ADC channel asynchronously using the config provided at initialization.
319 pub async fn read_channel(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) -> u16 {
320 channel.setup();
321
322 // CTL0.ENC must be 0 to write the MEMCTL register
323 self.wait_for_conversion().await;
324
325 self.info.regs.ctl2().modify(|reg| {
326 // Set end address to the number of used channels
327 reg.set_endadd(Self::SINGLE_CHANNEL);
328 });
329
330 self.setup_async_channel(Self::SINGLE_CHANNEL as usize, channel, self.config.vr_select);
331
332 self.enable_conversion();
333 self.start_conversion();
334 self.wait_for_conversion().await;
335
336 self.conversion_result(Self::SINGLE_CHANNEL as usize)
337 }
338
339 /// Read one or multiple ADC channels using the Vrsel provided per channel.
340 ///
341 /// `sequence` iterator and `readings` must have the same length.
342 ///
343 /// Example
344 /// ```rust,ignore
345 /// use embassy_mspm0::adc::{Adc, AdcChannel, Vrsel};
346 ///
347 /// let mut adc = Adc::new_async(p.ADC0, adc_config, Irqs);
348 /// let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PA20.into(), Vrsel::VddaVssa)];
349 /// let mut readings = [0u16; 2];
350 ///
351 /// adc.read_sequence(sequence.into_iter(), &mut readings).await;
352 /// defmt::info!("Measurements: {}", readings);
353 /// ```
354 pub async fn read_sequence<'a>(
355 &mut self,
356 sequence: impl ExactSizeIterator<Item = (&'a Peri<'d, AnyAdcChannel<T>>, Vrsel)>,
357 readings: &mut [u16],
358 ) where
359 'd: 'a,
360 {
361 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
362 assert!(
363 sequence.len() == readings.len(),
364 "Sequence length must be equal to readings length"
365 );
366 assert!(
367 sequence.len() <= Self::MAX_SEQUENCE_LEN,
368 "Asynchronous read sequence cannot be more than {} in length",
369 Self::MAX_SEQUENCE_LEN
370 );
371
372 // CTL0.ENC must be 0 to write the MEMCTL register
373 self.wait_for_conversion().await;
374
375 self.info.regs.ctl2().modify(|reg| {
376 // Set end address to the number of used channels
377 reg.set_endadd((sequence.len() - 1) as u8);
378 });
379
380 for (i, (channel, vrsel)) in sequence.enumerate() {
381 self.setup_async_channel(i, channel, vrsel);
382 }
383 self.enable_conversion();
384 self.start_conversion();
385 self.wait_for_conversion().await;
386
387 for (i, r) in readings.iter_mut().enumerate() {
388 *r = self.conversion_result(i);
389 }
390 }
391}
392
393/// Peripheral instance trait.
394#[allow(private_bounds)]
395pub trait Instance: PeripheralType + SealedInstance {
396 type Interrupt: crate::interrupt::typelevel::Interrupt;
397}
398
399/// Peripheral state.
400pub(crate) struct State {
401 waker: AtomicWaker,
402}
403
404impl State {
405 pub const fn new() -> Self {
406 Self {
407 waker: AtomicWaker::new(),
408 }
409 }
410}
411
412/// Peripheral information.
413pub(crate) struct Info {
414 pub(crate) regs: Regs,
415 pub(crate) interrupt: Interrupt,
416}
417
418/// Peripheral instance trait.
419pub(crate) trait SealedInstance {
420 fn info() -> &'static Info;
421 fn state() -> &'static State;
422}
423
424macro_rules! impl_adc_instance {
425 ($instance: ident) => {
426 impl crate::adc::SealedInstance for crate::peripherals::$instance {
427 fn info() -> &'static crate::adc::Info {
428 use crate::adc::Info;
429 use crate::interrupt::typelevel::Interrupt;
430
431 static INFO: Info = Info {
432 regs: crate::pac::$instance,
433 interrupt: crate::interrupt::typelevel::$instance::IRQ,
434 };
435 &INFO
436 }
437
438 fn state() -> &'static crate::adc::State {
439 use crate::adc::State;
440
441 static STATE: State = State::new();
442 &STATE
443 }
444 }
445
446 impl crate::adc::Instance for crate::peripherals::$instance {
447 type Interrupt = crate::interrupt::typelevel::$instance;
448 }
449 };
450}
451
452/// A type-erased channel for a given ADC instance.
453///
454/// This is useful in scenarios where you need the ADC channels to have the same type, such as
455/// storing them in an array.
456pub struct AnyAdcChannel<T> {
457 pub(crate) channel: u8,
458 pub(crate) _phantom: PhantomData<T>,
459}
460
461impl_peripheral!(AnyAdcChannel<T: Instance>);
462impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {}
463impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> {
464 fn channel(&self) -> u8 {
465 self.channel
466 }
467}
468
469impl<T> AnyAdcChannel<T> {
470 #[allow(unused)]
471 pub(crate) fn get_hw_channel(&self) -> u8 {
472 self.channel
473 }
474}
475
476/// ADC channel.
477#[allow(private_bounds)]
478pub trait AdcChannel<T>: PeripheralType + Into<AnyAdcChannel<T>> + SealedAdcChannel<T> + Sized {}
479
480pub(crate) trait SealedAdcChannel<T> {
481 fn setup(&self) {}
482
483 fn channel(&self) -> u8;
484}
485
486macro_rules! impl_adc_pin {
487 ($inst: ident, $pin: ident, $ch: expr) => {
488 impl crate::adc::AdcChannel<peripherals::$inst> for crate::peripherals::$pin {}
489 impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::peripherals::$pin {
490 fn setup(&self) {
491 crate::gpio::SealedPin::set_as_analog(self);
492 }
493
494 fn channel(&self) -> u8 {
495 $ch
496 }
497 }
498
499 impl From<crate::peripherals::$pin> for crate::adc::AnyAdcChannel<peripherals::$inst> {
500 fn from(val: crate::peripherals::$pin) -> Self {
501 crate::adc::SealedAdcChannel::<peripherals::$inst>::setup(&val);
502
503 Self {
504 channel: crate::adc::SealedAdcChannel::<peripherals::$inst>::channel(&val),
505 _phantom: core::marker::PhantomData,
506 }
507 }
508 }
509 };
510}
diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs
index f77848888..d5fd36dbf 100644
--- a/embassy-mspm0/src/gpio.rs
+++ b/embassy-mspm0/src/gpio.rs
@@ -10,7 +10,7 @@ use embassy_sync::waitqueue::AtomicWaker;
10 10
11use crate::pac::gpio::vals::*; 11use crate::pac::gpio::vals::*;
12use crate::pac::gpio::{self}; 12use crate::pac::gpio::{self};
13#[cfg(all(feature = "rt", any(mspm0c110x, mspm0l110x)))] 13#[cfg(all(feature = "rt", any(mspm0c110x, mspm0c1105_c1106, mspm0l110x)))]
14use crate::pac::interrupt; 14use crate::pac::interrupt;
15use crate::pac::{self}; 15use crate::pac::{self};
16 16
@@ -1108,24 +1108,30 @@ fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) {
1108// C110x and L110x have a dedicated interrupts just for GPIOA. 1108// C110x and L110x have a dedicated interrupts just for GPIOA.
1109// 1109//
1110// These chips do not have a GROUP1 interrupt. 1110// These chips do not have a GROUP1 interrupt.
1111#[cfg(all(feature = "rt", any(mspm0c110x, mspm0l110x)))] 1111#[cfg(all(feature = "rt", any(mspm0c110x, mspm0c1105_c1106, mspm0l110x)))]
1112#[interrupt] 1112#[interrupt]
1113fn GPIOA() { 1113fn GPIOA() {
1114 irq_handler(pac::GPIOA, &PORTA_WAKERS); 1114 irq_handler(pac::GPIOA, &PORTA_WAKERS);
1115} 1115}
1116 1116
1117#[cfg(all(feature = "rt", mspm0c1105_c1106))]
1118#[interrupt]
1119fn GPIOB() {
1120 irq_handler(pac::GPIOB, &PORTB_WAKERS);
1121}
1122
1117// These symbols are weakly defined as DefaultHandler and are called by the interrupt group implementation. 1123// These symbols are weakly defined as DefaultHandler and are called by the interrupt group implementation.
1118// 1124//
1119// Defining these as no_mangle is required so that the linker will pick these over the default handler. 1125// Defining these as no_mangle is required so that the linker will pick these over the default handler.
1120 1126
1121#[cfg(all(feature = "rt", not(any(mspm0c110x, mspm0l110x))))] 1127#[cfg(all(feature = "rt", not(any(mspm0c110x, mspm0c1105_c1106, mspm0l110x))))]
1122#[no_mangle] 1128#[no_mangle]
1123#[allow(non_snake_case)] 1129#[allow(non_snake_case)]
1124fn GPIOA() { 1130fn GPIOA() {
1125 irq_handler(pac::GPIOA, &PORTA_WAKERS); 1131 irq_handler(pac::GPIOA, &PORTA_WAKERS);
1126} 1132}
1127 1133
1128#[cfg(all(feature = "rt", gpio_pb))] 1134#[cfg(all(feature = "rt", gpio_pb, not(mspm0c1105_c1106)))]
1129#[no_mangle] 1135#[no_mangle]
1130#[allow(non_snake_case)] 1136#[allow(non_snake_case)]
1131fn GPIOB() { 1137fn GPIOB() {
diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs
index 7e22bb724..1906e37ba 100644
--- a/embassy-mspm0/src/i2c.rs
+++ b/embassy-mspm0/src/i2c.rs
@@ -195,7 +195,7 @@ impl Config {
195 .unwrap(); 195 .unwrap();
196 } 196 }
197 197
198 #[cfg(any(mspm0c110x))] 198 #[cfg(any(mspm0c110x, mspm0c1105_c1106))]
199 fn calculate_clock_source(&self) -> u32 { 199 fn calculate_clock_source(&self) -> u32 {
200 // Assume that BusClk has default value. 200 // Assume that BusClk has default value.
201 // TODO: calculate BusClk more precisely. 201 // TODO: calculate BusClk more precisely.
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs
index 0cb19a379..13f0ce662 100644
--- a/embassy-mspm0/src/lib.rs
+++ b/embassy-mspm0/src/lib.rs
@@ -13,6 +13,7 @@ pub(crate) mod fmt;
13// This must be declared early as well for 13// This must be declared early as well for
14mod macros; 14mod macros;
15 15
16pub mod adc;
16pub mod dma; 17pub mod dma;
17pub mod gpio; 18pub mod gpio;
18pub mod i2c; 19pub mod i2c;
diff --git a/embassy-net-wiznet/CHANGELOG.md b/embassy-net-wiznet/CHANGELOG.md
index e464efa69..a74dc3125 100644
--- a/embassy-net-wiznet/CHANGELOG.md
+++ b/embassy-net-wiznet/CHANGELOG.md
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Added experimental W6100 driver with disabled MAC filter (does not currently work with it enabled)
12- Introduced `SOCKET_INTR_CLR` register which is needed on W6100 and later models (on W5100/W5500 this is shared with `SOCKET_INTR` and the address is the same)
13
11## 0.2.1 - 2025-08-26 14## 0.2.1 - 2025-08-26
12 15
13## 0.1.1 - 2025-08-14 16## 0.1.1 - 2025-08-14
diff --git a/embassy-net-wiznet/src/chip/mod.rs b/embassy-net-wiznet/src/chip/mod.rs
index 2e7a9ed6c..47d7c5dc3 100644
--- a/embassy-net-wiznet/src/chip/mod.rs
+++ b/embassy-net-wiznet/src/chip/mod.rs
@@ -4,6 +4,8 @@ pub use w5500::W5500;
4mod w5100s; 4mod w5100s;
5use embedded_hal_async::spi::SpiDevice; 5use embedded_hal_async::spi::SpiDevice;
6pub use w5100s::W5100S; 6pub use w5100s::W5100S;
7mod w6100;
8pub use w6100::W6100;
7 9
8pub(crate) trait SealedChip { 10pub(crate) trait SealedChip {
9 type Address; 11 type Address;
@@ -28,7 +30,9 @@ pub(crate) trait SealedChip {
28 const SOCKET_RECVD_SIZE: Self::Address; 30 const SOCKET_RECVD_SIZE: Self::Address;
29 const SOCKET_RX_DATA_READ_PTR: Self::Address; 31 const SOCKET_RX_DATA_READ_PTR: Self::Address;
30 const SOCKET_INTR_MASK: Self::Address; 32 const SOCKET_INTR_MASK: Self::Address;
33 #[allow(dead_code)]
31 const SOCKET_INTR: Self::Address; 34 const SOCKET_INTR: Self::Address;
35 const SOCKET_INTR_CLR: Self::Address;
32 36
33 const SOCKET_MODE_VALUE: u8; 37 const SOCKET_MODE_VALUE: u8;
34 38
diff --git a/embassy-net-wiznet/src/chip/w5100s.rs b/embassy-net-wiznet/src/chip/w5100s.rs
index 4c4b7ab16..1eef2369e 100644
--- a/embassy-net-wiznet/src/chip/w5100s.rs
+++ b/embassy-net-wiznet/src/chip/w5100s.rs
@@ -29,6 +29,7 @@ impl super::SealedChip for W5100S {
29 const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28; 29 const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28;
30 const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C; 30 const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C;
31 const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02; 31 const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02;
32 const SOCKET_INTR_CLR: Self::Address = SOCKET_BASE + 0x02;
32 33
33 const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6); 34 const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6);
34 35
diff --git a/embassy-net-wiznet/src/chip/w5500.rs b/embassy-net-wiznet/src/chip/w5500.rs
index 5cfcb94e4..198ba3226 100644
--- a/embassy-net-wiznet/src/chip/w5500.rs
+++ b/embassy-net-wiznet/src/chip/w5500.rs
@@ -33,6 +33,7 @@ impl super::SealedChip for W5500 {
33 const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28); 33 const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28);
34 const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C); 34 const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C);
35 const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); 35 const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02);
36 const SOCKET_INTR_CLR: Self::Address = (RegisterBlock::Socket0, 0x02);
36 37
37 const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7); 38 const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7);
38 39
diff --git a/embassy-net-wiznet/src/chip/w6100.rs b/embassy-net-wiznet/src/chip/w6100.rs
new file mode 100644
index 000000000..740b0edaf
--- /dev/null
+++ b/embassy-net-wiznet/src/chip/w6100.rs
@@ -0,0 +1,83 @@
1use embedded_hal_async::spi::{Operation, SpiDevice};
2
3#[repr(u8)]
4pub enum RegisterBlock {
5 Common = 0x00,
6 Socket0 = 0x01,
7 TxBuf = 0x02,
8 RxBuf = 0x03,
9}
10
11/// Wiznet W6100 chip.
12pub enum W6100 {}
13
14impl super::Chip for W6100 {}
15impl super::SealedChip for W6100 {
16 type Address = (RegisterBlock, u16);
17
18 const CHIP_VERSION: u8 = 0x46;
19
20 const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x2004);
21 const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x4120);
22 // SIMR (SOCKET Interrupt Mask Register)
23 const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x2114);
24 const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x3000);
25 const COMMON_VERSION: Self::Address = (RegisterBlock::Common, 0x0002);
26
27 const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x0000);
28 const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x0010);
29 const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0220);
30 const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0200);
31 const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0204);
32 const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x020C);
33 const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0224);
34 const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x0228);
35 // Sn_IMR (SOCKET n Interrupt Mask Register)
36 const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x0024);
37 // Sn_IR (SOCKET n Interrupt Register)
38 const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x0020);
39 // Sn_IRCLR (Sn_IR Clear Register)
40 const SOCKET_INTR_CLR: Self::Address = (RegisterBlock::Socket0, 0x0028);
41
42 // MACRAW mode. See Page 57 of https://docs.wiznet.io/img/products/w6100/w6100_ds_v105e.pdf
43 // Note: Bit 7 is MAC filter. On the W5500 this is normally turned ON however the W6100 will not successfully retrieve an IP address with this enabled. Disabling for now and will have live with the extra noise.
44 const SOCKET_MODE_VALUE: u8 = 0b0000_0111;
45
46 const BUF_SIZE: u16 = 0x1000;
47 const AUTO_WRAP: bool = true;
48
49 fn rx_addr(addr: u16) -> Self::Address {
50 (RegisterBlock::RxBuf, addr)
51 }
52
53 fn tx_addr(addr: u16) -> Self::Address {
54 (RegisterBlock::TxBuf, addr)
55 }
56
57 async fn bus_read<SPI: SpiDevice>(
58 spi: &mut SPI,
59 address: Self::Address,
60 data: &mut [u8],
61 ) -> Result<(), SPI::Error> {
62 let address_phase = address.1.to_be_bytes();
63 let control_phase = [(address.0 as u8) << 3];
64 let operations = &mut [
65 Operation::Write(&address_phase),
66 Operation::Write(&control_phase),
67 Operation::TransferInPlace(data),
68 ];
69 spi.transaction(operations).await
70 }
71
72 async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> {
73 let address_phase = address.1.to_be_bytes();
74 let control_phase = [(address.0 as u8) << 3 | 0b0000_0100];
75 let data_phase = data;
76 let operations = &mut [
77 Operation::Write(&address_phase[..]),
78 Operation::Write(&control_phase),
79 Operation::Write(&data_phase),
80 ];
81 spi.transaction(operations).await
82 }
83}
diff --git a/embassy-net-wiznet/src/device.rs b/embassy-net-wiznet/src/device.rs
index d2b6bb0c3..8ef92b022 100644
--- a/embassy-net-wiznet/src/device.rs
+++ b/embassy-net-wiznet/src/device.rs
@@ -125,7 +125,7 @@ impl<C: Chip, SPI: SpiDevice> WiznetDevice<C, SPI> {
125 125
126 async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> { 126 async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> {
127 let data = [code as u8]; 127 let data = [code as u8];
128 self.bus_write(C::SOCKET_INTR, &data).await 128 self.bus_write(C::SOCKET_INTR_CLR, &data).await
129 } 129 }
130 130
131 async fn get_tx_write_ptr(&mut self) -> Result<u16, SPI::Error> { 131 async fn get_tx_write_ptr(&mut self) -> Result<u16, SPI::Error> {
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml
index 245626c14..61a2c858a 100644
--- a/embassy-net/Cargo.toml
+++ b/embassy-net/Cargo.toml
@@ -26,6 +26,19 @@ build = [
26 {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, 26 {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]},
27 {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ethernet", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, 27 {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ethernet", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]},
28 {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ethernet", "medium-ieee802154", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, 28 {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ethernet", "medium-ieee802154", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]},
29 # Xtensa builds
30 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "medium-ethernet", "packet-trace"]},
31 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "multicast", "medium-ethernet"]},
32 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "medium-ethernet"]},
33 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "medium-ethernet", "dhcpv4-hostname"]},
34 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ethernet"]},
35 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ieee802154"]},
36 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ethernet", "medium-ieee802154"]},
37 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ethernet"]},
38 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ethernet"]},
39 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ip"]},
40 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ip", "medium-ethernet"]},
41 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ip", "medium-ethernet", "medium-ieee802154"]},
29] 42]
30 43
31[package.metadata.embassy_docs] 44[package.metadata.embassy_docs]
diff --git a/embassy-net/README.md b/embassy-net/README.md
index 1722ffc7b..1c5b30a9c 100644
--- a/embassy-net/README.md
+++ b/embassy-net/README.md
@@ -25,6 +25,7 @@ unimplemented features of the network protocols.
25- [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5). 25- [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5).
26- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips (W5100S, W5500) 26- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips (W5100S, W5500)
27- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. 27- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
28- [`embassy-nrf`](https://github.com/embassy-rs/embassy/tree/main/embassy-nrf) for IEEE 802.15.4 support on nrf chips.
28 29
29## Examples 30## Examples
30 31
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md
index 5dc941b25..befa34ecf 100644
--- a/embassy-nrf/CHANGELOG.md
+++ b/embassy-nrf/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- changed: nrf54l: Disable glitch detection and enable DC/DC in init. 11- changed: nrf54l: Disable glitch detection and enable DC/DC in init.
12- changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4
12 13
13## 0.7.0 - 2025-08-26 14## 0.7.0 - 2025-08-26
14 15
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index 2ce75cfac..4afd28fbd 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -79,6 +79,9 @@ gpiote = []
79## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz 79## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz
80time-driver-rtc1 = ["_time-driver"] 80time-driver-rtc1 = ["_time-driver"]
81 81
82## Enable embassy-net 802.15.4 driver
83net-driver = ["_net-driver"]
84
82## Allow using the NFC pins as regular GPIO pins (P0_09/P0_10 on nRF52, P0_02/P0_03 on nRF53) 85## Allow using the NFC pins as regular GPIO pins (P0_09/P0_10 on nRF52, P0_02/P0_03 on nRF53)
83nfc-pins-as-gpio = [] 86nfc-pins-as-gpio = []
84 87
@@ -154,6 +157,8 @@ _nrf91 = []
154 157
155_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] 158_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"]
156 159
160_net-driver = ["dep:embassy-net-driver-channel","dep:embassy-futures"]
161
157# trustzone state. 162# trustzone state.
158_s = [] 163_s = []
159_ns = [] 164_ns = []
@@ -177,6 +182,8 @@ embassy-sync = { version = "0.7.2", path = "../embassy-sync" }
177embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } 182embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
178embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal", default-features = false } 183embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal", default-features = false }
179embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } 184embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" }
185embassy-net-driver-channel = { version = "0.3.2", path = "../embassy-net-driver-channel", optional = true}
186embassy-futures = { version = "0.1.2", path = "../embassy-futures", optional = true}
180 187
181embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 188embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
182embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 189embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
diff --git a/embassy-nrf/README.md b/embassy-nrf/README.md
index 3df5f1fa5..26b1fa5ea 100644
--- a/embassy-nrf/README.md
+++ b/embassy-nrf/README.md
@@ -28,6 +28,10 @@ allows running Rust code without a SPM or TF-M binary, saving flash space and si
28 28
29If the `time-driver-rtc1` feature is enabled, the HAL uses the RTC peripheral as a global time driver for [embassy-time](https://crates.io/crates/embassy-time), with a tick rate of 32768 Hz. 29If the `time-driver-rtc1` feature is enabled, the HAL uses the RTC peripheral as a global time driver for [embassy-time](https://crates.io/crates/embassy-time), with a tick rate of 32768 Hz.
30 30
31## Embassy-net-driver
32
33If the board supports IEEE 802.15.4 (see `src/radio/mod.rs`) the corresponding [embassy-net-driver](https://crates.io/crates/embassy-net-driver) implementation can be enabled with the feature `net-driver`.
34
31## Embedded-hal 35## Embedded-hal
32 36
33The `embassy-nrf` HAL implements the traits from [embedded-hal](https://crates.io/crates/embedded-hal) (v0.2 and 1.0) and [embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as [embedded-io](https://crates.io/crates/embedded-io) and [embedded-io-async](https://crates.io/crates/embedded-io-async). 37The `embassy-nrf` HAL implements the traits from [embedded-hal](https://crates.io/crates/embedded-hal) (v0.2 and 1.0) and [embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as [embedded-io](https://crates.io/crates/embedded-io) and [embedded-io-async](https://crates.io/crates/embedded-io-async).
diff --git a/embassy-nrf/src/embassy_net_802154_driver.rs b/embassy-nrf/src/embassy_net_802154_driver.rs
new file mode 100644
index 000000000..8662be787
--- /dev/null
+++ b/embassy-nrf/src/embassy_net_802154_driver.rs
@@ -0,0 +1,96 @@
1//! embassy-net IEEE 802.15.4 driver
2
3use embassy_futures::select::{select3, Either3};
4use embassy_net_driver_channel::driver::LinkState;
5use embassy_net_driver_channel::{self as ch};
6use embassy_time::{Duration, Ticker};
7
8use crate::radio::ieee802154::{Packet, Radio};
9use crate::radio::InterruptHandler;
10use crate::{self as nrf, interrupt};
11
12/// MTU for the nrf radio.
13pub const MTU: usize = Packet::CAPACITY as usize;
14
15/// embassy-net device for the driver.
16pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;
17
18/// Internal state for the embassy-net driver.
19pub struct State<const N_RX: usize, const N_TX: usize> {
20 ch_state: ch::State<MTU, N_RX, N_TX>,
21}
22
23impl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {
24 /// Create a new `State`.
25 pub const fn new() -> Self {
26 Self {
27 ch_state: ch::State::new(),
28 }
29 }
30}
31
32/// Background runner for the driver.
33///
34/// You must call `.run()` in a background task for the driver to operate.
35pub struct Runner<'d, T: nrf::radio::Instance> {
36 radio: nrf::radio::ieee802154::Radio<'d, T>,
37 ch: ch::Runner<'d, MTU>,
38}
39
40impl<'d, T: nrf::radio::Instance> Runner<'d, T> {
41 /// Drives the radio. Needs to run to use the driver.
42 pub async fn run(mut self) -> ! {
43 let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
44 let mut tick = Ticker::every(Duration::from_millis(500));
45 let mut packet = Packet::new();
46 state_chan.set_link_state(LinkState::Up);
47 loop {
48 match select3(
49 async {
50 let rx_buf = rx_chan.rx_buf().await;
51 self.radio.receive(&mut packet).await.ok().map(|_| rx_buf)
52 },
53 tx_chan.tx_buf(),
54 tick.next(),
55 )
56 .await
57 {
58 Either3::First(Some(rx_buf)) => {
59 let len = rx_buf.len().min(packet.len() as usize);
60 (&mut rx_buf[..len]).copy_from_slice(&*packet);
61 rx_chan.rx_done(len);
62 }
63 Either3::Second(tx_buf) => {
64 let len = tx_buf.len().min(Packet::CAPACITY as usize);
65 packet.copy_from_slice(&tx_buf[..len]);
66 self.radio.try_send(&mut packet).await.ok().unwrap();
67 tx_chan.tx_done();
68 }
69 _ => {}
70 }
71 }
72 }
73}
74
75/// Make sure to use `HfclkSource::ExternalXtal` as the `hfclk_source`
76/// to use the radio (nrf52840 product spec v1.11 5.4.1)
77/// ```
78/// # use embassy_nrf::config::*;
79/// let mut config = Config::default();
80/// config.hfclk_source = HfclkSource::ExternalXtal;
81/// ```
82pub async fn new<'a, const N_RX: usize, const N_TX: usize, T: nrf::radio::Instance, Irq>(
83 mac_addr: [u8; 8],
84 radio: nrf::Peri<'a, T>,
85 irq: Irq,
86 state: &'a mut State<N_RX, N_TX>,
87) -> Result<(Device<'a>, Runner<'a, T>), ()>
88where
89 Irq: interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,
90{
91 let radio = Radio::new(radio, irq);
92
93 let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ieee802154(mac_addr));
94
95 Ok((device, Runner { ch: runner, radio }))
96}
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index aa4801897..897e660b8 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -137,6 +137,17 @@ pub mod qspi;
137#[cfg(not(feature = "_nrf54l"))] // TODO 137#[cfg(not(feature = "_nrf54l"))] // TODO
138#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))] 138#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))]
139pub mod radio; 139pub mod radio;
140
141#[cfg(any(
142 feature = "nrf52811",
143 feature = "nrf52820",
144 feature = "nrf52833",
145 feature = "nrf52840",
146 feature = "_nrf5340-net"
147))]
148#[cfg(feature = "_net-driver")]
149pub mod embassy_net_802154_driver;
150
140#[cfg(not(feature = "_nrf54l"))] // TODO 151#[cfg(not(feature = "_nrf54l"))] // TODO
141#[cfg(feature = "_nrf5340")] 152#[cfg(feature = "_nrf5340")]
142pub mod reset; 153pub mod reset;
diff --git a/embassy-nxp/CHANGELOG.md b/embassy-nxp/CHANGELOG.md
index 7042ad14c..ab97c4185 100644
--- a/embassy-nxp/CHANGELOG.md
+++ b/embassy-nxp/CHANGELOG.md
@@ -7,5 +7,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7 7
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10- Moved NXP LPC55S69 from `lpc55-pac` to `nxp-pac`
11- First release with changelog. 11- First release with changelog.
diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml
index ab0bfbfd7..455915f29 100644
--- a/embassy-nxp/Cargo.toml
+++ b/embassy-nxp/Cargo.toml
@@ -7,7 +7,7 @@ publish = false
7 7
8[package.metadata.embassy] 8[package.metadata.embassy]
9build = [ 9build = [
10 {target = "thumbv8m.main-none-eabihf", features = ["defmt", "lpc55"]}, 10 {target = "thumbv8m.main-none-eabihf", features = ["defmt", "lpc55-core0"]},
11 {target = "thumbv7em-none-eabihf", features = ["defmt", "mimxrt1011", "rt", "time-driver-pit"]}, 11 {target = "thumbv7em-none-eabihf", features = ["defmt", "mimxrt1011", "rt", "time-driver-pit"]},
12 {target = "thumbv7em-none-eabihf", features = ["defmt", "mimxrt1062", "rt", "time-driver-pit"]}, 12 {target = "thumbv7em-none-eabihf", features = ["defmt", "mimxrt1062", "rt", "time-driver-pit"]},
13] 13]
@@ -18,7 +18,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nxp/s
18features = ["defmt", "unstable-pac" ] # TODO: Add time-driver-any, as both lpc55 and mimxrt1xxx use different drivers. 18features = ["defmt", "unstable-pac" ] # TODO: Add time-driver-any, as both lpc55 and mimxrt1xxx use different drivers.
19 19
20flavors = [ 20flavors = [
21 { regex_feature = "lpc55", target = "thumbv8m.main-none-eabihf" }, 21 { regex_feature = "lpc55-core0", target = "thumbv8m.main-none-eabihf" },
22 { regex_feature = "mimxrt.*", target = "thumbv7em-none-eabihf" }, 22 { regex_feature = "mimxrt.*", target = "thumbv7em-none-eabihf" },
23] 23]
24 24
@@ -36,21 +36,20 @@ embassy-time-queue-utils = { version = "0.3.0", path = "../embassy-time-queue-ut
36embedded-io = "0.6.1" 36embedded-io = "0.6.1"
37embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 37embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
38## Chip dependencies 38## Chip dependencies
39lpc55-pac = { version = "0.5.0", optional = true } 39nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "b736e3038254d593024aaa1a5a7b1f95a5728538"}
40nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e" }
41 40
42imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } 41imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] }
43 42
44[build-dependencies] 43[build-dependencies]
45cfg_aliases = "0.2.1" 44cfg_aliases = "0.2.1"
46nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e", features = ["metadata"], optional = true } 45nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "b736e3038254d593024aaa1a5a7b1f95a5728538", features = ["metadata"], optional = true }
47proc-macro2 = "1.0.95" 46proc-macro2 = "1.0.95"
48quote = "1.0.15" 47quote = "1.0.15"
49 48
50[features] 49[features]
51default = ["rt"] 50default = ["rt"]
52# Enable PACs as optional dependencies, since some chip families will use different pac crates (temporarily). 51# Enable PACs as optional dependencies, since some chip families will use different pac crates (temporarily).
53rt = ["lpc55-pac?/rt", "nxp-pac?/rt"] 52rt = ["nxp-pac?/rt"]
54 53
55## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. 54## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.
56defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] 55defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"]
@@ -79,6 +78,6 @@ _rt1xxx = []
79_time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"] 78_time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"]
80 79
81#! ### Chip selection features 80#! ### Chip selection features
82lpc55 = ["dep:lpc55-pac"] 81lpc55-core0 = ["nxp-pac/lpc55s69_cm33_core0"]
83mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"] 82mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"]
84mimxrt1062 = ["nxp-pac/mimxrt1062", "_rt1xxx", "dep:imxrt-rt"] 83mimxrt1062 = ["nxp-pac/mimxrt1062", "_rt1xxx", "dep:imxrt-rt"]
diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs
index e168ced00..711bff3e7 100644
--- a/embassy-nxp/src/chips/lpc55.rs
+++ b/embassy-nxp/src/chips/lpc55.rs
@@ -1,4 +1,4 @@
1pub use lpc55_pac as pac; 1pub use nxp_pac as pac;
2 2
3embassy_hal_internal::peripherals! { 3embassy_hal_internal::peripherals! {
4 // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other 4 // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
diff --git a/embassy-nxp/src/gpio.rs b/embassy-nxp/src/gpio.rs
index 3049cc12d..717b38d96 100644
--- a/embassy-nxp/src/gpio.rs
+++ b/embassy-nxp/src/gpio.rs
@@ -1,7 +1,7 @@
1//! General purpose input/output (GPIO) driver. 1//! General purpose input/output (GPIO) driver.
2#![macro_use] 2#![macro_use]
3 3
4#[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")] 4#[cfg_attr(feature = "lpc55-core0", path = "./gpio/lpc55.rs")]
5#[cfg_attr(rt1xxx, path = "./gpio/rt1xxx.rs")] 5#[cfg_attr(rt1xxx, path = "./gpio/rt1xxx.rs")]
6mod inner; 6mod inner;
7pub use inner::*; 7pub use inner::*;
diff --git a/embassy-nxp/src/gpio/lpc55.rs b/embassy-nxp/src/gpio/lpc55.rs
index 8f407bb3a..36ea99d21 100644
--- a/embassy-nxp/src/gpio/lpc55.rs
+++ b/embassy-nxp/src/gpio/lpc55.rs
@@ -1,12 +1,17 @@
1use embassy_hal_internal::{impl_peripheral, PeripheralType}; 1use embassy_hal_internal::{impl_peripheral, PeripheralType};
2 2
3use crate::pac::iocon::vals::{PioDigimode, PioMode};
4use crate::pac::{GPIO, IOCON, SYSCON};
3use crate::{peripherals, Peri}; 5use crate::{peripherals, Peri};
4 6
5pub(crate) fn init() { 7pub(crate) fn init() {
6 // Enable clocks for GPIO, PINT, and IOCON 8 // Enable clocks for GPIO, PINT, and IOCON
7 syscon_reg() 9 SYSCON.ahbclkctrl0().modify(|w| {
8 .ahbclkctrl0 10 w.set_gpio0(true);
9 .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable()); 11 w.set_gpio1(true);
12 w.set_mux(true);
13 w.set_iocon(true);
14 });
10 info!("GPIO initialized"); 15 info!("GPIO initialized");
11} 16}
12 17
@@ -59,21 +64,24 @@ impl<'d> Output<'d> {
59 } 64 }
60 65
61 pub fn set_high(&mut self) { 66 pub fn set_high(&mut self) {
62 gpio_reg().set[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) 67 GPIO.set(self.pin.pin_bank() as usize)
68 .write(|w| w.set_setp(self.pin.bit()));
63 } 69 }
64 70
65 pub fn set_low(&mut self) { 71 pub fn set_low(&mut self) {
66 gpio_reg().clr[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) 72 GPIO.clr(self.pin.pin_bank() as usize)
73 .write(|w| w.set_clrp(self.pin.bit()));
67 } 74 }
68 75
69 pub fn toggle(&mut self) { 76 pub fn toggle(&mut self) {
70 gpio_reg().not[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) 77 GPIO.not(self.pin.pin_bank() as usize)
78 .write(|w| w.set_notp(self.pin.bit()));
71 } 79 }
72 80
73 /// Get the current output level of the pin. Note that the value returned by this function is 81 /// Get the current output level of the pin. Note that the value returned by this function is
74 /// the voltage level reported by the pin, not the value set by the output driver. 82 /// the voltage level reported by the pin, not the value set by the output driver.
75 pub fn level(&self) -> Level { 83 pub fn level(&self) -> Level {
76 let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); 84 let bits = GPIO.pin(self.pin.pin_bank() as usize).read().port();
77 if bits & self.pin.bit() != 0 { 85 if bits & self.pin.bit() != 0 {
78 Level::High 86 Level::High
79 } else { 87 } else {
@@ -101,18 +109,18 @@ impl<'d> Input<'d> {
101 109
102 /// Set the pull configuration for the pin. To disable the pull, use [Pull::None]. 110 /// Set the pull configuration for the pin. To disable the pull, use [Pull::None].
103 pub fn set_pull(&mut self, pull: Pull) { 111 pub fn set_pull(&mut self, pull: Pull) {
104 match_iocon!(register, iocon_reg(), self.pin.pin_bank(), self.pin.pin_number(), { 112 match_iocon!(register, self.pin.pin_bank(), self.pin.pin_number(), {
105 register.modify(|_, w| match pull { 113 register.modify(|w| match pull {
106 Pull::None => w.mode().inactive(), 114 Pull::None => w.set_mode(PioMode::INACTIVE),
107 Pull::Up => w.mode().pull_up(), 115 Pull::Up => w.set_mode(PioMode::PULL_UP),
108 Pull::Down => w.mode().pull_down(), 116 Pull::Down => w.set_mode(PioMode::PULL_DOWN),
109 }); 117 });
110 }); 118 });
111 } 119 }
112 120
113 /// Get the current input level of the pin. 121 /// Get the current input level of the pin.
114 pub fn read(&self) -> Level { 122 pub fn read(&self) -> Level {
115 let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); 123 let bits = GPIO.pin(self.pin.pin_bank() as usize).read().port();
116 if bits & self.pin.bit() != 0 { 124 if bits & self.pin.bit() != 0 {
117 Level::High 125 Level::High
118 } else { 126 } else {
@@ -188,8 +196,8 @@ impl<'d> Flex<'d> {
188 /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default 196 /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default
189 /// setting for pins is (usually) non-digital. 197 /// setting for pins is (usually) non-digital.
190 fn set_as_digital(&mut self) { 198 fn set_as_digital(&mut self) {
191 match_iocon!(register, iocon_reg(), self.pin_bank(), self.pin_number(), { 199 match_iocon!(register, self.pin_bank(), self.pin_number(), {
192 register.modify(|_, w| w.digimode().digital()); 200 register.modify(|w| w.set_digimode(PioDigimode::DIGITAL));
193 }); 201 });
194 } 202 }
195 203
@@ -197,12 +205,14 @@ impl<'d> Flex<'d> {
197 /// function handles itself. 205 /// function handles itself.
198 pub fn set_as_output(&mut self) { 206 pub fn set_as_output(&mut self) {
199 self.set_as_digital(); 207 self.set_as_digital();
200 gpio_reg().dirset[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirsetp().bits(self.bit()) }) 208 GPIO.dirset(self.pin.pin_bank() as usize)
209 .write(|w| w.set_dirsetp(self.bit()))
201 } 210 }
202 211
203 pub fn set_as_input(&mut self) { 212 pub fn set_as_input(&mut self) {
204 self.set_as_digital(); 213 self.set_as_digital();
205 gpio_reg().dirclr[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirclrp().bits(self.bit()) }) 214 GPIO.dirclr(self.pin.pin_bank() as usize)
215 .write(|w| w.set_dirclrp(self.bit()))
206 } 216 }
207} 217}
208 218
@@ -262,52 +272,6 @@ impl SealedPin for AnyPin {
262 } 272 }
263} 273}
264 274
265/// Get the GPIO register block. This is used to configure all GPIO pins.
266///
267/// # Safety
268/// Due to the type system of peripherals, access to the settings of a single pin is possible only
269/// by a single thread at a time. Read/Write operations on a single registers are NOT atomic. You
270/// must ensure that the GPIO registers are not accessed concurrently by multiple threads.
271pub(crate) fn gpio_reg() -> &'static lpc55_pac::gpio::RegisterBlock {
272 unsafe { &*lpc55_pac::GPIO::ptr() }
273}
274
275/// Get the IOCON register block.
276///
277/// # Safety
278/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO
279/// registers are not accessed concurrently by multiple threads.
280pub(crate) fn iocon_reg() -> &'static lpc55_pac::iocon::RegisterBlock {
281 unsafe { &*lpc55_pac::IOCON::ptr() }
282}
283
284/// Get the INPUTMUX register block.
285///
286/// # Safety
287/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO
288/// registers are not accessed concurrently by multiple threads.
289pub(crate) fn inputmux_reg() -> &'static lpc55_pac::inputmux::RegisterBlock {
290 unsafe { &*lpc55_pac::INPUTMUX::ptr() }
291}
292
293/// Get the SYSCON register block.
294///
295/// # Safety
296/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO
297/// registers are not accessed concurrently by multiple threads.
298pub(crate) fn syscon_reg() -> &'static lpc55_pac::syscon::RegisterBlock {
299 unsafe { &*lpc55_pac::SYSCON::ptr() }
300}
301
302/// Get the PINT register block.
303///
304/// # Safety
305/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO
306/// registers are not accessed concurrently by multiple threads.
307pub(crate) fn pint_reg() -> &'static lpc55_pac::pint::RegisterBlock {
308 unsafe { &*lpc55_pac::PINT::ptr() }
309}
310
311/// Match the pin bank and number of a pin to the corresponding IOCON register. 275/// Match the pin bank and number of a pin to the corresponding IOCON register.
312/// 276///
313/// # Example 277/// # Example
@@ -316,270 +280,26 @@ pub(crate) fn pint_reg() -> &'static lpc55_pac::pint::RegisterBlock {
316/// use embassy_nxp::pac_utils::{iocon_reg, match_iocon}; 280/// use embassy_nxp::pac_utils::{iocon_reg, match_iocon};
317/// 281///
318/// // Make pin PIO1_6 digital and set it to pull-down mode. 282/// // Make pin PIO1_6 digital and set it to pull-down mode.
319/// match_iocon!(register, iocon_reg(), Bank::Bank1, 6, { 283/// match_iocon!(register, Bank::Bank1, 6, {
320/// register.modify(|_, w| w.mode().pull_down().digimode().digital()); 284/// register.modify(|w|{
285/// w.set_mode(PioMode::PULL_DOWN);
286/// w.set_digimode(PioDigimode::DIGITAL);
287///
288/// }
321/// }); 289/// });
322/// ``` 290/// ```
323macro_rules! match_iocon { 291macro_rules! match_iocon {
324 ($register:ident, $iocon_register:expr, $pin_bank:expr, $pin_number:expr, $action:expr) => { 292 ($register:ident, $pin_bank:expr, $pin_number:expr, $action:expr) => {
325 match ($pin_bank, $pin_number) { 293 match $pin_bank {
326 (Bank::Bank0, 0) => { 294 Bank::Bank0 => {
327 let $register = &($iocon_register).pio0_0; 295 let $register = IOCON.pio0($pin_number as usize);
328 $action;
329 }
330 (Bank::Bank0, 1) => {
331 let $register = &($iocon_register).pio0_1;
332 $action;
333 }
334 (Bank::Bank0, 2) => {
335 let $register = &($iocon_register).pio0_2;
336 $action;
337 }
338 (Bank::Bank0, 3) => {
339 let $register = &($iocon_register).pio0_3;
340 $action;
341 }
342 (Bank::Bank0, 4) => {
343 let $register = &($iocon_register).pio0_4;
344 $action;
345 }
346 (Bank::Bank0, 5) => {
347 let $register = &($iocon_register).pio0_5;
348 $action;
349 }
350 (Bank::Bank0, 6) => {
351 let $register = &($iocon_register).pio0_6;
352 $action;
353 }
354 (Bank::Bank0, 7) => {
355 let $register = &($iocon_register).pio0_7;
356 $action;
357 }
358 (Bank::Bank0, 8) => {
359 let $register = &($iocon_register).pio0_8;
360 $action;
361 }
362 (Bank::Bank0, 9) => {
363 let $register = &($iocon_register).pio0_9;
364 $action;
365 }
366 (Bank::Bank0, 10) => {
367 let $register = &($iocon_register).pio0_10;
368 $action; 296 $action;
369 } 297 }
370 (Bank::Bank0, 11) => { 298
371 let $register = &($iocon_register).pio0_11; 299 Bank::Bank1 => {
372 $action; 300 let $register = IOCON.pio1($pin_number as usize);
373 }
374 (Bank::Bank0, 12) => {
375 let $register = &($iocon_register).pio0_12;
376 $action;
377 }
378 (Bank::Bank0, 13) => {
379 let $register = &($iocon_register).pio0_13;
380 $action;
381 }
382 (Bank::Bank0, 14) => {
383 let $register = &($iocon_register).pio0_14;
384 $action;
385 }
386 (Bank::Bank0, 15) => {
387 let $register = &($iocon_register).pio0_15;
388 $action;
389 }
390 (Bank::Bank0, 16) => {
391 let $register = &($iocon_register).pio0_16;
392 $action;
393 }
394 (Bank::Bank0, 17) => {
395 let $register = &($iocon_register).pio0_17;
396 $action;
397 }
398 (Bank::Bank0, 18) => {
399 let $register = &($iocon_register).pio0_18;
400 $action;
401 }
402 (Bank::Bank0, 19) => {
403 let $register = &($iocon_register).pio0_19;
404 $action;
405 }
406 (Bank::Bank0, 20) => {
407 let $register = &($iocon_register).pio0_20;
408 $action;
409 }
410 (Bank::Bank0, 21) => {
411 let $register = &($iocon_register).pio0_21;
412 $action;
413 }
414 (Bank::Bank0, 22) => {
415 let $register = &($iocon_register).pio0_22;
416 $action;
417 }
418 (Bank::Bank0, 23) => {
419 let $register = &($iocon_register).pio0_23;
420 $action;
421 }
422 (Bank::Bank0, 24) => {
423 let $register = &($iocon_register).pio0_24;
424 $action;
425 }
426 (Bank::Bank0, 25) => {
427 let $register = &($iocon_register).pio0_25;
428 $action;
429 }
430 (Bank::Bank0, 26) => {
431 let $register = &($iocon_register).pio0_26;
432 $action;
433 }
434 (Bank::Bank0, 27) => {
435 let $register = &($iocon_register).pio0_27;
436 $action;
437 }
438 (Bank::Bank0, 28) => {
439 let $register = &($iocon_register).pio0_28;
440 $action;
441 }
442 (Bank::Bank0, 29) => {
443 let $register = &($iocon_register).pio0_29;
444 $action;
445 }
446 (Bank::Bank0, 30) => {
447 let $register = &($iocon_register).pio0_30;
448 $action;
449 }
450 (Bank::Bank0, 31) => {
451 let $register = &($iocon_register).pio0_31;
452 $action;
453 }
454 (Bank::Bank1, 0) => {
455 let $register = &($iocon_register).pio1_0;
456 $action;
457 }
458 (Bank::Bank1, 1) => {
459 let $register = &($iocon_register).pio1_1;
460 $action;
461 }
462 (Bank::Bank1, 2) => {
463 let $register = &($iocon_register).pio1_2;
464 $action;
465 }
466 (Bank::Bank1, 3) => {
467 let $register = &($iocon_register).pio1_3;
468 $action;
469 }
470 (Bank::Bank1, 4) => {
471 let $register = &($iocon_register).pio1_4;
472 $action;
473 }
474 (Bank::Bank1, 5) => {
475 let $register = &($iocon_register).pio1_5;
476 $action;
477 }
478 (Bank::Bank1, 6) => {
479 let $register = &($iocon_register).pio1_6;
480 $action;
481 }
482 (Bank::Bank1, 7) => {
483 let $register = &($iocon_register).pio1_7;
484 $action;
485 }
486 (Bank::Bank1, 8) => {
487 let $register = &($iocon_register).pio1_8;
488 $action;
489 }
490 (Bank::Bank1, 9) => {
491 let $register = &($iocon_register).pio1_9;
492 $action;
493 }
494 (Bank::Bank1, 10) => {
495 let $register = &($iocon_register).pio1_10;
496 $action;
497 }
498 (Bank::Bank1, 11) => {
499 let $register = &($iocon_register).pio1_11;
500 $action;
501 }
502 (Bank::Bank1, 12) => {
503 let $register = &($iocon_register).pio1_12;
504 $action;
505 }
506 (Bank::Bank1, 13) => {
507 let $register = &($iocon_register).pio1_13;
508 $action;
509 }
510 (Bank::Bank1, 14) => {
511 let $register = &($iocon_register).pio1_14;
512 $action;
513 }
514 (Bank::Bank1, 15) => {
515 let $register = &($iocon_register).pio1_15;
516 $action;
517 }
518 (Bank::Bank1, 16) => {
519 let $register = &($iocon_register).pio1_16;
520 $action;
521 }
522 (Bank::Bank1, 17) => {
523 let $register = &($iocon_register).pio1_17;
524 $action;
525 }
526 (Bank::Bank1, 18) => {
527 let $register = &($iocon_register).pio1_18;
528 $action;
529 }
530 (Bank::Bank1, 19) => {
531 let $register = &($iocon_register).pio1_19;
532 $action;
533 }
534 (Bank::Bank1, 20) => {
535 let $register = &($iocon_register).pio1_20;
536 $action;
537 }
538 (Bank::Bank1, 21) => {
539 let $register = &($iocon_register).pio1_21;
540 $action;
541 }
542 (Bank::Bank1, 22) => {
543 let $register = &($iocon_register).pio1_22;
544 $action;
545 }
546 (Bank::Bank1, 23) => {
547 let $register = &($iocon_register).pio1_23;
548 $action;
549 }
550 (Bank::Bank1, 24) => {
551 let $register = &($iocon_register).pio1_24;
552 $action;
553 }
554 (Bank::Bank1, 25) => {
555 let $register = &($iocon_register).pio1_25;
556 $action;
557 }
558 (Bank::Bank1, 26) => {
559 let $register = &($iocon_register).pio1_26;
560 $action;
561 }
562 (Bank::Bank1, 27) => {
563 let $register = &($iocon_register).pio1_27;
564 $action;
565 }
566 (Bank::Bank1, 28) => {
567 let $register = &($iocon_register).pio1_28;
568 $action;
569 }
570 (Bank::Bank1, 29) => {
571 let $register = &($iocon_register).pio1_29;
572 $action;
573 }
574 (Bank::Bank1, 30) => {
575 let $register = &($iocon_register).pio1_30;
576 $action;
577 }
578 (Bank::Bank1, 31) => {
579 let $register = &($iocon_register).pio1_31;
580 $action; 301 $action;
581 } 302 }
582 _ => unreachable!(),
583 } 303 }
584 }; 304 };
585} 305}
diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs
index 3fcb14b7e..74142a10b 100644
--- a/embassy-nxp/src/lib.rs
+++ b/embassy-nxp/src/lib.rs
@@ -4,9 +4,9 @@
4pub(crate) mod fmt; 4pub(crate) mod fmt;
5 5
6pub mod gpio; 6pub mod gpio;
7#[cfg(feature = "lpc55")] 7#[cfg(feature = "lpc55-core0")]
8pub mod pint; 8pub mod pint;
9#[cfg(feature = "lpc55")] 9#[cfg(feature = "lpc55-core0")]
10pub mod usart; 10pub mod usart;
11 11
12#[cfg(feature = "_time_driver")] 12#[cfg(feature = "_time_driver")]
@@ -15,7 +15,7 @@ pub mod usart;
15mod time_driver; 15mod time_driver;
16 16
17// This mod MUST go last, so that it sees all the `impl_foo!` macros 17// This mod MUST go last, so that it sees all the `impl_foo!` macros
18#[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] 18#[cfg_attr(feature = "lpc55-core0", path = "chips/lpc55.rs")]
19#[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")] 19#[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")]
20#[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] 20#[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")]
21mod chip; 21mod chip;
@@ -83,10 +83,10 @@ pub fn init(_config: config::Config) -> Peripherals {
83 pac::CCM.ccgr6().modify(|v| v.set_cg0(1)); 83 pac::CCM.ccgr6().modify(|v| v.set_cg0(1));
84 } 84 }
85 85
86 #[cfg(any(feature = "lpc55", rt1xxx))] 86 #[cfg(any(feature = "lpc55-core0", rt1xxx))]
87 gpio::init(); 87 gpio::init();
88 88
89 #[cfg(feature = "lpc55")] 89 #[cfg(feature = "lpc55-core0")]
90 pint::init(); 90 pint::init();
91 91
92 #[cfg(feature = "_time_driver")] 92 #[cfg(feature = "_time_driver")]
diff --git a/embassy-nxp/src/pint.rs b/embassy-nxp/src/pint.rs
index ff414b4e6..e594aaa6a 100644
--- a/embassy-nxp/src/pint.rs
+++ b/embassy-nxp/src/pint.rs
@@ -5,10 +5,11 @@ use core::pin::Pin as FuturePin;
5use core::task::{Context, Poll}; 5use core::task::{Context, Poll};
6 6
7use critical_section::Mutex; 7use critical_section::Mutex;
8use embassy_hal_internal::interrupt::InterruptExt;
8use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
9 10
10use crate::gpio::{self, inputmux_reg, pint_reg, syscon_reg, AnyPin, Level, SealedPin}; 11use crate::gpio::{self, AnyPin, Level, SealedPin};
11use crate::pac::interrupt; 12use crate::pac::{interrupt, INPUTMUX, PINT, SYSCON};
12use crate::Peri; 13use crate::Peri;
13 14
14struct PinInterrupt { 15struct PinInterrupt {
@@ -88,18 +89,18 @@ enum InterruptOn {
88} 89}
89 90
90pub(crate) fn init() { 91pub(crate) fn init() {
91 syscon_reg().ahbclkctrl0.modify(|_, w| w.pint().enable()); 92 SYSCON.ahbclkctrl0().modify(|w| w.set_pint(true));
92 93
93 // Enable interrupts 94 // Enable interrupts
94 unsafe { 95 unsafe {
95 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT0); 96 interrupt::PIN_INT0.enable();
96 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT1); 97 interrupt::PIN_INT1.enable();
97 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT2); 98 interrupt::PIN_INT2.enable();
98 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT3); 99 interrupt::PIN_INT3.enable();
99 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT4); 100 interrupt::PIN_INT4.enable();
100 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT5); 101 interrupt::PIN_INT5.enable();
101 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6); 102 interrupt::PIN_INT6.enable();
102 crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7); 103 interrupt::PIN_INT7.enable();
103 }; 104 };
104 105
105 info!("Pin interrupts initialized"); 106 info!("Pin interrupts initialized");
@@ -119,24 +120,19 @@ impl<'d> InputFuture<'d> {
119 let interrupt_number = next_available_interrupt()?; 120 let interrupt_number = next_available_interrupt()?;
120 121
121 // Clear interrupt, just in case 122 // Clear interrupt, just in case
122 pint_reg() 123 PINT.rise().write(|w| w.set_rdet(1 << interrupt_number));
123 .rise 124 PINT.fall().write(|w| w.set_fdet(1 << interrupt_number));
124 .write(|w| unsafe { w.rdet().bits(1 << interrupt_number) });
125 pint_reg()
126 .fall
127 .write(|w| unsafe { w.fdet().bits(1 << interrupt_number) });
128 125
129 // Enable input multiplexing on pin interrupt register 0 for pin (32*bank + pin_number) 126 // Enable input multiplexing on pin interrupt register 0 for pin (32*bank + pin_number)
130 inputmux_reg().pintsel[interrupt_number] 127 INPUTMUX
131 .write(|w| unsafe { w.intpin().bits(32 * pin.pin_bank() as u8 + pin.pin_number()) }); 128 .pintsel(interrupt_number as usize)
129 .write(|w| w.set_intpin(32 * pin.pin_bank() as u8 + pin.pin_number()));
132 130
133 match interrupt_on { 131 match interrupt_on {
134 InterruptOn::Level(level) => { 132 InterruptOn::Level(level) => {
135 // Set pin interrupt register to edge sensitive or level sensitive 133 // Set pin interrupt register to edge sensitive or level sensitive
136 // 0 = edge sensitive, 1 = level sensitive 134 // 0 = edge sensitive, 1 = level sensitive
137 pint_reg() 135 PINT.isel().modify(|w| w.set_pmode(w.pmode() | (1 << interrupt_number)));
138 .isel
139 .modify(|r, w| unsafe { w.bits(r.bits() | (1 << interrupt_number)) });
140 136
141 // Enable level interrupt. 137 // Enable level interrupt.
142 // 138 //
@@ -144,63 +140,44 @@ impl<'d> InputFuture<'d> {
144 // is activated. 140 // is activated.
145 141
146 // 0 = no-op, 1 = enable 142 // 0 = no-op, 1 = enable
147 pint_reg() 143 PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number));
148 .sienr
149 .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) });
150 144
151 // Set active level 145 // Set active level
152 match level { 146 match level {
153 Level::Low => { 147 Level::Low => {
154 // 0 = no-op, 1 = select LOW 148 // 0 = no-op, 1 = select LOW
155 pint_reg() 149 PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number));
156 .cienf
157 .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) });
158 } 150 }
159 Level::High => { 151 Level::High => {
160 // 0 = no-op, 1 = select HIGH 152 // 0 = no-op, 1 = select HIGH
161 pint_reg() 153 PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number));
162 .sienf
163 .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) });
164 } 154 }
165 } 155 }
166 } 156 }
167 InterruptOn::Edge(edge) => { 157 InterruptOn::Edge(edge) => {
168 // Set pin interrupt register to edge sensitive or level sensitive 158 // Set pin interrupt register to edge sensitive or level sensitive
169 // 0 = edge sensitive, 1 = level sensitive 159 // 0 = edge sensitive, 1 = level sensitive
170 pint_reg() 160 PINT.isel()
171 .isel 161 .modify(|w| w.set_pmode(w.pmode() & !(1 << interrupt_number)));
172 .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << interrupt_number)) });
173 162
174 // Enable rising/falling edge detection 163 // Enable rising/falling edge detection
175 match edge { 164 match edge {
176 Edge::Rising => { 165 Edge::Rising => {
177 // 0 = no-op, 1 = enable rising edge 166 // 0 = no-op, 1 = enable rising edge
178 pint_reg() 167 PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number));
179 .sienr
180 .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) });
181 // 0 = no-op, 1 = disable falling edge 168 // 0 = no-op, 1 = disable falling edge
182 pint_reg() 169 PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number));
183 .cienf
184 .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) });
185 } 170 }
186 Edge::Falling => { 171 Edge::Falling => {
187 // 0 = no-op, 1 = enable falling edge 172 // 0 = no-op, 1 = enable falling edge
188 pint_reg() 173 PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number));
189 .sienf
190 .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) });
191 // 0 = no-op, 1 = disable rising edge 174 // 0 = no-op, 1 = disable rising edge
192 pint_reg() 175 PINT.cienr().write(|w| w.set_cenrl(1 << interrupt_number));
193 .cienr
194 .write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) });
195 } 176 }
196 Edge::Both => { 177 Edge::Both => {
197 // 0 = no-op, 1 = enable 178 // 0 = no-op, 1 = enable
198 pint_reg() 179 PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number));
199 .sienr 180 PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number));
200 .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) });
201 pint_reg()
202 .sienf
203 .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) });
204 } 181 }
205 } 182 }
206 } 183 }
@@ -239,12 +216,8 @@ impl<'d> Drop for InputFuture<'d> {
239 216
240 // Disable pin interrupt 217 // Disable pin interrupt
241 // 0 = no-op, 1 = disable 218 // 0 = no-op, 1 = disable
242 pint_reg() 219 PINT.cienr().write(|w| w.set_cenrl(1 << interrupt_number));
243 .cienr 220 PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number));
244 .write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) });
245 pint_reg()
246 .cienf
247 .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) });
248 221
249 critical_section::with(|cs| { 222 critical_section::with(|cs| {
250 let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); 223 let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
@@ -277,12 +250,8 @@ impl<'d> Future for InputFuture<'d> {
277} 250}
278 251
279fn handle_interrupt(interrupt_number: usize) { 252fn handle_interrupt(interrupt_number: usize) {
280 pint_reg() 253 PINT.rise().write(|w| w.set_rdet(1 << interrupt_number));
281 .rise 254 PINT.fall().write(|w| w.set_fdet(1 << interrupt_number));
282 .write(|w| unsafe { w.rdet().bits(1 << interrupt_number) });
283 pint_reg()
284 .fall
285 .write(|w| unsafe { w.fdet().bits(1 << interrupt_number) });
286 255
287 critical_section::with(|cs| { 256 critical_section::with(|cs| {
288 let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); 257 let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
diff --git a/embassy-nxp/src/time_driver/rtc.rs b/embassy-nxp/src/time_driver/rtc.rs
index 94272e9c2..fb6de6a5e 100644
--- a/embassy-nxp/src/time_driver/rtc.rs
+++ b/embassy-nxp/src/time_driver/rtc.rs
@@ -6,7 +6,9 @@ use embassy_hal_internal::interrupt::{InterruptExt, Priority};
6use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; 6use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
7use embassy_time_driver::{time_driver_impl, Driver}; 7use embassy_time_driver::{time_driver_impl, Driver};
8use embassy_time_queue_utils::Queue; 8use embassy_time_queue_utils::Queue;
9use lpc55_pac::{interrupt, PMC, RTC, SYSCON}; 9
10use crate::pac::{interrupt, pmc, rtc, PMC, RTC, SYSCON};
11
10struct AlarmState { 12struct AlarmState {
11 timestamp: Cell<u64>, 13 timestamp: Cell<u64>,
12} 14}
@@ -32,33 +34,32 @@ time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
32}); 34});
33impl RtcDriver { 35impl RtcDriver {
34 fn init(&'static self) { 36 fn init(&'static self) {
35 let syscon = unsafe { &*SYSCON::ptr() }; 37 let syscon = SYSCON;
36 let pmc = unsafe { &*PMC::ptr() }; 38 let pmc = PMC;
37 let rtc = unsafe { &*RTC::ptr() }; 39 let rtc = RTC;
38 40
39 syscon.ahbclkctrl0.modify(|_, w| w.rtc().enable()); 41 syscon.ahbclkctrl0().modify(|w| w.set_rtc(true));
40 42
41 // By default the RTC enters software reset. If for some reason it is 43 // By default the RTC enters software reset. If for some reason it is
42 // not in reset, we enter and them promptly leave.q 44 // not in reset, we enter and them promptly leave.q
43 rtc.ctrl.modify(|_, w| w.swreset().set_bit()); 45 rtc.ctrl().modify(|w| w.set_swreset(true));
44 rtc.ctrl.modify(|_, w| w.swreset().clear_bit()); 46 rtc.ctrl().modify(|w| w.set_swreset(false));
45 47
46 // Select clock source - either XTAL or FRO 48 // Select clock source - either XTAL or FRO
47 // pmc.rtcosc32k.write(|w| w.sel().xtal32k()); 49 // pmc.rtcosc32k().write(|w| w.set_sel(pmc::vals::Sel::XTAL32K));
48 pmc.rtcosc32k.write(|w| w.sel().fro32k()); 50 pmc.rtcosc32k().write(|w| w.set_sel(pmc::vals::Sel::FRO32K));
49 51
50 // Start the RTC peripheral 52 // Start the RTC peripheral
51 rtc.ctrl.modify(|_, w| w.rtc_osc_pd().power_up()); 53 rtc.ctrl().modify(|w| w.set_rtc_osc_pd(rtc::vals::RtcOscPd::POWER_UP));
52
53 // rtc.ctrl.modify(|_, w| w.rtc_en().clear_bit()); // EXTRA
54 54
55 //reset/clear(?) counter 55 //reset/clear(?) counter
56 rtc.count.reset(); 56 rtc.count().modify(|w| w.set_val(0));
57 //en rtc main counter 57 //en rtc main counter
58 rtc.ctrl.modify(|_, w| w.rtc_en().set_bit()); 58 rtc.ctrl().modify(|w| w.set_rtc_en(true));
59 rtc.ctrl.modify(|_, w| w.rtc1khz_en().set_bit()); 59 rtc.ctrl().modify(|w| w.set_rtc1khz_en(true));
60 // subsec counter enable 60 // subsec counter enable
61 rtc.ctrl.modify(|_, w| w.rtc_subsec_ena().set_bit()); 61 rtc.ctrl()
62 .modify(|w| w.set_rtc_subsec_ena(rtc::vals::RtcSubsecEna::POWER_UP));
62 63
63 // enable irq 64 // enable irq
64 unsafe { 65 unsafe {
@@ -68,7 +69,7 @@ impl RtcDriver {
68 } 69 }
69 70
70 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { 71 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
71 let rtc = unsafe { &*RTC::ptr() }; 72 let rtc = RTC;
72 let alarm = &self.alarms.borrow(cs); 73 let alarm = &self.alarms.borrow(cs);
73 alarm.timestamp.set(timestamp); 74 alarm.timestamp.set(timestamp);
74 let now = self.now(); 75 let now = self.now();
@@ -83,33 +84,38 @@ impl RtcDriver {
83 let sec = (diff / 32768) as u32; 84 let sec = (diff / 32768) as u32;
84 let subsec = (diff % 32768) as u32; 85 let subsec = (diff % 32768) as u32;
85 86
86 let current_sec = rtc.count.read().val().bits(); 87 let current_sec = rtc.count().read().val();
87 let target_sec = current_sec.wrapping_add(sec as u32); 88 let target_sec = current_sec.wrapping_add(sec as u32);
88 89
89 rtc.match_.write(|w| unsafe { w.matval().bits(target_sec) }); 90 rtc.match_().write(|w| w.set_matval(target_sec));
90 rtc.wake.write(|w| unsafe { 91 rtc.wake().write(|w| {
91 let ms = (subsec * 1000) / 32768; 92 let ms = (subsec * 1000) / 32768;
92 w.val().bits(ms as u16) 93 w.set_val(ms as u16)
93 }); 94 });
95
94 if subsec > 0 { 96 if subsec > 0 {
95 let ms = (subsec * 1000) / 32768; 97 let ms = (subsec * 1000) / 32768;
96 rtc.wake.write(|w| unsafe { w.val().bits(ms as u16) }); 98 rtc.wake().write(|w| w.set_val(ms as u16));
97 } 99 }
98 rtc.ctrl.modify(|_, w| w.alarm1hz().clear_bit().wake1khz().clear_bit()); 100
101 rtc.ctrl().modify(|w| {
102 w.set_alarm1hz(false);
103 w.set_wake1khz(rtc::vals::Wake1khz::RUN)
104 });
99 true 105 true
100 } 106 }
101 107
102 fn on_interrupt(&self) { 108 fn on_interrupt(&self) {
103 critical_section::with(|cs| { 109 critical_section::with(|cs| {
104 let rtc = unsafe { &*RTC::ptr() }; 110 let rtc = RTC;
105 let flags = rtc.ctrl.read(); 111 let flags = rtc.ctrl().read();
106 if flags.alarm1hz().bit_is_clear() { 112 if flags.alarm1hz() == false {
107 rtc.ctrl.modify(|_, w| w.alarm1hz().set_bit()); 113 rtc.ctrl().modify(|w| w.set_alarm1hz(true));
108 self.trigger_alarm(cs); 114 self.trigger_alarm(cs);
109 } 115 }
110 116
111 if flags.wake1khz().bit_is_clear() { 117 if flags.wake1khz() == rtc::vals::Wake1khz::RUN {
112 rtc.ctrl.modify(|_, w| w.wake1khz().set_bit()); 118 rtc.ctrl().modify(|w| w.set_wake1khz(rtc::vals::Wake1khz::TIMEOUT));
113 self.trigger_alarm(cs); 119 self.trigger_alarm(cs);
114 } 120 }
115 }); 121 });
@@ -135,13 +141,13 @@ impl RtcDriver {
135 141
136impl Driver for RtcDriver { 142impl Driver for RtcDriver {
137 fn now(&self) -> u64 { 143 fn now(&self) -> u64 {
138 let rtc = unsafe { &*RTC::ptr() }; 144 let rtc = RTC;
139 145
140 loop { 146 loop {
141 let sec1 = rtc.count.read().val().bits() as u64; 147 let sec1 = rtc.count().read().val() as u64;
142 let sub1 = rtc.subsec.read().subsec().bits() as u64; 148 let sub1 = rtc.subsec().read().subsec() as u64;
143 let sec2 = rtc.count.read().val().bits() as u64; 149 let sec2 = rtc.count().read().val() as u64;
144 let sub2 = rtc.subsec.read().subsec().bits() as u64; 150 let sub2 = rtc.subsec().read().subsec() as u64;
145 151
146 if sec1 == sec2 && sub1 == sub2 { 152 if sec1 == sec2 && sub1 == sub2 {
147 return sec1 * 32768 + sub1; 153 return sec1 * 32768 + sub1;
@@ -162,7 +168,7 @@ impl Driver for RtcDriver {
162 }) 168 })
163 } 169 }
164} 170}
165#[cortex_m_rt::interrupt] 171#[interrupt]
166fn RTC() { 172fn RTC() {
167 DRIVER.on_interrupt(); 173 DRIVER.on_interrupt();
168} 174}
diff --git a/embassy-nxp/src/usart.rs b/embassy-nxp/src/usart.rs
index 009c251e2..1d8886f24 100644
--- a/embassy-nxp/src/usart.rs
+++ b/embassy-nxp/src/usart.rs
@@ -1,6 +1,5 @@
1//! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver. 1//! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver.
2#![macro_use]
3 2
4#[cfg_attr(feature = "lpc55", path = "./usart/lpc55.rs")] 3#[cfg_attr(feature = "lpc55-core0", path = "./usart/lpc55.rs")]
5mod inner; 4mod inner;
6pub use inner::*; 5pub use inner::*;
diff --git a/embassy-nxp/src/usart/lpc55.rs b/embassy-nxp/src/usart/lpc55.rs
index 3f7456a2e..428b80c4b 100644
--- a/embassy-nxp/src/usart/lpc55.rs
+++ b/embassy-nxp/src/usart/lpc55.rs
@@ -2,9 +2,12 @@ use core::marker::PhantomData;
2 2
3use embassy_hal_internal::{Peri, PeripheralType}; 3use embassy_hal_internal::{Peri, PeripheralType};
4use embedded_io::{self, ErrorKind}; 4use embedded_io::{self, ErrorKind};
5pub use sealed::SealedInstance;
6 5
7use crate::gpio::AnyPin; 6use crate::gpio::{match_iocon, AnyPin, Bank, SealedPin};
7use crate::pac::flexcomm::Flexcomm as FlexcommReg;
8use crate::pac::iocon::vals::PioFunc;
9use crate::pac::usart::Usart as UsartReg;
10use crate::pac::*;
8use crate::{Blocking, Mode}; 11use crate::{Blocking, Mode};
9 12
10/// Serial error 13/// Serial error
@@ -47,16 +50,6 @@ pub enum DataBits {
47 DataBits9, 50 DataBits9,
48} 51}
49 52
50impl DataBits {
51 fn bits(&self) -> u8 {
52 match self {
53 Self::DataBits7 => 0b00,
54 Self::DataBits8 => 0b01,
55 Self::DataBits9 => 0b10,
56 }
57 }
58}
59
60/// Parity bit. 53/// Parity bit.
61#[derive(Clone, Copy, PartialEq, Eq, Debug)] 54#[derive(Clone, Copy, PartialEq, Eq, Debug)]
62pub enum Parity { 55pub enum Parity {
@@ -68,16 +61,6 @@ pub enum Parity {
68 ParityOdd, 61 ParityOdd,
69} 62}
70 63
71impl Parity {
72 fn bits(&self) -> u8 {
73 match self {
74 Self::ParityNone => 0b00,
75 Self::ParityEven => 0b10,
76 Self::ParityOdd => 0b11,
77 }
78 }
79}
80
81/// Stop bits. 64/// Stop bits.
82#[derive(Clone, Copy, PartialEq, Eq, Debug)] 65#[derive(Clone, Copy, PartialEq, Eq, Debug)]
83pub enum StopBits { 66pub enum StopBits {
@@ -87,15 +70,6 @@ pub enum StopBits {
87 Stop2, 70 Stop2,
88} 71}
89 72
90impl StopBits {
91 fn bits(&self) -> bool {
92 return match self {
93 Self::Stop1 => false,
94 Self::Stop2 => true,
95 };
96 }
97}
98
99/// UART config. 73/// UART config.
100#[non_exhaustive] 74#[non_exhaustive]
101#[derive(Clone, Debug)] 75#[derive(Clone, Debug)]
@@ -117,7 +91,7 @@ pub struct Config {
117impl Default for Config { 91impl Default for Config {
118 fn default() -> Self { 92 fn default() -> Self {
119 Self { 93 Self {
120 baudrate: 9600, 94 baudrate: 115200,
121 data_bits: DataBits::DataBits8, 95 data_bits: DataBits::DataBits8,
122 stop_bits: StopBits::Stop1, 96 stop_bits: StopBits::Stop1,
123 parity: Parity::ParityNone, 97 parity: Parity::ParityNone,
@@ -131,59 +105,72 @@ impl Default for Config {
131/// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time 105/// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time
132/// T: the peripheral instance type allowing usage of peripheral specific registers 106/// T: the peripheral instance type allowing usage of peripheral specific registers
133/// M: the operating mode of USART peripheral 107/// M: the operating mode of USART peripheral
134pub struct Usart<'d, T: Instance, M: Mode> { 108pub struct Usart<'d, M: Mode> {
135 tx: UsartTx<'d, T, M>, 109 tx: UsartTx<'d, M>,
136 rx: UsartRx<'d, T, M>, 110 rx: UsartRx<'d, M>,
137} 111}
138 112
139pub struct UsartTx<'d, T: Instance, M: Mode> { 113pub struct UsartTx<'d, M: Mode> {
140 phantom: PhantomData<(&'d (), T, M)>, 114 info: &'static Info,
115 phantom: PhantomData<(&'d (), M)>,
141} 116}
142 117
143pub struct UsartRx<'d, T: Instance, M: Mode> { 118pub struct UsartRx<'d, M: Mode> {
144 phantom: PhantomData<(&'d (), T, M)>, 119 info: &'static Info,
120 phantom: PhantomData<(&'d (), M)>,
145} 121}
146 122
147impl<'d, T: Instance, M: Mode> UsartTx<'d, T, M> { 123impl<'d, M: Mode> UsartTx<'d, M> {
148 pub fn new(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self { 124 pub fn new<T: Instance>(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self {
149 Usart::<T, M>::init(Some(tx.into()), None, config); 125 Usart::<M>::init::<T>(Some(tx.into()), None, config);
150 Self::new_inner() 126 Self::new_inner(T::info())
151 } 127 }
152 128
153 #[inline] 129 #[inline]
154 fn new_inner() -> Self { 130 fn new_inner(info: &'static Info) -> Self {
155 Self { phantom: PhantomData } 131 Self {
132 info,
133 phantom: PhantomData,
134 }
156 } 135 }
157 136
158 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { 137 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
159 T::blocking_write(buffer) 138 for &b in buffer {
139 while !(self.info.usart_reg.fifostat().read().txnotfull()) {}
140 self.info.usart_reg.fifowr().write(|w| w.set_txdata(b as u16));
141 }
142 Ok(())
160 } 143 }
161 144
162 pub fn blocking_flush(&mut self) -> Result<(), Error> { 145 pub fn blocking_flush(&mut self) -> Result<(), Error> {
163 T::blocking_flush() 146 while !(self.info.usart_reg.fifostat().read().txempty()) {}
147 Ok(())
164 } 148 }
165 149
166 pub fn tx_busy(&self) -> bool { 150 pub fn tx_busy(&self) -> bool {
167 T::tx_busy() 151 !(self.info.usart_reg.fifostat().read().txempty())
168 } 152 }
169} 153}
170 154
171impl<'d, T: Instance> UsartTx<'d, T, Blocking> { 155impl<'d> UsartTx<'d, Blocking> {
172 pub fn new_blocking(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self { 156 pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self {
173 Usart::<T, Blocking>::init(Some(tx.into()), None, config); 157 Usart::<Blocking>::init::<T>(Some(tx.into()), None, config);
174 Self::new_inner() 158 Self::new_inner(T::info())
175 } 159 }
176} 160}
177 161
178impl<'d, T: Instance, M: Mode> UsartRx<'d, T, M> { 162impl<'d, M: Mode> UsartRx<'d, M> {
179 pub fn new(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self { 163 pub fn new<T: Instance>(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self {
180 Usart::<T, M>::init(None, Some(rx.into()), config); 164 Usart::<M>::init::<T>(None, Some(rx.into()), config);
181 Self::new_inner() 165 Self::new_inner(T::info())
182 } 166 }
183 167
184 #[inline] 168 #[inline]
185 fn new_inner() -> Self { 169 fn new_inner(info: &'static Info) -> Self {
186 Self { phantom: PhantomData } 170 Self {
171 info,
172 phantom: PhantomData,
173 }
187 } 174 }
188 175
189 pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> { 176 pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> {
@@ -201,19 +188,35 @@ impl<'d, T: Instance, M: Mode> UsartRx<'d, T, M> {
201 /// - Ok(n) -> read n bytes 188 /// - Ok(n) -> read n bytes
202 /// - Err(n, Error) -> read n-1 bytes, but encountered an error while reading nth byte 189 /// - Err(n, Error) -> read n-1 bytes, but encountered an error while reading nth byte
203 fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> { 190 fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> {
204 T::drain_fifo(buffer) 191 for (i, b) in buffer.iter_mut().enumerate() {
192 while !(self.info.usart_reg.fifostat().read().rxnotempty()) {}
193 if self.info.usart_reg.fifostat().read().rxerr() {
194 return Err((i, Error::Overrun));
195 } else if self.info.usart_reg.fifordnopop().read().parityerr() {
196 return Err((i, Error::Parity));
197 } else if self.info.usart_reg.fifordnopop().read().framerr() {
198 return Err((i, Error::Framing));
199 } else if self.info.usart_reg.fifordnopop().read().rxnoise() {
200 return Err((i, Error::Noise));
201 } else if self.info.usart_reg.intstat().read().deltarxbrk() {
202 return Err((i, Error::Break));
203 }
204 let dr = self.info.usart_reg.fiford().read().rxdata() as u8;
205 *b = dr;
206 }
207 Ok(buffer.len())
205 } 208 }
206} 209}
207 210
208impl<'d, T: Instance> UsartRx<'d, T, Blocking> { 211impl<'d> UsartRx<'d, Blocking> {
209 pub fn new_blocking(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self { 212 pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self {
210 Usart::<T, Blocking>::init(None, Some(rx.into()), config); 213 Usart::<Blocking>::init::<T>(None, Some(rx.into()), config);
211 Self::new_inner() 214 Self::new_inner(T::info())
212 } 215 }
213} 216}
214 217
215impl<'d, T: Instance> Usart<'d, T, Blocking> { 218impl<'d> Usart<'d, Blocking> {
216 pub fn new_blocking( 219 pub fn new_blocking<T: Instance>(
217 usart: Peri<'d, T>, 220 usart: Peri<'d, T>,
218 tx: Peri<'d, impl TxPin<T>>, 221 tx: Peri<'d, impl TxPin<T>>,
219 rx: Peri<'d, impl RxPin<T>>, 222 rx: Peri<'d, impl RxPin<T>>,
@@ -223,29 +226,70 @@ impl<'d, T: Instance> Usart<'d, T, Blocking> {
223 } 226 }
224} 227}
225 228
226impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { 229impl<'d, M: Mode> Usart<'d, M> {
227 fn new_inner(_usart: Peri<'d, T>, mut tx: Peri<'d, AnyPin>, mut rx: Peri<'d, AnyPin>, config: Config) -> Self { 230 fn new_inner<T: Instance>(
228 Self::init(Some(tx.reborrow()), Some(rx.reborrow()), config); 231 _usart: Peri<'d, T>,
232 mut tx: Peri<'d, AnyPin>,
233 mut rx: Peri<'d, AnyPin>,
234 config: Config,
235 ) -> Self {
236 Self::init::<T>(Some(tx.reborrow()), Some(rx.reborrow()), config);
229 Self { 237 Self {
230 tx: UsartTx::new_inner(), 238 tx: UsartTx::new_inner(T::info()),
231 rx: UsartRx::new_inner(), 239 rx: UsartRx::new_inner(T::info()),
232 } 240 }
233 } 241 }
234 242
235 fn init(_tx: Option<Peri<'_, AnyPin>>, _rx: Option<Peri<'_, AnyPin>>, config: Config) { 243 fn init<T: Instance>(tx: Option<Peri<'_, AnyPin>>, rx: Option<Peri<'_, AnyPin>>, config: Config) {
236 T::enable_clock(); 244 Self::configure_flexcomm(T::info().fc_reg, T::instance_number());
237 T::reset_flexcomm(); 245 Self::configure_clock::<T>(&config);
238 let source_clock: u32 = T::select_clock(config.baudrate); 246 Self::pin_config::<T>(tx, rx);
239 T::configure_flexcomm(); 247 Self::configure_usart(T::info(), &config);
240 T::tx_pin_config();
241 T::rx_pin_config();
242 Self::set_baudrate(source_clock, config.baudrate);
243 T::configure_usart(config);
244 T::disable_dma();
245 T::enable_usart();
246 } 248 }
247 249
248 fn set_baudrate(source_clock: u32, baudrate: u32) { 250 fn configure_clock<T: Instance>(config: &Config) {
251 // Select source clock
252
253 // Adaptive clock choice based on baud rate
254 // To get the desired baud rate, it is essential to choose the clock bigger than baud rate so that it can be 'chiseled'
255 // There are two types of dividers: integer divider (baud rate generator register and oversample selection value)
256 // and fractional divider (fractional rate divider).
257
258 // By default, oversampling rate is 16 which is an industry standard.
259 // That means 16 clocks are used to deliver the byte to recipient.
260 // In this way the probability of getting correct bytes instead of noise directly increases as oversampling increases as well.
261
262 // Minimum and maximum values were computed taking these formulas into account:
263 // For minimum value, MULT = 0, BRGVAL = 0
264 // For maximum value, MULT = 255, BRGVAL = 255
265 // Flexcomm Interface function clock = (clock selected via FCCLKSEL) / (1 + MULT / DIV)
266 // By default, OSRVAL = 15 (see above)
267 // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1)
268 let source_clock = match config.baudrate {
269 750_001..=6_000_000 => {
270 SYSCON
271 .fcclksel(T::instance_number())
272 .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X3)); // 96 MHz
273 96_000_000
274 }
275 1501..=750_000 => {
276 SYSCON
277 .fcclksel(T::instance_number())
278 .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X2)); // 12 MHz
279 12_000_000
280 }
281 121..=1500 => {
282 SYSCON
283 .fcclksel(T::instance_number())
284 .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X4)); // 1 MHz
285 1_000_000
286 }
287 _ => {
288 panic!("{} baudrate is not permitted in this mode", config.baudrate);
289 }
290 };
291 // Calculate MULT and BRG values based on baudrate
292
249 // There are two types of dividers: integer divider (baud rate generator register and oversample selection value) 293 // There are two types of dividers: integer divider (baud rate generator register and oversample selection value)
250 // and fractional divider (fractional rate divider). 294 // and fractional divider (fractional rate divider).
251 // For oversampling, the default is industry standard 16x oversampling, i.e. OSRVAL = 15 295 // For oversampling, the default is industry standard 16x oversampling, i.e. OSRVAL = 15
@@ -274,14 +318,167 @@ impl<'d, T: Instance, M: Mode> Usart<'d, T, M> {
274 // Secondly, MULT is calculated to ultimately 'chisel' the clock to get the baud rate. 318 // Secondly, MULT is calculated to ultimately 'chisel' the clock to get the baud rate.
275 // The deduced formulas are written below. 319 // The deduced formulas are written below.
276 320
277 let brg_value = (source_clock / (16 * baudrate)).min(255); 321 let brg_value = (source_clock / (16 * config.baudrate)).min(255);
278 let raw_clock = source_clock / (16 * brg_value); 322 let raw_clock = source_clock / (16 * brg_value);
279 let mult_value = ((raw_clock * 256 / baudrate) - 256).min(255); 323 let mult_value = ((raw_clock * 256 / config.baudrate) - 256).min(255);
280 T::set_baudrate(mult_value as u8, brg_value as u8); 324
325 // Write values to the registers
326
327 // FCLK = (clock selected via FCCLKSEL) / (1+ MULT / DIV)
328 // Remark: To use the fractional baud rate generator, 0xFF must be wirtten to the DIV value
329 // to yield a denominator vale of 256. All other values are not supported
330 SYSCON.flexfrgctrl(T::instance_number()).modify(|w| {
331 w.set_div(0xFF);
332 w.set_mult(mult_value as u8);
333 });
334
335 // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1)
336 // By default, oversampling is 16x, i.e. OSRVAL = 15
337
338 // Typical industry standard USARTs use a 16x oversample clock to transmit and receive
339 // asynchronous data. This is the number of BRG clocks used for one data bit. The
340 // Oversample Select Register (OSR) allows this USART to use a 16x down to a 5x
341 // oversample clock. There is no oversampling in synchronous modes.
342 T::info()
343 .usart_reg
344 .brg()
345 .modify(|w| w.set_brgval((brg_value - 1) as u16));
346 }
347
348 fn pin_config<T: Instance>(tx: Option<Peri<'_, AnyPin>>, rx: Option<Peri<'_, AnyPin>>) {
349 if let Some(tx_pin) = tx {
350 match_iocon!(register, tx_pin.pin_bank(), tx_pin.pin_number(), {
351 register.modify(|w| {
352 w.set_func(T::tx_pin_func());
353 w.set_mode(iocon::vals::PioMode::INACTIVE);
354 w.set_slew(iocon::vals::PioSlew::STANDARD);
355 w.set_invert(false);
356 w.set_digimode(iocon::vals::PioDigimode::DIGITAL);
357 w.set_od(iocon::vals::PioOd::NORMAL);
358 });
359 })
360 }
361
362 if let Some(rx_pin) = rx {
363 match_iocon!(register, rx_pin.pin_bank(), rx_pin.pin_number(), {
364 register.modify(|w| {
365 w.set_func(T::rx_pin_func());
366 w.set_mode(iocon::vals::PioMode::INACTIVE);
367 w.set_slew(iocon::vals::PioSlew::STANDARD);
368 w.set_invert(false);
369 w.set_digimode(iocon::vals::PioDigimode::DIGITAL);
370 w.set_od(iocon::vals::PioOd::NORMAL);
371 });
372 })
373 };
374 }
375
376 fn configure_flexcomm(flexcomm_register: crate::pac::flexcomm::Flexcomm, instance_number: usize) {
377 critical_section::with(|_cs| {
378 if !(SYSCON.ahbclkctrl0().read().iocon()) {
379 SYSCON.ahbclkctrl0().modify(|w| w.set_iocon(true));
380 }
381 });
382 critical_section::with(|_cs| {
383 if !(SYSCON.ahbclkctrl1().read().fc(instance_number)) {
384 SYSCON.ahbclkctrl1().modify(|w| w.set_fc(instance_number, true));
385 }
386 });
387 SYSCON
388 .presetctrl1()
389 .modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::ASSERTED));
390 SYSCON
391 .presetctrl1()
392 .modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::RELEASED));
393 flexcomm_register
394 .pselid()
395 .modify(|w| w.set_persel(flexcomm::vals::Persel::USART));
396 }
397
398 fn configure_usart(info: &'static Info, config: &Config) {
399 let registers = info.usart_reg;
400 // See section 34.6.1
401 registers.cfg().modify(|w| {
402 // LIN break mode enable
403 // Disabled. Break detect and generate is configured for normal operation.
404 w.set_linmode(false);
405 //CTS Enable. Determines whether CTS is used for flow control. CTS can be from the
406 //input pin, or from the USART’s own RTS if loopback mode is enabled.
407 // No flow control. The transmitter does not receive any automatic flow control signal.
408 w.set_ctsen(false);
409 // Selects synchronous or asynchronous operation.
410 w.set_syncen(usart::vals::Syncen::ASYNCHRONOUS_MODE);
411 // Selects the clock polarity and sampling edge of received data in synchronous mode.
412 w.set_clkpol(usart::vals::Clkpol::RISING_EDGE);
413 // Synchronous mode Master select.
414 // When synchronous mode is enabled, the USART is a master.
415 w.set_syncmst(usart::vals::Syncmst::MASTER);
416 // Selects data loopback mode
417 w.set_loop_(usart::vals::Loop::NORMAL);
418 // Output Enable Turnaround time enable for RS-485 operation.
419 // Disabled. If selected by OESEL, the Output Enable signal deasserted at the end of
420 // the last stop bit of a transmission.
421 w.set_oeta(false);
422 // Output enable select.
423 // Standard. The RTS signal is used as the standard flow control function.
424 w.set_oesel(usart::vals::Oesel::STANDARD);
425 // Automatic address matching enable.
426 // Disabled. When addressing is enabled by ADDRDET, address matching is done by
427 // software. This provides the possibility of versatile addressing (e.g. respond to more
428 // than one address)
429 w.set_autoaddr(false);
430 // Output enable polarity.
431 // Low. If selected by OESEL, the output enable is active low.
432 w.set_oepol(usart::vals::Oepol::LOW);
433 });
434
435 // Configurations based on the config written by a user
436 registers.cfg().modify(|w| {
437 w.set_datalen(match config.data_bits {
438 DataBits::DataBits7 => usart::vals::Datalen::BIT_7,
439 DataBits::DataBits8 => usart::vals::Datalen::BIT_8,
440 DataBits::DataBits9 => usart::vals::Datalen::BIT_9,
441 });
442 w.set_paritysel(match config.parity {
443 Parity::ParityNone => usart::vals::Paritysel::NO_PARITY,
444 Parity::ParityEven => usart::vals::Paritysel::EVEN_PARITY,
445 Parity::ParityOdd => usart::vals::Paritysel::ODD_PARITY,
446 });
447 w.set_stoplen(match config.stop_bits {
448 StopBits::Stop1 => usart::vals::Stoplen::BIT_1,
449 StopBits::Stop2 => usart::vals::Stoplen::BITS_2,
450 });
451 w.set_rxpol(match config.invert_rx {
452 false => usart::vals::Rxpol::STANDARD,
453 true => usart::vals::Rxpol::INVERTED,
454 });
455 w.set_txpol(match config.invert_tx {
456 false => usart::vals::Txpol::STANDARD,
457 true => usart::vals::Txpol::INVERTED,
458 });
459 });
460
461 // DMA-related settings
462 registers.fifocfg().modify(|w| {
463 w.set_dmatx(false);
464 w.set_dmatx(false);
465 });
466
467 // Enabling USART
468 registers.fifocfg().modify(|w| {
469 w.set_enabletx(true);
470 w.set_enablerx(true);
471 });
472 registers.cfg().modify(|w| w.set_enable(true));
473
474 // Drain RX FIFO in case it still has some unrelevant data
475 while registers.fifostat().read().rxnotempty() {
476 let _ = registers.fiford().read().0;
477 }
281 } 478 }
282} 479}
283 480
284impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { 481impl<'d, M: Mode> Usart<'d, M> {
285 /// Transmit the provided buffer blocking execution until done. 482 /// Transmit the provided buffer blocking execution until done.
286 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { 483 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
287 self.tx.blocking_write(buffer) 484 self.tx.blocking_write(buffer)
@@ -304,19 +501,19 @@ impl<'d, T: Instance, M: Mode> Usart<'d, T, M> {
304 501
305 /// Split the Usart into a transmitter and receiver, which is particularly 502 /// Split the Usart into a transmitter and receiver, which is particularly
306 /// useful when having two tasks correlating to transmitting and receiving. 503 /// useful when having two tasks correlating to transmitting and receiving.
307 pub fn split(self) -> (UsartTx<'d, T, M>, UsartRx<'d, T, M>) { 504 pub fn split(self) -> (UsartTx<'d, M>, UsartRx<'d, M>) {
308 (self.tx, self.rx) 505 (self.tx, self.rx)
309 } 506 }
310 507
311 /// Split the Usart into a transmitter and receiver by mutable reference, 508 /// Split the Usart into a transmitter and receiver by mutable reference,
312 /// which is particularly useful when having two tasks correlating to 509 /// which is particularly useful when having two tasks correlating to
313 /// transmitting and receiving. 510 /// transmitting and receiving.
314 pub fn split_ref(&mut self) -> (&mut UsartTx<'d, T, M>, &mut UsartRx<'d, T, M>) { 511 pub fn split_ref(&mut self) -> (&mut UsartTx<'d, M>, &mut UsartRx<'d, M>) {
315 (&mut self.tx, &mut self.rx) 512 (&mut self.tx, &mut self.rx)
316 } 513 }
317} 514}
318 515
319impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UsartTx<'d, T, M> { 516impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UsartTx<'d, M> {
320 type Error = Error; 517 type Error = Error;
321 518
322 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { 519 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
@@ -328,7 +525,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for
328 } 525 }
329} 526}
330 527
331impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Usart<'d, T, M> { 528impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Usart<'d, M> {
332 type Error = Error; 529 type Error = Error;
333 530
334 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { 531 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
@@ -340,11 +537,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for
340 } 537 }
341} 538}
342 539
343impl<'d, T: Instance> embedded_io::ErrorType for UsartTx<'d, T, Blocking> { 540impl<'d> embedded_io::ErrorType for UsartTx<'d, Blocking> {
344 type Error = Error; 541 type Error = Error;
345} 542}
346 543
347impl<'d, T: Instance> embedded_io::Write for UsartTx<'d, T, Blocking> { 544impl<'d> embedded_io::Write for UsartTx<'d, Blocking> {
348 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 545 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
349 self.blocking_write(buf).map(|_| buf.len()) 546 self.blocking_write(buf).map(|_| buf.len())
350 } 547 }
@@ -354,21 +551,21 @@ impl<'d, T: Instance> embedded_io::Write for UsartTx<'d, T, Blocking> {
354 } 551 }
355} 552}
356 553
357impl<'d, T: Instance> embedded_io::ErrorType for UsartRx<'d, T, Blocking> { 554impl<'d> embedded_io::ErrorType for UsartRx<'d, Blocking> {
358 type Error = Error; 555 type Error = Error;
359} 556}
360 557
361impl<'d, T: Instance> embedded_io::Read for UsartRx<'d, T, Blocking> { 558impl<'d> embedded_io::Read for UsartRx<'d, Blocking> {
362 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 559 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
363 self.blocking_read(buf).map(|_| buf.len()) 560 self.blocking_read(buf).map(|_| buf.len())
364 } 561 }
365} 562}
366 563
367impl<'d, T: Instance> embedded_io::ErrorType for Usart<'d, T, Blocking> { 564impl<'d> embedded_io::ErrorType for Usart<'d, Blocking> {
368 type Error = Error; 565 type Error = Error;
369} 566}
370 567
371impl<'d, T: Instance> embedded_io::Write for Usart<'d, T, Blocking> { 568impl<'d> embedded_io::Write for Usart<'d, Blocking> {
372 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 569 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
373 self.blocking_write(buf).map(|_| buf.len()) 570 self.blocking_write(buf).map(|_| buf.len())
374 } 571 }
@@ -378,468 +575,69 @@ impl<'d, T: Instance> embedded_io::Write for Usart<'d, T, Blocking> {
378 } 575 }
379} 576}
380 577
381impl<'d, T: Instance> embedded_io::Read for Usart<'d, T, Blocking> { 578impl<'d> embedded_io::Read for Usart<'d, Blocking> {
382 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 579 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
383 self.blocking_read(buf).map(|_| buf.len()) 580 self.blocking_read(buf).map(|_| buf.len())
384 } 581 }
385} 582}
386 583
387type UsartRegBlock = crate::pac::usart0::RegisterBlock; 584struct Info {
388 585 usart_reg: UsartReg,
389mod sealed { 586 fc_reg: FlexcommReg,
390 use crate::usart::inner::UsartRegBlock; 587}
391 use crate::usart::{Config, Error};
392 pub trait SealedInstance {
393 fn usart_reg() -> &'static UsartRegBlock;
394 fn enable_clock();
395 fn select_clock(baudrate: u32) -> u32;
396 fn configure_flexcomm();
397 fn set_baudrate(mult_value: u8, brg_value: u8);
398 fn reset_flexcomm();
399 fn tx_pin_config();
400 fn rx_pin_config();
401
402 fn configure_usart(config: Config) {
403 // See section 34.6.1
404 Self::usart_reg().cfg.modify(|_, w| {
405 // LIN break mode enable
406 w.linmode()
407 // Disabled. Break detect and generate is configured for normal operation.
408 .disabled()
409 //CTS Enable. Determines whether CTS is used for flow control. CTS can be from the
410 //input pin, or from the USART’s own RTS if loopback mode is enabled.
411 .ctsen()
412 // No flow control. The transmitter does not receive any automatic flow control signal.
413 .disabled()
414 // Selects synchronous or asynchronous operation.
415 .syncen()
416 .asynchronous_mode()
417 // Selects the clock polarity and sampling edge of received data in synchronous mode.
418 .clkpol()
419 .rising_edge()
420 // Synchronous mode Master select.
421 .syncmst()
422 // When synchronous mode is enabled, the USART is a master.
423 .master()
424 // Selects data loopback mode
425 .loop_()
426 // Normal operation
427 .normal()
428 // Output Enable Turnaround time enable for RS-485 operation.
429 .oeta()
430 // Disabled. If selected by OESEL, the Output Enable signal deasserted at the end of
431 // the last stop bit of a transmission.
432 .disabled()
433 // Output enable select.
434 .oesel()
435 // Standard. The RTS signal is used as the standard flow control function.
436 .standard()
437 // Automatic address matching enable.
438 .autoaddr()
439 // Disabled. When addressing is enabled by ADDRDET, address matching is done by
440 // software. This provides the possibility of versatile addressing (e.g. respond to more
441 // than one address)
442 .disabled()
443 // Output enable polarity.
444 .oepol()
445 // Low. If selected by OESEL, the output enable is active low.
446 .low()
447 });
448 588
449 Self::usart_reg().cfg.modify(|_, w| unsafe { 589trait SealedInstance {
450 w.datalen() 590 fn info() -> &'static Info;
451 .bits(config.data_bits.bits()) 591 fn instance_number() -> usize;
452 .paritysel() 592 fn tx_pin_func() -> PioFunc;
453 .bits(config.parity.bits()) 593 fn rx_pin_func() -> PioFunc;
454 .stoplen()
455 .bit(config.stop_bits.bits())
456 .rxpol()
457 .bit(config.invert_rx)
458 .txpol()
459 .bit(config.invert_tx)
460 });
461 }
462 fn disable_dma() {
463 Self::usart_reg()
464 .fifocfg
465 .modify(|_, w| w.dmatx().disabled().dmarx().disabled());
466 }
467 fn enable_usart() {
468 Self::usart_reg()
469 .fifocfg
470 .modify(|_, w| w.enabletx().enabled().enablerx().enabled());
471 Self::usart_reg().cfg.modify(|_, w| w.enable().enabled());
472 while Self::usart_reg().fifostat.read().rxnotempty().bit_is_set() {
473 let _ = Self::usart_reg().fiford.read().bits();
474 }
475 }
476 fn blocking_write(buffer: &[u8]) -> Result<(), Error> {
477 for &b in buffer {
478 while Self::usart_reg().fifostat.read().txnotfull().bit_is_clear() {}
479 Self::usart_reg()
480 .fifowr
481 .modify(|_, w| unsafe { w.txdata().bits(b as u16) });
482 }
483 Ok(())
484 }
485 fn blocking_flush() -> Result<(), Error> {
486 while Self::usart_reg().fifostat.read().txempty().bit_is_clear() {}
487 Ok(())
488 }
489 fn tx_busy() -> bool {
490 Self::usart_reg().fifostat.read().txempty().bit_is_clear()
491 }
492 fn drain_fifo(buffer: &mut [u8]) -> Result<usize, (usize, Error)> {
493 for (i, b) in buffer.iter_mut().enumerate() {
494 while Self::usart_reg().fifostat.read().rxnotempty().bit_is_clear() {}
495
496 if Self::usart_reg().fifostat.read().rxerr().bit_is_set() {
497 return Err((i, Error::Overrun));
498 } else if Self::usart_reg().fifordnopop.read().parityerr().bit_is_set() {
499 return Err((i, Error::Parity));
500 } else if Self::usart_reg().fifordnopop.read().framerr().bit_is_set() {
501 return Err((i, Error::Framing));
502 } else if Self::usart_reg().fifordnopop.read().rxnoise().bit_is_set() {
503 return Err((i, Error::Noise));
504 } else if Self::usart_reg().intstat.read().deltarxbrk().bit_is_set() {
505 return Err((i, Error::Break));
506 }
507 let dr = Self::usart_reg().fiford.read().bits() as u8;
508 *b = dr;
509 }
510 Ok(buffer.len())
511 }
512 }
513} 594}
514 595
515/// UART instance. 596/// UART instance.
516#[allow(private_bounds)] 597#[allow(private_bounds)]
517pub trait Instance: sealed::SealedInstance + PeripheralType {} 598pub trait Instance: SealedInstance + PeripheralType {}
518 599
519#[macro_export]
520macro_rules! impl_instance { 600macro_rules! impl_instance {
521 ( 601 ($inst:ident, $fc:ident, $tx_pin:ident, $rx_pin:ident, $fc_num:expr) => {
522 $inst:ident, 602 impl $crate::usart::inner::SealedInstance for $crate::peripherals::$inst {
523 usart_peripheral: $USARTX:ident, 603 fn info() -> &'static Info {
524 usart_crate: $usartX:ident, 604 static INFO: Info = Info {
525 605 usart_reg: crate::pac::$inst,
526 flexcomm: { 606 fc_reg: crate::pac::$fc,
527 field: $FLEXCOMM_FIELD:ident,
528 clock_field: $FLEXCOMM_CLK_FIELD:ident
529 },
530
531 reset: {
532 bit: $RESET_BIT:ident
533 },
534
535 clock: {
536 sel_field: $CLKSEL_FIELD:ident,
537 frg_field: $FRG_FIELD:ident
538 },
539
540 pins: {
541 tx: $TX_IOCON:ident => $TX_FUNC:expr,
542 rx: $RX_IOCON:ident => $RX_FUNC:expr
543 }
544
545 ) => {
546 impl $crate::usart::SealedInstance for $crate::peripherals::$inst {
547 fn usart_reg() -> &'static UsartRegBlock {
548 unsafe { &*$crate::pac::$USARTX::ptr() }
549 }
550
551 fn enable_clock() {
552 critical_section::with(|_cs| {
553 if syscon_reg().ahbclkctrl0.read().iocon().is_disable() {
554 syscon_reg().ahbclkctrl0.modify(|_, w| w.iocon().enable());
555 }
556 if syscon_reg().ahbclkctrl1.read().$FLEXCOMM_CLK_FIELD().is_disable() {
557 syscon_reg()
558 .ahbclkctrl1
559 .modify(|_, w| w.$FLEXCOMM_CLK_FIELD().enable());
560 }
561 });
562 }
563
564 fn configure_flexcomm() {
565 let flexcomm = unsafe { &*$crate::pac::$FLEXCOMM_FIELD::ptr() };
566 flexcomm.pselid.modify(|_, w| w.persel().usart());
567 }
568
569 fn reset_flexcomm() {
570 syscon_reg().presetctrl1.modify(|_, w| w.$RESET_BIT().set_bit());
571 syscon_reg().presetctrl1.modify(|_, w| w.$RESET_BIT().clear_bit());
572 }
573
574 fn select_clock(baudrate: u32) -> u32 {
575 // Adaptive clock choice based on baud rate
576 // To get the desired baud rate, it is essential to choose the clock bigger than baud rate so that it can be 'chiseled'
577 // There are two types of dividers: integer divider (baud rate generator register and oversample selection value)
578 // and fractional divider (fractional rate divider).
579
580 // By default, oversampling rate is 16 which is an industry standard.
581 // That means 16 clocks are used to deliver the byte to recipient.
582 // In this way the probability of getting correct bytes instead of noise directly increases as oversampling increases as well.
583
584 // Minimum and maximum values were computed taking these formulas into account:
585 // For minimum value, MULT = 0, BRGVAL = 0
586 // For maximum value, MULT = 255, BRGVAL = 255
587 // Flexcomm Interface function clock = (clock selected via FCCLKSEL) / (1 + MULT / DIV)
588 // By default, OSRVAL = 15 (see above)
589 // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1)
590 return match baudrate {
591 750_001..=6000000 => {
592 syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x3()); // 96 MHz
593 96_000_000
594 }
595 1501..=750_000 => {
596 syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x2()); // 12 MHz
597 12_000_000
598 }
599 121..=1500 => {
600 syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x4()); // 1 MHz
601 1_000_000
602 }
603 _ => {
604 panic!("{} baudrate is not permitted in this mode", baudrate);
605 }
606 }; 607 };
608 &INFO
607 } 609 }
608 610 #[inline]
609 fn set_baudrate(mult_value: u8, brg_value: u8) { 611 fn instance_number() -> usize {
610 // FCLK = (clock selected via FCCLKSEL) / (1+ MULT / DIV) 612 $fc_num
611 // Remark: To use the fractional baud rate generator, 0xFF must be wirtten to the DIV value
612 // to yield a denominator vale of 256. All other values are not supported
613 syscon_reg()
614 .$FRG_FIELD()
615 .modify(|_, w| unsafe { w.div().bits(0xFF).mult().bits(mult_value as u8) });
616
617 // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1)
618 // By default, oversampling is 16x, i.e. OSRVAL = 15
619
620 // Typical industry standard USARTs use a 16x oversample clock to transmit and receive
621 // asynchronous data. This is the number of BRG clocks used for one data bit. The
622 // Oversample Select Register (OSR) allows this USART to use a 16x down to a 5x
623 // oversample clock. There is no oversampling in synchronous modes.
624 Self::usart_reg()
625 .brg
626 .modify(|_, w| unsafe { w.brgval().bits((brg_value - 1) as u16) });
627 } 613 }
628 614 #[inline]
629 fn tx_pin_config() { 615 fn tx_pin_func() -> PioFunc {
630 iocon_reg().$TX_IOCON.modify(|_, w| unsafe { 616 PioFunc::$tx_pin
631 w.func()
632 .bits($TX_FUNC)
633 .digimode()
634 .digital()
635 .slew()
636 .standard()
637 .mode()
638 .inactive()
639 .invert()
640 .disabled()
641 .od()
642 .normal()
643 });
644 } 617 }
645 618 #[inline]
646 fn rx_pin_config() { 619 fn rx_pin_func() -> PioFunc {
647 iocon_reg().$RX_IOCON.modify(|_, w| unsafe { 620 PioFunc::$rx_pin
648 w.func()
649 .bits($RX_FUNC)
650 .digimode()
651 .digital()
652 .slew()
653 .standard()
654 .mode()
655 .inactive()
656 .invert()
657 .disabled()
658 .od()
659 .normal()
660 });
661 } 621 }
662 } 622 }
663
664 impl $crate::usart::Instance for $crate::peripherals::$inst {} 623 impl $crate::usart::Instance for $crate::peripherals::$inst {}
665 }; 624 };
666} 625}
667 626
668impl_instance!(USART0, usart_peripheral: USART0, usart_crate: usart0, 627impl_instance!(USART0, FLEXCOMM0, ALT1, ALT1, 0);
669 flexcomm: { 628impl_instance!(USART1, FLEXCOMM1, ALT2, ALT2, 1);
670 field: FLEXCOMM0, 629impl_instance!(USART2, FLEXCOMM2, ALT1, ALT1, 2);
671 clock_field: fc0 630impl_instance!(USART3, FLEXCOMM3, ALT1, ALT1, 3);
672 }, 631impl_instance!(USART4, FLEXCOMM4, ALT1, ALT2, 4);
673 632impl_instance!(USART5, FLEXCOMM5, ALT3, ALT3, 5);
674 reset: { 633impl_instance!(USART6, FLEXCOMM6, ALT2, ALT2, 6);
675 bit: fc0_rst 634impl_instance!(USART7, FLEXCOMM7, ALT7, ALT7, 7);
676 },
677
678 clock: {
679 sel_field: fcclksel0,
680 frg_field: flexfrg0ctrl
681 },
682
683 pins: {
684 tx: pio1_6 => 1,
685 rx: pio1_5 => 1
686 }
687);
688
689impl_instance!(USART1, usart_peripheral: USART1, usart_crate: usart1,
690 flexcomm: {
691 field: FLEXCOMM1,
692 clock_field: fc1
693 },
694
695 reset: {
696 bit: fc1_rst
697 },
698
699 clock: {
700 sel_field: fcclksel1,
701 frg_field: flexfrg1ctrl
702 },
703
704 pins: {
705 tx: pio1_11 => 2,
706 rx: pio1_10 => 2
707 }
708);
709
710impl_instance!(USART2, usart_peripheral: USART2, usart_crate: usart2,
711 flexcomm: {
712 field: FLEXCOMM2,
713 clock_field: fc2
714 },
715
716 reset: {
717 bit: fc2_rst
718 },
719
720 clock: {
721 sel_field: fcclksel2,
722 frg_field: flexfrg2ctrl
723 },
724
725 pins: {
726 tx: pio0_27 => 1,
727 rx: pio1_24 => 1
728 }
729);
730
731impl_instance!(USART3, usart_peripheral: USART3, usart_crate: usart3,
732 flexcomm: {
733 field: FLEXCOMM3,
734 clock_field: fc3
735 },
736
737 reset: {
738 bit: fc3_rst
739 },
740
741 clock: {
742 sel_field: fcclksel3,
743 frg_field: flexfrg3ctrl
744 },
745
746 pins: {
747 tx: pio0_2 => 1,
748 rx: pio0_3 => 1
749 }
750);
751
752impl_instance!(USART4, usart_peripheral: USART4, usart_crate: usart4,
753 flexcomm: {
754 field: FLEXCOMM4,
755 clock_field: fc4
756 },
757
758 reset: {
759 bit: fc4_rst
760 },
761
762 clock: {
763 sel_field: fcclksel4,
764 frg_field: flexfrg4ctrl
765 },
766
767 pins: {
768 tx: pio0_16 => 1,
769 rx: pio0_5 => 2
770 }
771);
772
773impl_instance!(USART5, usart_peripheral: USART5, usart_crate: usart5,
774 flexcomm: {
775 field: FLEXCOMM5,
776 clock_field: fc5
777 },
778
779 reset: {
780 bit: fc5_rst
781 },
782
783 clock: {
784 sel_field: fcclksel5,
785 frg_field: flexfrg5ctrl
786 },
787
788 pins: {
789 tx: pio0_9 => 3,
790 rx: pio0_8 => 3
791 }
792);
793
794impl_instance!(USART6, usart_peripheral: USART6, usart_crate: usart6,
795 flexcomm: {
796 field: FLEXCOMM6,
797 clock_field: fc6
798 },
799
800 reset: {
801 bit: fc6_rst
802 },
803
804 clock: {
805 sel_field: fcclksel6,
806 frg_field: flexfrg6ctrl
807 },
808
809 pins: {
810 tx: pio1_16 => 2,
811 rx: pio1_13 => 2
812 }
813);
814
815impl_instance!(USART7, usart_peripheral: USART7, usart_crate: usart7,
816 flexcomm: {
817 field: FLEXCOMM7,
818 clock_field: fc7
819 },
820
821 reset: {
822 bit: fc7_rst
823 },
824
825 clock: {
826 sel_field: fcclksel7,
827 frg_field: flexfrg7ctrl
828 },
829
830 pins: {
831 tx: pio0_19 => 7,
832 rx: pio0_20 => 7
833 }
834);
835 635
836/// Trait for TX pins. 636/// Trait for TX pins.
837pub trait TxPin<T: Instance>: crate::gpio::Pin {} 637pub trait TxPin<T: Instance>: crate::gpio::Pin {}
838/// Trait for RX pins. 638/// Trait for RX pins.
839pub trait RxPin<T: Instance>: crate::gpio::Pin {} 639pub trait RxPin<T: Instance>: crate::gpio::Pin {}
840 640
841// TODO: Add RTS, CTS and CLK pin traits
842
843macro_rules! impl_pin { 641macro_rules! impl_pin {
844 ($pin:ident, $instance:ident, Tx) => { 642 ($pin:ident, $instance:ident, Tx) => {
845 impl TxPin<crate::peripherals::$instance> for crate::peripherals::$pin {} 643 impl TxPin<crate::peripherals::$instance> for crate::peripherals::$pin {}
@@ -849,37 +647,19 @@ macro_rules! impl_pin {
849 }; 647 };
850} 648}
851 649
852impl_pin!(PIO1_5, USART0, Rx);
853impl_pin!(PIO1_6, USART0, Tx); 650impl_pin!(PIO1_6, USART0, Tx);
854impl_pin!(PIO1_10, USART1, Rx); 651impl_pin!(PIO1_5, USART0, Rx);
855impl_pin!(PIO1_11, USART1, Tx); 652impl_pin!(PIO1_11, USART1, Tx);
653impl_pin!(PIO1_10, USART1, Rx);
856impl_pin!(PIO0_27, USART2, Tx); 654impl_pin!(PIO0_27, USART2, Tx);
857impl_pin!(PIO1_24, USART2, Rx); 655impl_pin!(PIO1_24, USART2, Rx);
858impl_pin!(PIO0_2, USART3, Tx); 656impl_pin!(PIO0_2, USART3, Tx);
859impl_pin!(PIO0_3, USART3, Rx); 657impl_pin!(PIO0_3, USART3, Rx);
860impl_pin!(PIO0_16, USART4, Tx); 658impl_pin!(PIO0_16, USART4, Tx);
861impl_pin!(PIO0_5, USART4, Rx); 659impl_pin!(PIO0_5, USART4, Rx);
862impl_pin!(PIO0_8, USART5, Rx);
863impl_pin!(PIO0_9, USART5, Tx); 660impl_pin!(PIO0_9, USART5, Tx);
661impl_pin!(PIO0_8, USART5, Rx);
864impl_pin!(PIO1_16, USART6, Tx); 662impl_pin!(PIO1_16, USART6, Tx);
865impl_pin!(PIO1_13, USART6, Rx); 663impl_pin!(PIO1_13, USART6, Rx);
866impl_pin!(PIO0_20, USART7, Rx);
867impl_pin!(PIO0_19, USART7, Tx); 664impl_pin!(PIO0_19, USART7, Tx);
868 665impl_pin!(PIO0_20, USART7, Rx);
869/// Get the SYSCON register block.
870///
871/// # Safety
872/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO
873/// registers are not accessed concurrently by multiple threads.
874pub(crate) fn syscon_reg() -> &'static crate::pac::syscon::RegisterBlock {
875 unsafe { &*crate::pac::SYSCON::ptr() }
876}
877
878/// Get the IOCON register block.
879///
880/// # Safety
881/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO
882/// registers are not accessed concurrently by multiple threads.
883pub(crate) fn iocon_reg() -> &'static crate::pac::iocon::RegisterBlock {
884 unsafe { &*crate::pac::IOCON::ptr() }
885}
diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md
index ebdc3e1c8..d1265ffc4 100644
--- a/embassy-rp/CHANGELOG.md
+++ b/embassy-rp/CHANGELOG.md
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Add PIO SPI
12- Add PIO I2S input
13- Add PIO onewire parasite power strong pullup
14- add `wait_for_alarm` and `alarm_scheduled` methods to rtc module ([#4216](https://github.com/embassy-rs/embassy/pull/4216))
15
11## 0.8.0 - 2025-08-26 16## 0.8.0 - 2025-08-26
12 17
13## 0.7.1 - 2025-08-26 18## 0.7.1 - 2025-08-26
@@ -51,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
51 56
52## 0.4.0 - 2025-03-09 57## 0.4.0 - 2025-03-09
53 58
54- Add PIO functions. ([#3857](https://github.com/embassy-rs/embassy/pull/3857)) 59- Add PIO functions. ([#3857](https://github.com/embassy-rs/embassy/pull/3857))
55 The functions added in this change are `get_addr` `get_tx_threshold`, `set_tx_threshold`, `get_rx_threshold`, `set_rx_threshold`, `set_thresholds`. 60 The functions added in this change are `get_addr` `get_tx_threshold`, `set_tx_threshold`, `get_rx_threshold`, `set_rx_threshold`, `set_thresholds`.
56- Expose the watchdog reset reason. ([#3877](https://github.com/embassy-rs/embassy/pull/3877)) 61- Expose the watchdog reset reason. ([#3877](https://github.com/embassy-rs/embassy/pull/3877))
57- Update pio-rs, reexport, move instr methods to SM. ([#3865](https://github.com/embassy-rs/embassy/pull/3865)) 62- Update pio-rs, reexport, move instr methods to SM. ([#3865](https://github.com/embassy-rs/embassy/pull/3865))
@@ -92,7 +97,7 @@ Small release fixing a few gnarly bugs, upgrading is strongly recommended.
92- Add Clone and Copy to Error types 97- Add Clone and Copy to Error types
93- fix spinlocks staying locked after reset. 98- fix spinlocks staying locked after reset.
94- wait until read matches for PSM accesses. 99- wait until read matches for PSM accesses.
95- Remove generics 100- Remove generics
96- fix drop implementation of BufferedUartRx and BufferedUartTx 101- fix drop implementation of BufferedUartRx and BufferedUartTx
97- implement `embedded_storage_async::nor_flash::MultiwriteNorFlash` 102- implement `embedded_storage_async::nor_flash::MultiwriteNorFlash`
98- rp usb: wake ep-wakers after stalling 103- rp usb: wake ep-wakers after stalling
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index eb497de1a..6fb680b34 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -35,7 +35,11 @@ pub mod multicore;
35#[cfg(feature = "_rp235x")] 35#[cfg(feature = "_rp235x")]
36pub mod otp; 36pub mod otp;
37pub mod pio_programs; 37pub mod pio_programs;
38#[cfg(feature = "_rp235x")]
39pub mod psram;
38pub mod pwm; 40pub mod pwm;
41#[cfg(feature = "_rp235x")]
42pub mod qmi_cs1;
39mod reset; 43mod reset;
40pub mod rom_data; 44pub mod rom_data;
41#[cfg(feature = "rp2040")] 45#[cfg(feature = "rp2040")]
@@ -381,6 +385,8 @@ embassy_hal_internal::peripherals! {
381 SPI0, 385 SPI0,
382 SPI1, 386 SPI1,
383 387
388 QMI_CS1,
389
384 I2C0, 390 I2C0,
385 I2C1, 391 I2C1,
386 392
diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs
index 0d8a94776..5f554dfe3 100644
--- a/embassy-rp/src/pio/mod.rs
+++ b/embassy-rp/src/pio/mod.rs
@@ -12,7 +12,7 @@ use fixed::types::extra::U8;
12use fixed::FixedU32; 12use fixed::FixedU32;
13use pio::{Program, SideSet, Wrap}; 13use pio::{Program, SideSet, Wrap};
14 14
15use crate::dma::{Channel, Transfer, Word}; 15use crate::dma::{self, Channel, Transfer, Word};
16use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate}; 16use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate};
17use crate::interrupt::typelevel::{Binding, Handler, Interrupt}; 17use crate::interrupt::typelevel::{Binding, Handler, Interrupt};
18use crate::relocate::RelocatedProgram; 18use crate::relocate::RelocatedProgram;
@@ -281,6 +281,18 @@ impl<'l, PIO: Instance> Pin<'l, PIO> {
281 }); 281 });
282 } 282 }
283 283
284 /// Configure the output logic inversion of this pin.
285 #[inline]
286 pub fn set_output_inversion(&mut self, invert: bool) {
287 self.pin.gpio().ctrl().modify(|w| {
288 w.set_outover(if invert {
289 pac::io::vals::Outover::INVERT
290 } else {
291 pac::io::vals::Outover::NORMAL
292 })
293 });
294 }
295
284 /// Set the pin's input sync bypass. 296 /// Set the pin's input sync bypass.
285 pub fn set_input_sync_bypass(&mut self, bypass: bool) { 297 pub fn set_input_sync_bypass(&mut self, bypass: bool) {
286 let mask = 1 << self.pin(); 298 let mask = 1 << self.pin();
@@ -360,6 +372,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
360 FifoInFuture::new(self) 372 FifoInFuture::new(self)
361 } 373 }
362 374
375 fn dreq() -> crate::pac::dma::vals::TreqSel {
376 crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8 + 4)
377 }
378
363 /// Prepare DMA transfer from RX FIFO. 379 /// Prepare DMA transfer from RX FIFO.
364 pub fn dma_pull<'a, C: Channel, W: Word>( 380 pub fn dma_pull<'a, C: Channel, W: Word>(
365 &'a mut self, 381 &'a mut self,
@@ -367,7 +383,6 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
367 data: &'a mut [W], 383 data: &'a mut [W],
368 bswap: bool, 384 bswap: bool,
369 ) -> Transfer<'a, C> { 385 ) -> Transfer<'a, C> {
370 let pio_no = PIO::PIO_NO;
371 let p = ch.regs(); 386 let p = ch.regs();
372 p.write_addr().write_value(data.as_ptr() as u32); 387 p.write_addr().write_value(data.as_ptr() as u32);
373 p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32); 388 p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32);
@@ -377,8 +392,7 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
377 p.trans_count().write(|w| w.set_count(data.len() as u32)); 392 p.trans_count().write(|w| w.set_count(data.len() as u32));
378 compiler_fence(Ordering::SeqCst); 393 compiler_fence(Ordering::SeqCst);
379 p.ctrl_trig().write(|w| { 394 p.ctrl_trig().write(|w| {
380 // Set RX DREQ for this statemachine 395 w.set_treq_sel(Self::dreq());
381 w.set_treq_sel(crate::pac::dma::vals::TreqSel::from(pio_no * 8 + SM as u8 + 4));
382 w.set_data_size(W::size()); 396 w.set_data_size(W::size());
383 w.set_chain_to(ch.number()); 397 w.set_chain_to(ch.number());
384 w.set_incr_read(false); 398 w.set_incr_read(false);
@@ -389,6 +403,36 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
389 compiler_fence(Ordering::SeqCst); 403 compiler_fence(Ordering::SeqCst);
390 Transfer::new(ch) 404 Transfer::new(ch)
391 } 405 }
406
407 /// Prepare a repeated DMA transfer from RX FIFO.
408 pub fn dma_pull_repeated<'a, C: Channel, W: Word>(&'a mut self, ch: Peri<'a, C>, len: usize) -> Transfer<'a, C> {
409 // This is the read version of dma::write_repeated. This allows us to
410 // discard reads from the RX FIFO through DMA.
411
412 // static mut so it gets allocated in RAM
413 static mut DUMMY: u32 = 0;
414
415 let p = ch.regs();
416 p.write_addr().write_value(core::ptr::addr_of_mut!(DUMMY) as u32);
417 p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32);
418
419 #[cfg(feature = "rp2040")]
420 p.trans_count().write(|w| *w = len as u32);
421 #[cfg(feature = "_rp235x")]
422 p.trans_count().write(|w| w.set_count(len as u32));
423
424 compiler_fence(Ordering::SeqCst);
425 p.ctrl_trig().write(|w| {
426 w.set_treq_sel(Self::dreq());
427 w.set_data_size(W::size());
428 w.set_chain_to(ch.number());
429 w.set_incr_read(false);
430 w.set_incr_write(false);
431 w.set_en(true);
432 });
433 compiler_fence(Ordering::SeqCst);
434 Transfer::new(ch)
435 }
392} 436}
393 437
394/// Type representing a state machine TX FIFO. 438/// Type representing a state machine TX FIFO.
@@ -412,7 +456,7 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
412 (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f 456 (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f
413 } 457 }
414 458
415 /// Check state machine has stalled on empty TX FIFO. 459 /// Check if state machine has stalled on empty TX FIFO.
416 pub fn stalled(&self) -> bool { 460 pub fn stalled(&self) -> bool {
417 let fdebug = PIO::PIO.fdebug(); 461 let fdebug = PIO::PIO.fdebug();
418 let ret = fdebug.read().txstall() & (1 << SM) != 0; 462 let ret = fdebug.read().txstall() & (1 << SM) != 0;
@@ -451,6 +495,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
451 FifoOutFuture::new(self, value) 495 FifoOutFuture::new(self, value)
452 } 496 }
453 497
498 fn dreq() -> crate::pac::dma::vals::TreqSel {
499 crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8)
500 }
501
454 /// Prepare a DMA transfer to TX FIFO. 502 /// Prepare a DMA transfer to TX FIFO.
455 pub fn dma_push<'a, C: Channel, W: Word>( 503 pub fn dma_push<'a, C: Channel, W: Word>(
456 &'a mut self, 504 &'a mut self,
@@ -458,7 +506,6 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
458 data: &'a [W], 506 data: &'a [W],
459 bswap: bool, 507 bswap: bool,
460 ) -> Transfer<'a, C> { 508 ) -> Transfer<'a, C> {
461 let pio_no = PIO::PIO_NO;
462 let p = ch.regs(); 509 let p = ch.regs();
463 p.read_addr().write_value(data.as_ptr() as u32); 510 p.read_addr().write_value(data.as_ptr() as u32);
464 p.write_addr().write_value(PIO::PIO.txf(SM).as_ptr() as u32); 511 p.write_addr().write_value(PIO::PIO.txf(SM).as_ptr() as u32);
@@ -468,8 +515,7 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
468 p.trans_count().write(|w| w.set_count(data.len() as u32)); 515 p.trans_count().write(|w| w.set_count(data.len() as u32));
469 compiler_fence(Ordering::SeqCst); 516 compiler_fence(Ordering::SeqCst);
470 p.ctrl_trig().write(|w| { 517 p.ctrl_trig().write(|w| {
471 // Set TX DREQ for this statemachine 518 w.set_treq_sel(Self::dreq());
472 w.set_treq_sel(crate::pac::dma::vals::TreqSel::from(pio_no * 8 + SM as u8));
473 w.set_data_size(W::size()); 519 w.set_data_size(W::size());
474 w.set_chain_to(ch.number()); 520 w.set_chain_to(ch.number());
475 w.set_incr_read(true); 521 w.set_incr_read(true);
@@ -480,6 +526,11 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
480 compiler_fence(Ordering::SeqCst); 526 compiler_fence(Ordering::SeqCst);
481 Transfer::new(ch) 527 Transfer::new(ch)
482 } 528 }
529
530 /// Prepare a repeated DMA transfer to TX FIFO.
531 pub fn dma_push_repeated<'a, C: Channel, W: Word>(&'a mut self, ch: Peri<'a, C>, len: usize) -> Transfer<'a, C> {
532 unsafe { dma::write_repeated(ch, PIO::PIO.txf(SM).as_ptr(), len, Self::dreq()) }
533 }
483} 534}
484 535
485/// A type representing a single PIO state machine. 536/// A type representing a single PIO state machine.
@@ -926,13 +977,27 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
926 self.set_enable(enabled); 977 self.set_enable(enabled);
927 } 978 }
928 979
980 #[cfg(feature = "rp2040")]
981 fn pin_base() -> u8 {
982 0
983 }
984
985 #[cfg(feature = "_rp235x")]
986 fn pin_base() -> u8 {
987 if PIO::PIO.gpiobase().read().gpiobase() {
988 16
989 } else {
990 0
991 }
992 }
993
929 /// Sets pin directions. This pauses the current state machine to run `SET` commands 994 /// Sets pin directions. This pauses the current state machine to run `SET` commands
930 /// and temporarily unsets the `OUT_STICKY` bit. 995 /// and temporarily unsets the `OUT_STICKY` bit.
931 pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) { 996 pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) {
932 self.with_paused(|sm| { 997 self.with_paused(|sm| {
933 for pin in pins { 998 for pin in pins {
934 Self::this_sm().pinctrl().write(|w| { 999 Self::this_sm().pinctrl().write(|w| {
935 w.set_set_base(pin.pin()); 1000 w.set_set_base(pin.pin() - Self::pin_base());
936 w.set_set_count(1); 1001 w.set_set_count(1);
937 }); 1002 });
938 // SET PINDIRS, (dir) 1003 // SET PINDIRS, (dir)
@@ -947,7 +1012,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
947 self.with_paused(|sm| { 1012 self.with_paused(|sm| {
948 for pin in pins { 1013 for pin in pins {
949 Self::this_sm().pinctrl().write(|w| { 1014 Self::this_sm().pinctrl().write(|w| {
950 w.set_set_base(pin.pin()); 1015 w.set_set_base(pin.pin() - Self::pin_base());
951 w.set_set_count(1); 1016 w.set_set_count(1);
952 }); 1017 });
953 // SET PINS, (dir) 1018 // SET PINS, (dir)
@@ -1310,6 +1375,7 @@ impl<'d, PIO: Instance> Pio<'d, PIO> {
1310 PIO::state().users.store(5, Ordering::Release); 1375 PIO::state().users.store(5, Ordering::Release);
1311 PIO::state().used_pins.store(0, Ordering::Release); 1376 PIO::state().used_pins.store(0, Ordering::Release);
1312 PIO::Interrupt::unpend(); 1377 PIO::Interrupt::unpend();
1378
1313 unsafe { PIO::Interrupt::enable() }; 1379 unsafe { PIO::Interrupt::enable() };
1314 Self { 1380 Self {
1315 common: Common { 1381 common: Common {
diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs
index 7ceed3fa6..2382a3f9f 100644
--- a/embassy-rp/src/pio_programs/i2s.rs
+++ b/embassy-rp/src/pio_programs/i2s.rs
@@ -1,13 +1,101 @@
1//! Pio backed I2s output 1//! Pio backed I2s output and output drivers
2 2
3use fixed::traits::ToFixed; 3use fixed::traits::ToFixed;
4 4
5use crate::dma::{AnyChannel, Channel, Transfer}; 5use crate::dma::{AnyChannel, Channel, Transfer};
6use crate::gpio::Pull;
6use crate::pio::{ 7use crate::pio::{
7 Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, 8 Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine,
8}; 9};
9use crate::Peri; 10use crate::Peri;
10 11
12/// This struct represents an i2s receiver & controller driver program
13pub struct PioI2sInProgram<'d, PIO: Instance> {
14 prg: LoadedProgram<'d, PIO>,
15}
16
17impl<'d, PIO: Instance> PioI2sInProgram<'d, PIO> {
18 /// Load the input program into the given pio
19 pub fn new(common: &mut Common<'d, PIO>) -> Self {
20 let prg = pio::pio_asm! {
21 ".side_set 2",
22 " set x, 14 side 0b01",
23 "left_data:",
24 " in pins, 1 side 0b00", // read one left-channel bit from SD
25 " jmp x-- left_data side 0b01",
26 " in pins, 1 side 0b10", // ws changes 1 clock before MSB
27 " set x, 14 side 0b11",
28 "right_data:",
29 " in pins, 1 side 0b10",
30 " jmp x-- right_data side 0b11",
31 " in pins, 1 side 0b00" // ws changes 1 clock before ms
32 };
33 let prg = common.load_program(&prg.program);
34 Self { prg }
35 }
36}
37
38/// Pio backed I2s input driver
39pub struct PioI2sIn<'d, P: Instance, const S: usize> {
40 dma: Peri<'d, AnyChannel>,
41 sm: StateMachine<'d, P, S>,
42}
43
44impl<'d, P: Instance, const S: usize> PioI2sIn<'d, P, S> {
45 /// Configure a state machine to act as both the controller (provider of SCK and WS) and receiver (of SD) for an I2S signal
46 pub fn new(
47 common: &mut Common<'d, P>,
48 mut sm: StateMachine<'d, P, S>,
49 dma: Peri<'d, impl Channel>,
50 // Whether or not to use the MCU's internal pull-down resistor, as the
51 // Pico 2 is known to have problems with the inbuilt pulldowns, many
52 // opt to just use an external pull down resistor to meet requirements of common
53 // i2s microphones such as the INMP441
54 data_pulldown: bool,
55 data_pin: Peri<'d, impl PioPin>,
56 bit_clock_pin: Peri<'d, impl PioPin>,
57 lr_clock_pin: Peri<'d, impl PioPin>,
58 sample_rate: u32,
59 bit_depth: u32,
60 channels: u32,
61 program: &PioI2sInProgram<'d, P>,
62 ) -> Self {
63 let mut data_pin = common.make_pio_pin(data_pin);
64 if data_pulldown {
65 data_pin.set_pull(Pull::Down);
66 }
67 let bit_clock_pin = common.make_pio_pin(bit_clock_pin);
68 let left_right_clock_pin = common.make_pio_pin(lr_clock_pin);
69
70 let cfg = {
71 let mut cfg = Config::default();
72 cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]);
73 cfg.set_in_pins(&[&data_pin]);
74 let clock_frequency = sample_rate * bit_depth * channels;
75 cfg.clock_divider = (crate::clocks::clk_sys_freq() as f64 / clock_frequency as f64 / 2.).to_fixed();
76 cfg.shift_in = ShiftConfig {
77 threshold: 32,
78 direction: ShiftDirection::Left,
79 auto_fill: true,
80 };
81 // join fifos to have twice the time to start the next dma transfer
82 cfg.fifo_join = FifoJoin::RxOnly; // both control signals are sent via side-setting
83 cfg
84 };
85 sm.set_config(&cfg);
86 sm.set_pin_dirs(Direction::In, &[&data_pin]);
87 sm.set_pin_dirs(Direction::Out, &[&left_right_clock_pin, &bit_clock_pin]);
88 sm.set_enable(true);
89
90 Self { dma: dma.into(), sm }
91 }
92
93 /// Return an in-prograss dma transfer future. Awaiting it will guarentee a complete transfer.
94 pub fn read<'b>(&'b mut self, buff: &'b mut [u32]) -> Transfer<'b, AnyChannel> {
95 self.sm.rx().dma_pull(self.dma.reborrow(), buff, false)
96 }
97}
98
11/// This struct represents an i2s output driver program 99/// This struct represents an i2s output driver program
12/// 100///
13/// The sample bit-depth is set through scratch register `Y`. 101/// The sample bit-depth is set through scratch register `Y`.
@@ -26,12 +114,12 @@ impl<'d, PIO: Instance> PioI2sOutProgram<'d, PIO> {
26 "left_data:", 114 "left_data:",
27 " out pins, 1 side 0b00", 115 " out pins, 1 side 0b00",
28 " jmp x-- left_data side 0b01", 116 " jmp x-- left_data side 0b01",
29 " out pins 1 side 0b10", 117 " out pins, 1 side 0b10",
30 " mov x, y side 0b11", 118 " mov x, y side 0b11",
31 "right_data:", 119 "right_data:",
32 " out pins 1 side 0b10", 120 " out pins, 1 side 0b10",
33 " jmp x-- right_data side 0b11", 121 " jmp x-- right_data side 0b11",
34 " out pins 1 side 0b00", 122 " out pins, 1 side 0b00",
35 ); 123 );
36 124
37 let prg = common.load_program(&prg.program); 125 let prg = common.load_program(&prg.program);
diff --git a/embassy-rp/src/pio_programs/mod.rs b/embassy-rp/src/pio_programs/mod.rs
index 8eac328b3..d05ba3884 100644
--- a/embassy-rp/src/pio_programs/mod.rs
+++ b/embassy-rp/src/pio_programs/mod.rs
@@ -6,6 +6,7 @@ pub mod i2s;
6pub mod onewire; 6pub mod onewire;
7pub mod pwm; 7pub mod pwm;
8pub mod rotary_encoder; 8pub mod rotary_encoder;
9pub mod spi;
9pub mod stepper; 10pub mod stepper;
10pub mod uart; 11pub mod uart;
11pub mod ws2812; 12pub mod ws2812;
diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs
index 287ddab41..980d0fe5f 100644
--- a/embassy-rp/src/pio_programs/onewire.rs
+++ b/embassy-rp/src/pio_programs/onewire.rs
@@ -52,7 +52,8 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> {
52 52
53 ; The low pulse was already done, we only need to delay and poll the bit in case we are reading 53 ; The low pulse was already done, we only need to delay and poll the bit in case we are reading
54 write_1: 54 write_1:
55 nop side 0 [( 6 / CLK) - 1] ; Delay before sampling the input pin 55 jmp y--, continue_1 side 0 [( 6 / CLK) - 1] ; Delay before sampling input. Always decrement y
56 continue_1:
56 in pins, 1 side 0 [(48 / CLK) - 1] ; This writes the state of the pin into the ISR 57 in pins, 1 side 0 [(48 / CLK) - 1] ; This writes the state of the pin into the ISR
57 ; Fallthrough 58 ; Fallthrough
58 59
@@ -61,9 +62,24 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> {
61 .wrap_target 62 .wrap_target
62 out x, 1 side 0 [(12 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR 63 out x, 1 side 0 [(12 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR
63 jmp x--, write_1 side 1 [( 6 / CLK) - 1] ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit 64 jmp x--, write_1 side 1 [( 6 / CLK) - 1] ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit
64 in null, 1 side 1 [(54 / CLK) - 1] ; Do the remainder of the low part of a 0 bit 65 jmp y--, continue_0 side 1 [(48 / CLK) - 1] ; Do the remainder of the low part of a 0 bit
65 ; This writes 0 into the ISR so that the shift count stays in sync 66 jmp pullup side 1 [( 6 / CLK) - 1] ; Remain low while jumping
67 continue_0:
68 in null, 1 side 1 [( 6 / CLK) - 1] ; This writes 0 into the ISR so that the shift count stays in sync
66 .wrap 69 .wrap
70
71 ; Assume that strong pullup commands always have MSB (the last bit) = 0,
72 ; since the rising edge can be used to start the operation.
73 ; That's the case for DS18B20 (44h and 48h).
74 pullup:
75 set pins, 1 side 1[( 6 / CLK) - 1] ; Drive pin high output immediately.
76 ; Strong pullup must be within 10us of rise.
77 in null, 1 side 1[( 6 / CLK) - 1] ; Keep ISR in sync. Must occur after the y--.
78 out null, 8 side 1[( 6 / CLK) - 1] ; Wait for write_bytes_pullup() delay to complete.
79 ; The delay is hundreds of ms, so done externally.
80 set pins, 0 side 0[( 6 / CLK) - 1] ; Back to open drain, pin low when driven
81 in null, 8 side 1[( 6 / CLK) - 1] ; Inform write_bytes_pullup() it's ready
82 jmp next_bit side 0[( 6 / CLK) - 1] ; Continue
67 "# 83 "#
68 ); 84 );
69 85
@@ -98,6 +114,7 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
98 let mut cfg = Config::default(); 114 let mut cfg = Config::default();
99 cfg.use_program(&program.prg, &[&pin]); 115 cfg.use_program(&program.prg, &[&pin]);
100 cfg.set_in_pins(&[&pin]); 116 cfg.set_in_pins(&[&pin]);
117 cfg.set_set_pins(&[&pin]);
101 118
102 let shift_cfg = ShiftConfig { 119 let shift_cfg = ShiftConfig {
103 auto_fill: true, 120 auto_fill: true,
@@ -146,6 +163,19 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
146 163
147 /// Write bytes to the onewire bus 164 /// Write bytes to the onewire bus
148 pub async fn write_bytes(&mut self, data: &[u8]) { 165 pub async fn write_bytes(&mut self, data: &[u8]) {
166 unsafe { self.sm.set_y(u32::MAX as u32) };
167 let (rx, tx) = self.sm.rx_tx();
168 for b in data {
169 tx.wait_push(*b as u32).await;
170
171 // Empty the buffer that is being filled with every write
172 let _ = rx.wait_pull().await;
173 }
174 }
175
176 /// Write bytes to the onewire bus, then apply a strong pullup
177 pub async fn write_bytes_pullup(&mut self, data: &[u8], pullup_time: embassy_time::Duration) {
178 unsafe { self.sm.set_y(data.len() as u32 * 8 - 1) };
149 let (rx, tx) = self.sm.rx_tx(); 179 let (rx, tx) = self.sm.rx_tx();
150 for b in data { 180 for b in data {
151 tx.wait_push(*b as u32).await; 181 tx.wait_push(*b as u32).await;
@@ -153,10 +183,19 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
153 // Empty the buffer that is being filled with every write 183 // Empty the buffer that is being filled with every write
154 let _ = rx.wait_pull().await; 184 let _ = rx.wait_pull().await;
155 } 185 }
186
187 // Perform the delay, usually hundreds of ms.
188 embassy_time::Timer::after(pullup_time).await;
189
190 // Signal that delay has completed
191 tx.wait_push(0 as u32).await;
192 // Wait until it's back at 0 low, open drain
193 let _ = rx.wait_pull().await;
156 } 194 }
157 195
158 /// Read bytes from the onewire bus 196 /// Read bytes from the onewire bus
159 pub async fn read_bytes(&mut self, data: &mut [u8]) { 197 pub async fn read_bytes(&mut self, data: &mut [u8]) {
198 unsafe { self.sm.set_y(u32::MAX as u32) };
160 let (rx, tx) = self.sm.rx_tx(); 199 let (rx, tx) = self.sm.rx_tx();
161 for b in data { 200 for b in data {
162 // Write all 1's so that we can read what the device responds 201 // Write all 1's so that we can read what the device responds
diff --git a/embassy-rp/src/pio_programs/spi.rs b/embassy-rp/src/pio_programs/spi.rs
new file mode 100644
index 000000000..b10fc6628
--- /dev/null
+++ b/embassy-rp/src/pio_programs/spi.rs
@@ -0,0 +1,474 @@
1//! PIO backed SPi drivers
2
3use core::marker::PhantomData;
4
5use embassy_futures::join::join;
6use embassy_hal_internal::Peri;
7use embedded_hal_02::spi::{Phase, Polarity};
8use fixed::traits::ToFixed;
9use fixed::types::extra::U8;
10
11use crate::clocks::clk_sys_freq;
12use crate::dma::{AnyChannel, Channel};
13use crate::gpio::Level;
14use crate::pio::{Common, Direction, Instance, LoadedProgram, Pin, PioPin, ShiftDirection, StateMachine};
15use crate::spi::{Async, Blocking, Config, Mode};
16
17/// This struct represents an SPI program loaded into pio instruction memory.
18struct PioSpiProgram<'d, PIO: Instance> {
19 prg: LoadedProgram<'d, PIO>,
20 phase: Phase,
21}
22
23impl<'d, PIO: Instance> PioSpiProgram<'d, PIO> {
24 /// Load the spi program into the given pio
25 pub fn new(common: &mut Common<'d, PIO>, phase: Phase) -> Self {
26 // These PIO programs are taken straight from the datasheet (3.6.1 in
27 // RP2040 datasheet, 11.6.1 in RP2350 datasheet)
28
29 // Pin assignments:
30 // - SCK is side-set pin 0
31 // - MOSI is OUT pin 0
32 // - MISO is IN pin 0
33 //
34 // Auto-push and auto-pull must be enabled, and the serial frame size is set by
35 // configuring the push/pull threshold. Shift left/right is fine, but you must
36 // justify the data yourself. This is done most conveniently for frame sizes of
37 // 8 or 16 bits by using the narrow store replication and narrow load byte
38 // picking behavior of RP2040's IO fabric.
39
40 let prg = match phase {
41 Phase::CaptureOnFirstTransition => {
42 let prg = pio::pio_asm!(
43 r#"
44 .side_set 1
45
46 ; Clock phase = 0: data is captured on the leading edge of each SCK pulse, and
47 ; transitions on the trailing edge, or some time before the first leading edge.
48
49 out pins, 1 side 0 [1] ; Stall here on empty (sideset proceeds even if
50 in pins, 1 side 1 [1] ; instruction stalls, so we stall with SCK low)
51 "#
52 );
53
54 common.load_program(&prg.program)
55 }
56 Phase::CaptureOnSecondTransition => {
57 let prg = pio::pio_asm!(
58 r#"
59 .side_set 1
60
61 ; Clock phase = 1: data transitions on the leading edge of each SCK pulse, and
62 ; is captured on the trailing edge.
63
64 out x, 1 side 0 ; Stall here on empty (keep SCK de-asserted)
65 mov pins, x side 1 [1] ; Output data, assert SCK (mov pins uses OUT mapping)
66 in pins, 1 side 0 ; Input data, de-assert SCK
67 "#
68 );
69
70 common.load_program(&prg.program)
71 }
72 };
73
74 Self { prg, phase }
75 }
76}
77
78/// PIO SPI errors.
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80#[cfg_attr(feature = "defmt", derive(defmt::Format))]
81#[non_exhaustive]
82pub enum Error {
83 // No errors for now
84}
85
86/// PIO based Spi driver.
87/// Unlike other PIO programs, the PIO SPI driver owns and holds a reference to
88/// the PIO memory it uses. This is so that it can be reconfigured at runtime if
89/// desired.
90pub struct Spi<'d, PIO: Instance, const SM: usize, M: Mode> {
91 sm: StateMachine<'d, PIO, SM>,
92 cfg: crate::pio::Config<'d, PIO>,
93 program: Option<PioSpiProgram<'d, PIO>>,
94 clk_pin: Pin<'d, PIO>,
95 tx_dma: Option<Peri<'d, AnyChannel>>,
96 rx_dma: Option<Peri<'d, AnyChannel>>,
97 phantom: PhantomData<M>,
98}
99
100impl<'d, PIO: Instance, const SM: usize, M: Mode> Spi<'d, PIO, SM, M> {
101 #[allow(clippy::too_many_arguments)]
102 fn new_inner(
103 pio: &mut Common<'d, PIO>,
104 mut sm: StateMachine<'d, PIO, SM>,
105 clk_pin: Peri<'d, impl PioPin>,
106 mosi_pin: Peri<'d, impl PioPin>,
107 miso_pin: Peri<'d, impl PioPin>,
108 tx_dma: Option<Peri<'d, AnyChannel>>,
109 rx_dma: Option<Peri<'d, AnyChannel>>,
110 config: Config,
111 ) -> Self {
112 let program = PioSpiProgram::new(pio, config.phase);
113
114 let mut clk_pin = pio.make_pio_pin(clk_pin);
115 let mosi_pin = pio.make_pio_pin(mosi_pin);
116 let miso_pin = pio.make_pio_pin(miso_pin);
117
118 if let Polarity::IdleHigh = config.polarity {
119 clk_pin.set_output_inversion(true);
120 } else {
121 clk_pin.set_output_inversion(false);
122 }
123
124 let mut cfg = crate::pio::Config::default();
125
126 cfg.use_program(&program.prg, &[&clk_pin]);
127 cfg.set_out_pins(&[&mosi_pin]);
128 cfg.set_in_pins(&[&miso_pin]);
129
130 cfg.shift_in.auto_fill = true;
131 cfg.shift_in.direction = ShiftDirection::Left;
132 cfg.shift_in.threshold = 8;
133
134 cfg.shift_out.auto_fill = true;
135 cfg.shift_out.direction = ShiftDirection::Left;
136 cfg.shift_out.threshold = 8;
137
138 cfg.clock_divider = calculate_clock_divider(config.frequency);
139
140 sm.set_config(&cfg);
141
142 sm.set_pins(Level::Low, &[&clk_pin, &mosi_pin]);
143 sm.set_pin_dirs(Direction::Out, &[&clk_pin, &mosi_pin]);
144 sm.set_pin_dirs(Direction::In, &[&miso_pin]);
145
146 sm.set_enable(true);
147
148 Self {
149 sm,
150 program: Some(program),
151 cfg,
152 clk_pin,
153 tx_dma,
154 rx_dma,
155 phantom: PhantomData,
156 }
157 }
158
159 fn blocking_read_u8(&mut self) -> Result<u8, Error> {
160 while self.sm.rx().empty() {}
161 let value = self.sm.rx().pull() as u8;
162
163 Ok(value)
164 }
165
166 fn blocking_write_u8(&mut self, v: u8) -> Result<(), Error> {
167 let value = u32::from_be_bytes([v, 0, 0, 0]);
168
169 while !self.sm.tx().try_push(value) {}
170
171 // need to clear here for flush to work correctly
172 self.sm.tx().stalled();
173
174 Ok(())
175 }
176
177 /// Read data from SPI blocking execution until done.
178 pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> {
179 for v in data {
180 self.blocking_write_u8(0)?;
181 *v = self.blocking_read_u8()?;
182 }
183 self.flush()?;
184 Ok(())
185 }
186
187 /// Write data to SPI blocking execution until done.
188 pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> {
189 for v in data {
190 self.blocking_write_u8(*v)?;
191 let _ = self.blocking_read_u8()?;
192 }
193 self.flush()?;
194 Ok(())
195 }
196
197 /// Transfer data to SPI blocking execution until done.
198 pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
199 let len = read.len().max(write.len());
200 for i in 0..len {
201 let wb = write.get(i).copied().unwrap_or(0);
202 self.blocking_write_u8(wb)?;
203
204 let rb = self.blocking_read_u8()?;
205 if let Some(r) = read.get_mut(i) {
206 *r = rb;
207 }
208 }
209 self.flush()?;
210 Ok(())
211 }
212
213 /// Transfer data in place to SPI blocking execution until done.
214 pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {
215 for v in data {
216 self.blocking_write_u8(*v)?;
217 *v = self.blocking_read_u8()?;
218 }
219 self.flush()?;
220 Ok(())
221 }
222
223 /// Block execution until SPI is done.
224 pub fn flush(&mut self) -> Result<(), Error> {
225 // Wait for all words in the FIFO to have been pulled by the SM
226 while !self.sm.tx().empty() {}
227
228 // Wait for last value to be written out to the wire
229 while !self.sm.tx().stalled() {}
230
231 Ok(())
232 }
233
234 /// Set SPI frequency.
235 pub fn set_frequency(&mut self, freq: u32) {
236 self.sm.set_enable(false);
237
238 let divider = calculate_clock_divider(freq);
239
240 // save into the config for later but dont use sm.set_config() since
241 // that operation is relatively more expensive than just setting the
242 // clock divider
243 self.cfg.clock_divider = divider;
244 self.sm.set_clock_divider(divider);
245
246 self.sm.set_enable(true);
247 }
248
249 /// Set SPI config.
250 ///
251 /// This operation will panic if the PIO program needs to be reloaded and
252 /// there is insufficient room. This is unlikely since the programs for each
253 /// phase only differ in size by a single instruction.
254 pub fn set_config(&mut self, pio: &mut Common<'d, PIO>, config: &Config) {
255 self.sm.set_enable(false);
256
257 self.cfg.clock_divider = calculate_clock_divider(config.frequency);
258
259 if let Polarity::IdleHigh = config.polarity {
260 self.clk_pin.set_output_inversion(true);
261 } else {
262 self.clk_pin.set_output_inversion(false);
263 }
264
265 if self.program.as_ref().unwrap().phase != config.phase {
266 let old_program = self.program.take().unwrap();
267
268 // SAFETY: the state machine is disabled while this happens
269 unsafe { pio.free_instr(old_program.prg.used_memory) };
270
271 let new_program = PioSpiProgram::new(pio, config.phase);
272
273 self.cfg.use_program(&new_program.prg, &[&self.clk_pin]);
274 self.program = Some(new_program);
275 }
276
277 self.sm.set_config(&self.cfg);
278 self.sm.restart();
279
280 self.sm.set_enable(true);
281 }
282}
283
284fn calculate_clock_divider(frequency_hz: u32) -> fixed::FixedU32<U8> {
285 // we multiply by 4 since each clock period is equal to 4 instructions
286
287 let sys_freq = clk_sys_freq().to_fixed::<fixed::FixedU64<U8>>();
288 let target_freq = (frequency_hz * 4).to_fixed::<fixed::FixedU64<U8>>();
289 (sys_freq / target_freq).to_fixed()
290}
291
292impl<'d, PIO: Instance, const SM: usize> Spi<'d, PIO, SM, Blocking> {
293 /// Create an SPI driver in blocking mode.
294 pub fn new_blocking(
295 pio: &mut Common<'d, PIO>,
296 sm: StateMachine<'d, PIO, SM>,
297 clk: Peri<'d, impl PioPin>,
298 mosi: Peri<'d, impl PioPin>,
299 miso: Peri<'d, impl PioPin>,
300 config: Config,
301 ) -> Self {
302 Self::new_inner(pio, sm, clk, mosi, miso, None, None, config)
303 }
304}
305
306impl<'d, PIO: Instance, const SM: usize> Spi<'d, PIO, SM, Async> {
307 /// Create an SPI driver in async mode supporting DMA operations.
308 #[allow(clippy::too_many_arguments)]
309 pub fn new(
310 pio: &mut Common<'d, PIO>,
311 sm: StateMachine<'d, PIO, SM>,
312 clk: Peri<'d, impl PioPin>,
313 mosi: Peri<'d, impl PioPin>,
314 miso: Peri<'d, impl PioPin>,
315 tx_dma: Peri<'d, impl Channel>,
316 rx_dma: Peri<'d, impl Channel>,
317 config: Config,
318 ) -> Self {
319 Self::new_inner(
320 pio,
321 sm,
322 clk,
323 mosi,
324 miso,
325 Some(tx_dma.into()),
326 Some(rx_dma.into()),
327 config,
328 )
329 }
330
331 /// Read data from SPI using DMA.
332 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
333 let (rx, tx) = self.sm.rx_tx();
334
335 let len = buffer.len();
336
337 let rx_ch = self.rx_dma.as_mut().unwrap().reborrow();
338 let rx_transfer = rx.dma_pull(rx_ch, buffer, false);
339
340 let tx_ch = self.tx_dma.as_mut().unwrap().reborrow();
341 let tx_transfer = tx.dma_push_repeated::<_, u8>(tx_ch, len);
342
343 join(tx_transfer, rx_transfer).await;
344
345 Ok(())
346 }
347
348 /// Write data to SPI using DMA.
349 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
350 let (rx, tx) = self.sm.rx_tx();
351
352 let rx_ch = self.rx_dma.as_mut().unwrap().reborrow();
353 let rx_transfer = rx.dma_pull_repeated::<_, u8>(rx_ch, buffer.len());
354
355 let tx_ch = self.tx_dma.as_mut().unwrap().reborrow();
356 let tx_transfer = tx.dma_push(tx_ch, buffer, false);
357
358 join(tx_transfer, rx_transfer).await;
359
360 Ok(())
361 }
362
363 /// Transfer data to SPI using DMA.
364 pub async fn transfer(&mut self, rx_buffer: &mut [u8], tx_buffer: &[u8]) -> Result<(), Error> {
365 self.transfer_inner(rx_buffer, tx_buffer).await
366 }
367
368 /// Transfer data in place to SPI using DMA.
369 pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
370 self.transfer_inner(words, words).await
371 }
372
373 async fn transfer_inner(&mut self, rx_buffer: *mut [u8], tx_buffer: *const [u8]) -> Result<(), Error> {
374 let (rx, tx) = self.sm.rx_tx();
375
376 let mut rx_ch = self.rx_dma.as_mut().unwrap().reborrow();
377 let rx_transfer = async {
378 rx.dma_pull(rx_ch.reborrow(), unsafe { &mut *rx_buffer }, false).await;
379
380 if tx_buffer.len() > rx_buffer.len() {
381 let read_bytes_len = tx_buffer.len() - rx_buffer.len();
382
383 rx.dma_pull_repeated::<_, u8>(rx_ch, read_bytes_len).await;
384 }
385 };
386
387 let mut tx_ch = self.tx_dma.as_mut().unwrap().reborrow();
388 let tx_transfer = async {
389 tx.dma_push(tx_ch.reborrow(), unsafe { &*tx_buffer }, false).await;
390
391 if rx_buffer.len() > tx_buffer.len() {
392 let write_bytes_len = rx_buffer.len() - tx_buffer.len();
393
394 tx.dma_push_repeated::<_, u8>(tx_ch, write_bytes_len).await;
395 }
396 };
397
398 join(tx_transfer, rx_transfer).await;
399
400 Ok(())
401 }
402}
403
404// ====================
405
406impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, PIO, SM, M> {
407 type Error = Error;
408 fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
409 self.blocking_transfer_in_place(words)?;
410 Ok(words)
411 }
412}
413
414impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, PIO, SM, M> {
415 type Error = Error;
416
417 fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
418 self.blocking_write(words)
419 }
420}
421
422impl embedded_hal_1::spi::Error for Error {
423 fn kind(&self) -> embedded_hal_1::spi::ErrorKind {
424 match *self {}
425 }
426}
427
428impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_1::spi::ErrorType for Spi<'d, PIO, SM, M> {
429 type Error = Error;
430}
431
432impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, PIO, SM, M> {
433 fn flush(&mut self) -> Result<(), Self::Error> {
434 Ok(())
435 }
436
437 fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
438 self.blocking_transfer(words, &[])
439 }
440
441 fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
442 self.blocking_write(words)
443 }
444
445 fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
446 self.blocking_transfer(read, write)
447 }
448
449 fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
450 self.blocking_transfer_in_place(words)
451 }
452}
453
454impl<'d, PIO: Instance, const SM: usize> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, PIO, SM, Async> {
455 async fn flush(&mut self) -> Result<(), Self::Error> {
456 Ok(())
457 }
458
459 async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
460 self.write(words).await
461 }
462
463 async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
464 self.read(words).await
465 }
466
467 async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
468 self.transfer(read, write).await
469 }
470
471 async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
472 self.transfer_in_place(words).await
473 }
474}
diff --git a/embassy-rp/src/psram.rs b/embassy-rp/src/psram.rs
new file mode 100644
index 000000000..ae43dd5aa
--- /dev/null
+++ b/embassy-rp/src/psram.rs
@@ -0,0 +1,682 @@
1//! PSRAM driver for APS6404L and compatible devices
2//!
3//! This driver provides support for PSRAM (Pseudo-Static RAM) devices connected via QMI CS1.
4//! It handles device verification, initialization, and memory-mapped access configuration.
5//!
6//! This driver is only available on RP235x chips as it requires the QMI CS1 peripheral.
7
8// Credit: Initially based on https://github.com/Altaflux/gb-rp2350 (also licensed Apache 2.0 + MIT).
9// Copyright (c) Altaflux
10
11#![cfg(feature = "_rp235x")]
12
13use critical_section::{acquire, release, CriticalSection, RestoreState};
14
15use crate::pac;
16use crate::qmi_cs1::QmiCs1;
17
18/// PSRAM errors.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21#[non_exhaustive]
22pub enum Error {
23 /// PSRAM device is not detected or not supported
24 DeviceNotFound,
25 /// Invalid configuration
26 InvalidConfig,
27 /// Detected PSRAM size does not match the expected size
28 SizeMismatch,
29}
30
31/// PSRAM device verification type.
32#[derive(Clone, Copy)]
33pub enum VerificationType {
34 /// Skip device verification
35 None,
36 /// Verify as APS6404L device
37 Aps6404l,
38}
39
40/// Memory configuration.
41#[derive(Clone)]
42pub struct Config {
43 /// System clock frequency in Hz
44 pub clock_hz: u32,
45 /// Maximum memory operating frequency in Hz
46 pub max_mem_freq: u32,
47 /// Maximum CS assert time in microseconds (must be <= 8 us)
48 pub max_select_us: u32,
49 /// Minimum CS deassert time in nanoseconds (must be >= 18 ns)
50 pub min_deselect_ns: u32,
51 /// Cooldown period between operations (in SCLK cycles)
52 pub cooldown: u8,
53 /// Page break size for memory operations
54 pub page_break: PageBreak,
55 /// Clock divisor for direct mode operations during initialization
56 pub init_clkdiv: u8,
57 /// Enter Quad Mode command
58 pub enter_quad_cmd: Option<u8>,
59 /// Quad Read command (fast read with 4-bit data)
60 pub quad_read_cmd: u8,
61 /// Quad Write command (page program with 4-bit data)
62 pub quad_write_cmd: Option<u8>,
63 /// Number of dummy cycles for quad read operations
64 pub dummy_cycles: u8,
65 /// Read format configuration
66 pub read_format: FormatConfig,
67 /// Write format configuration
68 pub write_format: Option<FormatConfig>,
69 /// Expected memory size in bytes
70 pub mem_size: usize,
71 /// Device verification type
72 pub verification_type: VerificationType,
73 /// Whether the memory is writable via XIP (e.g., PSRAM vs. read-only flash)
74 pub xip_writable: bool,
75}
76
77/// Page break configuration for memory window operations.
78#[derive(Clone, Copy)]
79pub enum PageBreak {
80 /// No page breaks
81 None,
82 /// Break at 256-byte boundaries
83 _256,
84 /// Break at 1024-byte boundaries
85 _1024,
86 /// Break at 4096-byte boundaries
87 _4096,
88}
89
90/// Format configuration for read/write operations.
91#[derive(Clone)]
92pub struct FormatConfig {
93 /// Width of command prefix phase
94 pub prefix_width: Width,
95 /// Width of address phase
96 pub addr_width: Width,
97 /// Width of command suffix phase
98 pub suffix_width: Width,
99 /// Width of dummy/turnaround phase
100 pub dummy_width: Width,
101 /// Width of data phase
102 pub data_width: Width,
103 /// Length of prefix (None or 8 bits)
104 pub prefix_len: bool,
105 /// Length of suffix (None or 8 bits)
106 pub suffix_len: bool,
107}
108
109/// Interface width for different phases of SPI transfer.
110#[derive(Clone, Copy)]
111pub enum Width {
112 /// Single-bit (standard SPI)
113 Single,
114 /// Dual-bit (2 data lines)
115 Dual,
116 /// Quad-bit (4 data lines)
117 Quad,
118}
119
120impl Default for Config {
121 fn default() -> Self {
122 Self::aps6404l()
123 }
124}
125
126impl Config {
127 /// Create configuration for APS6404L PSRAM.
128 pub fn aps6404l() -> Self {
129 Self {
130 clock_hz: 125_000_000, // Default to 125MHz
131 max_mem_freq: 133_000_000, // APS6404L max frequency
132 max_select_us: 8, // 8 microseconds max CS assert
133 min_deselect_ns: 18, // 18 nanoseconds min CS deassert
134 cooldown: 1, // 1 SCLK cycle cooldown
135 page_break: PageBreak::_1024, // 1024-byte page boundaries
136 init_clkdiv: 10, // Medium clock for initialization
137 enter_quad_cmd: Some(0x35), // Enter Quad Mode
138 quad_read_cmd: 0xEB, // Fast Quad Read
139 quad_write_cmd: Some(0x38), // Quad Page Program
140 dummy_cycles: 24, // 24 dummy cycles for quad read
141 read_format: FormatConfig {
142 prefix_width: Width::Quad,
143 addr_width: Width::Quad,
144 suffix_width: Width::Quad,
145 dummy_width: Width::Quad,
146 data_width: Width::Quad,
147 prefix_len: true, // 8-bit prefix
148 suffix_len: false, // No suffix
149 },
150 write_format: Some(FormatConfig {
151 prefix_width: Width::Quad,
152 addr_width: Width::Quad,
153 suffix_width: Width::Quad,
154 dummy_width: Width::Quad,
155 data_width: Width::Quad,
156 prefix_len: true, // 8-bit prefix
157 suffix_len: false, // No suffix
158 }),
159 mem_size: 8 * 1024 * 1024, // 8MB for APS6404L
160 verification_type: VerificationType::Aps6404l,
161 xip_writable: true, // PSRAM is writable
162 }
163 }
164
165 /// Create a custom memory configuration.
166 pub fn custom(
167 clock_hz: u32,
168 max_mem_freq: u32,
169 max_select_us: u32,
170 min_deselect_ns: u32,
171 cooldown: u8,
172 page_break: PageBreak,
173 init_clkdiv: u8,
174 enter_quad_cmd: Option<u8>,
175 quad_read_cmd: u8,
176 quad_write_cmd: Option<u8>,
177 dummy_cycles: u8,
178 read_format: FormatConfig,
179 write_format: Option<FormatConfig>,
180 mem_size: usize,
181 verification_type: VerificationType,
182 xip_writable: bool,
183 ) -> Self {
184 Self {
185 clock_hz,
186 max_mem_freq,
187 max_select_us,
188 min_deselect_ns,
189 cooldown,
190 page_break,
191 init_clkdiv,
192 enter_quad_cmd,
193 quad_read_cmd,
194 quad_write_cmd,
195 dummy_cycles,
196 read_format,
197 write_format,
198 mem_size,
199 verification_type,
200 xip_writable,
201 }
202 }
203}
204
205/// PSRAM driver.
206pub struct Psram<'d> {
207 #[allow(dead_code)]
208 qmi_cs1: QmiCs1<'d>,
209 size: usize,
210}
211
212impl<'d> Psram<'d> {
213 /// Create a new PSRAM driver instance.
214 ///
215 /// This will detect the PSRAM device and configure it for memory-mapped access.
216 pub fn new(qmi_cs1: QmiCs1<'d>, config: Config) -> Result<Self, Error> {
217 let qmi = pac::QMI;
218 let xip = pac::XIP_CTRL;
219
220 // Verify PSRAM device if requested
221 match config.verification_type {
222 VerificationType::Aps6404l => {
223 Self::verify_aps6404l(&qmi, config.mem_size)?;
224 debug!("APS6404L PSRAM verified, size: {:#x}", config.mem_size);
225 }
226 VerificationType::None => {
227 debug!("Skipping PSRAM verification, assuming size: {:#x}", config.mem_size);
228 }
229 }
230
231 // Initialize PSRAM with proper timing
232 Self::init_psram(&qmi, &xip, &config)?;
233
234 Ok(Self {
235 qmi_cs1,
236 size: config.mem_size,
237 })
238 }
239
240 /// Get the detected PSRAM size in bytes.
241 pub fn size(&self) -> usize {
242 self.size
243 }
244
245 /// Get the base address for memory-mapped access.
246 ///
247 /// After initialization, PSRAM can be accessed directly through memory mapping.
248 /// The base address for CS1 is typically 0x11000000.
249 pub fn base_address(&self) -> *mut u8 {
250 0x1100_0000 as *mut u8
251 }
252
253 /// Verify APS6404L PSRAM device matches expected configuration.
254 #[link_section = ".data.ram_func"]
255 #[inline(never)]
256 fn verify_aps6404l(qmi: &pac::qmi::Qmi, expected_size: usize) -> Result<(), Error> {
257 // APS6404L-specific constants
258 const EXPECTED_KGD: u8 = 0x5D;
259 crate::multicore::pause_core1();
260 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
261
262 {
263 // Helper for making sure `release` is called even if `f` panics.
264 struct Guard {
265 state: RestoreState,
266 }
267
268 impl Drop for Guard {
269 #[inline(always)]
270 fn drop(&mut self) {
271 unsafe { release(self.state) }
272 }
273 }
274
275 let state = unsafe { acquire() };
276 let _guard = Guard { state };
277
278 let _cs = unsafe { CriticalSection::new() };
279
280 let (kgd, eid) = unsafe { Self::read_aps6404l_kgd_eid(qmi) };
281
282 let mut detected_size: u32 = 0;
283 if kgd == EXPECTED_KGD as u32 {
284 detected_size = 1024 * 1024;
285 let size_id = eid >> 5;
286 if eid == 0x26 || size_id == 2 {
287 // APS6404L-3SQR-SN or 8MB variants
288 detected_size *= 8;
289 } else if size_id == 0 {
290 detected_size *= 2;
291 } else if size_id == 1 {
292 detected_size *= 4;
293 }
294 }
295
296 // Verify the detected size matches the expected size
297 if detected_size as usize != expected_size {
298 return Err(Error::SizeMismatch);
299 }
300
301 Ok(())
302 }?;
303
304 crate::multicore::resume_core1();
305
306 Ok(())
307 }
308
309 #[link_section = ".data.ram_func"]
310 #[inline(never)]
311 unsafe fn read_aps6404l_kgd_eid(qmi: &pac::qmi::Qmi) -> (u32, u32) {
312 const RESET_ENABLE_CMD: u8 = 0xf5;
313 const READ_ID_CMD: u8 = 0x9f;
314
315 #[allow(unused_assignments)]
316 let mut kgd: u32 = 0;
317 #[allow(unused_assignments)]
318 let mut eid: u32 = 0;
319
320 let qmi_base = qmi.as_ptr() as usize;
321
322 #[cfg(target_arch = "arm")]
323 core::arch::asm!(
324 // Configure DIRECT_CSR: shift clkdiv (30) to bits 29:22 and set EN (bit 0)
325 "movs {temp}, #30",
326 "lsls {temp}, {temp}, #22",
327 "orr {temp}, {temp}, #1", // Set EN bit
328 "str {temp}, [{qmi_base}]",
329
330 // Poll for BUSY to clear before first operation
331 "1:",
332 "ldr {temp}, [{qmi_base}]",
333 "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position
334 "bmi 1b", // Branch if negative (BUSY = 1)
335
336 // Assert CS1N (bit 3)
337 "ldr {temp}, [{qmi_base}]",
338 "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit (bit 3)
339 "str {temp}, [{qmi_base}]",
340
341 // Transmit RESET_ENABLE_CMD as quad
342 // DIRECT_TX: OE=1 (bit 19), IWIDTH=2 (bits 17:16), DATA=RESET_ENABLE_CMD
343 "movs {temp}, {reset_enable_cmd}",
344 "orr {temp}, {temp}, #0x80000", // Set OE (bit 19)
345 "orr {temp}, {temp}, #0x20000", // Set IWIDTH=2 (quad, bits 17:16)
346 "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX
347
348 // Wait for BUSY to clear
349 "2:",
350 "ldr {temp}, [{qmi_base}]",
351 "lsls {temp}, {temp}, #30",
352 "bmi 2b",
353
354 // Read and discard RX data
355 "ldr {temp}, [{qmi_base}, #8]",
356
357 // Deassert CS1N
358 "ldr {temp}, [{qmi_base}]",
359 "bic {temp}, {temp}, #8", // Clear ASSERT_CS1N bit
360 "str {temp}, [{qmi_base}]",
361
362 // Assert CS1N again
363 "ldr {temp}, [{qmi_base}]",
364 "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit
365 "str {temp}, [{qmi_base}]",
366
367 // Read ID loop (7 iterations)
368 "movs {counter}, #0", // Initialize counter
369
370 "3:", // Loop start
371 "cmp {counter}, #0",
372 "bne 4f", // If not first iteration, send 0xFF
373
374 // First iteration: send READ_ID_CMD
375 "movs {temp}, {read_id_cmd}",
376 "b 5f",
377 "4:", // Other iterations: send 0xFF
378 "movs {temp}, #0xFF",
379 "5:",
380 "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX
381
382 // Wait for TXEMPTY
383 "6:",
384 "ldr {temp}, [{qmi_base}]",
385 "lsls {temp}, {temp}, #20", // Shift TXEMPTY (bit 11) to bit 31
386 "bpl 6b", // Branch if positive (TXEMPTY = 0)
387
388 // Wait for BUSY to clear
389 "7:",
390 "ldr {temp}, [{qmi_base}]",
391 "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position
392 "bmi 7b", // Branch if negative (BUSY = 1)
393
394 // Read RX data
395 "ldr {temp}, [{qmi_base}, #8]",
396 "uxth {temp}, {temp}", // Extract lower 16 bits
397
398 // Store KGD or EID based on iteration
399 "cmp {counter}, #5",
400 "bne 8f",
401 "mov {kgd}, {temp}", // Store KGD
402 "b 9f",
403 "8:",
404 "cmp {counter}, #6",
405 "bne 9f",
406 "mov {eid}, {temp}", // Store EID
407
408 "9:",
409 "adds {counter}, #1",
410 "cmp {counter}, #7",
411 "blt 3b", // Continue loop if counter < 7
412
413 // Disable direct mode: clear EN and ASSERT_CS1N
414 "movs {temp}, #0",
415 "str {temp}, [{qmi_base}]",
416
417 // Memory barriers
418 "dmb",
419 "dsb",
420 "isb",
421 qmi_base = in(reg) qmi_base,
422 temp = out(reg) _,
423 counter = out(reg) _,
424 kgd = out(reg) kgd,
425 eid = out(reg) eid,
426 reset_enable_cmd = const RESET_ENABLE_CMD as u32,
427 read_id_cmd = const READ_ID_CMD as u32,
428 options(nostack),
429 );
430
431 #[cfg(target_arch = "riscv32")]
432 unimplemented!("APS6404L PSRAM verification not implemented for RISC-V");
433
434 (kgd, eid)
435 }
436
437 /// Initialize PSRAM with proper timing.
438 #[link_section = ".data.ram_func"]
439 #[inline(never)]
440 fn init_psram(qmi: &pac::qmi::Qmi, xip_ctrl: &pac::xip_ctrl::XipCtrl, config: &Config) -> Result<(), Error> {
441 // Set PSRAM timing for APS6404
442 //
443 // Using an rxdelay equal to the divisor isn't enough when running the APS6404 close to 133 MHz.
444 // So: don't allow running at divisor 1 above 100 MHz (because delay of 2 would be too late),
445 // and add an extra 1 to the rxdelay if the divided clock is > 100 MHz (i.e., sys clock > 200 MHz).
446 let clock_hz = config.clock_hz;
447 let max_psram_freq = config.max_mem_freq;
448
449 let mut divisor: u32 = (clock_hz + max_psram_freq - 1) / max_psram_freq;
450 if divisor == 1 && clock_hz > 100_000_000 {
451 divisor = 2;
452 }
453 let mut rxdelay: u32 = divisor;
454 if clock_hz / divisor > 100_000_000 {
455 rxdelay += 1;
456 }
457
458 // - Max select must be <= 8 us. The value is given in multiples of 64 system clocks.
459 // - Min deselect must be >= 18ns. The value is given in system clock cycles - ceil(divisor / 2).
460 let clock_period_fs: u64 = 1_000_000_000_000_000_u64 / u64::from(clock_hz);
461 let max_select: u8 = ((config.max_select_us as u64 * 1_000_000) / clock_period_fs) as u8;
462 let min_deselect: u32 = ((config.min_deselect_ns as u64 * 1_000_000 + (clock_period_fs - 1)) / clock_period_fs
463 - u64::from(divisor + 1) / 2) as u32;
464
465 crate::multicore::pause_core1();
466 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
467
468 if let Some(enter_quad_cmd) = config.enter_quad_cmd {
469 // Helper for making sure `release` is called even if `f` panics.
470 struct Guard {
471 state: RestoreState,
472 }
473
474 impl Drop for Guard {
475 #[inline(always)]
476 fn drop(&mut self) {
477 unsafe { release(self.state) }
478 }
479 }
480
481 let state = unsafe { acquire() };
482 let _guard = Guard { state };
483
484 let _cs = unsafe { CriticalSection::new() };
485
486 unsafe { Self::direct_csr_send_init_command(config, enter_quad_cmd) };
487
488 qmi.mem(1).timing().write(|w| {
489 w.set_cooldown(config.cooldown);
490 w.set_pagebreak(match config.page_break {
491 PageBreak::None => pac::qmi::vals::Pagebreak::NONE,
492 PageBreak::_256 => pac::qmi::vals::Pagebreak::_256,
493 PageBreak::_1024 => pac::qmi::vals::Pagebreak::_1024,
494 PageBreak::_4096 => pac::qmi::vals::Pagebreak::_4096,
495 });
496 w.set_max_select(max_select);
497 w.set_min_deselect(min_deselect as u8);
498 w.set_rxdelay(rxdelay as u8);
499 w.set_clkdiv(divisor as u8);
500 });
501
502 // Set PSRAM commands and formats
503 qmi.mem(1).rfmt().write(|w| {
504 let width_to_pac = |w: Width| match w {
505 Width::Single => pac::qmi::vals::PrefixWidth::S,
506 Width::Dual => pac::qmi::vals::PrefixWidth::D,
507 Width::Quad => pac::qmi::vals::PrefixWidth::Q,
508 };
509
510 w.set_prefix_width(width_to_pac(config.read_format.prefix_width));
511 w.set_addr_width(match config.read_format.addr_width {
512 Width::Single => pac::qmi::vals::AddrWidth::S,
513 Width::Dual => pac::qmi::vals::AddrWidth::D,
514 Width::Quad => pac::qmi::vals::AddrWidth::Q,
515 });
516 w.set_suffix_width(match config.read_format.suffix_width {
517 Width::Single => pac::qmi::vals::SuffixWidth::S,
518 Width::Dual => pac::qmi::vals::SuffixWidth::D,
519 Width::Quad => pac::qmi::vals::SuffixWidth::Q,
520 });
521 w.set_dummy_width(match config.read_format.dummy_width {
522 Width::Single => pac::qmi::vals::DummyWidth::S,
523 Width::Dual => pac::qmi::vals::DummyWidth::D,
524 Width::Quad => pac::qmi::vals::DummyWidth::Q,
525 });
526 w.set_data_width(match config.read_format.data_width {
527 Width::Single => pac::qmi::vals::DataWidth::S,
528 Width::Dual => pac::qmi::vals::DataWidth::D,
529 Width::Quad => pac::qmi::vals::DataWidth::Q,
530 });
531 w.set_prefix_len(if config.read_format.prefix_len {
532 pac::qmi::vals::PrefixLen::_8
533 } else {
534 pac::qmi::vals::PrefixLen::NONE
535 });
536 w.set_suffix_len(if config.read_format.suffix_len {
537 pac::qmi::vals::SuffixLen::_8
538 } else {
539 pac::qmi::vals::SuffixLen::NONE
540 });
541 w.set_dummy_len(match config.dummy_cycles {
542 0 => pac::qmi::vals::DummyLen::NONE,
543 4 => pac::qmi::vals::DummyLen::_4,
544 8 => pac::qmi::vals::DummyLen::_8,
545 12 => pac::qmi::vals::DummyLen::_12,
546 16 => pac::qmi::vals::DummyLen::_16,
547 20 => pac::qmi::vals::DummyLen::_20,
548 24 => pac::qmi::vals::DummyLen::_24,
549 28 => pac::qmi::vals::DummyLen::_28,
550 _ => pac::qmi::vals::DummyLen::_24, // Default to 24
551 });
552 });
553
554 qmi.mem(1).rcmd().write(|w| w.set_prefix(config.quad_read_cmd));
555
556 if let Some(ref write_format) = config.write_format {
557 qmi.mem(1).wfmt().write(|w| {
558 w.set_prefix_width(match write_format.prefix_width {
559 Width::Single => pac::qmi::vals::PrefixWidth::S,
560 Width::Dual => pac::qmi::vals::PrefixWidth::D,
561 Width::Quad => pac::qmi::vals::PrefixWidth::Q,
562 });
563 w.set_addr_width(match write_format.addr_width {
564 Width::Single => pac::qmi::vals::AddrWidth::S,
565 Width::Dual => pac::qmi::vals::AddrWidth::D,
566 Width::Quad => pac::qmi::vals::AddrWidth::Q,
567 });
568 w.set_suffix_width(match write_format.suffix_width {
569 Width::Single => pac::qmi::vals::SuffixWidth::S,
570 Width::Dual => pac::qmi::vals::SuffixWidth::D,
571 Width::Quad => pac::qmi::vals::SuffixWidth::Q,
572 });
573 w.set_dummy_width(match write_format.dummy_width {
574 Width::Single => pac::qmi::vals::DummyWidth::S,
575 Width::Dual => pac::qmi::vals::DummyWidth::D,
576 Width::Quad => pac::qmi::vals::DummyWidth::Q,
577 });
578 w.set_data_width(match write_format.data_width {
579 Width::Single => pac::qmi::vals::DataWidth::S,
580 Width::Dual => pac::qmi::vals::DataWidth::D,
581 Width::Quad => pac::qmi::vals::DataWidth::Q,
582 });
583 w.set_prefix_len(if write_format.prefix_len {
584 pac::qmi::vals::PrefixLen::_8
585 } else {
586 pac::qmi::vals::PrefixLen::NONE
587 });
588 w.set_suffix_len(if write_format.suffix_len {
589 pac::qmi::vals::SuffixLen::_8
590 } else {
591 pac::qmi::vals::SuffixLen::NONE
592 });
593 });
594 }
595
596 if let Some(quad_write_cmd) = config.quad_write_cmd {
597 qmi.mem(1).wcmd().write(|w| w.set_prefix(quad_write_cmd));
598 }
599
600 if config.xip_writable {
601 // Enable XIP writable mode for PSRAM
602 xip_ctrl.ctrl().modify(|w| w.set_writable_m1(true));
603 } else {
604 // Disable XIP writable mode
605 xip_ctrl.ctrl().modify(|w| w.set_writable_m1(false));
606 }
607 }
608 crate::multicore::resume_core1();
609
610 Ok(())
611 }
612
613 #[link_section = ".data.ram_func"]
614 #[inline(never)]
615 unsafe fn direct_csr_send_init_command(config: &Config, init_cmd: u8) {
616 #[cfg(target_arch = "arm")]
617 core::arch::asm!(
618 // Full memory barrier
619 "dmb",
620 "dsb",
621 "isb",
622
623 // Configure QMI Direct CSR register
624 // Load base address of QMI (0x400D0000)
625 "movw {base}, #0x0000",
626 "movt {base}, #0x400D",
627
628 // Load init_clkdiv and shift to bits 29:22
629 "lsl {temp}, {clkdiv}, #22",
630
631 // OR with EN (bit 0) and AUTO_CS1N (bit 7)
632 "orr {temp}, {temp}, #0x81",
633
634 // Store to DIRECT_CSR register
635 "str {temp}, [{base}, #0]",
636
637 // Memory barrier
638 "dmb",
639
640 // First busy wait loop
641 "1:",
642 "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR
643 "tst {temp}, #0x2", // Test BUSY bit (bit 1)
644 "bne 1b", // Branch if busy
645
646 // Write to Direct TX register
647 "mov {temp}, {enter_quad_cmd}",
648
649 // OR with NOPUSH (bit 20)
650 "orr {temp}, {temp}, #0x100000",
651
652 // Store to DIRECT_TX register (offset 0x4)
653 "str {temp}, [{base}, #4]",
654
655 // Memory barrier
656 "dmb",
657
658 // Second busy wait loop
659 "2:",
660 "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR
661 "tst {temp}, #0x2", // Test BUSY bit (bit 1)
662 "bne 2b", // Branch if busy
663
664 // Disable Direct CSR
665 "mov {temp}, #0",
666 "str {temp}, [{base}, #0]", // Clear DIRECT_CSR register
667
668 // Full memory barrier to ensure no prefetching
669 "dmb",
670 "dsb",
671 "isb",
672 base = out(reg) _,
673 temp = out(reg) _,
674 clkdiv = in(reg) config.init_clkdiv as u32,
675 enter_quad_cmd = in(reg) u32::from(init_cmd),
676 options(nostack),
677 );
678
679 #[cfg(target_arch = "riscv32")]
680 unimplemented!("Direct CSR command sending is not implemented for RISC-V yet");
681 }
682}
diff --git a/embassy-rp/src/qmi_cs1.rs b/embassy-rp/src/qmi_cs1.rs
new file mode 100644
index 000000000..b8ae41e35
--- /dev/null
+++ b/embassy-rp/src/qmi_cs1.rs
@@ -0,0 +1,57 @@
1//! QMI CS1 peripheral for RP235x
2//!
3//! This module provides access to the QMI CS1 functionality for use with external memory devices
4//! such as PSRAM. The QMI (Quad SPI) controller supports CS1 as a second chip select signal.
5//!
6//! This peripheral is only available on RP235x chips.
7
8#![cfg(feature = "_rp235x")]
9
10use embassy_hal_internal::{Peri, PeripheralType};
11
12use crate::gpio::Pin as GpioPin;
13use crate::{pac, peripherals};
14
15/// QMI CS1 driver.
16pub struct QmiCs1<'d> {
17 _inner: Peri<'d, peripherals::QMI_CS1>,
18}
19
20impl<'d> QmiCs1<'d> {
21 /// Create a new QMI CS1 instance.
22 pub fn new(qmi_cs1: Peri<'d, peripherals::QMI_CS1>, cs1: Peri<'d, impl QmiCs1Pin>) -> Self {
23 // Configure CS1 pin for QMI function (funcsel = 9)
24 cs1.gpio().ctrl().write(|w| w.set_funcsel(9));
25
26 // Configure pad settings for high-speed operation
27 cs1.pad_ctrl().write(|w| {
28 #[cfg(feature = "_rp235x")]
29 w.set_iso(false);
30 w.set_ie(true);
31 w.set_drive(pac::pads::vals::Drive::_12M_A);
32 w.set_slewfast(true);
33 });
34
35 Self { _inner: qmi_cs1 }
36 }
37}
38
39trait SealedInstance {}
40
41/// QMI CS1 instance trait.
42#[allow(private_bounds)]
43pub trait Instance: SealedInstance + PeripheralType {}
44
45impl SealedInstance for peripherals::QMI_CS1 {}
46
47impl Instance for peripherals::QMI_CS1 {}
48
49/// CS1 pin trait for QMI.
50pub trait QmiCs1Pin: GpioPin {}
51
52// Implement pin traits for CS1-capable GPIO pins
53impl QmiCs1Pin for peripherals::PIN_0 {}
54impl QmiCs1Pin for peripherals::PIN_8 {}
55impl QmiCs1Pin for peripherals::PIN_19 {}
56#[cfg(feature = "rp235xb")]
57impl QmiCs1Pin for peripherals::PIN_47 {}
diff --git a/embassy-rp/src/rtc/datetime_no_deps.rs b/embassy-rp/src/rtc/datetime_no_deps.rs
index 5de00e6b4..77d4a3055 100644
--- a/embassy-rp/src/rtc/datetime_no_deps.rs
+++ b/embassy-rp/src/rtc/datetime_no_deps.rs
@@ -46,6 +46,7 @@ pub struct DateTime {
46/// A day of the week 46/// A day of the week
47#[repr(u8)] 47#[repr(u8)]
48#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] 48#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
49#[cfg_attr(feature = "defmt", derive(defmt::Format))]
49#[allow(missing_docs)] 50#[allow(missing_docs)]
50pub enum DayOfWeek { 51pub enum DayOfWeek {
51 Sunday = 0, 52 Sunday = 0,
diff --git a/embassy-rp/src/rtc/filter.rs b/embassy-rp/src/rtc/filter.rs
index d4a3bab2f..433053613 100644
--- a/embassy-rp/src/rtc/filter.rs
+++ b/embassy-rp/src/rtc/filter.rs
@@ -4,7 +4,8 @@ use crate::pac::rtc::regs::{IrqSetup0, IrqSetup1};
4/// A filter used for [`RealTimeClock::schedule_alarm`]. 4/// A filter used for [`RealTimeClock::schedule_alarm`].
5/// 5///
6/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm 6/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm
7#[derive(Default)] 7#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub struct DateTimeFilter { 9pub struct DateTimeFilter {
9 /// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value. 10 /// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value.
10 pub year: Option<u16>, 11 pub year: Option<u16>,
diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs
index 63cf91d28..8b0deed21 100644
--- a/embassy-rp/src/rtc/mod.rs
+++ b/embassy-rp/src/rtc/mod.rs
@@ -1,7 +1,12 @@
1//! RTC driver. 1//! RTC driver.
2mod filter; 2mod filter;
3 3
4use core::future::poll_fn;
5use core::sync::atomic::{compiler_fence, AtomicBool, Ordering};
6use core::task::Poll;
7
4use embassy_hal_internal::{Peri, PeripheralType}; 8use embassy_hal_internal::{Peri, PeripheralType};
9use embassy_sync::waitqueue::AtomicWaker;
5 10
6pub use self::filter::DateTimeFilter; 11pub use self::filter::DateTimeFilter;
7 12
@@ -11,6 +16,13 @@ mod datetime;
11 16
12pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; 17pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
13use crate::clocks::clk_rtc_freq; 18use crate::clocks::clk_rtc_freq;
19use crate::interrupt::typelevel::Binding;
20use crate::interrupt::{self, InterruptExt};
21
22// Static waker for the interrupt handler
23static WAKER: AtomicWaker = AtomicWaker::new();
24// Static flag to indicate if an alarm has occurred
25static ALARM_OCCURRED: AtomicBool = AtomicBool::new(false);
14 26
15/// A reference to the real time clock of the system 27/// A reference to the real time clock of the system
16pub struct Rtc<'d, T: Instance> { 28pub struct Rtc<'d, T: Instance> {
@@ -23,10 +35,15 @@ impl<'d, T: Instance> Rtc<'d, T> {
23 /// # Errors 35 /// # Errors
24 /// 36 ///
25 /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. 37 /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
26 pub fn new(inner: Peri<'d, T>) -> Self { 38 pub fn new(inner: Peri<'d, T>, _irq: impl Binding<interrupt::typelevel::RTC_IRQ, InterruptHandler>) -> Self {
27 // Set the RTC divider 39 // Set the RTC divider
28 inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)); 40 inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1));
29 41
42 // Setup the IRQ
43 // Clear any pending interrupts from the RTC_IRQ interrupt and enable it, so we do not have unexpected interrupts after initialization
44 interrupt::RTC_IRQ.unpend();
45 unsafe { interrupt::RTC_IRQ.enable() };
46
30 Self { inner } 47 Self { inner }
31 } 48 }
32 49
@@ -174,6 +191,110 @@ impl<'d, T: Instance> Rtc<'d, T> {
174 pub fn clear_interrupt(&mut self) { 191 pub fn clear_interrupt(&mut self) {
175 self.disable_alarm(); 192 self.disable_alarm();
176 } 193 }
194
195 /// Check if an alarm is scheduled.
196 ///
197 /// This function checks if the RTC alarm is currently active. If it is, it returns the alarm configuration
198 /// as a [`DateTimeFilter`]. Otherwise, it returns `None`.
199 pub fn alarm_scheduled(&self) -> Option<DateTimeFilter> {
200 // Check if alarm is active
201 if !self.inner.regs().irq_setup_0().read().match_active() {
202 return None;
203 }
204
205 // Get values from both alarm registers
206 let irq_0 = self.inner.regs().irq_setup_0().read();
207 let irq_1 = self.inner.regs().irq_setup_1().read();
208
209 // Create a DateTimeFilter and populate it based on which fields are enabled
210 let mut filter = DateTimeFilter::default();
211
212 if irq_0.year_ena() {
213 filter.year = Some(irq_0.year());
214 }
215
216 if irq_0.month_ena() {
217 filter.month = Some(irq_0.month());
218 }
219
220 if irq_0.day_ena() {
221 filter.day = Some(irq_0.day());
222 }
223
224 if irq_1.dotw_ena() {
225 // Convert day of week value to DayOfWeek enum
226 let day_of_week = match irq_1.dotw() {
227 0 => DayOfWeek::Sunday,
228 1 => DayOfWeek::Monday,
229 2 => DayOfWeek::Tuesday,
230 3 => DayOfWeek::Wednesday,
231 4 => DayOfWeek::Thursday,
232 5 => DayOfWeek::Friday,
233 6 => DayOfWeek::Saturday,
234 _ => return None, // Invalid day of week
235 };
236 filter.day_of_week = Some(day_of_week);
237 }
238
239 if irq_1.hour_ena() {
240 filter.hour = Some(irq_1.hour());
241 }
242
243 if irq_1.min_ena() {
244 filter.minute = Some(irq_1.min());
245 }
246
247 if irq_1.sec_ena() {
248 filter.second = Some(irq_1.sec());
249 }
250
251 Some(filter)
252 }
253
254 /// Wait for an RTC alarm to trigger.
255 ///
256 /// This function will wait until the RTC alarm is triggered. If the alarm is already triggered, it will return immediately.
257 /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered.
258 pub async fn wait_for_alarm(&mut self) {
259 poll_fn(|cx| {
260 WAKER.register(cx.waker());
261
262 // Atomically check and clear the alarm occurred flag to prevent race conditions
263 if critical_section::with(|_| {
264 let occurred = ALARM_OCCURRED.load(Ordering::SeqCst);
265 if occurred {
266 ALARM_OCCURRED.store(false, Ordering::SeqCst);
267 }
268 occurred
269 }) {
270 // Clear the interrupt and disable the alarm
271 self.clear_interrupt();
272
273 compiler_fence(Ordering::SeqCst);
274 return Poll::Ready(());
275 } else {
276 return Poll::Pending;
277 }
278 })
279 .await;
280 }
281}
282
283/// Interrupt handler.
284pub struct InterruptHandler {
285 _empty: (),
286}
287
288impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::RTC_IRQ> for InterruptHandler {
289 unsafe fn on_interrupt() {
290 // Disable the alarm first thing, to prevent unexpected re-entry
291 let rtc = crate::pac::RTC;
292 rtc.irq_setup_0().modify(|w| w.set_match_ena(false));
293
294 // Set the alarm occurred flag and wake the waker
295 ALARM_OCCURRED.store(true, Ordering::SeqCst);
296 WAKER.wake();
297 }
177} 298}
178 299
179/// Errors that can occur on methods on [Rtc] 300/// Errors that can occur on methods on [Rtc]
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index dfb8ca066..190e68d6d 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -9,6 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- fix: Fixed STM32H5 builds requiring time feature 11- fix: Fixed STM32H5 builds requiring time feature
12- feat: Derive Clone, Copy for QSPI Config
13- fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received
14- feat: stm32/timer: add set_polarity functions for main and complementary outputs in complementary_pwm
15- Add I2S support for STM32F1, STM32C0, STM32F0, STM32F3, STM32F7, STM32G0, STM32WL, STM32H5, STM32H7RS
16- fix: STM32: Prevent dropped DacChannel from disabling Dac peripheral if another DacChannel is still in scope ([#4577](https://github.com/embassy-rs/embassy/pull/4577))
17- feat: Added support for more OctoSPI configurations (e.g. APS6408 RAM) ([#4581](https://github.com/embassy-rs/embassy/pull/4581))
18- fix: stm32/usart: fix bug with blocking flush in buffered uart ([#4648](https://github.com/embassy-rs/embassy/pull/4648))
19- fix: stm32/(ospi/hspi/xspi): Fix the alternate bytes register config sticking around for subsequent writes
20- feat: Configurable gpio speed for QSPI
21- feat: derive Clone, Copy and defmt::Format for all *SPI-related configs
22- fix: handle address and data-length errors in OSPI
23- feat: Allow OSPI DMA writes larger than 64kB using chunking
24- feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times
12 25
13## 0.4.0 - 2025-08-26 26## 0.4.0 - 2025-08-26
14 27
@@ -22,6 +35,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
22- feat: stm32/adc/v3: allow DMA reads to loop through enable channels 35- feat: stm32/adc/v3: allow DMA reads to loop through enable channels
23- fix: Fix XSPI not disabling alternate bytes when they were previously enabled 36- fix: Fix XSPI not disabling alternate bytes when they were previously enabled
24- fix: Fix stm32h7rs init when using external flash via XSPI 37- fix: Fix stm32h7rs init when using external flash via XSPI
38- feat: Add Adc::new_with_clock() to configure analog clock
39- feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923))
40- feat: Added support for STM32F1 peripheral pin remapping (AFIO) ([#4430](https://github.com/embassy-rs/embassy/pull/4430))
25 41
26## 0.3.0 - 2025-08-12 42## 0.3.0 - 2025-08-12
27 43
@@ -132,7 +148,7 @@ GPIO:
132- Refactor AfType ([#3031](https://github.com/embassy-rs/embassy/pull/3031)) 148- Refactor AfType ([#3031](https://github.com/embassy-rs/embassy/pull/3031))
133- Gpiov1: Do not call set_speed for AFType::Input ([#2996](https://github.com/embassy-rs/embassy/pull/2996)) 149- Gpiov1: Do not call set_speed for AFType::Input ([#2996](https://github.com/embassy-rs/embassy/pull/2996))
134 150
135UART: 151UART:
136- Add embedded-io impls ([#2739](https://github.com/embassy-rs/embassy/pull/2739)) 152- Add embedded-io impls ([#2739](https://github.com/embassy-rs/embassy/pull/2739))
137- Add support for changing baud rate ([#3512](https://github.com/embassy-rs/embassy/pull/3512)) 153- Add support for changing baud rate ([#3512](https://github.com/embassy-rs/embassy/pull/3512))
138- Add split_ref ([#3500](https://github.com/embassy-rs/embassy/pull/3500)) 154- Add split_ref ([#3500](https://github.com/embassy-rs/embassy/pull/3500))
@@ -156,7 +172,7 @@ UART:
156 - Wake receive task for each received byte ([#2722](https://github.com/embassy-rs/embassy/pull/2722)) 172 - Wake receive task for each received byte ([#2722](https://github.com/embassy-rs/embassy/pull/2722))
157 - Fix dma and idle line detection in ringbuffereduartrx ([#3319](https://github.com/embassy-rs/embassy/pull/3319)) 173 - Fix dma and idle line detection in ringbuffereduartrx ([#3319](https://github.com/embassy-rs/embassy/pull/3319))
158 174
159SPI: 175SPI:
160- Add MISO pullup configuration option ([#2943](https://github.com/embassy-rs/embassy/pull/2943)) 176- Add MISO pullup configuration option ([#2943](https://github.com/embassy-rs/embassy/pull/2943))
161- Add slew rate configuration options ([#3669](https://github.com/embassy-rs/embassy/pull/3669)) 177- Add slew rate configuration options ([#3669](https://github.com/embassy-rs/embassy/pull/3669))
162- Fix blocking_write on nosck spi. ([#3035](https://github.com/embassy-rs/embassy/pull/3035)) 178- Fix blocking_write on nosck spi. ([#3035](https://github.com/embassy-rs/embassy/pull/3035))
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index cdb4e07d1..6cd8ed262 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -173,8 +173,8 @@ cortex-m = "0.7.6"
173futures-util = { version = "0.3.30", default-features = false } 173futures-util = { version = "0.3.30", default-features = false }
174sdio-host = "0.9.0" 174sdio-host = "0.9.0"
175critical-section = "1.1" 175critical-section = "1.1"
176stm32-metapac = { version = "18" } 176#stm32-metapac = { version = "18" }
177#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ecb93d42a6cbcd9e09cab74873908a2ca22327f7" } 177stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7" }
178 178
179vcell = "0.1.3" 179vcell = "0.1.3"
180nb = "1.0.0" 180nb = "1.0.0"
@@ -192,6 +192,7 @@ bitflags = "2.4.2"
192 192
193block-device-driver = { version = "0.2" } 193block-device-driver = { version = "0.2" }
194aligned = "0.4.1" 194aligned = "0.4.1"
195heapless = "0.9.1"
195 196
196[dev-dependencies] 197[dev-dependencies]
197critical-section = { version = "1.1", features = ["std"] } 198critical-section = { version = "1.1", features = ["std"] }
@@ -202,8 +203,8 @@ proptest-state-machine = "0.3.0"
202proc-macro2 = "1.0.36" 203proc-macro2 = "1.0.36"
203quote = "1.0.15" 204quote = "1.0.15"
204 205
205stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} 206#stm32-metapac = { version = "18", default-features = false, features = ["metadata"]}
206#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ecb93d42a6cbcd9e09cab74873908a2ca22327f7", default-features = false, features = ["metadata"] } 207stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7", default-features = false, features = ["metadata"] }
207 208
208[features] 209[features]
209default = ["rt"] 210default = ["rt"]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index deefb13c1..b5f1261fe 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -16,6 +16,27 @@ use stm32_metapac::metadata::{
16#[path = "./build_common.rs"] 16#[path = "./build_common.rs"]
17mod common; 17mod common;
18 18
19/// Helper function to handle peripheral versions with underscores.
20/// For a version like "v1_foo_bar", this generates all prefix combinations:
21/// - "kind_v1"
22/// - "kind_v1_foo"
23/// - "kind_v1_foo_bar"
24fn foreach_version_cfg(
25 cfgs: &mut common::CfgSet,
26 kind: &str,
27 version: &str,
28 mut cfg_fn: impl FnMut(&mut common::CfgSet, &str),
29) {
30 let parts: Vec<&str> = version.split('_').collect();
31
32 // Generate all possible prefix combinations
33 for i in 1..=parts.len() {
34 let partial_version = parts[0..i].join("_");
35 let cfg_name = format!("{}_{}", kind, partial_version);
36 cfg_fn(cfgs, &cfg_name);
37 }
38}
39
19fn main() { 40fn main() {
20 let mut cfgs = common::CfgSet::new(); 41 let mut cfgs = common::CfgSet::new();
21 common::set_target_cfgs(&mut cfgs); 42 common::set_target_cfgs(&mut cfgs);
@@ -38,14 +59,18 @@ fn main() {
38 for p in METADATA.peripherals { 59 for p in METADATA.peripherals {
39 if let Some(r) = &p.registers { 60 if let Some(r) = &p.registers {
40 cfgs.enable(r.kind); 61 cfgs.enable(r.kind);
41 cfgs.enable(format!("{}_{}", r.kind, r.version)); 62 foreach_version_cfg(&mut cfgs, r.kind, r.version, |cfgs, cfg_name| {
63 cfgs.enable(cfg_name);
64 });
42 } 65 }
43 } 66 }
44 67
45 for &(kind, versions) in ALL_PERIPHERAL_VERSIONS.iter() { 68 for &(kind, versions) in ALL_PERIPHERAL_VERSIONS.iter() {
46 cfgs.declare(kind); 69 cfgs.declare(kind);
47 for &version in versions.iter() { 70 for &version in versions.iter() {
48 cfgs.declare(format!("{}_{}", kind, version)); 71 foreach_version_cfg(&mut cfgs, kind, version, |cfgs, cfg_name| {
72 cfgs.declare(cfg_name);
73 });
49 } 74 }
50 } 75 }
51 76
@@ -1366,9 +1391,53 @@ fn main() {
1366 }) 1391 })
1367 } 1392 }
1368 1393
1369 g.extend(quote! { 1394 let pin_trait_impl = if let Some(afio) = &p.afio {
1370 pin_trait_impl!(#tr, #peri, #pin_name, #af); 1395 let values = afio
1371 }) 1396 .values
1397 .iter()
1398 .filter(|v| v.pins.contains(&pin.pin))
1399 .map(|v| v.value)
1400 .collect::<Vec<_>>();
1401
1402 if values.is_empty() {
1403 None
1404 } else {
1405 let reg = format_ident!("{}", afio.register.to_lowercase());
1406 let setter = format_ident!("set_{}", afio.field.to_lowercase());
1407 let type_and_values = if is_bool_field("AFIO", afio.register, afio.field) {
1408 let values = values.iter().map(|&v| v > 0);
1409 quote!(AfioRemapBool, [#(#values),*])
1410 } else {
1411 quote!(AfioRemap, [#(#values),*])
1412 };
1413
1414 Some(quote! {
1415 pin_trait_afio_impl!(#tr, #peri, #pin_name, {#reg, #setter, #type_and_values});
1416 })
1417 }
1418 } else {
1419 let peripherals_with_afio = [
1420 "CAN",
1421 "CEC",
1422 "ETH",
1423 "I2C",
1424 "SPI",
1425 "SUBGHZSPI",
1426 "USART",
1427 "UART",
1428 "LPUART",
1429 "TIM",
1430 ];
1431 let not_applicable = if peripherals_with_afio.iter().any(|&x| p.name.starts_with(x)) {
1432 quote!(, crate::gpio::AfioRemapNotApplicable)
1433 } else {
1434 quote!()
1435 };
1436
1437 Some(quote!(pin_trait_impl!(#tr, #peri, #pin_name, #af #not_applicable);))
1438 };
1439
1440 g.extend(pin_trait_impl);
1372 } 1441 }
1373 1442
1374 // ADC is special 1443 // ADC is special
@@ -1563,17 +1632,7 @@ fn main() {
1563 let register = format_ident!("{}", remap_info.register.to_lowercase()); 1632 let register = format_ident!("{}", remap_info.register.to_lowercase());
1564 let setter = format_ident!("set_{}", remap_info.field.to_lowercase()); 1633 let setter = format_ident!("set_{}", remap_info.field.to_lowercase());
1565 1634
1566 let field_metadata = METADATA 1635 let value = if is_bool_field("SYSCFG", &remap_info.register, &remap_info.field) {
1567 .peripherals
1568 .iter()
1569 .filter(|p| p.name == "SYSCFG")
1570 .flat_map(|p| p.registers.as_ref().unwrap().ir.fieldsets.iter())
1571 .filter(|f| f.name.eq_ignore_ascii_case(remap_info.register))
1572 .flat_map(|f| f.fields.iter())
1573 .find(|f| f.name.eq_ignore_ascii_case(remap_info.field))
1574 .unwrap();
1575
1576 let value = if field_metadata.bit_size == 1 {
1577 let bool_value = format_ident!("{}", remap_info.value > 0); 1636 let bool_value = format_ident!("{}", remap_info.value > 0);
1578 quote!(#bool_value) 1637 quote!(#bool_value)
1579 } else { 1638 } else {
@@ -2275,3 +2334,17 @@ fn gcd(a: u32, b: u32) -> u32 {
2275 } 2334 }
2276 gcd(b, a % b) 2335 gcd(b, a % b)
2277} 2336}
2337
2338fn is_bool_field(peripheral: &str, register: &str, field: &str) -> bool {
2339 let field_metadata = METADATA
2340 .peripherals
2341 .iter()
2342 .filter(|p| p.name == peripheral)
2343 .flat_map(|p| p.registers.as_ref().unwrap().ir.fieldsets.iter())
2344 .filter(|f| f.name.eq_ignore_ascii_case(register))
2345 .flat_map(|f| f.fields.iter())
2346 .find(|f| f.name.eq_ignore_ascii_case(field))
2347 .unwrap();
2348
2349 field_metadata.bit_size == 1
2350}
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs
index 31cbdc0d7..255dc7956 100644
--- a/embassy-stm32/src/adc/adc4.rs
+++ b/embassy-stm32/src/adc/adc4.rs
@@ -83,7 +83,8 @@ pub enum DacChannel {
83} 83}
84 84
85/// Number of samples used for averaging. 85/// Number of samples used for averaging.
86#[derive(Copy, Clone)] 86#[derive(Copy, Clone, Debug)]
87#[cfg_attr(feature = "defmt", derive(defmt::Format))]
87pub enum Averaging { 88pub enum Averaging {
88 Disabled, 89 Disabled,
89 Samples2, 90 Samples2,
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs
index f5870801e..f2837a8f1 100644
--- a/embassy-stm32/src/adc/c0.rs
+++ b/embassy-stm32/src/adc/c0.rs
@@ -138,7 +138,8 @@ impl<'a> defmt::Format for Prescaler {
138/// Number of samples used for averaging. 138/// Number of samples used for averaging.
139/// TODO: Implement hardware averaging setting. 139/// TODO: Implement hardware averaging setting.
140#[allow(unused)] 140#[allow(unused)]
141#[derive(Copy, Clone)] 141#[derive(Copy, Clone, Debug)]
142#[cfg_attr(feature = "defmt", derive(defmt::Format))]
142pub enum Averaging { 143pub enum Averaging {
143 Disabled, 144 Disabled,
144 Samples2, 145 Samples2,
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 778edc6f6..ea986f4cf 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -2,12 +2,12 @@
2 2
3#![macro_use] 3#![macro_use]
4#![allow(missing_docs)] // TODO 4#![allow(missing_docs)] // TODO
5#![cfg_attr(adc_f3_v2, allow(unused))] 5#![cfg_attr(adc_f3v3, allow(unused))]
6 6
7#[cfg(not(any(adc_f3_v2, adc_wba)))] 7#[cfg(not(any(adc_f3v3, adc_wba)))]
8#[cfg_attr(adc_f1, path = "f1.rs")] 8#[cfg_attr(adc_f1, path = "f1.rs")]
9#[cfg_attr(adc_f3, path = "f3.rs")] 9#[cfg_attr(adc_f3v1, path = "f3.rs")]
10#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] 10#[cfg_attr(adc_f3v2, path = "f3_v1_1.rs")]
11#[cfg_attr(adc_v1, path = "v1.rs")] 11#[cfg_attr(adc_v1, path = "v1.rs")]
12#[cfg_attr(adc_l0, path = "v1.rs")] 12#[cfg_attr(adc_l0, path = "v1.rs")]
13#[cfg_attr(adc_v2, path = "v2.rs")] 13#[cfg_attr(adc_v2, path = "v2.rs")]
@@ -20,10 +20,10 @@ mod _version;
20use core::marker::PhantomData; 20use core::marker::PhantomData;
21 21
22#[allow(unused)] 22#[allow(unused)]
23#[cfg(not(any(adc_f3_v2, adc_wba)))] 23#[cfg(not(any(adc_f3v3, adc_wba)))]
24pub use _version::*; 24pub use _version::*;
25use embassy_hal_internal::{impl_peripheral, PeripheralType}; 25use embassy_hal_internal::{impl_peripheral, PeripheralType};
26#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 26#[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
27use embassy_sync::waitqueue::AtomicWaker; 27use embassy_sync::waitqueue::AtomicWaker;
28 28
29#[cfg(any(adc_u5, adc_wba))] 29#[cfg(any(adc_u5, adc_wba))]
@@ -31,7 +31,7 @@ use embassy_sync::waitqueue::AtomicWaker;
31pub mod adc4; 31pub mod adc4;
32 32
33pub use crate::pac::adc::vals; 33pub use crate::pac::adc::vals;
34#[cfg(not(any(adc_f1, adc_f3_v2)))] 34#[cfg(not(any(adc_f1, adc_f3v3)))]
35pub use crate::pac::adc::vals::Res as Resolution; 35pub use crate::pac::adc::vals::Res as Resolution;
36pub use crate::pac::adc::vals::SampleTime; 36pub use crate::pac::adc::vals::SampleTime;
37use crate::peripherals; 37use crate::peripherals;
@@ -47,16 +47,16 @@ dma_trait!(RxDma4, adc4::Instance);
47pub struct Adc<'d, T: Instance> { 47pub struct Adc<'d, T: Instance> {
48 #[allow(unused)] 48 #[allow(unused)]
49 adc: crate::Peri<'d, T>, 49 adc: crate::Peri<'d, T>,
50 #[cfg(not(any(adc_f3_v2, adc_f3_v1_1, adc_wba)))] 50 #[cfg(not(any(adc_f3v3, adc_f3v2, adc_wba)))]
51 sample_time: SampleTime, 51 sample_time: SampleTime,
52} 52}
53 53
54#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 54#[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
55pub struct State { 55pub struct State {
56 pub waker: AtomicWaker, 56 pub waker: AtomicWaker,
57} 57}
58 58
59#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 59#[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
60impl State { 60impl State {
61 pub const fn new() -> Self { 61 pub const fn new() -> Self {
62 Self { 62 Self {
@@ -69,10 +69,10 @@ trait SealedInstance {
69 #[cfg(not(adc_wba))] 69 #[cfg(not(adc_wba))]
70 #[allow(unused)] 70 #[allow(unused)]
71 fn regs() -> crate::pac::adc::Adc; 71 fn regs() -> crate::pac::adc::Adc;
72 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] 72 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0)))]
73 #[allow(unused)] 73 #[allow(unused)]
74 fn common_regs() -> crate::pac::adccommon::AdcCommon; 74 fn common_regs() -> crate::pac::adccommon::AdcCommon;
75 #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 75 #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
76 fn state() -> &'static State; 76 fn state() -> &'static State;
77} 77}
78 78
@@ -100,22 +100,8 @@ pub(crate) fn blocking_delay_us(us: u32) {
100 100
101/// ADC instance. 101/// ADC instance.
102#[cfg(not(any( 102#[cfg(not(any(
103 adc_f1, 103 adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs,
104 adc_v1, 104 adc_u5, adc_c0, adc_wba,
105 adc_l0,
106 adc_v2,
107 adc_v3,
108 adc_v4,
109 adc_g4,
110 adc_f3,
111 adc_f3_v1_1,
112 adc_g0,
113 adc_u0,
114 adc_h5,
115 adc_h7rs,
116 adc_u5,
117 adc_c0,
118 adc_wba,
119)))] 105)))]
120#[allow(private_bounds)] 106#[allow(private_bounds)]
121pub trait Instance: SealedInstance + crate::PeripheralType { 107pub trait Instance: SealedInstance + crate::PeripheralType {
@@ -123,22 +109,8 @@ pub trait Instance: SealedInstance + crate::PeripheralType {
123} 109}
124/// ADC instance. 110/// ADC instance.
125#[cfg(any( 111#[cfg(any(
126 adc_f1, 112 adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs,
127 adc_v1, 113 adc_u5, adc_c0, adc_wba,
128 adc_l0,
129 adc_v2,
130 adc_v3,
131 adc_v4,
132 adc_g4,
133 adc_f3,
134 adc_f3_v1_1,
135 adc_g0,
136 adc_u0,
137 adc_h5,
138 adc_h7rs,
139 adc_u5,
140 adc_c0,
141 adc_wba,
142))] 114))]
143#[allow(private_bounds)] 115#[allow(private_bounds)]
144pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { 116pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral {
@@ -258,12 +230,12 @@ foreach_adc!(
258 crate::pac::$inst 230 crate::pac::$inst
259 } 231 }
260 232
261 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0, adc_u5, adc_wba)))] 233 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0, adc_u5, adc_wba)))]
262 fn common_regs() -> crate::pac::adccommon::AdcCommon { 234 fn common_regs() -> crate::pac::adccommon::AdcCommon {
263 return crate::pac::$common_inst 235 return crate::pac::$common_inst
264 } 236 }
265 237
266 #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 238 #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
267 fn state() -> &'static State { 239 fn state() -> &'static State {
268 static STATE: State = State::new(); 240 static STATE: State = State::new();
269 &STATE 241 &STATE
@@ -295,7 +267,7 @@ macro_rules! impl_adc_pin {
295/// Get the maximum reading value for this resolution. 267/// Get the maximum reading value for this resolution.
296/// 268///
297/// This is `2**n - 1`. 269/// This is `2**n - 1`.
298#[cfg(not(any(adc_f1, adc_f3_v2)))] 270#[cfg(not(any(adc_f1, adc_f3v3)))]
299pub const fn resolution_to_max_count(res: Resolution) -> u32 { 271pub const fn resolution_to_max_count(res: Resolution) -> u32 {
300 match res { 272 match res {
301 #[cfg(adc_v4)] 273 #[cfg(adc_v4)]
@@ -309,7 +281,7 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 {
309 Resolution::BITS12 => (1 << 12) - 1, 281 Resolution::BITS12 => (1 << 12) - 1,
310 Resolution::BITS10 => (1 << 10) - 1, 282 Resolution::BITS10 => (1 << 10) - 1,
311 Resolution::BITS8 => (1 << 8) - 1, 283 Resolution::BITS8 => (1 << 8) - 1,
312 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_c0, adc_g0, adc_f3, adc_f3_v1_1, adc_h5))] 284 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_c0, adc_g0, adc_f3v1, adc_f3v2, adc_h5))]
313 Resolution::BITS6 => (1 << 6) - 1, 285 Resolution::BITS6 => (1 << 6) - 1,
314 #[allow(unreachable_patterns)] 286 #[allow(unreachable_patterns)]
315 _ => core::unreachable!(), 287 _ => core::unreachable!(),
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs
index 7fe502da0..d09374876 100644
--- a/embassy-stm32/src/adc/v1.rs
+++ b/embassy-stm32/src/adc/v1.rs
@@ -66,7 +66,11 @@ pub struct Temperature;
66impl AdcChannel<ADC1> for Temperature {} 66impl AdcChannel<ADC1> for Temperature {}
67impl super::SealedAdcChannel<ADC1> for Temperature { 67impl super::SealedAdcChannel<ADC1> for Temperature {
68 fn channel(&self) -> u8 { 68 fn channel(&self) -> u8 {
69 16 69 if cfg!(adc_l0) {
70 18
71 } else {
72 16
73 }
70 } 74 }
71} 75}
72 76
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index dc1faa4d1..16063ce4d 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -1,7 +1,13 @@
1use cfg_if::cfg_if; 1use cfg_if::cfg_if;
2#[cfg(adc_g0)]
3use heapless::Vec;
2use pac::adc::vals::Dmacfg; 4use pac::adc::vals::Dmacfg;
5#[cfg(adc_g0)]
6use pac::adc::vals::{Ckmode, Smpsel};
3#[cfg(adc_v3)] 7#[cfg(adc_v3)]
4use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; 8use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs};
9#[cfg(adc_g0)]
10pub use pac::adc::vals::{Ovsr, Ovss, Presc};
5 11
6use super::{ 12use super::{
7 blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, 13 blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel,
@@ -14,6 +20,11 @@ pub const VREF_DEFAULT_MV: u32 = 3300;
14/// VREF voltage used for factory calibration of VREFINTCAL register. 20/// VREF voltage used for factory calibration of VREFINTCAL register.
15pub const VREF_CALIB_MV: u32 = 3000; 21pub const VREF_CALIB_MV: u32 = 3000;
16 22
23#[cfg(adc_g0)]
24/// The number of variants in Smpsel
25// TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable
26const SAMPLE_TIMES_CAPACITY: usize = 2;
27
17pub struct VrefInt; 28pub struct VrefInt;
18impl<T: Instance> AdcChannel<T> for VrefInt {} 29impl<T: Instance> AdcChannel<T> for VrefInt {}
19impl<T: Instance> SealedAdcChannel<T> for VrefInt { 30impl<T: Instance> SealedAdcChannel<T> for VrefInt {
@@ -96,6 +107,8 @@ cfg_if! {
96} 107}
97 108
98/// Number of samples used for averaging. 109/// Number of samples used for averaging.
110#[derive(Copy, Clone, Debug)]
111#[cfg_attr(feature = "defmt", derive(defmt::Format))]
99pub enum Averaging { 112pub enum Averaging {
100 Disabled, 113 Disabled,
101 Samples2, 114 Samples2,
@@ -107,8 +120,30 @@ pub enum Averaging {
107 Samples128, 120 Samples128,
108 Samples256, 121 Samples256,
109} 122}
123
124cfg_if! { if #[cfg(adc_g0)] {
125
126/// Synchronous PCLK prescaler
127pub enum CkModePclk {
128 DIV1,
129 DIV2,
130 DIV4,
131}
132
133/// The analog clock is either the synchronous prescaled PCLK or
134/// the asynchronous prescaled ADCCLK configured by the RCC mux.
135/// The data sheet states the maximum analog clock frequency -
136/// for STM32WL55CC it is 36 MHz.
137pub enum Clock {
138 Sync { div: CkModePclk },
139 Async { div: Presc },
140}
141
142}}
143
110impl<'d, T: Instance> Adc<'d, T> { 144impl<'d, T: Instance> Adc<'d, T> {
111 pub fn new(adc: Peri<'d, T>) -> Self { 145 /// Enable the voltage regulator
146 fn init_regulator() {
112 rcc::enable_and_reset::<T>(); 147 rcc::enable_and_reset::<T>();
113 T::regs().cr().modify(|reg| { 148 T::regs().cr().modify(|reg| {
114 #[cfg(not(any(adc_g0, adc_u0)))] 149 #[cfg(not(any(adc_g0, adc_u0)))]
@@ -117,13 +152,17 @@ impl<'d, T: Instance> Adc<'d, T> {
117 }); 152 });
118 153
119 // If this is false then each ADC_CHSELR bit enables an input channel. 154 // If this is false then each ADC_CHSELR bit enables an input channel.
155 // This is the reset value, so has no effect.
120 #[cfg(any(adc_g0, adc_u0))] 156 #[cfg(any(adc_g0, adc_u0))]
121 T::regs().cfgr1().modify(|reg| { 157 T::regs().cfgr1().modify(|reg| {
122 reg.set_chselrmod(false); 158 reg.set_chselrmod(false);
123 }); 159 });
124 160
125 blocking_delay_us(20); 161 blocking_delay_us(20);
162 }
126 163
164 /// Calibrate to remove conversion offset
165 fn init_calibrate() {
127 T::regs().cr().modify(|reg| { 166 T::regs().cr().modify(|reg| {
128 reg.set_adcal(true); 167 reg.set_adcal(true);
129 }); 168 });
@@ -133,6 +172,51 @@ impl<'d, T: Instance> Adc<'d, T> {
133 } 172 }
134 173
135 blocking_delay_us(1); 174 blocking_delay_us(1);
175 }
176
177 /// Initialize the ADC leaving any analog clock at reset value.
178 /// For G0 and WL, this is the async clock without prescaler.
179 pub fn new(adc: Peri<'d, T>) -> Self {
180 Self::init_regulator();
181 Self::init_calibrate();
182 Self {
183 adc,
184 sample_time: SampleTime::from_bits(0),
185 }
186 }
187
188 #[cfg(adc_g0)]
189 /// Initialize ADC with explicit clock for the analog ADC
190 pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self {
191 Self::init_regulator();
192
193 #[cfg(any(stm32wl5x))]
194 {
195 // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual
196 let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0;
197 match clock {
198 Clock::Async { div: _ } => {
199 assert!(async_clock_available);
200 }
201 Clock::Sync { div: _ } => {
202 if async_clock_available {
203 warn!("Not using configured ADC clock");
204 }
205 }
206 }
207 }
208 match clock {
209 Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)),
210 Clock::Sync { div } => T::regs().cfgr2().modify(|reg| {
211 reg.set_ckmode(match div {
212 CkModePclk::DIV1 => Ckmode::PCLK,
213 CkModePclk::DIV2 => Ckmode::PCLK_DIV2,
214 CkModePclk::DIV4 => Ckmode::PCLK_DIV4,
215 })
216 }),
217 }
218
219 Self::init_calibrate();
136 220
137 Self { 221 Self {
138 adc, 222 adc,
@@ -349,53 +433,78 @@ impl<'d, T: Instance> Adc<'d, T> {
349 w.set_l(sequence.len() as u8 - 1); 433 w.set_l(sequence.len() as u8 - 1);
350 }); 434 });
351 435
352 #[cfg(any(adc_g0, adc_u0))] 436 #[cfg(adc_g0)]
353 let mut channel_mask = 0; 437 {
354 438 let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new();
355 // Configure channels and ranks 439
356 for (_i, (channel, sample_time)) in sequence.enumerate() { 440 T::regs().chselr().write(|chselr| {
357 Self::configure_channel(channel, sample_time); 441 T::regs().smpr().write(|smpr| {
358 442 for (channel, sample_time) in sequence {
359 // Each channel is sampled according to sequence 443 chselr.set_chsel(channel.channel.into(), true);
360 #[cfg(not(any(adc_g0, adc_u0)))] 444 if let Some(i) = sample_times.iter().position(|&t| t == sample_time) {
361 match _i { 445 smpr.set_smpsel(channel.channel.into(), (i as u8).into());
362 0..=3 => { 446 } else {
363 T::regs().sqr1().modify(|w| { 447 smpr.set_sample_time(sample_times.len(), sample_time);
364 w.set_sq(_i, channel.channel()); 448 if let Err(_) = sample_times.push(sample_time) {
365 }); 449 panic!(
366 } 450 "Implementation is limited to {} unique sample times among all channels.",
367 4..=8 => { 451 SAMPLE_TIMES_CAPACITY
368 T::regs().sqr2().modify(|w| { 452 );
369 w.set_sq(_i - 4, channel.channel()); 453 }
370 }); 454 }
371 } 455 }
372 9..=13 => { 456 })
373 T::regs().sqr3().modify(|w| { 457 });
374 w.set_sq(_i - 9, channel.channel()); 458 }
375 }); 459 #[cfg(not(adc_g0))]
460 {
461 #[cfg(adc_u0)]
462 let mut channel_mask = 0;
463
464 // Configure channels and ranks
465 for (_i, (channel, sample_time)) in sequence.enumerate() {
466 Self::configure_channel(channel, sample_time);
467
468 // Each channel is sampled according to sequence
469 #[cfg(not(any(adc_g0, adc_u0)))]
470 match _i {
471 0..=3 => {
472 T::regs().sqr1().modify(|w| {
473 w.set_sq(_i, channel.channel());
474 });
475 }
476 4..=8 => {
477 T::regs().sqr2().modify(|w| {
478 w.set_sq(_i - 4, channel.channel());
479 });
480 }
481 9..=13 => {
482 T::regs().sqr3().modify(|w| {
483 w.set_sq(_i - 9, channel.channel());
484 });
485 }
486 14..=15 => {
487 T::regs().sqr4().modify(|w| {
488 w.set_sq(_i - 14, channel.channel());
489 });
490 }
491 _ => unreachable!(),
376 } 492 }
377 14..=15 => { 493
378 T::regs().sqr4().modify(|w| { 494 #[cfg(adc_u0)]
379 w.set_sq(_i - 14, channel.channel()); 495 {
380 }); 496 channel_mask |= 1 << channel.channel();
381 } 497 }
382 _ => unreachable!(),
383 } 498 }
384 499
385 #[cfg(any(adc_g0, adc_u0))] 500 // On G0 and U0 enabled channels are sampled from 0 to last channel.
386 { 501 // It is possible to add up to 8 sequences if CHSELRMOD = 1.
387 channel_mask |= 1 << channel.channel(); 502 // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used.
388 } 503 #[cfg(adc_u0)]
504 T::regs().chselr().modify(|reg| {
505 reg.set_chsel(channel_mask);
506 });
389 } 507 }
390
391 // On G0 and U0 enabled channels are sampled from 0 to last channel.
392 // It is possible to add up to 8 sequences if CHSELRMOD = 1.
393 // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used.
394 #[cfg(any(adc_g0, adc_u0))]
395 T::regs().chselr().modify(|reg| {
396 reg.set_chsel(channel_mask);
397 });
398
399 // Set continuous mode with oneshot dma. 508 // Set continuous mode with oneshot dma.
400 // Clear overrun flag before starting transfer. 509 // Clear overrun flag before starting transfer.
401 T::regs().isr().modify(|reg| { 510 T::regs().isr().modify(|reg| {
@@ -450,6 +559,7 @@ impl<'d, T: Instance> Adc<'d, T> {
450 }); 559 });
451 } 560 }
452 561
562 #[cfg(not(adc_g0))]
453 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { 563 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) {
454 // RM0492, RM0481, etc. 564 // RM0492, RM0481, etc.
455 // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." 565 // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected."
@@ -464,13 +574,23 @@ impl<'d, T: Instance> Adc<'d, T> {
464 574
465 fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { 575 fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
466 self.enable(); 576 self.enable();
577 #[cfg(not(adc_g0))]
467 Self::configure_channel(channel, self.sample_time); 578 Self::configure_channel(channel, self.sample_time);
468 579 #[cfg(adc_g0)]
580 T::regs().smpr().write(|reg| {
581 reg.set_sample_time(0, self.sample_time);
582 reg.set_smpsel(channel.channel().into(), Smpsel::SMP1);
583 });
469 // Select channel 584 // Select channel
470 #[cfg(not(any(adc_g0, adc_u0)))] 585 #[cfg(not(any(adc_g0, adc_u0)))]
471 T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); 586 T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel()));
472 #[cfg(any(adc_g0, adc_u0))] 587 #[cfg(any(adc_g0, adc_u0))]
473 T::regs().chselr().write(|reg| reg.set_chsel(1 << channel.channel())); 588 T::regs().chselr().write(|reg| {
589 #[cfg(adc_g0)]
590 reg.set_chsel(channel.channel().into(), true);
591 #[cfg(adc_u0)]
592 reg.set_chsel(1 << channel.channel());
593 });
474 594
475 // Some models are affected by an erratum: 595 // Some models are affected by an erratum:
476 // If we perform conversions slower than 1 kHz, the first read ADC value can be 596 // If we perform conversions slower than 1 kHz, the first read ADC value can be
@@ -494,12 +614,20 @@ impl<'d, T: Instance> Adc<'d, T> {
494 val 614 val
495 } 615 }
496 616
497 #[cfg(any(adc_g0, adc_u0))] 617 #[cfg(adc_g0)]
618 pub fn set_oversampling_shift(&mut self, shift: Ovss) {
619 T::regs().cfgr2().modify(|reg| reg.set_ovss(shift));
620 }
621 #[cfg(adc_u0)]
498 pub fn set_oversampling_shift(&mut self, shift: u8) { 622 pub fn set_oversampling_shift(&mut self, shift: u8) {
499 T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); 623 T::regs().cfgr2().modify(|reg| reg.set_ovss(shift));
500 } 624 }
501 625
502 #[cfg(any(adc_g0, adc_u0))] 626 #[cfg(adc_g0)]
627 pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) {
628 T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio));
629 }
630 #[cfg(adc_u0)]
503 pub fn set_oversampling_ratio(&mut self, ratio: u8) { 631 pub fn set_oversampling_ratio(&mut self, ratio: u8) {
504 T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); 632 T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio));
505 } 633 }
@@ -526,9 +654,10 @@ impl<'d, T: Instance> Adc<'d, T> {
526 T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); 654 T::regs().cfgr2().modify(|reg| reg.set_ovss(shift));
527 } 655 }
528 656
657 #[cfg(not(adc_g0))]
529 fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { 658 fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
530 cfg_if! { 659 cfg_if! {
531 if #[cfg(any(adc_g0, adc_u0))] { 660 if #[cfg(adc_u0)] {
532 // On G0 and U6 all channels use the same sampling time. 661 // On G0 and U6 all channels use the same sampling time.
533 T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); 662 T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into()));
534 } else if #[cfg(any(adc_h5, adc_h7rs))] { 663 } else if #[cfg(any(adc_h5, adc_h7rs))] {
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs
index b0871019a..b66437e6e 100644
--- a/embassy-stm32/src/adc/v4.rs
+++ b/embassy-stm32/src/adc/v4.rs
@@ -142,7 +142,8 @@ impl Prescaler {
142} 142}
143 143
144/// Number of samples used for averaging. 144/// Number of samples used for averaging.
145#[derive(Copy, Clone)] 145#[derive(Copy, Clone, Debug)]
146#[cfg_attr(feature = "defmt", derive(defmt::Format))]
146pub enum Averaging { 147pub enum Averaging {
147 Disabled, 148 Disabled,
148 Samples2, 149 Samples2,
diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs
index 4c0795a2a..8eb188560 100644
--- a/embassy-stm32/src/can/bxcan/mod.rs
+++ b/embassy-stm32/src/can/bxcan/mod.rs
@@ -181,10 +181,10 @@ pub enum TryWriteError {
181impl<'d> Can<'d> { 181impl<'d> Can<'d> {
182 /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. 182 /// Creates a new Bxcan instance, keeping the peripheral in sleep mode.
183 /// You must call [Can::enable_non_blocking] to use the peripheral. 183 /// You must call [Can::enable_non_blocking] to use the peripheral.
184 pub fn new<T: Instance>( 184 pub fn new<T: Instance, #[cfg(afio)] A>(
185 _peri: Peri<'d, T>, 185 _peri: Peri<'d, T>,
186 rx: Peri<'d, impl RxPin<T>>, 186 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
187 tx: Peri<'d, impl TxPin<T>>, 187 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
188 _irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>> 188 _irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>>
189 + interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>> 189 + interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>>
190 + interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>> 190 + interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>>
@@ -194,8 +194,8 @@ impl<'d> Can<'d> {
194 let info = T::info(); 194 let info = T::info();
195 let regs = &T::info().regs; 195 let regs = &T::info().regs;
196 196
197 rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); 197 set_as_af!(rx, AfType::input(Pull::None));
198 tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 198 set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh));
199 199
200 rcc::enable_and_reset::<T>(); 200 rcc::enable_and_reset::<T>();
201 201
@@ -229,8 +229,8 @@ impl<'d> Can<'d> {
229 info.sce_interrupt.enable(); 229 info.sce_interrupt.enable();
230 } 230 }
231 231
232 rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); 232 set_as_af!(rx, AfType::input(Pull::None));
233 tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 233 set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh));
234 234
235 Registers(T::regs()).leave_init_mode(); 235 Registers(T::regs()).leave_init_mode();
236 236
@@ -1218,8 +1218,8 @@ foreach_peripheral!(
1218 }; 1218 };
1219); 1219);
1220 1220
1221pin_trait!(RxPin, Instance); 1221pin_trait!(RxPin, Instance, @A);
1222pin_trait!(TxPin, Instance); 1222pin_trait!(TxPin, Instance, @A);
1223 1223
1224trait Index { 1224trait Index {
1225 fn index(&self) -> usize; 1225 fn index(&self) -> usize;
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs
index 99e40ba62..d8f71e03e 100644
--- a/embassy-stm32/src/can/fdcan.rs
+++ b/embassy-stm32/src/can/fdcan.rs
@@ -185,8 +185,8 @@ impl<'d> CanConfigurator<'d> {
185 + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> 185 + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>>
186 + 'd, 186 + 'd,
187 ) -> CanConfigurator<'d> { 187 ) -> CanConfigurator<'d> {
188 rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); 188 set_as_af!(rx, AfType::input(Pull::None));
189 tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 189 set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh));
190 190
191 rcc::enable_and_reset::<T>(); 191 rcc::enable_and_reset::<T>();
192 192
diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs
index 35d9f8cce..0173b2b5d 100644
--- a/embassy-stm32/src/cryp/mod.rs
+++ b/embassy-stm32/src/cryp/mod.rs
@@ -1814,7 +1814,6 @@ impl<'d, T: Instance> Cryp<'d, T, Async> {
1814 // Configure DMA to transfer input to crypto core. 1814 // Configure DMA to transfer input to crypto core.
1815 let dst_ptr: *mut u32 = T::regs().din().as_ptr(); 1815 let dst_ptr: *mut u32 = T::regs().din().as_ptr();
1816 let options = TransferOptions { 1816 let options = TransferOptions {
1817 #[cfg(not(gpdma))]
1818 priority: crate::dma::Priority::High, 1817 priority: crate::dma::Priority::High,
1819 ..Default::default() 1818 ..Default::default()
1820 }; 1819 };
@@ -1834,7 +1833,6 @@ impl<'d, T: Instance> Cryp<'d, T, Async> {
1834 // Configure DMA to transfer input to crypto core. 1833 // Configure DMA to transfer input to crypto core.
1835 let dst_ptr: *mut u32 = T::regs().din().as_ptr(); 1834 let dst_ptr: *mut u32 = T::regs().din().as_ptr();
1836 let options = TransferOptions { 1835 let options = TransferOptions {
1837 #[cfg(not(gpdma))]
1838 priority: crate::dma::Priority::High, 1836 priority: crate::dma::Priority::High,
1839 ..Default::default() 1837 ..Default::default()
1840 }; 1838 };
@@ -1853,7 +1851,6 @@ impl<'d, T: Instance> Cryp<'d, T, Async> {
1853 // Configure DMA to get output from crypto core. 1851 // Configure DMA to get output from crypto core.
1854 let src_ptr = T::regs().dout().as_ptr(); 1852 let src_ptr = T::regs().dout().as_ptr();
1855 let options = TransferOptions { 1853 let options = TransferOptions {
1856 #[cfg(not(gpdma))]
1857 priority: crate::dma::Priority::VeryHigh, 1854 priority: crate::dma::Priority::VeryHigh,
1858 ..Default::default() 1855 ..Default::default()
1859 }; 1856 };
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs
index d8f1f96f2..08e001337 100644
--- a/embassy-stm32/src/dac/mod.rs
+++ b/embassy-stm32/src/dac/mod.rs
@@ -12,6 +12,7 @@ use crate::{peripherals, Peri};
12 12
13mod tsel; 13mod tsel;
14use embassy_hal_internal::PeripheralType; 14use embassy_hal_internal::PeripheralType;
15use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
15pub use tsel::TriggerSel; 16pub use tsel::TriggerSel;
16 17
17/// Operating mode for DAC channel 18/// Operating mode for DAC channel
@@ -96,6 +97,41 @@ pub enum ValueArray<'a> {
96 Bit12Right(&'a [u16]), 97 Bit12Right(&'a [u16]),
97} 98}
98 99
100#[derive(Debug)]
101#[cfg_attr(feature = "defmt", derive(defmt::Format))]
102enum ChannelEvent {
103 Enable,
104 Disable,
105}
106
107struct InnerState {
108 channel_count: usize,
109}
110
111type SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<InnerState>>;
112struct State {
113 state: SharedState,
114}
115
116impl State {
117 /// Adjusts the channel count in response to a `ChannelEvent`, returning the updated value.
118 pub fn adjust_channel_count(&self, event: ChannelEvent) -> usize {
119 self.state.lock(|state| {
120 {
121 let mut mut_state = state.borrow_mut();
122 match event {
123 ChannelEvent::Enable => {
124 mut_state.channel_count += 1;
125 }
126 ChannelEvent::Disable => {
127 mut_state.channel_count -= 1;
128 }
129 };
130 }
131 state.borrow().channel_count
132 })
133 }
134}
99/// Driver for a single DAC channel. 135/// Driver for a single DAC channel.
100/// 136///
101/// If you want to use both channels, either together or independently, 137/// If you want to use both channels, either together or independently,
@@ -249,6 +285,16 @@ impl<'d, T: Instance, C: Channel, M: PeriMode> DacChannel<'d, T, C, M> {
249 reg.set_en(C::IDX, on); 285 reg.set_en(C::IDX, on);
250 }); 286 });
251 }); 287 });
288 let event = if on {
289 ChannelEvent::Enable
290 } else {
291 ChannelEvent::Disable
292 };
293 let channel_count = T::state().adjust_channel_count(event);
294 // Disable the DAC only if no more channels are using it.
295 if channel_count == 0 {
296 rcc::disable::<T>();
297 }
252 } 298 }
253 299
254 /// Enable this channel. 300 /// Enable this channel.
@@ -354,7 +400,7 @@ impl<'d, T: Instance, C: Channel, M: PeriMode> DacChannel<'d, T, C, M> {
354 400
355impl<'d, T: Instance, C: Channel, M: PeriMode> Drop for DacChannel<'d, T, C, M> { 401impl<'d, T: Instance, C: Channel, M: PeriMode> Drop for DacChannel<'d, T, C, M> {
356 fn drop(&mut self) { 402 fn drop(&mut self) {
357 rcc::disable::<T>(); 403 self.disable();
358 } 404 }
359} 405}
360 406
@@ -529,8 +575,6 @@ impl<'d, T: Instance, M: PeriMode> Dac<'d, T, M> {
529 dma_ch2: Option<ChannelAndRequest<'d>>, 575 dma_ch2: Option<ChannelAndRequest<'d>>,
530 #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] mode: Mode, 576 #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] mode: Mode,
531 ) -> Self { 577 ) -> Self {
532 // Enable twice to increment the DAC refcount for each channel.
533 rcc::enable_and_reset::<T>();
534 rcc::enable_and_reset::<T>(); 578 rcc::enable_and_reset::<T>();
535 579
536 let mut ch1 = DacCh1 { 580 let mut ch1 = DacCh1 {
@@ -597,6 +641,13 @@ impl<'d, T: Instance, M: PeriMode> Dac<'d, T, M> {
597 641
598trait SealedInstance { 642trait SealedInstance {
599 fn regs() -> crate::pac::dac::Dac; 643 fn regs() -> crate::pac::dac::Dac;
644
645 fn state() -> &'static State {
646 static STATE: State = State {
647 state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(InnerState { channel_count: 0 })),
648 };
649 &STATE
650 }
600} 651}
601 652
602/// DAC instance. 653/// DAC instance.
diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs
index d05faee21..bd03f1e00 100644
--- a/embassy-stm32/src/dcmi.rs
+++ b/embassy-stm32/src/dcmi.rs
@@ -108,7 +108,7 @@ macro_rules! config_pins {
108 ($($pin:ident),*) => { 108 ($($pin:ident),*) => {
109 critical_section::with(|_| { 109 critical_section::with(|_| {
110 $( 110 $(
111 $pin.set_as_af($pin.af_num(), AfType::input(Pull::None)); 111 set_as_af!($pin, AfType::input(Pull::None));
112 )* 112 )*
113 }) 113 })
114 }; 114 };
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs
index 464823bfc..73ecab070 100644
--- a/embassy-stm32/src/dma/dma_bdma.rs
+++ b/embassy-stm32/src/dma/dma_bdma.rs
@@ -498,46 +498,52 @@ impl AnyChannel {
498 } 498 }
499 } 499 }
500 500
501 fn request_stop(&self) { 501 fn request_pause(&self) {
502 let info = self.info(); 502 let info = self.info();
503 match self.info().dma { 503 match self.info().dma {
504 #[cfg(dma)] 504 #[cfg(dma)]
505 DmaInfo::Dma(r) => { 505 DmaInfo::Dma(r) => {
506 // Disable the channel. Keep the IEs enabled so the irqs still fire. 506 // Disable the channel without overwriting the existing configuration
507 r.st(info.num).cr().write(|w| { 507 r.st(info.num).cr().modify(|w| {
508 w.set_teie(true); 508 w.set_en(false);
509 w.set_tcie(true);
510 }); 509 });
511 } 510 }
512 #[cfg(bdma)] 511 #[cfg(bdma)]
513 DmaInfo::Bdma(r) => { 512 DmaInfo::Bdma(r) => {
514 // Disable the channel. Keep the IEs enabled so the irqs still fire. 513 // Disable the channel without overwriting the existing configuration
515 r.ch(info.num).cr().write(|w| { 514 r.ch(info.num).cr().modify(|w| {
516 w.set_teie(true); 515 w.set_en(false);
517 w.set_tcie(true);
518 }); 516 });
519 } 517 }
520 } 518 }
521 } 519 }
522 520
523 fn request_pause(&self) { 521 fn request_resume(&self) {
522 self.start()
523 }
524
525 fn request_reset(&self) {
524 let info = self.info(); 526 let info = self.info();
525 match self.info().dma { 527 match self.info().dma {
526 #[cfg(dma)] 528 #[cfg(dma)]
527 DmaInfo::Dma(r) => { 529 DmaInfo::Dma(r) => {
528 // Disable the channel without overwriting the existing configuration 530 // Disable the channel. Keep the IEs enabled so the irqs still fire.
529 r.st(info.num).cr().modify(|w| { 531 r.st(info.num).cr().write(|w| {
530 w.set_en(false); 532 w.set_teie(true);
533 w.set_tcie(true);
531 }); 534 });
532 } 535 }
533 #[cfg(bdma)] 536 #[cfg(bdma)]
534 DmaInfo::Bdma(r) => { 537 DmaInfo::Bdma(r) => {
535 // Disable the channel without overwriting the existing configuration 538 // Disable the channel. Keep the IEs enabled so the irqs still fire.
536 r.ch(info.num).cr().modify(|w| { 539 r.ch(info.num).cr().write(|w| {
537 w.set_en(false); 540 w.set_teie(true);
541 w.set_tcie(true);
538 }); 542 });
539 } 543 }
540 } 544 }
545
546 while self.is_running() {}
541 } 547 }
542 548
543 fn is_running(&self) -> bool { 549 fn is_running(&self) -> bool {
@@ -710,27 +716,31 @@ impl<'a> Transfer<'a> {
710 Self { channel } 716 Self { channel }
711 } 717 }
712 718
713 /// Request the transfer to stop.
714 /// The configuration for this channel will **not be preserved**. If you need to restart the transfer
715 /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
716 ///
717 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
718 pub fn request_stop(&mut self) {
719 self.channel.request_stop()
720 }
721
722 /// Request the transfer to pause, keeping the existing configuration for this channel. 719 /// Request the transfer to pause, keeping the existing configuration for this channel.
723 /// To restart the transfer, call [`start`](Self::start) again.
724 /// 720 ///
721 /// To resume the transfer, call [`request_resume`](Self::request_resume) again.
725 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. 722 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
726 pub fn request_pause(&mut self) { 723 pub fn request_pause(&mut self) {
727 self.channel.request_pause() 724 self.channel.request_pause()
728 } 725 }
729 726
727 /// Request the transfer to resume after having been paused.
728 pub fn request_resume(&mut self) {
729 self.channel.request_resume()
730 }
731
732 /// Request the DMA to reset.
733 ///
734 /// The configuration for this channel will **not be preserved**. If you need to restart the transfer
735 /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
736 pub fn request_reset(&mut self) {
737 self.channel.request_reset()
738 }
739
730 /// Return whether this transfer is still running. 740 /// Return whether this transfer is still running.
731 /// 741 ///
732 /// If this returns `false`, it can be because either the transfer finished, or 742 /// If this returns `false`, it can be because either the transfer finished, or
733 /// it was requested to stop early with [`request_stop`](Self::request_stop). 743 /// it was requested to stop early with [`request_pause`](Self::request_pause).
734 pub fn is_running(&mut self) -> bool { 744 pub fn is_running(&mut self) -> bool {
735 self.channel.is_running() 745 self.channel.is_running()
736 } 746 }
@@ -754,7 +764,7 @@ impl<'a> Transfer<'a> {
754 764
755impl<'a> Drop for Transfer<'a> { 765impl<'a> Drop for Transfer<'a> {
756 fn drop(&mut self) { 766 fn drop(&mut self) {
757 self.request_stop(); 767 self.request_reset();
758 while self.is_running() {} 768 while self.is_running() {}
759 769
760 // "Subsequent reads and writes cannot be moved ahead of preceding reads." 770 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
@@ -901,15 +911,6 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
901 DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); 911 DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
902 } 912 }
903 913
904 /// Request the DMA to stop.
905 /// The configuration for this channel will **not be preserved**. If you need to restart the transfer
906 /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
907 ///
908 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
909 pub fn request_stop(&mut self) {
910 self.channel.request_stop()
911 }
912
913 /// Request the transfer to pause, keeping the existing configuration for this channel. 914 /// Request the transfer to pause, keeping the existing configuration for this channel.
914 /// To restart the transfer, call [`start`](Self::start) again. 915 /// To restart the transfer, call [`start`](Self::start) again.
915 /// 916 ///
@@ -918,10 +919,23 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
918 self.channel.request_pause() 919 self.channel.request_pause()
919 } 920 }
920 921
922 /// Request the transfer to resume after having been paused.
923 pub fn request_resume(&mut self) {
924 self.channel.request_resume()
925 }
926
927 /// Request the DMA to reset.
928 ///
929 /// The configuration for this channel will **not be preserved**. If you need to restart the transfer
930 /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
931 pub fn request_reset(&mut self) {
932 self.channel.request_reset()
933 }
934
921 /// Return whether DMA is still running. 935 /// Return whether DMA is still running.
922 /// 936 ///
923 /// If this returns `false`, it can be because either the transfer finished, or 937 /// If this returns `false`, it can be because either the transfer finished, or
924 /// it was requested to stop early with [`request_stop`](Self::request_stop). 938 /// it was requested to stop early with [`request_reset`](Self::request_reset).
925 pub fn is_running(&mut self) -> bool { 939 pub fn is_running(&mut self) -> bool {
926 self.channel.is_running() 940 self.channel.is_running()
927 } 941 }
@@ -934,7 +948,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
934 /// This is designed to be used with streaming input data such as the 948 /// This is designed to be used with streaming input data such as the
935 /// I2S/SAI or ADC. 949 /// I2S/SAI or ADC.
936 /// 950 ///
937 /// When using the UART, you probably want `request_stop()`. 951 /// When using the UART, you probably want `request_reset()`.
938 pub async fn stop(&mut self) { 952 pub async fn stop(&mut self) {
939 self.channel.disable_circular_mode(); 953 self.channel.disable_circular_mode();
940 //wait until cr.susp reads as true 954 //wait until cr.susp reads as true
@@ -948,7 +962,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
948 962
949impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { 963impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> {
950 fn drop(&mut self) { 964 fn drop(&mut self) {
951 self.request_stop(); 965 self.request_reset();
952 while self.is_running() {} 966 while self.is_running() {}
953 967
954 // "Subsequent reads and writes cannot be moved ahead of preceding reads." 968 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
@@ -1058,8 +1072,8 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
1058 /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. 1072 /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
1059 /// 1073 ///
1060 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. 1074 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
1061 pub fn request_stop(&mut self) { 1075 pub fn request_reset(&mut self) {
1062 self.channel.request_stop() 1076 self.channel.request_reset()
1063 } 1077 }
1064 1078
1065 /// Request the transfer to pause, keeping the existing configuration for this channel. 1079 /// Request the transfer to pause, keeping the existing configuration for this channel.
@@ -1073,7 +1087,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
1073 /// Return whether DMA is still running. 1087 /// Return whether DMA is still running.
1074 /// 1088 ///
1075 /// If this returns `false`, it can be because either the transfer finished, or 1089 /// If this returns `false`, it can be because either the transfer finished, or
1076 /// it was requested to stop early with [`request_stop`](Self::request_stop). 1090 /// it was requested to stop early with [`request_reset`](Self::request_reset).
1077 pub fn is_running(&mut self) -> bool { 1091 pub fn is_running(&mut self) -> bool {
1078 self.channel.is_running() 1092 self.channel.is_running()
1079 } 1093 }
@@ -1098,7 +1112,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
1098 1112
1099impl<'a, W: Word> Drop for WritableRingBuffer<'a, W> { 1113impl<'a, W: Word> Drop for WritableRingBuffer<'a, W> {
1100 fn drop(&mut self) { 1114 fn drop(&mut self) {
1101 self.request_stop(); 1115 self.request_reset();
1102 while self.is_running() {} 1116 while self.is_running() {}
1103 1117
1104 // "Subsequent reads and writes cannot be moved ahead of preceding reads." 1118 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs
deleted file mode 100644
index 151e4ab9f..000000000
--- a/embassy-stm32/src/dma/gpdma.rs
+++ /dev/null
@@ -1,339 +0,0 @@
1#![macro_use]
2
3use core::future::Future;
4use core::pin::Pin;
5use core::sync::atomic::{fence, Ordering};
6use core::task::{Context, Poll};
7
8use embassy_hal_internal::Peri;
9use embassy_sync::waitqueue::AtomicWaker;
10
11use super::word::{Word, WordSize};
12use super::{AnyChannel, Channel, Dir, Request, STATE};
13use crate::interrupt::typelevel::Interrupt;
14use crate::interrupt::Priority;
15use crate::pac;
16use crate::pac::gpdma::vals;
17
18pub(crate) struct ChannelInfo {
19 pub(crate) dma: pac::gpdma::Gpdma,
20 pub(crate) num: usize,
21 #[cfg(feature = "_dual-core")]
22 pub(crate) irq: pac::Interrupt,
23}
24
25/// GPDMA transfer options.
26#[derive(Debug, Copy, Clone, PartialEq, Eq)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28#[non_exhaustive]
29pub struct TransferOptions {}
30
31impl Default for TransferOptions {
32 fn default() -> Self {
33 Self {}
34 }
35}
36
37impl From<WordSize> for vals::Dw {
38 fn from(raw: WordSize) -> Self {
39 match raw {
40 WordSize::OneByte => Self::BYTE,
41 WordSize::TwoBytes => Self::HALF_WORD,
42 WordSize::FourBytes => Self::WORD,
43 }
44 }
45}
46
47pub(crate) struct ChannelState {
48 waker: AtomicWaker,
49}
50
51impl ChannelState {
52 pub(crate) const NEW: Self = Self {
53 waker: AtomicWaker::new(),
54 };
55}
56
57/// safety: must be called only once
58pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: Priority) {
59 foreach_interrupt! {
60 ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => {
61 crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority);
62 #[cfg(not(feature = "_dual-core"))]
63 crate::interrupt::typelevel::$irq::enable();
64 };
65 }
66 crate::_generated::init_gpdma();
67}
68
69impl AnyChannel {
70 /// Safety: Must be called with a matching set of parameters for a valid dma channel
71 pub(crate) unsafe fn on_irq(&self) {
72 let info = self.info();
73 #[cfg(feature = "_dual-core")]
74 {
75 use embassy_hal_internal::interrupt::InterruptExt as _;
76 info.irq.enable();
77 }
78
79 let state = &STATE[self.id as usize];
80
81 let ch = info.dma.ch(info.num);
82 let sr = ch.sr().read();
83
84 if sr.dtef() {
85 panic!(
86 "DMA: data transfer error on DMA@{:08x} channel {}",
87 info.dma.as_ptr() as u32,
88 info.num
89 );
90 }
91 if sr.usef() {
92 panic!(
93 "DMA: user settings error on DMA@{:08x} channel {}",
94 info.dma.as_ptr() as u32,
95 info.num
96 );
97 }
98
99 if sr.suspf() || sr.tcf() {
100 // disable all xxIEs to prevent the irq from firing again.
101 ch.cr().write(|_| {});
102
103 // Wake the future. It'll look at tcf and see it's set.
104 state.waker.wake();
105 }
106 }
107}
108
109/// DMA transfer.
110#[must_use = "futures do nothing unless you `.await` or poll them"]
111pub struct Transfer<'a> {
112 channel: Peri<'a, AnyChannel>,
113}
114
115impl<'a> Transfer<'a> {
116 /// Create a new read DMA transfer (peripheral to memory).
117 pub unsafe fn new_read<W: Word>(
118 channel: Peri<'a, impl Channel>,
119 request: Request,
120 peri_addr: *mut W,
121 buf: &'a mut [W],
122 options: TransferOptions,
123 ) -> Self {
124 Self::new_read_raw(channel, request, peri_addr, buf, options)
125 }
126
127 /// Create a new read DMA transfer (peripheral to memory), using raw pointers.
128 pub unsafe fn new_read_raw<MW: Word, PW: Word>(
129 channel: Peri<'a, impl Channel>,
130 request: Request,
131 peri_addr: *mut PW,
132 buf: *mut [MW],
133 options: TransferOptions,
134 ) -> Self {
135 Self::new_inner(
136 channel.into(),
137 request,
138 Dir::PeripheralToMemory,
139 peri_addr as *const u32,
140 buf as *mut MW as *mut u32,
141 buf.len(),
142 true,
143 PW::size(),
144 MW::size(),
145 options,
146 )
147 }
148
149 /// Create a new write DMA transfer (memory to peripheral).
150 pub unsafe fn new_write<MW: Word, PW: Word>(
151 channel: Peri<'a, impl Channel>,
152 request: Request,
153 buf: &'a [MW],
154 peri_addr: *mut PW,
155 options: TransferOptions,
156 ) -> Self {
157 Self::new_write_raw(channel, request, buf, peri_addr, options)
158 }
159
160 /// Create a new write DMA transfer (memory to peripheral), using raw pointers.
161 pub unsafe fn new_write_raw<MW: Word, PW: Word>(
162 channel: Peri<'a, impl Channel>,
163 request: Request,
164 buf: *const [MW],
165 peri_addr: *mut PW,
166 options: TransferOptions,
167 ) -> Self {
168 Self::new_inner(
169 channel.into(),
170 request,
171 Dir::MemoryToPeripheral,
172 peri_addr as *const u32,
173 buf as *const MW as *mut u32,
174 buf.len(),
175 true,
176 MW::size(),
177 PW::size(),
178 options,
179 )
180 }
181
182 /// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly.
183 pub unsafe fn new_write_repeated<MW: Word, PW: Word>(
184 channel: Peri<'a, impl Channel>,
185 request: Request,
186 repeated: &'a MW,
187 count: usize,
188 peri_addr: *mut PW,
189 options: TransferOptions,
190 ) -> Self {
191 Self::new_inner(
192 channel.into(),
193 request,
194 Dir::MemoryToPeripheral,
195 peri_addr as *const u32,
196 repeated as *const MW as *mut u32,
197 count,
198 false,
199 MW::size(),
200 PW::size(),
201 options,
202 )
203 }
204
205 unsafe fn new_inner(
206 channel: Peri<'a, AnyChannel>,
207 request: Request,
208 dir: Dir,
209 peri_addr: *const u32,
210 mem_addr: *mut u32,
211 mem_len: usize,
212 incr_mem: bool,
213 data_size: WordSize,
214 dst_size: WordSize,
215 _options: TransferOptions,
216 ) -> Self {
217 // BNDT is specified as bytes, not as number of transfers.
218 let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else {
219 panic!("DMA transfers may not be larger than 65535 bytes.");
220 };
221
222 let info = channel.info();
223 let ch = info.dma.ch(info.num);
224
225 // "Preceding reads and writes cannot be moved past subsequent writes."
226 fence(Ordering::SeqCst);
227
228 let this = Self { channel };
229
230 ch.cr().write(|w| w.set_reset(true));
231 ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs
232 ch.llr().write(|_| {}); // no linked list
233 ch.tr1().write(|w| {
234 w.set_sdw(data_size.into());
235 w.set_ddw(dst_size.into());
236 w.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem);
237 w.set_dinc(dir == Dir::PeripheralToMemory && incr_mem);
238 });
239 ch.tr2().write(|w| {
240 w.set_dreq(match dir {
241 Dir::MemoryToPeripheral => vals::Dreq::DESTINATION_PERIPHERAL,
242 Dir::PeripheralToMemory => vals::Dreq::SOURCE_PERIPHERAL,
243 });
244 w.set_reqsel(request);
245 });
246 ch.tr3().write(|_| {}); // no address offsets.
247 ch.br1().write(|w| w.set_bndt(bndt));
248
249 match dir {
250 Dir::MemoryToPeripheral => {
251 ch.sar().write_value(mem_addr as _);
252 ch.dar().write_value(peri_addr as _);
253 }
254 Dir::PeripheralToMemory => {
255 ch.sar().write_value(peri_addr as _);
256 ch.dar().write_value(mem_addr as _);
257 }
258 }
259
260 ch.cr().write(|w| {
261 // Enable interrupts
262 w.set_tcie(true);
263 w.set_useie(true);
264 w.set_dteie(true);
265 w.set_suspie(true);
266
267 // Start it
268 w.set_en(true);
269 });
270
271 this
272 }
273
274 /// Request the transfer to stop.
275 ///
276 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
277 pub fn request_stop(&mut self) {
278 let info = self.channel.info();
279 let ch = info.dma.ch(info.num);
280
281 ch.cr().modify(|w| w.set_susp(true))
282 }
283
284 /// Return whether this transfer is still running.
285 ///
286 /// If this returns `false`, it can be because either the transfer finished, or
287 /// it was requested to stop early with [`request_stop`](Self::request_stop).
288 pub fn is_running(&mut self) -> bool {
289 let info = self.channel.info();
290 let ch = info.dma.ch(info.num);
291
292 let sr = ch.sr().read();
293 !sr.tcf() && !sr.suspf()
294 }
295
296 /// Gets the total remaining transfers for the channel
297 /// Note: this will be zero for transfers that completed without cancellation.
298 pub fn get_remaining_transfers(&self) -> u16 {
299 let info = self.channel.info();
300 let ch = info.dma.ch(info.num);
301
302 ch.br1().read().bndt()
303 }
304
305 /// Blocking wait until the transfer finishes.
306 pub fn blocking_wait(mut self) {
307 while self.is_running() {}
308
309 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
310 fence(Ordering::SeqCst);
311
312 core::mem::forget(self);
313 }
314}
315
316impl<'a> Drop for Transfer<'a> {
317 fn drop(&mut self) {
318 self.request_stop();
319 while self.is_running() {}
320
321 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
322 fence(Ordering::SeqCst);
323 }
324}
325
326impl<'a> Unpin for Transfer<'a> {}
327impl<'a> Future for Transfer<'a> {
328 type Output = ();
329 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
330 let state = &STATE[self.channel.id as usize];
331 state.waker.register(cx.waker());
332
333 if self.is_running() {
334 Poll::Pending
335 } else {
336 Poll::Ready(())
337 }
338 }
339}
diff --git a/embassy-stm32/src/dma/gpdma/linked_list.rs b/embassy-stm32/src/dma/gpdma/linked_list.rs
new file mode 100644
index 000000000..f7c1fbbed
--- /dev/null
+++ b/embassy-stm32/src/dma/gpdma/linked_list.rs
@@ -0,0 +1,267 @@
1//! Implementation of the GPDMA linked list and linked list items.
2#![macro_use]
3
4use stm32_metapac::gpdma::regs;
5use stm32_metapac::gpdma::vals::Dreq;
6
7use crate::dma::word::{Word, WordSize};
8use crate::dma::{Dir, Request};
9
10/// The mode in which to run the linked list.
11#[derive(Debug)]
12pub enum RunMode {
13 /// List items are not linked together.
14 Unlinked,
15 /// The list is linked sequentially and only run once.
16 Once,
17 /// The list is linked sequentially, and the end of the list is linked to the beginning.
18 Circular,
19}
20
21/// A linked-list item for linear GPDMA transfers.
22///
23/// Also works for 2D-capable GPDMA channels, but does not use 2D capabilities.
24#[derive(Debug, Copy, Clone, Default)]
25#[repr(C)]
26pub struct LinearItem {
27 /// Transfer register 1.
28 pub tr1: regs::ChTr1,
29 /// Transfer register 2.
30 pub tr2: regs::ChTr2,
31 /// Block register 2.
32 pub br1: regs::ChBr1,
33 /// Source address register.
34 pub sar: u32,
35 /// Destination address register.
36 pub dar: u32,
37 /// Linked-list address register.
38 pub llr: regs::ChLlr,
39}
40
41impl LinearItem {
42 /// Create a new read DMA transfer (peripheral to memory).
43 pub unsafe fn new_read<'d, W: Word>(request: Request, peri_addr: *mut W, buf: &'d mut [W]) -> Self {
44 Self::new_inner(
45 request,
46 Dir::PeripheralToMemory,
47 peri_addr as *const u32,
48 buf as *mut [W] as *mut W as *mut u32,
49 buf.len(),
50 true,
51 W::size(),
52 W::size(),
53 )
54 }
55
56 /// Create a new write DMA transfer (memory to peripheral).
57 pub unsafe fn new_write<'d, MW: Word, PW: Word>(request: Request, buf: &'d [MW], peri_addr: *mut PW) -> Self {
58 Self::new_inner(
59 request,
60 Dir::MemoryToPeripheral,
61 peri_addr as *const u32,
62 buf as *const [MW] as *const MW as *mut u32,
63 buf.len(),
64 true,
65 MW::size(),
66 PW::size(),
67 )
68 }
69
70 unsafe fn new_inner(
71 request: Request,
72 dir: Dir,
73 peri_addr: *const u32,
74 mem_addr: *mut u32,
75 mem_len: usize,
76 incr_mem: bool,
77 data_size: WordSize,
78 dst_size: WordSize,
79 ) -> Self {
80 // BNDT is specified as bytes, not as number of transfers.
81 let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else {
82 panic!("DMA transfers may not be larger than 65535 bytes.");
83 };
84
85 let mut br1 = regs::ChBr1(0);
86 br1.set_bndt(bndt);
87
88 let mut tr1 = regs::ChTr1(0);
89 tr1.set_sdw(data_size.into());
90 tr1.set_ddw(dst_size.into());
91 tr1.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem);
92 tr1.set_dinc(dir == Dir::PeripheralToMemory && incr_mem);
93
94 let mut tr2 = regs::ChTr2(0);
95 tr2.set_dreq(match dir {
96 Dir::MemoryToPeripheral => Dreq::DESTINATION_PERIPHERAL,
97 Dir::PeripheralToMemory => Dreq::SOURCE_PERIPHERAL,
98 });
99 tr2.set_reqsel(request);
100
101 let (sar, dar) = match dir {
102 Dir::MemoryToPeripheral => (mem_addr as _, peri_addr as _),
103 Dir::PeripheralToMemory => (peri_addr as _, mem_addr as _),
104 };
105
106 let llr = regs::ChLlr(0);
107
108 Self {
109 tr1,
110 tr2,
111 br1,
112 sar,
113 dar,
114 llr,
115 }
116 }
117
118 /// Link to the next linear item at the given address.
119 ///
120 /// Enables channel update bits.
121 fn link_to(&mut self, next: u16) {
122 let mut llr = regs::ChLlr(0);
123
124 llr.set_ut1(true);
125 llr.set_ut2(true);
126 llr.set_ub1(true);
127 llr.set_usa(true);
128 llr.set_uda(true);
129 llr.set_ull(true);
130
131 // Lower two bits are ignored: 32 bit aligned.
132 llr.set_la(next >> 2);
133
134 self.llr = llr;
135 }
136
137 /// Unlink the next linear item.
138 ///
139 /// Disables channel update bits.
140 fn unlink(&mut self) {
141 self.llr = regs::ChLlr(0);
142 }
143
144 /// The item's transfer count in number of words.
145 fn transfer_count(&self) -> usize {
146 let word_size: WordSize = self.tr1.ddw().into();
147 self.br1.bndt() as usize / word_size.bytes()
148 }
149}
150
151/// A table of linked list items.
152#[repr(C)]
153pub struct Table<const ITEM_COUNT: usize> {
154 /// The items.
155 pub items: [LinearItem; ITEM_COUNT],
156}
157
158impl<const ITEM_COUNT: usize> Table<ITEM_COUNT> {
159 /// Create a new table.
160 pub fn new(items: [LinearItem; ITEM_COUNT]) -> Self {
161 assert!(!items.is_empty());
162
163 Self { items }
164 }
165
166 /// Create a ping-pong linked-list table.
167 ///
168 /// This uses two linked-list items, one for each half of the buffer.
169 pub unsafe fn new_ping_pong<W: Word>(
170 request: Request,
171 peri_addr: *mut W,
172 buffer: &mut [W],
173 direction: Dir,
174 ) -> Table<2> {
175 // Buffer halves should be the same length.
176 let half_len = buffer.len() / 2;
177 assert_eq!(half_len * 2, buffer.len());
178
179 let items = match direction {
180 Dir::MemoryToPeripheral => [
181 LinearItem::new_write(request, &mut buffer[..half_len], peri_addr),
182 LinearItem::new_write(request, &mut buffer[half_len..], peri_addr),
183 ],
184 Dir::PeripheralToMemory => [
185 LinearItem::new_read(request, peri_addr, &mut buffer[..half_len]),
186 LinearItem::new_read(request, peri_addr, &mut buffer[half_len..]),
187 ],
188 };
189
190 Table::new(items)
191 }
192
193 /// Link the table as given by the run mode.
194 pub fn link(&mut self, run_mode: RunMode) {
195 if matches!(run_mode, RunMode::Once | RunMode::Circular) {
196 self.link_sequential();
197 }
198
199 if matches!(run_mode, RunMode::Circular) {
200 self.link_repeat();
201 }
202 }
203
204 /// The number of linked list items.
205 pub fn len(&self) -> usize {
206 self.items.len()
207 }
208
209 /// The total transfer count of the table in number of words.
210 pub fn transfer_count(&self) -> usize {
211 let mut count = 0;
212 for item in self.items {
213 count += item.transfer_count() as usize
214 }
215
216 count
217 }
218
219 /// Link items of given indices together: first -> second.
220 pub fn link_indices(&mut self, first: usize, second: usize) {
221 assert!(first < self.len());
222 assert!(second < self.len());
223
224 let second_item = self.offset_address(second);
225 self.items[first].link_to(second_item);
226 }
227
228 /// Link items sequentially.
229 pub fn link_sequential(&mut self) {
230 if self.len() > 1 {
231 for index in 0..(self.items.len() - 1) {
232 let next = self.offset_address(index + 1);
233 self.items[index].link_to(next);
234 }
235 }
236 }
237
238 /// Link last to first item.
239 pub fn link_repeat(&mut self) {
240 let first_address = self.offset_address(0);
241 self.items.last_mut().unwrap().link_to(first_address);
242 }
243
244 /// Unlink all items.
245 pub fn unlink(&mut self) {
246 for item in self.items.iter_mut() {
247 item.unlink();
248 }
249 }
250
251 /// Linked list base address (upper 16 address bits).
252 pub fn base_address(&self) -> u16 {
253 ((&raw const self.items as u32) >> 16) as _
254 }
255
256 /// Linked list offset address (lower 16 address bits) at the selected index.
257 pub fn offset_address(&self, index: usize) -> u16 {
258 assert!(self.items.len() > index);
259
260 let address = &raw const self.items[index] as _;
261
262 // Ensure 32 bit address alignment.
263 assert_eq!(address & 0b11, 0);
264
265 address
266 }
267}
diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs
new file mode 100644
index 000000000..4a14c2a8e
--- /dev/null
+++ b/embassy-stm32/src/dma/gpdma/mod.rs
@@ -0,0 +1,699 @@
1#![macro_use]
2
3use core::future::Future;
4use core::pin::Pin;
5use core::sync::atomic::{fence, AtomicUsize, Ordering};
6use core::task::{Context, Poll};
7
8use embassy_hal_internal::Peri;
9use embassy_sync::waitqueue::AtomicWaker;
10use linked_list::Table;
11
12use super::word::{Word, WordSize};
13use super::{AnyChannel, Channel, Dir, Request, STATE};
14use crate::interrupt::typelevel::Interrupt;
15use crate::pac;
16use crate::pac::gpdma::vals;
17
18pub mod linked_list;
19pub mod ringbuffered;
20
21pub(crate) struct ChannelInfo {
22 pub(crate) dma: pac::gpdma::Gpdma,
23 pub(crate) num: usize,
24 #[cfg(feature = "_dual-core")]
25 pub(crate) irq: pac::Interrupt,
26}
27
28/// DMA request priority
29#[derive(Debug, Copy, Clone, PartialEq, Eq)]
30#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31pub enum Priority {
32 /// Low Priority
33 Low,
34 /// Medium Priority
35 Medium,
36 /// High Priority
37 High,
38 /// Very High Priority
39 VeryHigh,
40}
41
42impl From<Priority> for pac::gpdma::vals::Prio {
43 fn from(value: Priority) -> Self {
44 match value {
45 Priority::Low => pac::gpdma::vals::Prio::LOW_WITH_LOWH_WEIGHT,
46 Priority::Medium => pac::gpdma::vals::Prio::LOW_WITH_MID_WEIGHT,
47 Priority::High => pac::gpdma::vals::Prio::LOW_WITH_HIGH_WEIGHT,
48 Priority::VeryHigh => pac::gpdma::vals::Prio::HIGH,
49 }
50 }
51}
52
53/// GPDMA transfer options.
54#[derive(Debug, Copy, Clone, PartialEq, Eq)]
55#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56#[non_exhaustive]
57pub struct TransferOptions {
58 /// Request priority level.
59 pub priority: Priority,
60 /// Enable half transfer interrupt.
61 pub half_transfer_ir: bool,
62 /// Enable transfer complete interrupt.
63 pub complete_transfer_ir: bool,
64}
65
66impl Default for TransferOptions {
67 fn default() -> Self {
68 Self {
69 priority: Priority::VeryHigh,
70 half_transfer_ir: false,
71 complete_transfer_ir: true,
72 }
73 }
74}
75
76impl From<WordSize> for vals::Dw {
77 fn from(raw: WordSize) -> Self {
78 match raw {
79 WordSize::OneByte => Self::BYTE,
80 WordSize::TwoBytes => Self::HALF_WORD,
81 WordSize::FourBytes => Self::WORD,
82 }
83 }
84}
85
86impl From<vals::Dw> for WordSize {
87 fn from(raw: vals::Dw) -> Self {
88 match raw {
89 vals::Dw::BYTE => Self::OneByte,
90 vals::Dw::HALF_WORD => Self::TwoBytes,
91 vals::Dw::WORD => Self::FourBytes,
92 _ => panic!("Invalid word size"),
93 }
94 }
95}
96
97pub(crate) struct LLiState {
98 /// The number of linked-list items.
99 count: AtomicUsize,
100 /// The index of the current linked-list item.
101 index: AtomicUsize,
102 /// The total transfer count of all linked-list items in number of words.
103 transfer_count: AtomicUsize,
104}
105
106pub(crate) struct ChannelState {
107 waker: AtomicWaker,
108 complete_count: AtomicUsize,
109 lli_state: LLiState,
110}
111
112impl ChannelState {
113 pub(crate) const NEW: Self = Self {
114 waker: AtomicWaker::new(),
115 complete_count: AtomicUsize::new(0),
116
117 lli_state: LLiState {
118 count: AtomicUsize::new(0),
119 index: AtomicUsize::new(0),
120 transfer_count: AtomicUsize::new(0),
121 },
122 };
123}
124
125/// safety: must be called only once
126pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: crate::interrupt::Priority) {
127 foreach_interrupt! {
128 ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => {
129 crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority);
130 #[cfg(not(feature = "_dual-core"))]
131 crate::interrupt::typelevel::$irq::enable();
132 };
133 }
134 crate::_generated::init_gpdma();
135}
136
137impl AnyChannel {
138 /// Safety: Must be called with a matching set of parameters for a valid dma channel
139 pub(crate) unsafe fn on_irq(&self) {
140 let info = self.info();
141 #[cfg(feature = "_dual-core")]
142 {
143 use embassy_hal_internal::interrupt::InterruptExt as _;
144 info.irq.enable();
145 }
146
147 let state = &STATE[self.id as usize];
148
149 let ch = info.dma.ch(info.num);
150 let sr = ch.sr().read();
151
152 if sr.dtef() {
153 panic!(
154 "DMA: data transfer error on DMA@{:08x} channel {}",
155 info.dma.as_ptr() as u32,
156 info.num
157 );
158 }
159 if sr.usef() {
160 panic!(
161 "DMA: user settings error on DMA@{:08x} channel {}",
162 info.dma.as_ptr() as u32,
163 info.num
164 );
165 }
166 if sr.ulef() {
167 panic!(
168 "DMA: link transfer error on DMA@{:08x} channel {}",
169 info.dma.as_ptr() as u32,
170 info.num
171 );
172 }
173
174 if sr.htf() {
175 ch.fcr().write(|w| w.set_htf(true));
176 }
177
178 if sr.tcf() {
179 ch.fcr().write(|w| w.set_tcf(true));
180
181 let lli_count = state.lli_state.count.load(Ordering::Acquire);
182 let complete = if lli_count > 0 {
183 let next_lli_index = state.lli_state.index.load(Ordering::Acquire) + 1;
184 let complete = next_lli_index >= lli_count;
185
186 state
187 .lli_state
188 .index
189 .store(if complete { 0 } else { next_lli_index }, Ordering::Release);
190
191 complete
192 } else {
193 true
194 };
195
196 if complete {
197 state.complete_count.fetch_add(1, Ordering::Release);
198 }
199 }
200
201 if sr.suspf() {
202 // Disable all xxIEs to prevent the irq from firing again.
203 ch.cr().write(|_| {});
204 }
205 state.waker.wake();
206 }
207
208 fn get_remaining_transfers(&self) -> u16 {
209 let info = self.info();
210 let ch = info.dma.ch(info.num);
211 let word_size: WordSize = ch.tr1().read().ddw().into();
212
213 ch.br1().read().bndt() / word_size.bytes() as u16
214 }
215
216 unsafe fn configure(
217 &self,
218 request: Request,
219 dir: Dir,
220 peri_addr: *const u32,
221 mem_addr: *mut u32,
222 mem_len: usize,
223 incr_mem: bool,
224 data_size: WordSize,
225 dst_size: WordSize,
226 options: TransferOptions,
227 ) {
228 // BNDT is specified as bytes, not as number of transfers.
229 let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else {
230 panic!("DMA transfers may not be larger than 65535 bytes.");
231 };
232
233 let info = self.info();
234 let ch = info.dma.ch(info.num);
235
236 // "Preceding reads and writes cannot be moved past subsequent writes."
237 fence(Ordering::SeqCst);
238
239 ch.cr().write(|w| w.set_reset(true));
240 ch.fcr().write(|w| {
241 // Clear all irqs
242 w.set_dtef(true);
243 w.set_htf(true);
244 w.set_suspf(true);
245 w.set_tcf(true);
246 w.set_tof(true);
247 w.set_ulef(true);
248 w.set_usef(true);
249 });
250 ch.llr().write(|_| {}); // no linked list
251 ch.tr1().write(|w| {
252 w.set_sdw(data_size.into());
253 w.set_ddw(dst_size.into());
254 w.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem);
255 w.set_dinc(dir == Dir::PeripheralToMemory && incr_mem);
256 });
257 ch.tr2().write(|w| {
258 w.set_dreq(match dir {
259 Dir::MemoryToPeripheral => vals::Dreq::DESTINATION_PERIPHERAL,
260 Dir::PeripheralToMemory => vals::Dreq::SOURCE_PERIPHERAL,
261 });
262 w.set_reqsel(request);
263 });
264 ch.tr3().write(|_| {}); // no address offsets.
265 ch.br1().write(|w| w.set_bndt(bndt));
266
267 match dir {
268 Dir::MemoryToPeripheral => {
269 ch.sar().write_value(mem_addr as _);
270 ch.dar().write_value(peri_addr as _);
271 }
272 Dir::PeripheralToMemory => {
273 ch.sar().write_value(peri_addr as _);
274 ch.dar().write_value(mem_addr as _);
275 }
276 }
277
278 ch.cr().write(|w| {
279 w.set_prio(options.priority.into());
280 w.set_htie(options.half_transfer_ir);
281 w.set_tcie(options.complete_transfer_ir);
282 w.set_useie(true);
283 w.set_dteie(true);
284 w.set_suspie(true);
285 });
286
287 let state = &STATE[self.id as usize];
288 state.lli_state.count.store(0, Ordering::Relaxed);
289 state.lli_state.index.store(0, Ordering::Relaxed);
290 state.lli_state.transfer_count.store(0, Ordering::Relaxed)
291 }
292
293 /// Configure a linked-list transfer.
294 unsafe fn configure_linked_list<const ITEM_COUNT: usize>(
295 &self,
296 table: &Table<ITEM_COUNT>,
297 options: TransferOptions,
298 ) {
299 let info = self.info();
300 let ch = info.dma.ch(info.num);
301
302 // "Preceding reads and writes cannot be moved past subsequent writes."
303 fence(Ordering::SeqCst);
304
305 ch.cr().write(|w| w.set_reset(true));
306 ch.fcr().write(|w| {
307 // Clear all irqs
308 w.set_dtef(true);
309 w.set_htf(true);
310 w.set_suspf(true);
311 w.set_tcf(true);
312 w.set_tof(true);
313 w.set_ulef(true);
314 w.set_usef(true);
315 });
316 ch.lbar().write(|reg| reg.set_lba(table.base_address()));
317
318 // Empty LLI0.
319 ch.br1().write(|w| w.set_bndt(0));
320
321 // Enable all linked-list field updates.
322 ch.llr().write(|w| {
323 w.set_ut1(true);
324 w.set_ut2(true);
325 w.set_ub1(true);
326 w.set_usa(true);
327 w.set_uda(true);
328 w.set_ull(true);
329
330 // Lower two bits are ignored: 32 bit aligned.
331 w.set_la(table.offset_address(0) >> 2);
332 });
333
334 ch.tr3().write(|_| {}); // no address offsets.
335
336 ch.cr().write(|w| {
337 w.set_prio(options.priority.into());
338 w.set_htie(options.half_transfer_ir);
339 w.set_tcie(options.complete_transfer_ir);
340 w.set_useie(true);
341 w.set_uleie(true);
342 w.set_dteie(true);
343 w.set_suspie(true);
344 });
345
346 let state = &STATE[self.id as usize];
347 state.lli_state.count.store(ITEM_COUNT, Ordering::Relaxed);
348 state.lli_state.index.store(0, Ordering::Relaxed);
349 state
350 .lli_state
351 .transfer_count
352 .store(table.transfer_count(), Ordering::Relaxed)
353 }
354
355 fn start(&self) {
356 let info = self.info();
357 let ch = info.dma.ch(info.num);
358
359 ch.cr().modify(|w| w.set_en(true));
360 }
361
362 fn request_pause(&self) {
363 let info = self.info();
364 let ch = info.dma.ch(info.num);
365
366 ch.cr().modify(|w| w.set_susp(true))
367 }
368
369 fn request_resume(&self) {
370 let info = self.info();
371 let ch = info.dma.ch(info.num);
372
373 ch.cr().modify(|w| w.set_susp(false));
374 }
375
376 fn request_reset(&self) {
377 let info = self.info();
378 let ch = info.dma.ch(info.num);
379
380 self.request_pause();
381 while self.is_running() {}
382
383 ch.cr().modify(|w| w.set_reset(true));
384 }
385
386 fn is_running(&self) -> bool {
387 let info = self.info();
388 let ch = info.dma.ch(info.num);
389
390 let sr = ch.sr().read();
391
392 !sr.suspf() && !sr.idlef()
393 }
394
395 fn poll_stop(&self) -> Poll<()> {
396 use core::sync::atomic::compiler_fence;
397 compiler_fence(Ordering::SeqCst);
398
399 if !self.is_running() {
400 Poll::Ready(())
401 } else {
402 Poll::Pending
403 }
404 }
405}
406
407/// Linked-list DMA transfer.
408#[must_use = "futures do nothing unless you `.await` or poll them"]
409pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> {
410 channel: Peri<'a, AnyChannel>,
411}
412
413impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> {
414 /// Create a new linked-list transfer.
415 pub unsafe fn new_linked_list<const N: usize>(
416 channel: Peri<'a, impl Channel>,
417 table: Table<ITEM_COUNT>,
418 options: TransferOptions,
419 ) -> Self {
420 Self::new_inner_linked_list(channel.into(), table, options)
421 }
422
423 unsafe fn new_inner_linked_list(
424 channel: Peri<'a, AnyChannel>,
425 table: Table<ITEM_COUNT>,
426 options: TransferOptions,
427 ) -> Self {
428 channel.configure_linked_list(&table, options);
429 channel.start();
430
431 Self { channel }
432 }
433
434 /// Request the transfer to pause, keeping the existing configuration for this channel.
435 ///
436 /// To resume the transfer, call [`request_resume`](Self::request_resume) again.
437 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
438 pub fn request_pause(&mut self) {
439 self.channel.request_pause()
440 }
441
442 /// Request the transfer to resume after having been paused.
443 pub fn request_resume(&mut self) {
444 self.channel.request_resume()
445 }
446
447 /// Request the DMA to reset.
448 ///
449 /// The configuration for this channel will **not be preserved**. If you need to restart the transfer
450 /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
451 pub fn request_reset(&mut self) {
452 self.channel.request_reset()
453 }
454
455 /// Return whether this transfer is still running.
456 ///
457 /// If this returns `false`, it can be because either the transfer finished, or
458 /// it was requested to stop early with [`request_pause`](Self::request_pause).
459 pub fn is_running(&mut self) -> bool {
460 self.channel.is_running()
461 }
462
463 /// Gets the total remaining transfers for the channel
464 /// Note: this will be zero for transfers that completed without cancellation.
465 pub fn get_remaining_transfers(&self) -> u16 {
466 self.channel.get_remaining_transfers()
467 }
468
469 /// Blocking wait until the transfer finishes.
470 pub fn blocking_wait(mut self) {
471 while self.is_running() {}
472
473 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
474 fence(Ordering::SeqCst);
475
476 core::mem::forget(self);
477 }
478}
479
480impl<'a, const ITEM_COUNT: usize> Drop for LinkedListTransfer<'a, ITEM_COUNT> {
481 fn drop(&mut self) {
482 self.request_reset();
483
484 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
485 fence(Ordering::SeqCst);
486 }
487}
488
489impl<'a, const ITEM_COUNT: usize> Unpin for LinkedListTransfer<'a, ITEM_COUNT> {}
490impl<'a, const ITEM_COUNT: usize> Future for LinkedListTransfer<'a, ITEM_COUNT> {
491 type Output = ();
492 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
493 let state = &STATE[self.channel.id as usize];
494 state.waker.register(cx.waker());
495
496 if self.is_running() {
497 Poll::Pending
498 } else {
499 Poll::Ready(())
500 }
501 }
502}
503
504/// DMA transfer.
505#[must_use = "futures do nothing unless you `.await` or poll them"]
506pub struct Transfer<'a> {
507 channel: Peri<'a, AnyChannel>,
508}
509
510impl<'a> Transfer<'a> {
511 /// Create a new read DMA transfer (peripheral to memory).
512 pub unsafe fn new_read<W: Word>(
513 channel: Peri<'a, impl Channel>,
514 request: Request,
515 peri_addr: *mut W,
516 buf: &'a mut [W],
517 options: TransferOptions,
518 ) -> Self {
519 Self::new_read_raw(channel, request, peri_addr, buf, options)
520 }
521
522 /// Create a new read DMA transfer (peripheral to memory), using raw pointers.
523 pub unsafe fn new_read_raw<MW: Word, PW: Word>(
524 channel: Peri<'a, impl Channel>,
525 request: Request,
526 peri_addr: *mut PW,
527 buf: *mut [MW],
528 options: TransferOptions,
529 ) -> Self {
530 Self::new_inner(
531 channel.into(),
532 request,
533 Dir::PeripheralToMemory,
534 peri_addr as *const u32,
535 buf as *mut MW as *mut u32,
536 buf.len(),
537 true,
538 PW::size(),
539 MW::size(),
540 options,
541 )
542 }
543
544 /// Create a new write DMA transfer (memory to peripheral).
545 pub unsafe fn new_write<MW: Word, PW: Word>(
546 channel: Peri<'a, impl Channel>,
547 request: Request,
548 buf: &'a [MW],
549 peri_addr: *mut PW,
550 options: TransferOptions,
551 ) -> Self {
552 Self::new_write_raw(channel, request, buf, peri_addr, options)
553 }
554
555 /// Create a new write DMA transfer (memory to peripheral), using raw pointers.
556 pub unsafe fn new_write_raw<MW: Word, PW: Word>(
557 channel: Peri<'a, impl Channel>,
558 request: Request,
559 buf: *const [MW],
560 peri_addr: *mut PW,
561 options: TransferOptions,
562 ) -> Self {
563 Self::new_inner(
564 channel.into(),
565 request,
566 Dir::MemoryToPeripheral,
567 peri_addr as *const u32,
568 buf as *const MW as *mut u32,
569 buf.len(),
570 true,
571 MW::size(),
572 PW::size(),
573 options,
574 )
575 }
576
577 /// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly.
578 pub unsafe fn new_write_repeated<MW: Word, PW: Word>(
579 channel: Peri<'a, impl Channel>,
580 request: Request,
581 repeated: &'a MW,
582 count: usize,
583 peri_addr: *mut PW,
584 options: TransferOptions,
585 ) -> Self {
586 Self::new_inner(
587 channel.into(),
588 request,
589 Dir::MemoryToPeripheral,
590 peri_addr as *const u32,
591 repeated as *const MW as *mut u32,
592 count,
593 false,
594 MW::size(),
595 PW::size(),
596 options,
597 )
598 }
599
600 unsafe fn new_inner(
601 channel: Peri<'a, AnyChannel>,
602 request: Request,
603 dir: Dir,
604 peri_addr: *const u32,
605 mem_addr: *mut u32,
606 mem_len: usize,
607 incr_mem: bool,
608 data_size: WordSize,
609 peripheral_size: WordSize,
610 options: TransferOptions,
611 ) -> Self {
612 assert!(mem_len > 0 && mem_len <= 0xFFFF);
613
614 channel.configure(
615 request,
616 dir,
617 peri_addr,
618 mem_addr,
619 mem_len,
620 incr_mem,
621 data_size,
622 peripheral_size,
623 options,
624 );
625 channel.start();
626
627 Self { channel }
628 }
629
630 /// Request the transfer to pause, keeping the existing configuration for this channel.
631 /// To restart the transfer, call [`start`](Self::start) again.
632 ///
633 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
634 pub fn request_pause(&mut self) {
635 self.channel.request_pause()
636 }
637
638 /// Request the transfer to resume after being suspended.
639 pub fn request_resume(&mut self) {
640 self.channel.request_resume()
641 }
642
643 /// Request the DMA to reset.
644 ///
645 /// The configuration for this channel will **not be preserved**. If you need to restart the transfer
646 /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
647 pub fn request_reset(&mut self) {
648 self.channel.request_reset()
649 }
650
651 /// Return whether this transfer is still running.
652 ///
653 /// If this returns `false`, it can be because either the transfer finished, or
654 /// it was requested to stop early with [`request_pause`](Self::request_pause).
655 pub fn is_running(&mut self) -> bool {
656 self.channel.is_running()
657 }
658
659 /// Gets the total remaining transfers for the channel
660 /// Note: this will be zero for transfers that completed without cancellation.
661 pub fn get_remaining_transfers(&self) -> u16 {
662 self.channel.get_remaining_transfers()
663 }
664
665 /// Blocking wait until the transfer finishes.
666 pub fn blocking_wait(mut self) {
667 while self.is_running() {}
668
669 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
670 fence(Ordering::SeqCst);
671
672 core::mem::forget(self);
673 }
674}
675
676impl<'a> Drop for Transfer<'a> {
677 fn drop(&mut self) {
678 self.request_pause();
679 while self.is_running() {}
680
681 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
682 fence(Ordering::SeqCst);
683 }
684}
685
686impl<'a> Unpin for Transfer<'a> {}
687impl<'a> Future for Transfer<'a> {
688 type Output = ();
689 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
690 let state = &STATE[self.channel.id as usize];
691 state.waker.register(cx.waker());
692
693 if self.is_running() {
694 Poll::Pending
695 } else {
696 Poll::Ready(())
697 }
698 }
699}
diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs
new file mode 100644
index 000000000..9ee52193b
--- /dev/null
+++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs
@@ -0,0 +1,332 @@
1//! GPDMA ring buffer implementation.
2//!
3//! FIXME: Add request_pause functionality?
4//! FIXME: Stop the DMA, if a user does not queue new transfers (chain of linked-list items ends automatically).
5use core::future::poll_fn;
6use core::sync::atomic::{fence, Ordering};
7use core::task::Waker;
8
9use embassy_hal_internal::Peri;
10
11use super::{AnyChannel, TransferOptions, STATE};
12use crate::dma::gpdma::linked_list::{RunMode, Table};
13use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer};
14use crate::dma::word::Word;
15use crate::dma::{Channel, Dir, Request};
16
17struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>);
18
19impl<'a> DmaCtrl for DmaCtrlImpl<'a> {
20 fn get_remaining_transfers(&self) -> usize {
21 let state = &STATE[self.0.id as usize];
22 let current_remaining = self.0.get_remaining_transfers() as usize;
23
24 let lli_count = state.lli_state.count.load(Ordering::Acquire);
25
26 if lli_count > 0 {
27 // In linked-list mode, the remaining transfers are the sum of the full lengths of LLIs that follow,
28 // and the remaining transfers for the current LLI.
29 let lli_index = state.lli_state.index.load(Ordering::Acquire);
30 let single_transfer_count = state.lli_state.transfer_count.load(Ordering::Acquire) / lli_count;
31
32 (lli_count - lli_index - 1) * single_transfer_count + current_remaining
33 } else {
34 // No linked-list mode.
35 current_remaining
36 }
37 }
38
39 fn reset_complete_count(&mut self) -> usize {
40 let state = &STATE[self.0.id as usize];
41
42 state.complete_count.swap(0, Ordering::AcqRel)
43 }
44
45 fn set_waker(&mut self, waker: &Waker) {
46 STATE[self.0.id as usize].waker.register(waker);
47 }
48}
49
50/// Ringbuffer for receiving data using GPDMA linked-list mode.
51pub struct ReadableRingBuffer<'a, W: Word> {
52 channel: Peri<'a, AnyChannel>,
53 ringbuf: ReadableDmaRingBuffer<'a, W>,
54 table: Table<2>,
55 options: TransferOptions,
56}
57
58impl<'a, W: Word> ReadableRingBuffer<'a, W> {
59 /// Create a new ring buffer.
60 ///
61 /// Transfer options are applied to the individual linked list items.
62 pub unsafe fn new(
63 channel: Peri<'a, impl Channel>,
64 request: Request,
65 peri_addr: *mut W,
66 buffer: &'a mut [W],
67 options: TransferOptions,
68 ) -> Self {
69 let channel: Peri<'a, AnyChannel> = channel.into();
70 let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory);
71
72 Self {
73 channel,
74 ringbuf: ReadableDmaRingBuffer::new(buffer),
75 table,
76 options,
77 }
78 }
79
80 /// Start the ring buffer operation.
81 pub fn start(&mut self) {
82 // Apply the default configuration to the channel.
83 unsafe { self.channel.configure_linked_list(&self.table, self.options) };
84 self.table.link(RunMode::Circular);
85 self.channel.start();
86 }
87
88 /// Clear all data in the ring buffer.
89 pub fn clear(&mut self) {
90 self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow()));
91 }
92
93 /// Read elements from the ring buffer
94 /// Return a tuple of the length read and the length remaining in the buffer
95 /// If not all of the elements were read, then there will be some elements in the buffer remaining
96 /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read
97 /// Error is returned if the portion to be read was overwritten by the DMA controller.
98 pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), Error> {
99 self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf)
100 }
101
102 /// Read an exact number of elements from the ringbuffer.
103 ///
104 /// Returns the remaining number of elements available for immediate reading.
105 /// Error is returned if the portion to be read was overwritten by the DMA controller.
106 ///
107 /// Async/Wake Behavior:
108 /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point,
109 /// and when it wraps around. This means that when called with a buffer of length 'M', when this
110 /// ring buffer was created with a buffer of size 'N':
111 /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.
112 /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.
113 pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, Error> {
114 self.ringbuf
115 .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
116 .await
117 }
118
119 /// The current length of the ringbuffer
120 pub fn len(&mut self) -> Result<usize, Error> {
121 Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?)
122 }
123
124 /// The capacity of the ringbuffer
125 pub const fn capacity(&self) -> usize {
126 self.ringbuf.cap()
127 }
128
129 /// Set a waker to be woken when at least one byte is received.
130 pub fn set_waker(&mut self, waker: &Waker) {
131 DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
132 }
133
134 /// Request the transfer to pause, keeping the existing configuration for this channel.
135 ///
136 /// To resume the transfer, call [`request_resume`](Self::request_resume) again.
137 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
138 pub fn request_pause(&mut self) {
139 self.channel.request_pause()
140 }
141
142 /// Request the transfer to resume after having been paused.
143 pub fn request_resume(&mut self) {
144 self.channel.request_resume()
145 }
146
147 /// Request the DMA to reset.
148 ///
149 /// The configuration for this channel will **not be preserved**. If you need to restart the transfer
150 /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
151 pub fn request_reset(&mut self) {
152 self.channel.request_reset()
153 }
154
155 /// Return whether this transfer is still running.
156 ///
157 /// If this returns `false`, it can be because either the transfer finished, or
158 /// it was requested to stop early with [`request_pause`](Self::request_pause).
159 pub fn is_running(&mut self) -> bool {
160 self.channel.is_running()
161 }
162
163 /// Stop the DMA transfer and await until the buffer is full.
164 ///
165 /// This disables the DMA transfer's circular mode so that the transfer
166 /// stops when the buffer is full.
167 ///
168 /// This is designed to be used with streaming input data such as the
169 /// I2S/SAI or ADC.
170 pub async fn stop(&mut self) {
171 // wait until cr.susp reads as true
172 poll_fn(|cx| {
173 self.set_waker(cx.waker());
174 self.channel.poll_stop()
175 })
176 .await
177 }
178}
179
180impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> {
181 fn drop(&mut self) {
182 self.request_pause();
183 while self.is_running() {}
184
185 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
186 fence(Ordering::SeqCst);
187 }
188}
189
190/// Ringbuffer for writing data using GPDMA linked-list mode.
191pub struct WritableRingBuffer<'a, W: Word> {
192 channel: Peri<'a, AnyChannel>,
193 ringbuf: WritableDmaRingBuffer<'a, W>,
194 table: Table<2>,
195 options: TransferOptions,
196}
197
198impl<'a, W: Word> WritableRingBuffer<'a, W> {
199 /// Create a new ring buffer.
200 ///
201 /// Transfer options are applied to the individual linked list items.
202 pub unsafe fn new(
203 channel: Peri<'a, impl Channel>,
204 request: Request,
205 peri_addr: *mut W,
206 buffer: &'a mut [W],
207 options: TransferOptions,
208 ) -> Self {
209 let channel: Peri<'a, AnyChannel> = channel.into();
210 let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral);
211
212 Self {
213 channel,
214 ringbuf: WritableDmaRingBuffer::new(buffer),
215 table,
216 options,
217 }
218 }
219
220 /// Start the ring buffer operation.
221 pub fn start(&mut self) {
222 // Apply the default configuration to the channel.
223 unsafe { self.channel.configure_linked_list(&self.table, self.options) };
224 self.table.link(RunMode::Circular);
225
226 self.channel.start();
227 }
228
229 /// Clear all data in the ring buffer.
230 pub fn clear(&mut self) {
231 self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow()));
232 }
233
234 /// Write elements directly to the raw buffer.
235 /// This can be used to fill the buffer before starting the DMA transfer.
236 pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {
237 self.ringbuf.write_immediate(buf)
238 }
239
240 /// Write elements from the ring buffer
241 /// Return a tuple of the length written and the length remaining in the buffer
242 pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {
243 self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf)
244 }
245
246 /// Write an exact number of elements to the ringbuffer.
247 pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, Error> {
248 self.ringbuf
249 .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
250 .await
251 }
252
253 /// Wait for any ring buffer write error.
254 pub async fn wait_write_error(&mut self) -> Result<usize, Error> {
255 self.ringbuf
256 .wait_write_error(&mut DmaCtrlImpl(self.channel.reborrow()))
257 .await
258 }
259
260 /// The current length of the ringbuffer
261 pub fn len(&mut self) -> Result<usize, Error> {
262 Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?)
263 }
264
265 /// The capacity of the ringbuffer
266 pub const fn capacity(&self) -> usize {
267 self.ringbuf.cap()
268 }
269
270 /// Set a waker to be woken when at least one byte is received.
271 pub fn set_waker(&mut self, waker: &Waker) {
272 DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
273 }
274
275 /// Request the DMA to suspend.
276 ///
277 /// To resume the transfer, call [`request_resume`](Self::request_resume) again.
278 ///
279 /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
280 pub fn request_pause(&mut self) {
281 self.channel.request_pause()
282 }
283
284 /// Request the DMA to resume transfers after being suspended.
285 pub fn request_resume(&mut self) {
286 self.channel.request_resume()
287 }
288
289 /// Request the DMA to reset.
290 ///
291 /// The configuration for this channel will **not be preserved**. If you need to restart the transfer
292 /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.
293 pub fn request_reset(&mut self) {
294 self.channel.request_reset()
295 }
296
297 /// Return whether DMA is still running.
298 ///
299 /// If this returns `false`, it can be because either the transfer finished, or
300 /// it was requested to stop early with [`request_stop`](Self::request_stop).
301 pub fn is_running(&mut self) -> bool {
302 self.channel.is_running()
303 }
304
305 /// Stop the DMA transfer and await until the buffer is full.
306 ///
307 /// This disables the DMA transfer's circular mode so that the transfer
308 /// stops when the buffer is full.
309 ///
310 /// This is designed to be used with streaming input data such as the
311 /// I2S/SAI or ADC.
312 ///
313 /// When using the UART, you probably want `request_stop()`.
314 pub async fn stop(&mut self) {
315 // wait until cr.susp reads as true
316 poll_fn(|cx| {
317 self.set_waker(cx.waker());
318 self.channel.poll_stop()
319 })
320 .await
321 }
322}
323
324impl<'a, W: Word> Drop for WritableRingBuffer<'a, W> {
325 fn drop(&mut self) {
326 self.request_pause();
327 while self.is_running() {}
328
329 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
330 fence(Ordering::SeqCst);
331 }
332}
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs
index d3b070a6d..5989bfd7c 100644
--- a/embassy-stm32/src/dma/mod.rs
+++ b/embassy-stm32/src/dma/mod.rs
@@ -9,6 +9,8 @@ pub use dma_bdma::*;
9#[cfg(gpdma)] 9#[cfg(gpdma)]
10pub(crate) mod gpdma; 10pub(crate) mod gpdma;
11#[cfg(gpdma)] 11#[cfg(gpdma)]
12pub use gpdma::ringbuffered::*;
13#[cfg(gpdma)]
12pub use gpdma::*; 14pub use gpdma::*;
13 15
14#[cfg(dmamux)] 16#[cfg(dmamux)]
@@ -26,10 +28,13 @@ use embassy_hal_internal::{impl_peripheral, PeripheralType};
26 28
27use crate::interrupt; 29use crate::interrupt;
28 30
31/// The direction of a DMA transfer.
29#[derive(Debug, Copy, Clone, PartialEq, Eq)] 32#[derive(Debug, Copy, Clone, PartialEq, Eq)]
30#[cfg_attr(feature = "defmt", derive(defmt::Format))] 33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31enum Dir { 34pub enum Dir {
35 /// Transfer from memory to a peripheral.
32 MemoryToPeripheral, 36 MemoryToPeripheral,
37 /// Transfer from a peripheral to memory.
33 PeripheralToMemory, 38 PeripheralToMemory,
34} 39}
35 40
diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs
index 44ea497fe..659ffa9e5 100644
--- a/embassy-stm32/src/dma/ringbuffer/mod.rs
+++ b/embassy-stm32/src/dma/ringbuffer/mod.rs
@@ -1,5 +1,3 @@
1#![cfg_attr(gpdma, allow(unused))]
2
3use core::future::poll_fn; 1use core::future::poll_fn;
4use core::task::{Poll, Waker}; 2use core::task::{Poll, Waker};
5 3
@@ -285,17 +283,20 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
285 } 283 }
286 284
287 /// Write an exact number of elements to the ringbuffer. 285 /// Write an exact number of elements to the ringbuffer.
286 ///
287 /// Returns the remaining write capacity in the buffer.
288 #[allow(dead_code)]
288 pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, Error> { 289 pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, Error> {
289 let mut written_data = 0; 290 let mut written_len = 0;
290 let buffer_len = buffer.len(); 291 let buffer_len = buffer.len();
291 292
292 poll_fn(|cx| { 293 poll_fn(|cx| {
293 dma.set_waker(cx.waker()); 294 dma.set_waker(cx.waker());
294 295
295 match self.write(dma, &buffer[written_data..buffer_len]) { 296 match self.write(dma, &buffer[written_len..buffer_len]) {
296 Ok((len, remaining)) => { 297 Ok((len, remaining)) => {
297 written_data += len; 298 written_len += len;
298 if written_data == buffer_len { 299 if written_len == buffer_len {
299 Poll::Ready(Ok(remaining)) 300 Poll::Ready(Ok(remaining))
300 } else { 301 } else {
301 Poll::Pending 302 Poll::Pending
diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs
index e97ccd9d0..deda956af 100644
--- a/embassy-stm32/src/dsihost.rs
+++ b/embassy-stm32/src/dsihost.rs
@@ -78,7 +78,7 @@ impl<'d, T: Instance> DsiHost<'d, T> {
78 rcc::enable_and_reset::<T>(); 78 rcc::enable_and_reset::<T>();
79 79
80 // Set Tearing Enable pin according to CubeMx example 80 // Set Tearing Enable pin according to CubeMx example
81 te.set_as_af(te.af_num(), AfType::output(OutputType::PushPull, Speed::Low)); 81 set_as_af!(te, AfType::output(OutputType::PushPull, Speed::Low));
82 /* 82 /*
83 T::regs().wcr().modify(|w| { 83 T::regs().wcr().modify(|w| {
84 w.set_dsien(true); 84 w.set_dsien(true);
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs
index 97d7b4347..10b3a0517 100644
--- a/embassy-stm32/src/eth/mod.rs
+++ b/embassy-stm32/src/eth/mod.rs
@@ -209,19 +209,19 @@ impl SealedInstance for crate::peripherals::ETH {
209} 209}
210impl Instance for crate::peripherals::ETH {} 210impl Instance for crate::peripherals::ETH {}
211 211
212pin_trait!(RXClkPin, Instance); 212pin_trait!(RXClkPin, Instance, @A);
213pin_trait!(TXClkPin, Instance); 213pin_trait!(TXClkPin, Instance, @A);
214pin_trait!(RefClkPin, Instance); 214pin_trait!(RefClkPin, Instance, @A);
215pin_trait!(MDIOPin, Instance); 215pin_trait!(MDIOPin, Instance, @A);
216pin_trait!(MDCPin, Instance); 216pin_trait!(MDCPin, Instance, @A);
217pin_trait!(RXDVPin, Instance); 217pin_trait!(RXDVPin, Instance, @A);
218pin_trait!(CRSPin, Instance); 218pin_trait!(CRSPin, Instance, @A);
219pin_trait!(RXD0Pin, Instance); 219pin_trait!(RXD0Pin, Instance, @A);
220pin_trait!(RXD1Pin, Instance); 220pin_trait!(RXD1Pin, Instance, @A);
221pin_trait!(RXD2Pin, Instance); 221pin_trait!(RXD2Pin, Instance, @A);
222pin_trait!(RXD3Pin, Instance); 222pin_trait!(RXD3Pin, Instance, @A);
223pin_trait!(TXD0Pin, Instance); 223pin_trait!(TXD0Pin, Instance, @A);
224pin_trait!(TXD1Pin, Instance); 224pin_trait!(TXD1Pin, Instance, @A);
225pin_trait!(TXD2Pin, Instance); 225pin_trait!(TXD2Pin, Instance, @A);
226pin_trait!(TXD3Pin, Instance); 226pin_trait!(TXD3Pin, Instance, @A);
227pin_trait!(TXEnPin, Instance); 227pin_trait!(TXEnPin, Instance, @A);
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs
index b9746231f..5be1c9739 100644
--- a/embassy-stm32/src/eth/v1/mod.rs
+++ b/embassy-stm32/src/eth/v1/mod.rs
@@ -69,7 +69,7 @@ macro_rules! config_in_pins {
69 critical_section::with(|_| { 69 critical_section::with(|_| {
70 $( 70 $(
71 // TODO properly create a set_as_input function 71 // TODO properly create a set_as_input function
72 $pin.set_as_af($pin.af_num(), AfType::input(Pull::None)); 72 set_as_af!($pin, AfType::input(Pull::None));
73 )* 73 )*
74 }) 74 })
75 } 75 }
@@ -80,7 +80,7 @@ macro_rules! config_af_pins {
80 ($($pin:ident),*) => { 80 ($($pin:ident),*) => {
81 critical_section::with(|_| { 81 critical_section::with(|_| {
82 $( 82 $(
83 $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 83 set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));
84 )* 84 )*
85 }) 85 })
86 }; 86 };
@@ -91,7 +91,7 @@ macro_rules! config_pins {
91 ($($pin:ident),*) => { 91 ($($pin:ident),*) => {
92 critical_section::with(|_| { 92 critical_section::with(|_| {
93 $( 93 $(
94 $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 94 set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));
95 )* 95 )*
96 }) 96 })
97 }; 97 };
@@ -99,19 +99,19 @@ macro_rules! config_pins {
99 99
100impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { 100impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
101 /// safety: the returned instance is not leak-safe 101 /// safety: the returned instance is not leak-safe
102 pub fn new<const TX: usize, const RX: usize>( 102 pub fn new<const TX: usize, const RX: usize, #[cfg(afio)] A>(
103 queue: &'d mut PacketQueue<TX, RX>, 103 queue: &'d mut PacketQueue<TX, RX>,
104 peri: Peri<'d, T>, 104 peri: Peri<'d, T>,
105 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, 105 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,
106 ref_clk: Peri<'d, impl RefClkPin<T>>, 106 ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>,
107 mdio: Peri<'d, impl MDIOPin<T>>, 107 mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>,
108 mdc: Peri<'d, impl MDCPin<T>>, 108 mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>,
109 crs: Peri<'d, impl CRSPin<T>>, 109 crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>,
110 rx_d0: Peri<'d, impl RXD0Pin<T>>, 110 rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>,
111 rx_d1: Peri<'d, impl RXD1Pin<T>>, 111 rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>,
112 tx_d0: Peri<'d, impl TXD0Pin<T>>, 112 tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>,
113 tx_d1: Peri<'d, impl TXD1Pin<T>>, 113 tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>,
114 tx_en: Peri<'d, impl TXEnPin<T>>, 114 tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>,
115 phy: P, 115 phy: P,
116 mac_addr: [u8; 6], 116 mac_addr: [u8; 6],
117 ) -> Self { 117 ) -> Self {
@@ -289,24 +289,24 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
289 } 289 }
290 290
291 /// Create a new MII ethernet driver using 14 pins. 291 /// Create a new MII ethernet driver using 14 pins.
292 pub fn new_mii<const TX: usize, const RX: usize>( 292 pub fn new_mii<const TX: usize, const RX: usize, #[cfg(afio)] A>(
293 queue: &'d mut PacketQueue<TX, RX>, 293 queue: &'d mut PacketQueue<TX, RX>,
294 peri: Peri<'d, T>, 294 peri: Peri<'d, T>,
295 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, 295 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,
296 rx_clk: Peri<'d, impl RXClkPin<T>>, 296 rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>,
297 tx_clk: Peri<'d, impl TXClkPin<T>>, 297 tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>,
298 mdio: Peri<'d, impl MDIOPin<T>>, 298 mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>,
299 mdc: Peri<'d, impl MDCPin<T>>, 299 mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>,
300 rxdv: Peri<'d, impl RXDVPin<T>>, 300 rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>,
301 rx_d0: Peri<'d, impl RXD0Pin<T>>, 301 rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>,
302 rx_d1: Peri<'d, impl RXD1Pin<T>>, 302 rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>,
303 rx_d2: Peri<'d, impl RXD2Pin<T>>, 303 rx_d2: Peri<'d, if_afio!(impl RXD2Pin<T, A>)>,
304 rx_d3: Peri<'d, impl RXD3Pin<T>>, 304 rx_d3: Peri<'d, if_afio!(impl RXD3Pin<T, A>)>,
305 tx_d0: Peri<'d, impl TXD0Pin<T>>, 305 tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>,
306 tx_d1: Peri<'d, impl TXD1Pin<T>>, 306 tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>,
307 tx_d2: Peri<'d, impl TXD2Pin<T>>, 307 tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>,
308 tx_d3: Peri<'d, impl TXD3Pin<T>>, 308 tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>,
309 tx_en: Peri<'d, impl TXEnPin<T>>, 309 tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>,
310 phy: P, 310 phy: P,
311 mac_addr: [u8; 6], 311 mac_addr: [u8; 6],
312 ) -> Self { 312 ) -> Self {
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs
index 034c5dd88..cf7a9901b 100644
--- a/embassy-stm32/src/eth/v2/mod.rs
+++ b/embassy-stm32/src/eth/v2/mod.rs
@@ -57,7 +57,7 @@ macro_rules! config_pins {
57 critical_section::with(|_| { 57 critical_section::with(|_| {
58 $( 58 $(
59 // TODO: shouldn't some pins be configured as inputs? 59 // TODO: shouldn't some pins be configured as inputs?
60 $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 60 set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));
61 )* 61 )*
62 }) 62 })
63 }; 63 };
diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs
index 71ca775cb..ff18a8bee 100644
--- a/embassy-stm32/src/fmc.rs
+++ b/embassy-stm32/src/fmc.rs
@@ -75,7 +75,7 @@ where
75macro_rules! config_pins { 75macro_rules! config_pins {
76 ($($pin:ident),*) => { 76 ($($pin:ident),*) => {
77 $( 77 $(
78 $pin.set_as_af($pin.af_num(), AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)); 78 set_as_af!($pin, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up));
79 )* 79 )*
80 }; 80 };
81} 81}
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs
index bb37c4194..5a8d23183 100644
--- a/embassy-stm32/src/gpio.rs
+++ b/embassy-stm32/src/gpio.rs
@@ -150,9 +150,13 @@ impl<'d> Flex<'d> {
150 /// This puts the pin into the AF mode, with the requested number and AF type. This is 150 /// This puts the pin into the AF mode, with the requested number and AF type. This is
151 /// completely unchecked, it can attach the pin to literally any peripheral, so use with care. 151 /// completely unchecked, it can attach the pin to literally any peripheral, so use with care.
152 #[inline] 152 #[inline]
153 pub fn set_as_af_unchecked(&mut self, af_num: u8, af_type: AfType) { 153 pub fn set_as_af_unchecked(&mut self, #[cfg(not(afio))] af_num: u8, af_type: AfType) {
154 critical_section::with(|_| { 154 critical_section::with(|_| {
155 self.pin.set_as_af(af_num, af_type); 155 self.pin.set_as_af(
156 #[cfg(not(afio))]
157 af_num,
158 af_type,
159 );
156 }); 160 });
157 } 161 }
158 162
@@ -588,7 +592,7 @@ impl AfType {
588 592
589#[inline(never)] 593#[inline(never)]
590#[cfg(gpio_v1)] 594#[cfg(gpio_v1)]
591fn set_as_af(pin_port: u8, _af_num: u8, af_type: AfType) { 595fn set_as_af(pin_port: u8, af_type: AfType) {
592 let pin = unsafe { AnyPin::steal(pin_port) }; 596 let pin = unsafe { AnyPin::steal(pin_port) };
593 let r = pin.block(); 597 let r = pin.block();
594 let n = pin._pin() as usize; 598 let n = pin._pin() as usize;
@@ -710,6 +714,18 @@ fn get_pull(pin_port: u8) -> Pull {
710 }; 714 };
711} 715}
712 716
717#[cfg(afio)]
718/// Holds the AFIO remap value for a peripheral's pin
719pub struct AfioRemap<const V: u8>;
720
721#[cfg(afio)]
722/// Holds the AFIO remap value for a peripheral's pin
723pub struct AfioRemapBool<const V: bool>;
724
725#[cfg(afio)]
726/// Placeholder for a peripheral's pin which cannot be remapped via AFIO.
727pub struct AfioRemapNotApplicable;
728
713pub(crate) trait SealedPin { 729pub(crate) trait SealedPin {
714 fn pin_port(&self) -> u8; 730 fn pin_port(&self) -> u8;
715 731
@@ -743,8 +759,13 @@ pub(crate) trait SealedPin {
743 } 759 }
744 760
745 #[inline] 761 #[inline]
746 fn set_as_af(&self, af_num: u8, af_type: AfType) { 762 fn set_as_af(&self, #[cfg(not(afio))] af_num: u8, af_type: AfType) {
747 set_as_af(self.pin_port(), af_num, af_type) 763 set_as_af(
764 self.pin_port(),
765 #[cfg(not(afio))]
766 af_num,
767 af_type,
768 )
748 } 769 }
749 770
750 #[inline] 771 #[inline]
diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs
index 1d0594125..6fece5eb2 100644
--- a/embassy-stm32/src/hrtim/mod.rs
+++ b/embassy-stm32/src/hrtim/mod.rs
@@ -79,10 +79,7 @@ macro_rules! advanced_channel_impl {
79 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { 79 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self {
80 critical_section::with(|_| { 80 critical_section::with(|_| {
81 pin.set_low(); 81 pin.set_low();
82 pin.set_as_af( 82 set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));
83 pin.af_num(),
84 AfType::output(OutputType::PushPull, Speed::VeryHigh),
85 );
86 }); 83 });
87 PwmPin { 84 PwmPin {
88 _pin: pin.into(), 85 _pin: pin.into(),
@@ -96,10 +93,7 @@ macro_rules! advanced_channel_impl {
96 pub fn $new_chx(pin: Peri<'d, impl $complementary_pin_trait<T>>) -> Self { 93 pub fn $new_chx(pin: Peri<'d, impl $complementary_pin_trait<T>>) -> Self {
97 critical_section::with(|_| { 94 critical_section::with(|_| {
98 pin.set_low(); 95 pin.set_low();
99 pin.set_as_af( 96 set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));
100 pin.af_num(),
101 AfType::output(OutputType::PushPull, Speed::VeryHigh),
102 );
103 }); 97 });
104 ComplementaryPwmPin { 98 ComplementaryPwmPin {
105 _pin: pin.into(), 99 _pin: pin.into(),
diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs
index 62bc0e979..95d9e5099 100644
--- a/embassy-stm32/src/hspi/mod.rs
+++ b/embassy-stm32/src/hspi/mod.rs
@@ -86,6 +86,8 @@ impl Default for Config {
86} 86}
87 87
88/// HSPI transfer configuration. 88/// HSPI transfer configuration.
89#[derive(Clone, Copy)]
90#[cfg_attr(feature = "defmt", derive(defmt::Format))]
89pub struct TransferConfig { 91pub struct TransferConfig {
90 /// Instruction width (IMODE) 92 /// Instruction width (IMODE)
91 pub iwidth: HspiWidth, 93 pub iwidth: HspiWidth,
@@ -116,7 +118,7 @@ pub struct TransferConfig {
116 118
117 /// Data width (DMODE) 119 /// Data width (DMODE)
118 pub dwidth: HspiWidth, 120 pub dwidth: HspiWidth,
119 /// Data buffer 121 /// Data Double Transfer rate enable
120 pub ddtr: bool, 122 pub ddtr: bool,
121 123
122 /// Number of dummy cycles (DCYC) 124 /// Number of dummy cycles (DCYC)
@@ -395,11 +397,6 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {
395 // Configure alternate bytes 397 // Configure alternate bytes
396 if let Some(ab) = command.alternate_bytes { 398 if let Some(ab) = command.alternate_bytes {
397 T::REGS.abr().write(|v| v.set_alternate(ab)); 399 T::REGS.abr().write(|v| v.set_alternate(ab));
398 T::REGS.ccr().modify(|w| {
399 w.set_abmode(command.abwidth.into());
400 w.set_abdtr(command.abdtr);
401 w.set_absize(command.absize.into());
402 })
403 } 400 }
404 401
405 // Configure dummy cycles 402 // Configure dummy cycles
@@ -411,14 +408,14 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {
411 if let Some(data_length) = data_len { 408 if let Some(data_length) = data_len {
412 T::REGS.dlr().write(|v| { 409 T::REGS.dlr().write(|v| {
413 v.set_dl((data_length - 1) as u32); 410 v.set_dl((data_length - 1) as u32);
414 }) 411 });
415 } else { 412 } else {
416 T::REGS.dlr().write(|v| { 413 T::REGS.dlr().write(|v| {
417 v.set_dl((0) as u32); 414 v.set_dl((0) as u32);
418 }) 415 });
419 } 416 }
420 417
421 // Configure instruction/address/data modes 418 // Configure instruction/address/alternate bytes/data modes
422 T::REGS.ccr().modify(|w| { 419 T::REGS.ccr().modify(|w| {
423 w.set_imode(command.iwidth.into()); 420 w.set_imode(command.iwidth.into());
424 w.set_idtr(command.idtr); 421 w.set_idtr(command.idtr);
@@ -428,6 +425,10 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {
428 w.set_addtr(command.addtr); 425 w.set_addtr(command.addtr);
429 w.set_adsize(command.adsize.into()); 426 w.set_adsize(command.adsize.into());
430 427
428 w.set_abmode(command.abwidth.into());
429 w.set_abdtr(command.abdtr);
430 w.set_absize(command.absize.into());
431
431 w.set_dmode(command.dwidth.into()); 432 w.set_dmode(command.dwidth.into());
432 w.set_ddtr(command.ddtr); 433 w.set_ddtr(command.ddtr);
433 }); 434 });
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs
index 5fb49f943..249bac41c 100644
--- a/embassy-stm32/src/i2c/mod.rs
+++ b/embassy-stm32/src/i2c/mod.rs
@@ -149,10 +149,10 @@ pub struct I2c<'d, M: Mode, IM: MasterMode> {
149 149
150impl<'d> I2c<'d, Async, Master> { 150impl<'d> I2c<'d, Async, Master> {
151 /// Create a new I2C driver. 151 /// Create a new I2C driver.
152 pub fn new<T: Instance>( 152 pub fn new<T: Instance, #[cfg(afio)] A>(
153 peri: Peri<'d, T>, 153 peri: Peri<'d, T>,
154 scl: Peri<'d, impl SclPin<T>>, 154 scl: Peri<'d, if_afio!(impl SclPin<T, A>)>,
155 sda: Peri<'d, impl SdaPin<T>>, 155 sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>,
156 _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> 156 _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>
157 + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> 157 + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>
158 + 'd, 158 + 'd,
@@ -173,10 +173,10 @@ impl<'d> I2c<'d, Async, Master> {
173 173
174impl<'d> I2c<'d, Blocking, Master> { 174impl<'d> I2c<'d, Blocking, Master> {
175 /// Create a new blocking I2C driver. 175 /// Create a new blocking I2C driver.
176 pub fn new_blocking<T: Instance>( 176 pub fn new_blocking<T: Instance, #[cfg(afio)] A>(
177 peri: Peri<'d, T>, 177 peri: Peri<'d, T>,
178 scl: Peri<'d, impl SclPin<T>>, 178 scl: Peri<'d, if_afio!(impl SclPin<T, A>)>,
179 sda: Peri<'d, impl SdaPin<T>>, 179 sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>,
180 config: Config, 180 config: Config,
181 ) -> Self { 181 ) -> Self {
182 Self::new_inner( 182 Self::new_inner(
@@ -296,8 +296,8 @@ peri_trait!(
296 irqs: [EventInterrupt, ErrorInterrupt], 296 irqs: [EventInterrupt, ErrorInterrupt],
297); 297);
298 298
299pin_trait!(SclPin, Instance); 299pin_trait!(SclPin, Instance, @A);
300pin_trait!(SdaPin, Instance); 300pin_trait!(SdaPin, Instance, @A);
301dma_trait!(RxDma, Instance); 301dma_trait!(RxDma, Instance);
302dma_trait!(TxDma, Instance); 302dma_trait!(TxDma, Instance);
303 303
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 3b09f1b34..0bfc795ac 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -454,7 +454,8 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
454 // (START has been ACKed or last byte when 454 // (START has been ACKed or last byte when
455 // through) 455 // through)
456 if let Err(err) = self.wait_txis(timeout) { 456 if let Err(err) = self.wait_txis(timeout) {
457 if send_stop { 457 if send_stop && err != Error::Nack {
458 // STOP is sent automatically if a NACK was received
458 self.master_stop(); 459 self.master_stop();
459 } 460 }
460 return Err(err); 461 return Err(err);
@@ -548,7 +549,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
548 (idx != last_slice_index) || (slice_len > 255), 549 (idx != last_slice_index) || (slice_len > 255),
549 timeout, 550 timeout,
550 ) { 551 ) {
551 self.master_stop(); 552 if err != Error::Nack {
553 self.master_stop();
554 }
552 return Err(err); 555 return Err(err);
553 } 556 }
554 } 557 }
@@ -561,7 +564,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
561 (number != last_chunk_idx) || (idx != last_slice_index), 564 (number != last_chunk_idx) || (idx != last_slice_index),
562 timeout, 565 timeout,
563 ) { 566 ) {
564 self.master_stop(); 567 if err != Error::Nack {
568 self.master_stop();
569 }
565 return Err(err); 570 return Err(err);
566 } 571 }
567 } 572 }
@@ -571,7 +576,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
571 // (START has been ACKed or last byte when 576 // (START has been ACKed or last byte when
572 // through) 577 // through)
573 if let Err(err) = self.wait_txis(timeout) { 578 if let Err(err) = self.wait_txis(timeout) {
574 self.master_stop(); 579 if err != Error::Nack {
580 self.master_stop();
581 }
575 return Err(err); 582 return Err(err);
576 } 583 }
577 584
@@ -1276,7 +1283,7 @@ impl<'d> I2c<'d, Async, MultiMaster> {
1276 } else if isr.stopf() { 1283 } else if isr.stopf() {
1277 self.info.regs.icr().write(|reg| reg.set_stopcf(true)); 1284 self.info.regs.icr().write(|reg| reg.set_stopcf(true));
1278 if remaining_len > 0 { 1285 if remaining_len > 0 {
1279 dma_transfer.request_stop(); 1286 dma_transfer.request_pause();
1280 Poll::Ready(Ok(SendStatus::LeftoverBytes(remaining_len as usize))) 1287 Poll::Ready(Ok(SendStatus::LeftoverBytes(remaining_len as usize)))
1281 } else { 1288 } else {
1282 Poll::Ready(Ok(SendStatus::Done)) 1289 Poll::Ready(Ok(SendStatus::Done))
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs
index a51d21bb0..b6d3daf54 100644
--- a/embassy-stm32/src/i2s.rs
+++ b/embassy-stm32/src/i2s.rs
@@ -27,7 +27,8 @@ enum Function {
27 Transmit, 27 Transmit,
28 /// Receive audio data 28 /// Receive audio data
29 Receive, 29 Receive,
30 #[cfg(spi_v3)] 30 #[cfg(any(spi_v4, spi_v5))]
31
31 /// Transmit and Receive audio data 32 /// Transmit and Receive audio data
32 FullDuplex, 33 FullDuplex,
33} 34}
@@ -72,7 +73,6 @@ impl From<ringbuffer::Error> for Error {
72} 73}
73 74
74impl Standard { 75impl Standard {
75 #[cfg(any(spi_v1, spi_v3, spi_f1))]
76 const fn i2sstd(&self) -> vals::I2sstd { 76 const fn i2sstd(&self) -> vals::I2sstd {
77 match self { 77 match self {
78 Standard::Philips => vals::I2sstd::PHILIPS, 78 Standard::Philips => vals::I2sstd::PHILIPS,
@@ -83,7 +83,6 @@ impl Standard {
83 } 83 }
84 } 84 }
85 85
86 #[cfg(any(spi_v1, spi_v3, spi_f1))]
87 const fn pcmsync(&self) -> vals::Pcmsync { 86 const fn pcmsync(&self) -> vals::Pcmsync {
88 match self { 87 match self {
89 Standard::PcmLongSync => vals::Pcmsync::LONG, 88 Standard::PcmLongSync => vals::Pcmsync::LONG,
@@ -106,7 +105,6 @@ pub enum Format {
106} 105}
107 106
108impl Format { 107impl Format {
109 #[cfg(any(spi_v1, spi_v3, spi_f1))]
110 const fn datlen(&self) -> vals::Datlen { 108 const fn datlen(&self) -> vals::Datlen {
111 match self { 109 match self {
112 Format::Data16Channel16 => vals::Datlen::BITS16, 110 Format::Data16Channel16 => vals::Datlen::BITS16,
@@ -116,7 +114,6 @@ impl Format {
116 } 114 }
117 } 115 }
118 116
119 #[cfg(any(spi_v1, spi_v3, spi_f1))]
120 const fn chlen(&self) -> vals::Chlen { 117 const fn chlen(&self) -> vals::Chlen {
121 match self { 118 match self {
122 Format::Data16Channel16 => vals::Chlen::BITS16, 119 Format::Data16Channel16 => vals::Chlen::BITS16,
@@ -137,7 +134,6 @@ pub enum ClockPolarity {
137} 134}
138 135
139impl ClockPolarity { 136impl ClockPolarity {
140 #[cfg(any(spi_v1, spi_v3, spi_f1))]
141 const fn ckpol(&self) -> vals::Ckpol { 137 const fn ckpol(&self) -> vals::Ckpol {
142 match self { 138 match self {
143 ClockPolarity::IdleHigh => vals::Ckpol::IDLE_HIGH, 139 ClockPolarity::IdleHigh => vals::Ckpol::IDLE_HIGH,
@@ -241,12 +237,12 @@ pub struct I2S<'d, W: Word> {
241 237
242impl<'d, W: Word> I2S<'d, W> { 238impl<'d, W: Word> I2S<'d, W> {
243 /// Create a transmitter driver. 239 /// Create a transmitter driver.
244 pub fn new_txonly<T: Instance>( 240 pub fn new_txonly<T: Instance, #[cfg(afio)] A>(
245 peri: Peri<'d, T>, 241 peri: Peri<'d, T>,
246 sd: Peri<'d, impl MosiPin<T>>, 242 sd: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
247 ws: Peri<'d, impl WsPin<T>>, 243 ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,
248 ck: Peri<'d, impl CkPin<T>>, 244 ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,
249 mck: Peri<'d, impl MckPin<T>>, 245 mck: Peri<'d, if_afio!(impl MckPin<T, A>)>,
250 txdma: Peri<'d, impl TxDma<T>>, 246 txdma: Peri<'d, impl TxDma<T>>,
251 txdma_buf: &'d mut [W], 247 txdma_buf: &'d mut [W],
252 config: Config, 248 config: Config,
@@ -266,11 +262,11 @@ impl<'d, W: Word> I2S<'d, W> {
266 } 262 }
267 263
268 /// Create a transmitter driver without a master clock pin. 264 /// Create a transmitter driver without a master clock pin.
269 pub fn new_txonly_nomck<T: Instance>( 265 pub fn new_txonly_nomck<T: Instance, #[cfg(afio)] A>(
270 peri: Peri<'d, T>, 266 peri: Peri<'d, T>,
271 sd: Peri<'d, impl MosiPin<T>>, 267 sd: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
272 ws: Peri<'d, impl WsPin<T>>, 268 ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,
273 ck: Peri<'d, impl CkPin<T>>, 269 ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,
274 txdma: Peri<'d, impl TxDma<T>>, 270 txdma: Peri<'d, impl TxDma<T>>,
275 txdma_buf: &'d mut [W], 271 txdma_buf: &'d mut [W],
276 config: Config, 272 config: Config,
@@ -290,12 +286,12 @@ impl<'d, W: Word> I2S<'d, W> {
290 } 286 }
291 287
292 /// Create a receiver driver. 288 /// Create a receiver driver.
293 pub fn new_rxonly<T: Instance>( 289 pub fn new_rxonly<T: Instance, #[cfg(afio)] A>(
294 peri: Peri<'d, T>, 290 peri: Peri<'d, T>,
295 sd: Peri<'d, impl MisoPin<T>>, 291 sd: Peri<'d, if_afio!(impl MisoPin<T, A>)>,
296 ws: Peri<'d, impl WsPin<T>>, 292 ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,
297 ck: Peri<'d, impl CkPin<T>>, 293 ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,
298 mck: Peri<'d, impl MckPin<T>>, 294 mck: Peri<'d, if_afio!(impl MckPin<T, A>)>,
299 rxdma: Peri<'d, impl RxDma<T>>, 295 rxdma: Peri<'d, impl RxDma<T>>,
300 rxdma_buf: &'d mut [W], 296 rxdma_buf: &'d mut [W],
301 config: Config, 297 config: Config,
@@ -314,15 +310,16 @@ impl<'d, W: Word> I2S<'d, W> {
314 ) 310 )
315 } 311 }
316 312
317 #[cfg(spi_v3)] 313 #[cfg(any(spi_v4, spi_v5))]
314
318 /// Create a full duplex driver. 315 /// Create a full duplex driver.
319 pub fn new_full_duplex<T: Instance>( 316 pub fn new_full_duplex<T: Instance, #[cfg(afio)] A>(
320 peri: Peri<'d, T>, 317 peri: Peri<'d, T>,
321 txsd: Peri<'d, impl MosiPin<T>>, 318 txsd: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
322 rxsd: Peri<'d, impl MisoPin<T>>, 319 rxsd: Peri<'d, if_afio!(impl MisoPin<T, A>)>,
323 ws: Peri<'d, impl WsPin<T>>, 320 ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,
324 ck: Peri<'d, impl CkPin<T>>, 321 ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,
325 mck: Peri<'d, impl MckPin<T>>, 322 mck: Peri<'d, if_afio!(impl MckPin<T, A>)>,
326 txdma: Peri<'d, impl TxDma<T>>, 323 txdma: Peri<'d, impl TxDma<T>>,
327 txdma_buf: &'d mut [W], 324 txdma_buf: &'d mut [W],
328 rxdma: Peri<'d, impl RxDma<T>>, 325 rxdma: Peri<'d, impl RxDma<T>>,
@@ -357,7 +354,7 @@ impl<'d, W: Word> I2S<'d, W> {
357 if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { 354 if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {
358 rx_ring_buffer.start(); 355 rx_ring_buffer.start();
359 // SPIv3 clears rxfifo on SPE=0 356 // SPIv3 clears rxfifo on SPE=0
360 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 357 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
361 flush_rx_fifo(self.spi.info.regs); 358 flush_rx_fifo(self.spi.info.regs);
362 359
363 set_rxdmaen(self.spi.info.regs, true); 360 set_rxdmaen(self.spi.info.regs, true);
@@ -365,7 +362,7 @@ impl<'d, W: Word> I2S<'d, W> {
365 self.spi.info.regs.cr1().modify(|w| { 362 self.spi.info.regs.cr1().modify(|w| {
366 w.set_spe(true); 363 w.set_spe(true);
367 }); 364 });
368 #[cfg(any(spi_v3, spi_v4, spi_v5))] 365 #[cfg(any(spi_v4, spi_v5, spi_v6))]
369 self.spi.info.regs.cr1().modify(|w| { 366 self.spi.info.regs.cr1().modify(|w| {
370 w.set_cstart(true); 367 w.set_cstart(true);
371 }); 368 });
@@ -404,7 +401,7 @@ impl<'d, W: Word> I2S<'d, W> {
404 401
405 join(rx_f, tx_f).await; 402 join(rx_f, tx_f).await;
406 403
407 #[cfg(any(spi_v3, spi_v4, spi_v5))] 404 #[cfg(any(spi_v4, spi_v5, spi_v6))]
408 { 405 {
409 if let Mode::Master = self.mode { 406 if let Mode::Master = self.mode {
410 regs.cr1().modify(|w| { 407 regs.cr1().modify(|w| {
@@ -462,20 +459,20 @@ impl<'d, W: Word> I2S<'d, W> {
462 } 459 }
463 } 460 }
464 461
465 fn new_inner<T: Instance>( 462 fn new_inner<T: Instance, #[cfg(afio)] A>(
466 peri: Peri<'d, T>, 463 peri: Peri<'d, T>,
467 txsd: Option<Peri<'d, AnyPin>>, 464 txsd: Option<Peri<'d, AnyPin>>,
468 rxsd: Option<Peri<'d, AnyPin>>, 465 rxsd: Option<Peri<'d, AnyPin>>,
469 ws: Peri<'d, impl WsPin<T>>, 466 ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,
470 ck: Peri<'d, impl CkPin<T>>, 467 ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,
471 mck: Option<Peri<'d, AnyPin>>, 468 mck: Option<Peri<'d, AnyPin>>,
472 txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, 469 txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>,
473 rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, 470 rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>,
474 config: Config, 471 config: Config,
475 function: Function, 472 function: Function,
476 ) -> Self { 473 ) -> Self {
477 ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, config.gpio_speed)); 474 set_as_af!(ws, AfType::output(OutputType::PushPull, config.gpio_speed));
478 ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, config.gpio_speed)); 475 set_as_af!(ck, AfType::output(OutputType::PushPull, config.gpio_speed));
479 476
480 let spi = Spi::new_internal(peri, None, None, { 477 let spi = Spi::new_internal(peri, None, None, {
481 let mut spi_config = SpiConfig::default(); 478 let mut spi_config = SpiConfig::default();
@@ -492,103 +489,98 @@ impl<'d, W: Word> I2S<'d, W> {
492 489
493 let (odd, div) = compute_baud_rate(pclk, config.frequency, config.master_clock, config.format); 490 let (odd, div) = compute_baud_rate(pclk, config.frequency, config.master_clock, config.format);
494 491
495 #[cfg(any(spi_v1, spi_v3, spi_f1))] 492 #[cfg(any(spi_v4, spi_v5))]
496 { 493 {
497 #[cfg(spi_v3)] 494 regs.cr1().modify(|w| w.set_spe(false));
498 {
499 regs.cr1().modify(|w| w.set_spe(false));
500 495
501 reset_incompatible_bitfields::<T>(); 496 reset_incompatible_bitfields::<T>();
502 } 497 }
503 498
504 use stm32_metapac::spi::vals::{I2scfg, Odd}; 499 use stm32_metapac::spi::vals::{I2scfg, Odd};
505 500
506 // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR/SPI_I2SCFGR register to define the serial clock baud 501 // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR/SPI_I2SCFGR register to define the serial clock baud
507 // rate to reach the proper audio sample frequency. The ODD bit in the 502 // rate to reach the proper audio sample frequency. The ODD bit in the
508 // SPI_I2SPR/SPI_I2SCFGR register also has to be defined. 503 // SPI_I2SPR/SPI_I2SCFGR register also has to be defined.
509
510 // 2. Select the CKPOL bit to define the steady level for the communication clock. Set the
511 // MCKOE bit in the SPI_I2SPR/SPI_I2SCFGR register if the master clock MCK needs to be provided to
512 // the external DAC/ADC audio component (the I2SDIV and ODD values should be
513 // computed depending on the state of the MCK output, for more details refer to
514 // Section 28.4.4: Clock generator).
515
516 // 3. Set the I2SMOD bit in SPI_I2SCFGR to activate the I2S functionalities and choose the
517 // I2S standard through the I2SSTD[1:0] and PCMSYNC bits, the data length through the
518 // DATLEN[1:0] bits and the number of bits per channel by configuring the CHLEN bit.
519 // Select also the I2S master mode and direction (Transmitter or Receiver) through the
520 // I2SCFG[1:0] bits in the SPI_I2SCFGR register.
521
522 // 4. If needed, select all the potential interruption sources and the DMA capabilities by
523 // writing the SPI_CR2 register.
524
525 // 5. The I2SE bit in SPI_I2SCFGR register must be set.
526
527 let clk_reg = {
528 #[cfg(any(spi_v1, spi_f1))]
529 {
530 regs.i2spr()
531 }
532 #[cfg(spi_v3)]
533 {
534 regs.i2scfgr()
535 }
536 };
537
538 clk_reg.modify(|w| {
539 w.set_i2sdiv(div);
540 w.set_odd(match odd {
541 true => Odd::ODD,
542 false => Odd::EVEN,
543 });
544 504
545 w.set_mckoe(config.master_clock); 505 // 2. Select the CKPOL bit to define the steady level for the communication clock. Set the
506 // MCKOE bit in the SPI_I2SPR/SPI_I2SCFGR register if the master clock MCK needs to be provided to
507 // the external DAC/ADC audio component (the I2SDIV and ODD values should be
508 // computed depending on the state of the MCK output, for more details refer to
509 // Section 28.4.4: Clock generator).
510
511 // 3. Set the I2SMOD bit in SPI_I2SCFGR to activate the I2S functionalities and choose the
512 // I2S standard through the I2SSTD[1:0] and PCMSYNC bits, the data length through the
513 // DATLEN[1:0] bits and the number of bits per channel by configuring the CHLEN bit.
514 // Select also the I2S master mode and direction (Transmitter or Receiver) through the
515 // I2SCFG[1:0] bits in the SPI_I2SCFGR register.
516
517 // 4. If needed, select all the potential interruption sources and the DMA capabilities by
518 // writing the SPI_CR2 register.
519
520 // 5. The I2SE bit in SPI_I2SCFGR register must be set.
521
522 let clk_reg = {
523 #[cfg(any(spi_v1, spi_v2, spi_v3))]
524 {
525 regs.i2spr()
526 }
527 #[cfg(any(spi_v4, spi_v5))]
528 {
529 regs.i2scfgr()
530 }
531 };
532
533 clk_reg.modify(|w| {
534 w.set_i2sdiv(div);
535 w.set_odd(match odd {
536 true => Odd::ODD,
537 false => Odd::EVEN,
546 }); 538 });
547 539
548 regs.i2scfgr().modify(|w| { 540 w.set_mckoe(config.master_clock);
549 w.set_ckpol(config.clock_polarity.ckpol()); 541 });
550 542
551 w.set_i2smod(true); 543 regs.i2scfgr().modify(|w| {
544 w.set_ckpol(config.clock_polarity.ckpol());
552 545
553 w.set_i2sstd(config.standard.i2sstd()); 546 w.set_i2smod(true);
554 w.set_pcmsync(config.standard.pcmsync());
555 547
556 w.set_datlen(config.format.datlen()); 548 w.set_i2sstd(config.standard.i2sstd());
557 w.set_chlen(config.format.chlen()); 549 w.set_pcmsync(config.standard.pcmsync());
558 550
559 w.set_i2scfg(match (config.mode, function) { 551 w.set_datlen(config.format.datlen());
560 (Mode::Master, Function::Transmit) => I2scfg::MASTER_TX, 552 w.set_chlen(config.format.chlen());
561 (Mode::Master, Function::Receive) => I2scfg::MASTER_RX,
562 #[cfg(spi_v3)]
563 (Mode::Master, Function::FullDuplex) => I2scfg::MASTER_FULL_DUPLEX,
564 (Mode::Slave, Function::Transmit) => I2scfg::SLAVE_TX,
565 (Mode::Slave, Function::Receive) => I2scfg::SLAVE_RX,
566 #[cfg(spi_v3)]
567 (Mode::Slave, Function::FullDuplex) => I2scfg::SLAVE_FULL_DUPLEX,
568 });
569 553
570 #[cfg(any(spi_v1, spi_f1))] 554 w.set_i2scfg(match (config.mode, function) {
571 w.set_i2se(true); 555 (Mode::Master, Function::Transmit) => I2scfg::MASTER_TX,
556 (Mode::Master, Function::Receive) => I2scfg::MASTER_RX,
557 #[cfg(any(spi_v4, spi_v5))]
558 (Mode::Master, Function::FullDuplex) => I2scfg::MASTER_FULL_DUPLEX,
559 (Mode::Slave, Function::Transmit) => I2scfg::SLAVE_TX,
560 (Mode::Slave, Function::Receive) => I2scfg::SLAVE_RX,
561 #[cfg(any(spi_v4, spi_v5))]
562 (Mode::Slave, Function::FullDuplex) => I2scfg::SLAVE_FULL_DUPLEX,
572 }); 563 });
573 564
574 let mut opts = TransferOptions::default(); 565 #[cfg(any(spi_v1, spi_v2, spi_v3))]
575 opts.half_transfer_ir = true; 566 w.set_i2se(true);
576 567 });
577 Self { 568
578 mode: config.mode, 569 let mut opts = TransferOptions::default();
579 spi, 570 opts.half_transfer_ir = true;
580 txsd: txsd.map(|w| w.into()), 571
581 rxsd: rxsd.map(|w| w.into()), 572 Self {
582 ws: Some(ws.into()), 573 mode: config.mode,
583 ck: Some(ck.into()), 574 spi,
584 mck: mck.map(|w| w.into()), 575 txsd: txsd.map(|w| w.into()),
585 tx_ring_buffer: txdma.map(|(ch, buf)| unsafe { 576 rxsd: rxsd.map(|w| w.into()),
586 WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts) 577 ws: Some(ws.into()),
587 }), 578 ck: Some(ck.into()),
588 rx_ring_buffer: rxdma.map(|(ch, buf)| unsafe { 579 mck: mck.map(|w| w.into()),
589 ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts) 580 tx_ring_buffer: txdma
590 }), 581 .map(|(ch, buf)| unsafe { WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts) }),
591 } 582 rx_ring_buffer: rxdma
583 .map(|(ch, buf)| unsafe { ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts) }),
592 } 584 }
593 } 585 }
594} 586}
@@ -639,7 +631,8 @@ fn compute_baud_rate(i2s_clock: Hertz, request_freq: Hertz, mclk: bool, data_for
639 } 631 }
640} 632}
641 633
642#[cfg(spi_v3)] 634#[cfg(any(spi_v4, spi_v5))]
635
643// The STM32H7 reference manual specifies that any incompatible bitfields should be reset 636// The STM32H7 reference manual specifies that any incompatible bitfields should be reset
644// to their reset values while operating in I2S mode. 637// to their reset values while operating in I2S mode.
645fn reset_incompatible_bitfields<T: Instance>() { 638fn reset_incompatible_bitfields<T: Instance>() {
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 3be98c462..7e0f7884e 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -87,7 +87,7 @@ pub mod hsem;
87pub mod hspi; 87pub mod hspi;
88#[cfg(i2c)] 88#[cfg(i2c)]
89pub mod i2c; 89pub mod i2c;
90#[cfg(any(all(spi_v1, rcc_f4), spi_v3))] 90#[cfg(any(spi_v1_i2s, spi_v2_i2s, spi_v3_i2s, spi_v4_i2s, spi_v5_i2s))]
91pub mod i2s; 91pub mod i2s;
92#[cfg(stm32wb)] 92#[cfg(stm32wb)]
93pub mod ipcc; 93pub mod ipcc;
diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs
index 2f2d7ba01..96af9f4d9 100644
--- a/embassy-stm32/src/lptim/pwm.rs
+++ b/embassy-stm32/src/lptim/pwm.rs
@@ -50,10 +50,7 @@ macro_rules! channel_impl {
50 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { 50 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self {
51 critical_section::with(|_| { 51 critical_section::with(|_| {
52 pin.set_low(); 52 pin.set_low();
53 pin.set_as_af( 53 set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));
54 pin.af_num(),
55 AfType::output(OutputType::PushPull, Speed::VeryHigh),
56 );
57 }); 54 });
58 PwmPin { 55 PwmPin {
59 _pin: pin.into(), 56 _pin: pin.into(),
@@ -64,12 +61,12 @@ macro_rules! channel_impl {
64 pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T>>, pin_config: PwmPinConfig) -> Self { 61 pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T>>, pin_config: PwmPinConfig) -> Self {
65 critical_section::with(|_| { 62 critical_section::with(|_| {
66 pin.set_low(); 63 pin.set_low();
67 pin.set_as_af( 64 #[cfg(gpio_v1)]
68 pin.af_num(), 65 set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed));
69 #[cfg(gpio_v1)] 66 #[cfg(gpio_v2)]
70 AfType::output(pin_config.output_type, pin_config.speed), 67 set_as_af!(
71 #[cfg(gpio_v2)] 68 pin,
72 AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), 69 AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull)
73 ); 70 );
74 }); 71 });
75 PwmPin { 72 PwmPin {
diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs
index 3a0b490ba..22cc2e049 100644
--- a/embassy-stm32/src/macros.rs
+++ b/embassy-stm32/src/macros.rs
@@ -41,17 +41,30 @@ macro_rules! peri_trait_impl {
41} 41}
42 42
43macro_rules! pin_trait { 43macro_rules! pin_trait {
44 ($signal:ident, $instance:path $(, $mode:path)?) => { 44 ($signal:ident, $instance:path $(, $mode:path)? $(, @$afio:ident)?) => {
45 #[doc = concat!(stringify!($signal), " pin trait")] 45 #[doc = concat!(stringify!($signal), " pin trait")]
46 pub trait $signal<T: $instance $(, M: $mode)?>: crate::gpio::Pin { 46 pub trait $signal<T: $instance $(, M: $mode)? $(, #[cfg(afio)] $afio)?>: crate::gpio::Pin {
47 #[cfg(not(afio))]
47 #[doc = concat!("Get the AF number needed to use this pin as ", stringify!($signal))] 48 #[doc = concat!("Get the AF number needed to use this pin as ", stringify!($signal))]
48 fn af_num(&self) -> u8; 49 fn af_num(&self) -> u8;
50
51 #[cfg(afio)]
52 #[doc = concat!("Configures AFIO_MAPR to use this pin as ", stringify!($signal))]
53 fn afio_remap(&self);
49 } 54 }
50 }; 55 };
51} 56}
52 57
53macro_rules! pin_trait_impl { 58macro_rules! pin_trait_impl {
54 (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $af:expr) => { 59 (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $af:expr $(, $afio:path)?) => {
60 #[cfg(afio)]
61 impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)? $(, $afio)?> for crate::peripherals::$pin {
62 fn afio_remap(&self) {
63 // nothing
64 }
65 }
66
67 #[cfg(not(afio))]
55 impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$pin { 68 impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$pin {
56 fn af_num(&self) -> u8 { 69 fn af_num(&self) -> u8 {
57 $af 70 $af
@@ -60,6 +73,39 @@ macro_rules! pin_trait_impl {
60 }; 73 };
61} 74}
62 75
76#[cfg(afio)]
77macro_rules! pin_trait_afio_impl {
78 (@set mapr, $setter:ident, $val:expr) => {
79 crate::pac::AFIO.mapr().modify(|w| {
80 w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP);
81 w.$setter($val);
82 });
83 };
84 (@set mapr2, $setter:ident, $val:expr) => {
85 crate::pac::AFIO.mapr2().modify(|w| {
86 w.$setter($val);
87 });
88 };
89 (crate::$mod:ident::$trait:ident<$mode:ident>, $instance:ident, $pin:ident, {$reg:ident, $setter:ident, $type:ident, [$($val:expr),+]}) => {
90 $(
91 impl crate::$mod::$trait<crate::peripherals::$instance, crate::$mod::$mode, crate::gpio::$type<$val>> for crate::peripherals::$pin {
92 fn afio_remap(&self) {
93 pin_trait_afio_impl!(@set $reg, $setter, $val);
94 }
95 }
96 )+
97 };
98 (crate::$mod:ident::$trait:ident, $instance:ident, $pin:ident, {$reg:ident, $setter:ident, $type:ident, [$($val:expr),+]}) => {
99 $(
100 impl crate::$mod::$trait<crate::peripherals::$instance, crate::gpio::$type<$val>> for crate::peripherals::$pin {
101 fn afio_remap(&self) {
102 pin_trait_afio_impl!(@set $reg, $setter, $val);
103 }
104 }
105 )+
106 };
107}
108
63#[allow(unused_macros)] 109#[allow(unused_macros)]
64macro_rules! sel_trait_impl { 110macro_rules! sel_trait_impl {
65 (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $sel:expr) => { 111 (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $sel:expr) => {
@@ -134,7 +180,73 @@ macro_rules! new_dma {
134macro_rules! new_pin { 180macro_rules! new_pin {
135 ($name:ident, $af_type:expr) => {{ 181 ($name:ident, $af_type:expr) => {{
136 let pin = $name; 182 let pin = $name;
137 pin.set_as_af(pin.af_num(), $af_type); 183 #[cfg(afio)]
184 pin.afio_remap();
185 pin.set_as_af(
186 #[cfg(not(afio))]
187 pin.af_num(),
188 $af_type,
189 );
138 Some(pin.into()) 190 Some(pin.into())
139 }}; 191 }};
140} 192}
193
194/// Macro to configure a pin for alternate function use.
195/// For AFIO chips (STM32F1), it calls afio_remap().
196/// For non-AFIO chips, it calls set_as_af() with the pin's af_num().
197macro_rules! set_as_af {
198 ($pin:expr, $af_type:expr) => {
199 #[cfg(afio)]
200 {
201 $pin.set_as_af($af_type);
202 $pin.afio_remap();
203 }
204 #[cfg(not(afio))]
205 {
206 $pin.set_as_af($pin.af_num(), $af_type);
207 }
208 };
209}
210
211#[cfg(afio)]
212macro_rules! if_afio {
213 ($($t:tt)*) => {
214 $($t)*
215 }
216}
217#[cfg(not(afio))]
218macro_rules! if_afio {
219 (($a:ty, A)) => {
220 ($a,)
221 };
222 (($a:ty, $b:ty, A)) => {
223 ($a,$b)
224 };
225 (($a:ty, $b:ty, $c:ty, A)) => {
226 ($a,$b, $c)
227 };
228 ($type:ident<$lt:lifetime, $a:ty, $b:ty, A>) => {
229 $type<$lt, $a, $b>
230 };
231 ($type:ident<$lt:lifetime, $a:ty, $b:ty, $c:ty, A>) => {
232 $type<$lt, $a, $b, $c>
233 };
234 ($type:ident<$a:ty, A>) => {
235 $type<$a>
236 };
237 ($type:ident<$a:ty, $b:ty, A>) => {
238 $type<$a, $b>
239 };
240 ($type:ident<$a:ty, $b:ty, $c:ty, A>) => {
241 $type<$a, $b, $c>
242 };
243 (impl $trait:ident<$a:ty, A>) => {
244 impl $trait<$a>
245 };
246 (impl $trait:ident<$a:ty, $b:ty, A>) => {
247 impl $trait<$a, $b>
248 };
249 (impl $trait:ident<$a:ty, $b:ty, $c:ty, A>) => {
250 impl $trait<$a, $b, $c>
251 };
252}
diff --git a/embassy-stm32/src/ospi/enums.rs b/embassy-stm32/src/ospi/enums.rs
index 4021f7ce3..4db801752 100644
--- a/embassy-stm32/src/ospi/enums.rs
+++ b/embassy-stm32/src/ospi/enums.rs
@@ -23,6 +23,7 @@ impl Into<u8> for OspiMode {
23/// Ospi lane width 23/// Ospi lane width
24#[allow(dead_code)] 24#[allow(dead_code)]
25#[derive(Copy, Clone)] 25#[derive(Copy, Clone)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub enum OspiWidth { 27pub enum OspiWidth {
27 /// None 28 /// None
28 NONE, 29 NONE,
@@ -71,6 +72,7 @@ impl Into<bool> for FlashSelection {
71#[allow(dead_code)] 72#[allow(dead_code)]
72#[allow(missing_docs)] 73#[allow(missing_docs)]
73#[derive(Copy, Clone)] 74#[derive(Copy, Clone)]
75#[cfg_attr(feature = "defmt", derive(defmt::Format))]
74pub enum WrapSize { 76pub enum WrapSize {
75 None, 77 None,
76 _16Bytes, 78 _16Bytes,
@@ -95,6 +97,7 @@ impl Into<u8> for WrapSize {
95#[allow(missing_docs)] 97#[allow(missing_docs)]
96#[allow(dead_code)] 98#[allow(dead_code)]
97#[derive(Copy, Clone)] 99#[derive(Copy, Clone)]
100#[cfg_attr(feature = "defmt", derive(defmt::Format))]
98pub enum MemoryType { 101pub enum MemoryType {
99 Micron, 102 Micron,
100 Macronix, 103 Macronix,
@@ -120,6 +123,7 @@ impl Into<u8> for MemoryType {
120/// Ospi memory size. 123/// Ospi memory size.
121#[allow(missing_docs)] 124#[allow(missing_docs)]
122#[derive(Copy, Clone)] 125#[derive(Copy, Clone)]
126#[cfg_attr(feature = "defmt", derive(defmt::Format))]
123pub enum MemorySize { 127pub enum MemorySize {
124 _1KiB, 128 _1KiB,
125 _2KiB, 129 _2KiB,
@@ -180,6 +184,7 @@ impl Into<u8> for MemorySize {
180 184
181/// Ospi Address size 185/// Ospi Address size
182#[derive(Copy, Clone)] 186#[derive(Copy, Clone)]
187#[cfg_attr(feature = "defmt", derive(defmt::Format))]
183pub enum AddressSize { 188pub enum AddressSize {
184 /// 8-bit address 189 /// 8-bit address
185 _8Bit, 190 _8Bit,
@@ -205,6 +210,7 @@ impl Into<u8> for AddressSize {
205/// Time the Chip Select line stays high. 210/// Time the Chip Select line stays high.
206#[allow(missing_docs)] 211#[allow(missing_docs)]
207#[derive(Copy, Clone)] 212#[derive(Copy, Clone)]
213#[cfg_attr(feature = "defmt", derive(defmt::Format))]
208pub enum ChipSelectHighTime { 214pub enum ChipSelectHighTime {
209 _1Cycle, 215 _1Cycle,
210 _2Cycle, 216 _2Cycle,
@@ -234,6 +240,7 @@ impl Into<u8> for ChipSelectHighTime {
234/// FIFO threshold. 240/// FIFO threshold.
235#[allow(missing_docs)] 241#[allow(missing_docs)]
236#[derive(Copy, Clone)] 242#[derive(Copy, Clone)]
243#[cfg_attr(feature = "defmt", derive(defmt::Format))]
237pub enum FIFOThresholdLevel { 244pub enum FIFOThresholdLevel {
238 _1Bytes, 245 _1Bytes,
239 _2Bytes, 246 _2Bytes,
@@ -311,6 +318,7 @@ impl Into<u8> for FIFOThresholdLevel {
311/// Dummy cycle count 318/// Dummy cycle count
312#[allow(missing_docs)] 319#[allow(missing_docs)]
313#[derive(Copy, Clone)] 320#[derive(Copy, Clone)]
321#[cfg_attr(feature = "defmt", derive(defmt::Format))]
314pub enum DummyCycles { 322pub enum DummyCycles {
315 _0, 323 _0,
316 _1, 324 _1,
diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs
index 74edfd5e4..d93cecb69 100644
--- a/embassy-stm32/src/ospi/mod.rs
+++ b/embassy-stm32/src/ospi/mod.rs
@@ -23,6 +23,7 @@ use crate::{peripherals, Peri};
23 23
24/// OPSI driver config. 24/// OPSI driver config.
25#[derive(Clone, Copy)] 25#[derive(Clone, Copy)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct Config { 27pub struct Config {
27 /// Fifo threshold used by the peripheral to generate the interrupt indicating data 28 /// Fifo threshold used by the peripheral to generate the interrupt indicating data
28 /// or space is available in the FIFO 29 /// or space is available in the FIFO
@@ -30,7 +31,9 @@ pub struct Config {
30 /// Indicates the type of external device connected 31 /// Indicates the type of external device connected
31 pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface 32 pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface
32 /// Defines the size of the external device connected to the OSPI corresponding 33 /// Defines the size of the external device connected to the OSPI corresponding
33 /// to the number of address bits required to access the device 34 /// to the number of address bits required to access the device.
35 /// When using indirect mode, [`TransferConfig::address`] + the length of the data being read
36 /// or written must fit within the configured `device_size`, otherwise an error is returned.
34 pub device_size: MemorySize, 37 pub device_size: MemorySize,
35 /// Sets the minimum number of clock cycles that the chip select signal must be held high 38 /// Sets the minimum number of clock cycles that the chip select signal must be held high
36 /// between commands 39 /// between commands
@@ -83,6 +86,8 @@ impl Default for Config {
83} 86}
84 87
85/// OSPI transfer configuration. 88/// OSPI transfer configuration.
89#[derive(Clone, Copy)]
90#[cfg_attr(feature = "defmt", derive(defmt::Format))]
86pub struct TransferConfig { 91pub struct TransferConfig {
87 /// Instruction width (IMODE) 92 /// Instruction width (IMODE)
88 pub iwidth: OspiWidth, 93 pub iwidth: OspiWidth,
@@ -92,10 +97,11 @@ pub struct TransferConfig {
92 pub isize: AddressSize, 97 pub isize: AddressSize,
93 /// Instruction Double Transfer rate enable 98 /// Instruction Double Transfer rate enable
94 pub idtr: bool, 99 pub idtr: bool,
95
96 /// Address width (ADMODE) 100 /// Address width (ADMODE)
97 pub adwidth: OspiWidth, 101 pub adwidth: OspiWidth,
98 /// Device memory address 102 /// Device memory address.
103 /// In indirect mode, this value + the length of the data being read or written must be within
104 /// configured [`Config::device_size`], otherwise the transfer returns an error.
99 pub address: Option<u32>, 105 pub address: Option<u32>,
100 /// Number of Address Bytes 106 /// Number of Address Bytes
101 pub adsize: AddressSize, 107 pub adsize: AddressSize,
@@ -113,11 +119,16 @@ pub struct TransferConfig {
113 119
114 /// Data width (DMODE) 120 /// Data width (DMODE)
115 pub dwidth: OspiWidth, 121 pub dwidth: OspiWidth,
116 /// Data buffer 122 /// Data Double Transfer rate enable
117 pub ddtr: bool, 123 pub ddtr: bool,
118 124
119 /// Number of dummy cycles (DCYC) 125 /// Number of dummy cycles (DCYC)
120 pub dummy: DummyCycles, 126 pub dummy: DummyCycles,
127
128 /// Data strobe (DQS) management enable
129 pub dqse: bool,
130 /// Send instruction only once (SIOO) mode enable
131 pub sioo: bool,
121} 132}
122 133
123impl Default for TransferConfig { 134impl Default for TransferConfig {
@@ -142,6 +153,9 @@ impl Default for TransferConfig {
142 ddtr: false, 153 ddtr: false,
143 154
144 dummy: DummyCycles::_0, 155 dummy: DummyCycles::_0,
156
157 dqse: false,
158 sioo: true,
145 } 159 }
146 } 160 }
147} 161}
@@ -192,26 +206,27 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {
192 let reg = T::REGS; 206 let reg = T::REGS;
193 while reg.sr().read().busy() {} 207 while reg.sr().read().busy() {}
194 208
195 reg.ccr().modify(|r| { 209 if let Some(instruction) = write_config.instruction {
196 r.set_dqse(false); 210 reg.wir().write(|r| {
197 r.set_sioo(true); 211 r.set_instruction(instruction);
198 }); 212 });
213 }
199 214
200 // Set wrting configurations, there are separate registers for write configurations in memory mapped mode 215 // Set writing configurations, there are separate registers for write configurations in memory mapped mode
201 reg.wccr().modify(|w| { 216 reg.wccr().modify(|w| {
202 w.set_imode(PhaseMode::from_bits(write_config.iwidth.into())); 217 w.set_imode(PhaseMode::from_bits(write_config.iwidth.into()));
203 w.set_idtr(write_config.idtr); 218 w.set_idtr(write_config.idtr);
204 w.set_isize(SizeInBits::from_bits(write_config.isize.into())); 219 w.set_isize(SizeInBits::from_bits(write_config.isize.into()));
205 220
206 w.set_admode(PhaseMode::from_bits(write_config.adwidth.into())); 221 w.set_admode(PhaseMode::from_bits(write_config.adwidth.into()));
207 w.set_addtr(write_config.idtr); 222 w.set_addtr(write_config.addtr);
208 w.set_adsize(SizeInBits::from_bits(write_config.adsize.into())); 223 w.set_adsize(SizeInBits::from_bits(write_config.adsize.into()));
209 224
210 w.set_dmode(PhaseMode::from_bits(write_config.dwidth.into())); 225 w.set_dmode(PhaseMode::from_bits(write_config.dwidth.into()));
211 w.set_ddtr(write_config.ddtr); 226 w.set_ddtr(write_config.ddtr);
212 227
213 w.set_abmode(PhaseMode::from_bits(write_config.abwidth.into())); 228 w.set_abmode(PhaseMode::from_bits(write_config.abwidth.into()));
214 w.set_dqse(true); 229 w.set_dqse(write_config.dqse);
215 }); 230 });
216 231
217 reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into())); 232 reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into()));
@@ -442,11 +457,6 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {
442 // Configure alternate bytes 457 // Configure alternate bytes
443 if let Some(ab) = command.alternate_bytes { 458 if let Some(ab) = command.alternate_bytes {
444 T::REGS.abr().write(|v| v.set_alternate(ab)); 459 T::REGS.abr().write(|v| v.set_alternate(ab));
445 T::REGS.ccr().modify(|w| {
446 w.set_abmode(PhaseMode::from_bits(command.abwidth.into()));
447 w.set_abdtr(command.abdtr);
448 w.set_absize(SizeInBits::from_bits(command.absize.into()));
449 })
450 } 460 }
451 461
452 // Configure dummy cycles 462 // Configure dummy cycles
@@ -458,28 +468,35 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {
458 if let Some(data_length) = data_len { 468 if let Some(data_length) = data_len {
459 T::REGS.dlr().write(|v| { 469 T::REGS.dlr().write(|v| {
460 v.set_dl((data_length - 1) as u32); 470 v.set_dl((data_length - 1) as u32);
461 }) 471 });
462 } else { 472 } else {
463 T::REGS.dlr().write(|v| { 473 T::REGS.dlr().write(|v| {
464 v.set_dl((0) as u32); 474 v.set_dl((0) as u32);
465 }) 475 });
466 } 476 }
467 477
468 // Configure instruction/address/data modes 478 // Configure instruction/address/alternate bytes/data/communication modes
469 T::REGS.ccr().modify(|w| { 479 T::REGS.ccr().modify(|w| {
470 w.set_imode(PhaseMode::from_bits(command.iwidth.into())); 480 w.set_imode(PhaseMode::from_bits(command.iwidth.into()));
471 w.set_idtr(command.idtr); 481 w.set_idtr(command.idtr);
472 w.set_isize(SizeInBits::from_bits(command.isize.into())); 482 w.set_isize(SizeInBits::from_bits(command.isize.into()));
473 483
474 w.set_admode(PhaseMode::from_bits(command.adwidth.into())); 484 w.set_admode(PhaseMode::from_bits(command.adwidth.into()));
475 w.set_addtr(command.idtr); 485 w.set_addtr(command.addtr);
476 w.set_adsize(SizeInBits::from_bits(command.adsize.into())); 486 w.set_adsize(SizeInBits::from_bits(command.adsize.into()));
477 487
488 w.set_abmode(PhaseMode::from_bits(command.abwidth.into()));
489 w.set_abdtr(command.abdtr);
490 w.set_absize(SizeInBits::from_bits(command.absize.into()));
491
478 w.set_dmode(PhaseMode::from_bits(command.dwidth.into())); 492 w.set_dmode(PhaseMode::from_bits(command.dwidth.into()));
479 w.set_ddtr(command.ddtr); 493 w.set_ddtr(command.ddtr);
494
495 w.set_dqse(command.dqse);
496 w.set_sioo(command.sioo);
480 }); 497 });
481 498
482 // Set informationrequired to initiate transaction 499 // Set information required to initiate transaction
483 if let Some(instruction) = command.instruction { 500 if let Some(instruction) = command.instruction {
484 if let Some(address) = command.address { 501 if let Some(address) = command.address {
485 T::REGS.ir().write(|v| { 502 T::REGS.ir().write(|v| {
@@ -514,6 +531,18 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {
514 } 531 }
515 } 532 }
516 533
534 // The following errors set the TEF flag in OCTOSPI_SR register:
535 // - in indirect or automatic status-polling mode, when a wrong address has been programmed
536 // in OCTOSPI_AR (according to the device size defined by DEVSIZE[4:0])
537 // - in indirect mode, if the address plus the data length exceed the device size: TEF is
538 // set as soon as the access is triggered.
539 if T::REGS.sr().read().tef() {
540 // Clear the TEF register to make it ready for the next transfer.
541 T::REGS.fcr().write(|w| w.set_ctef(true));
542
543 return Err(OspiError::InvalidCommand);
544 }
545
517 Ok(()) 546 Ok(())
518 } 547 }
519 548
@@ -854,6 +883,45 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> {
854 false, 883 false,
855 ) 884 )
856 } 885 }
886
887 /// Create new blocking OSPI driver for octospi external chips with DQS support
888 pub fn new_blocking_octospi_with_dqs(
889 peri: Peri<'d, T>,
890 sck: Peri<'d, impl SckPin<T>>,
891 d0: Peri<'d, impl D0Pin<T>>,
892 d1: Peri<'d, impl D1Pin<T>>,
893 d2: Peri<'d, impl D2Pin<T>>,
894 d3: Peri<'d, impl D3Pin<T>>,
895 d4: Peri<'d, impl D4Pin<T>>,
896 d5: Peri<'d, impl D5Pin<T>>,
897 d6: Peri<'d, impl D6Pin<T>>,
898 d7: Peri<'d, impl D7Pin<T>>,
899 nss: Peri<'d, impl NSSPin<T>>,
900 dqs: Peri<'d, impl DQSPin<T>>,
901 config: Config,
902 ) -> Self {
903 Self::new_inner(
904 peri,
905 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
906 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
907 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
908 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
909 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
910 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
911 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
912 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
913 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
914 new_pin!(
915 nss,
916 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
917 ),
918 new_pin!(dqs, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
919 None,
920 config,
921 OspiWidth::OCTO,
922 false,
923 )
924 }
857} 925}
858 926
859impl<'d, T: Instance> Ospi<'d, T, Async> { 927impl<'d, T: Instance> Ospi<'d, T, Async> {
@@ -1036,6 +1104,46 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1036 ) 1104 )
1037 } 1105 }
1038 1106
1107 /// Create new blocking OSPI driver for octospi external chips with DQS support
1108 pub fn new_octospi_with_dqs(
1109 peri: Peri<'d, T>,
1110 sck: Peri<'d, impl SckPin<T>>,
1111 d0: Peri<'d, impl D0Pin<T>>,
1112 d1: Peri<'d, impl D1Pin<T>>,
1113 d2: Peri<'d, impl D2Pin<T>>,
1114 d3: Peri<'d, impl D3Pin<T>>,
1115 d4: Peri<'d, impl D4Pin<T>>,
1116 d5: Peri<'d, impl D5Pin<T>>,
1117 d6: Peri<'d, impl D6Pin<T>>,
1118 d7: Peri<'d, impl D7Pin<T>>,
1119 nss: Peri<'d, impl NSSPin<T>>,
1120 dqs: Peri<'d, impl DQSPin<T>>,
1121 dma: Peri<'d, impl OctoDma<T>>,
1122 config: Config,
1123 ) -> Self {
1124 Self::new_inner(
1125 peri,
1126 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1127 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1128 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1129 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1130 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1131 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1132 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1133 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1134 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1135 new_pin!(
1136 nss,
1137 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
1138 ),
1139 new_pin!(dqs, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1140 new_dma!(dma),
1141 config,
1142 OspiWidth::OCTO,
1143 false,
1144 )
1145 }
1146
1039 /// Blocking read with DMA transfer 1147 /// Blocking read with DMA transfer
1040 pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> { 1148 pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> {
1041 if buf.is_empty() { 1149 if buf.is_empty() {
@@ -1090,16 +1198,19 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1090 .cr() 1198 .cr()
1091 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); 1199 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));
1092 1200
1093 let transfer = unsafe { 1201 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.
1094 self.dma 1202 for chunk in buf.chunks(0xFFFF) {
1095 .as_mut() 1203 let transfer = unsafe {
1096 .unwrap() 1204 self.dma
1097 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) 1205 .as_mut()
1098 }; 1206 .unwrap()
1207 .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())
1208 };
1099 1209
1100 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1210 T::REGS.cr().modify(|w| w.set_dmaen(true));
1101 1211
1102 transfer.blocking_wait(); 1212 transfer.blocking_wait();
1213 }
1103 1214
1104 finish_dma(T::REGS); 1215 finish_dma(T::REGS);
1105 1216
@@ -1160,16 +1271,19 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1160 .cr() 1271 .cr()
1161 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); 1272 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));
1162 1273
1163 let transfer = unsafe { 1274 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.
1164 self.dma 1275 for chunk in buf.chunks(0xFFFF) {
1165 .as_mut() 1276 let transfer = unsafe {
1166 .unwrap() 1277 self.dma
1167 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) 1278 .as_mut()
1168 }; 1279 .unwrap()
1280 .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())
1281 };
1169 1282
1170 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1283 T::REGS.cr().modify(|w| w.set_dmaen(true));
1171 1284
1172 transfer.await; 1285 transfer.await;
1286 }
1173 1287
1174 finish_dma(T::REGS); 1288 finish_dma(T::REGS);
1175 1289
diff --git a/embassy-stm32/src/qspi/enums.rs b/embassy-stm32/src/qspi/enums.rs
index fa5e36d06..8a8420127 100644
--- a/embassy-stm32/src/qspi/enums.rs
+++ b/embassy-stm32/src/qspi/enums.rs
@@ -23,6 +23,7 @@ impl From<QspiMode> for u8 {
23/// QSPI lane width 23/// QSPI lane width
24#[allow(dead_code)] 24#[allow(dead_code)]
25#[derive(Copy, Clone)] 25#[derive(Copy, Clone)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub enum QspiWidth { 27pub enum QspiWidth {
27 /// None 28 /// None
28 NONE, 29 NONE,
@@ -67,6 +68,7 @@ impl From<FlashSelection> for bool {
67/// QSPI memory size. 68/// QSPI memory size.
68#[allow(missing_docs)] 69#[allow(missing_docs)]
69#[derive(Copy, Clone)] 70#[derive(Copy, Clone)]
71#[cfg_attr(feature = "defmt", derive(defmt::Format))]
70pub enum MemorySize { 72pub enum MemorySize {
71 _1KiB, 73 _1KiB,
72 _2KiB, 74 _2KiB,
@@ -127,6 +129,7 @@ impl From<MemorySize> for u8 {
127 129
128/// QSPI Address size 130/// QSPI Address size
129#[derive(Copy, Clone)] 131#[derive(Copy, Clone)]
132#[cfg_attr(feature = "defmt", derive(defmt::Format))]
130pub enum AddressSize { 133pub enum AddressSize {
131 /// 8-bit address 134 /// 8-bit address
132 _8Bit, 135 _8Bit,
@@ -152,6 +155,7 @@ impl From<AddressSize> for u8 {
152/// Time the Chip Select line stays high. 155/// Time the Chip Select line stays high.
153#[allow(missing_docs)] 156#[allow(missing_docs)]
154#[derive(Copy, Clone)] 157#[derive(Copy, Clone)]
158#[cfg_attr(feature = "defmt", derive(defmt::Format))]
155pub enum ChipSelectHighTime { 159pub enum ChipSelectHighTime {
156 _1Cycle, 160 _1Cycle,
157 _2Cycle, 161 _2Cycle,
@@ -181,6 +185,7 @@ impl From<ChipSelectHighTime> for u8 {
181/// FIFO threshold. 185/// FIFO threshold.
182#[allow(missing_docs)] 186#[allow(missing_docs)]
183#[derive(Copy, Clone)] 187#[derive(Copy, Clone)]
188#[cfg_attr(feature = "defmt", derive(defmt::Format))]
184pub enum FIFOThresholdLevel { 189pub enum FIFOThresholdLevel {
185 _1Bytes, 190 _1Bytes,
186 _2Bytes, 191 _2Bytes,
@@ -258,6 +263,7 @@ impl From<FIFOThresholdLevel> for u8 {
258/// Dummy cycle count 263/// Dummy cycle count
259#[allow(missing_docs)] 264#[allow(missing_docs)]
260#[derive(Copy, Clone)] 265#[derive(Copy, Clone)]
266#[cfg_attr(feature = "defmt", derive(defmt::Format))]
261pub enum DummyCycles { 267pub enum DummyCycles {
262 _0, 268 _0,
263 _1, 269 _1,
@@ -334,6 +340,7 @@ impl From<DummyCycles> for u8 {
334 340
335#[allow(missing_docs)] 341#[allow(missing_docs)]
336#[derive(Copy, Clone)] 342#[derive(Copy, Clone)]
343#[cfg_attr(feature = "defmt", derive(defmt::Format))]
337pub enum SampleShifting { 344pub enum SampleShifting {
338 None, 345 None,
339 HalfCycle, 346 HalfCycle,
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs
index 1e20d7cd3..b03cd9009 100644
--- a/embassy-stm32/src/qspi/mod.rs
+++ b/embassy-stm32/src/qspi/mod.rs
@@ -17,6 +17,8 @@ use crate::rcc::{self, RccPeripheral};
17use crate::{peripherals, Peri}; 17use crate::{peripherals, Peri};
18 18
19/// QSPI transfer configuration. 19/// QSPI transfer configuration.
20#[derive(Clone, Copy)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20pub struct TransferConfig { 22pub struct TransferConfig {
21 /// Instruction width (IMODE) 23 /// Instruction width (IMODE)
22 pub iwidth: QspiWidth, 24 pub iwidth: QspiWidth,
@@ -46,6 +48,9 @@ impl Default for TransferConfig {
46} 48}
47 49
48/// QSPI driver configuration. 50/// QSPI driver configuration.
51#[derive(Clone, Copy)]
52#[cfg_attr(feature = "defmt", derive(defmt::Format))]
53#[non_exhaustive]
49pub struct Config { 54pub struct Config {
50 /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. 55 /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.
51 /// If you need other value the whose predefined use `Other` variant. 56 /// If you need other value the whose predefined use `Other` variant.
@@ -60,6 +65,8 @@ pub struct Config {
60 pub cs_high_time: ChipSelectHighTime, 65 pub cs_high_time: ChipSelectHighTime,
61 /// Shift sampling point of input data (none, or half-cycle) 66 /// Shift sampling point of input data (none, or half-cycle)
62 pub sample_shifting: SampleShifting, 67 pub sample_shifting: SampleShifting,
68 /// GPIO Speed
69 pub gpio_speed: Speed,
63} 70}
64 71
65impl Default for Config { 72impl Default for Config {
@@ -71,6 +78,7 @@ impl Default for Config {
71 fifo_threshold: FIFOThresholdLevel::_17Bytes, 78 fifo_threshold: FIFOThresholdLevel::_17Bytes,
72 cs_high_time: ChipSelectHighTime::_5Cycle, 79 cs_high_time: ChipSelectHighTime::_5Cycle,
73 sample_shifting: SampleShifting::None, 80 sample_shifting: SampleShifting::None,
81 gpio_speed: Speed::VeryHigh,
74 } 82 }
75 } 83 }
76} 84}
@@ -284,14 +292,14 @@ impl<'d, T: Instance> Qspi<'d, T, Blocking> {
284 ) -> Self { 292 ) -> Self {
285 Self::new_inner( 293 Self::new_inner(
286 peri, 294 peri,
287 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 295 new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),
288 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 296 new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),
289 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 297 new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),
290 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 298 new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),
291 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 299 new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),
292 new_pin!( 300 new_pin!(
293 nss, 301 nss,
294 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) 302 AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)
295 ), 303 ),
296 None, 304 None,
297 config, 305 config,
@@ -312,14 +320,14 @@ impl<'d, T: Instance> Qspi<'d, T, Blocking> {
312 ) -> Self { 320 ) -> Self {
313 Self::new_inner( 321 Self::new_inner(
314 peri, 322 peri,
315 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 323 new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),
316 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 324 new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),
317 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 325 new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),
318 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 326 new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),
319 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 327 new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),
320 new_pin!( 328 new_pin!(
321 nss, 329 nss,
322 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) 330 AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)
323 ), 331 ),
324 None, 332 None,
325 config, 333 config,
@@ -343,14 +351,14 @@ impl<'d, T: Instance> Qspi<'d, T, Async> {
343 ) -> Self { 351 ) -> Self {
344 Self::new_inner( 352 Self::new_inner(
345 peri, 353 peri,
346 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 354 new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),
347 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 355 new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),
348 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 356 new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),
349 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 357 new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),
350 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 358 new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),
351 new_pin!( 359 new_pin!(
352 nss, 360 nss,
353 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) 361 AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)
354 ), 362 ),
355 new_dma!(dma), 363 new_dma!(dma),
356 config, 364 config,
@@ -372,14 +380,14 @@ impl<'d, T: Instance> Qspi<'d, T, Async> {
372 ) -> Self { 380 ) -> Self {
373 Self::new_inner( 381 Self::new_inner(
374 peri, 382 peri,
375 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 383 new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),
376 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 384 new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),
377 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 385 new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),
378 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 386 new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),
379 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 387 new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),
380 new_pin!( 388 new_pin!(
381 nss, 389 nss,
382 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) 390 AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)
383 ), 391 ),
384 new_dma!(dma), 392 new_dma!(dma),
385 config, 393 config,
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs
index 96e628b1a..59ccc8cb5 100644
--- a/embassy-stm32/src/rcc/mco.rs
+++ b/embassy-stm32/src/rcc/mco.rs
@@ -94,7 +94,7 @@ impl<'d, T: McoInstance> Mco<'d, T> {
94 pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, prescaler: McoPrescaler) -> Self { 94 pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, prescaler: McoPrescaler) -> Self {
95 critical_section::with(|_| unsafe { 95 critical_section::with(|_| unsafe {
96 T::_apply_clock_settings(source, prescaler); 96 T::_apply_clock_settings(source, prescaler);
97 pin.set_as_af(pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 97 set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));
98 }); 98 });
99 99
100 Self { phantom: PhantomData } 100 Self { phantom: PhantomData }
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs
index 4965f8b04..fb8b23b79 100644
--- a/embassy-stm32/src/sai/mod.rs
+++ b/embassy-stm32/src/sai/mod.rs
@@ -1,15 +1,14 @@
1//! Serial Audio Interface (SAI) 1//! Serial Audio Interface (SAI)
2#![macro_use] 2#![macro_use]
3#![cfg_attr(gpdma, allow(unused))]
4 3
5use core::marker::PhantomData; 4use core::marker::PhantomData;
6 5
7use embassy_hal_internal::PeripheralType; 6use embassy_hal_internal::PeripheralType;
8 7
9pub use crate::dma::word; 8pub use crate::dma::word;
10#[cfg(not(gpdma))]
11use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer}; 9use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer};
12use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; 10use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
11pub use crate::pac::sai::vals::Mckdiv as MasterClockDivider;
13use crate::pac::sai::{vals, Sai as Regs}; 12use crate::pac::sai::{vals, Sai as Regs};
14use crate::rcc::{self, RccPeripheral}; 13use crate::rcc::{self, RccPeripheral};
15use crate::{peripherals, Peri}; 14use crate::{peripherals, Peri};
@@ -26,7 +25,6 @@ pub enum Error {
26 Overrun, 25 Overrun,
27} 26}
28 27
29#[cfg(not(gpdma))]
30impl From<ringbuffer::Error> for Error { 28impl From<ringbuffer::Error> for Error {
31 fn from(#[allow(unused)] err: ringbuffer::Error) -> Self { 29 fn from(#[allow(unused)] err: ringbuffer::Error) -> Self {
32 #[cfg(feature = "defmt")] 30 #[cfg(feature = "defmt")]
@@ -48,7 +46,6 @@ pub enum Mode {
48} 46}
49 47
50impl Mode { 48impl Mode {
51 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
52 const fn mode(&self, tx_rx: TxRx) -> vals::Mode { 49 const fn mode(&self, tx_rx: TxRx) -> vals::Mode {
53 match tx_rx { 50 match tx_rx {
54 TxRx::Transmitter => match self { 51 TxRx::Transmitter => match self {
@@ -83,7 +80,6 @@ pub enum SlotSize {
83} 80}
84 81
85impl SlotSize { 82impl SlotSize {
86 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
87 const fn slotsz(&self) -> vals::Slotsz { 83 const fn slotsz(&self) -> vals::Slotsz {
88 match self { 84 match self {
89 SlotSize::DataSize => vals::Slotsz::DATA_SIZE, 85 SlotSize::DataSize => vals::Slotsz::DATA_SIZE,
@@ -106,7 +102,6 @@ pub enum DataSize {
106} 102}
107 103
108impl DataSize { 104impl DataSize {
109 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
110 const fn ds(&self) -> vals::Ds { 105 const fn ds(&self) -> vals::Ds {
111 match self { 106 match self {
112 DataSize::Data8 => vals::Ds::BIT8, 107 DataSize::Data8 => vals::Ds::BIT8,
@@ -131,7 +126,6 @@ pub enum FifoThreshold {
131} 126}
132 127
133impl FifoThreshold { 128impl FifoThreshold {
134 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
135 const fn fth(&self) -> vals::Fth { 129 const fn fth(&self) -> vals::Fth {
136 match self { 130 match self {
137 FifoThreshold::Empty => vals::Fth::EMPTY, 131 FifoThreshold::Empty => vals::Fth::EMPTY,
@@ -152,7 +146,6 @@ pub enum MuteValue {
152} 146}
153 147
154impl MuteValue { 148impl MuteValue {
155 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
156 const fn muteval(&self) -> vals::Muteval { 149 const fn muteval(&self) -> vals::Muteval {
157 match self { 150 match self {
158 MuteValue::Zero => vals::Muteval::SEND_ZERO, 151 MuteValue::Zero => vals::Muteval::SEND_ZERO,
@@ -171,7 +164,6 @@ pub enum Protocol {
171} 164}
172 165
173impl Protocol { 166impl Protocol {
174 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
175 const fn prtcfg(&self) -> vals::Prtcfg { 167 const fn prtcfg(&self) -> vals::Prtcfg {
176 match self { 168 match self {
177 Protocol::Free => vals::Prtcfg::FREE, 169 Protocol::Free => vals::Prtcfg::FREE,
@@ -190,7 +182,7 @@ pub enum SyncInput {
190 /// Syncs with the other A/B sub-block within the SAI unit 182 /// Syncs with the other A/B sub-block within the SAI unit
191 Internal, 183 Internal,
192 /// Syncs with a sub-block in the other SAI unit 184 /// Syncs with a sub-block in the other SAI unit
193 #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 185 #[cfg(any(sai_v3, sai_v4))]
194 External(SyncInputInstance), 186 External(SyncInputInstance),
195} 187}
196 188
@@ -199,14 +191,14 @@ impl SyncInput {
199 match self { 191 match self {
200 SyncInput::None => vals::Syncen::ASYNCHRONOUS, 192 SyncInput::None => vals::Syncen::ASYNCHRONOUS,
201 SyncInput::Internal => vals::Syncen::INTERNAL, 193 SyncInput::Internal => vals::Syncen::INTERNAL,
202 #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 194 #[cfg(any(sai_v3, sai_v4))]
203 SyncInput::External(_) => vals::Syncen::EXTERNAL, 195 SyncInput::External(_) => vals::Syncen::EXTERNAL,
204 } 196 }
205 } 197 }
206} 198}
207 199
208/// SAI instance to sync from. 200/// SAI instance to sync from.
209#[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 201#[cfg(any(sai_v3, sai_v4))]
210#[derive(Copy, Clone, PartialEq)] 202#[derive(Copy, Clone, PartialEq)]
211#[allow(missing_docs)] 203#[allow(missing_docs)]
212pub enum SyncInputInstance { 204pub enum SyncInputInstance {
@@ -229,7 +221,6 @@ pub enum StereoMono {
229} 221}
230 222
231impl StereoMono { 223impl StereoMono {
232 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
233 const fn mono(&self) -> vals::Mono { 224 const fn mono(&self) -> vals::Mono {
234 match self { 225 match self {
235 StereoMono::Stereo => vals::Mono::STEREO, 226 StereoMono::Stereo => vals::Mono::STEREO,
@@ -248,7 +239,6 @@ pub enum BitOrder {
248} 239}
249 240
250impl BitOrder { 241impl BitOrder {
251 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
252 const fn lsbfirst(&self) -> vals::Lsbfirst { 242 const fn lsbfirst(&self) -> vals::Lsbfirst {
253 match self { 243 match self {
254 BitOrder::LsbFirst => vals::Lsbfirst::LSB_FIRST, 244 BitOrder::LsbFirst => vals::Lsbfirst::LSB_FIRST,
@@ -267,7 +257,6 @@ pub enum FrameSyncOffset {
267} 257}
268 258
269impl FrameSyncOffset { 259impl FrameSyncOffset {
270 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
271 const fn fsoff(&self) -> vals::Fsoff { 260 const fn fsoff(&self) -> vals::Fsoff {
272 match self { 261 match self {
273 FrameSyncOffset::OnFirstBit => vals::Fsoff::ON_FIRST, 262 FrameSyncOffset::OnFirstBit => vals::Fsoff::ON_FIRST,
@@ -286,7 +275,6 @@ pub enum FrameSyncPolarity {
286} 275}
287 276
288impl FrameSyncPolarity { 277impl FrameSyncPolarity {
289 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
290 const fn fspol(&self) -> vals::Fspol { 278 const fn fspol(&self) -> vals::Fspol {
291 match self { 279 match self {
292 FrameSyncPolarity::ActiveLow => vals::Fspol::FALLING_EDGE, 280 FrameSyncPolarity::ActiveLow => vals::Fspol::FALLING_EDGE,
@@ -304,7 +292,6 @@ pub enum FrameSyncDefinition {
304} 292}
305 293
306impl FrameSyncDefinition { 294impl FrameSyncDefinition {
307 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
308 const fn fsdef(&self) -> bool { 295 const fn fsdef(&self) -> bool {
309 match self { 296 match self {
310 FrameSyncDefinition::StartOfFrame => false, 297 FrameSyncDefinition::StartOfFrame => false,
@@ -322,7 +309,6 @@ pub enum ClockStrobe {
322} 309}
323 310
324impl ClockStrobe { 311impl ClockStrobe {
325 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
326 const fn ckstr(&self) -> vals::Ckstr { 312 const fn ckstr(&self) -> vals::Ckstr {
327 match self { 313 match self {
328 ClockStrobe::Falling => vals::Ckstr::FALLING_EDGE, 314 ClockStrobe::Falling => vals::Ckstr::FALLING_EDGE,
@@ -340,7 +326,6 @@ pub enum ComplementFormat {
340} 326}
341 327
342impl ComplementFormat { 328impl ComplementFormat {
343 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
344 const fn cpl(&self) -> vals::Cpl { 329 const fn cpl(&self) -> vals::Cpl {
345 match self { 330 match self {
346 ComplementFormat::OnesComplement => vals::Cpl::ONES_COMPLEMENT, 331 ComplementFormat::OnesComplement => vals::Cpl::ONES_COMPLEMENT,
@@ -359,7 +344,6 @@ pub enum Companding {
359} 344}
360 345
361impl Companding { 346impl Companding {
362 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
363 const fn comp(&self) -> vals::Comp { 347 const fn comp(&self) -> vals::Comp {
364 match self { 348 match self {
365 Companding::None => vals::Comp::NO_COMPANDING, 349 Companding::None => vals::Comp::NO_COMPANDING,
@@ -378,7 +362,6 @@ pub enum OutputDrive {
378} 362}
379 363
380impl OutputDrive { 364impl OutputDrive {
381 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
382 const fn outdriv(&self) -> vals::Outdriv { 365 const fn outdriv(&self) -> vals::Outdriv {
383 match self { 366 match self {
384 OutputDrive::OnStart => vals::Outdriv::ON_START, 367 OutputDrive::OnStart => vals::Outdriv::ON_START,
@@ -387,196 +370,6 @@ impl OutputDrive {
387 } 370 }
388} 371}
389 372
390/// Master clock divider.
391#[derive(Copy, Clone, PartialEq)]
392#[allow(missing_docs)]
393#[cfg(any(sai_v1, sai_v2))]
394pub enum MasterClockDivider {
395 MasterClockDisabled,
396 Div1,
397 Div2,
398 Div4,
399 Div6,
400 Div8,
401 Div10,
402 Div12,
403 Div14,
404 Div16,
405 Div18,
406 Div20,
407 Div22,
408 Div24,
409 Div26,
410 Div28,
411 Div30,
412}
413
414/// Master clock divider.
415#[derive(Copy, Clone, PartialEq)]
416#[allow(missing_docs)]
417#[cfg(any(sai_v1_4pdm, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
418pub enum MasterClockDivider {
419 MasterClockDisabled,
420 Div1,
421 Div2,
422 Div3,
423 Div4,
424 Div5,
425 Div6,
426 Div7,
427 Div8,
428 Div9,
429 Div10,
430 Div11,
431 Div12,
432 Div13,
433 Div14,
434 Div15,
435 Div16,
436 Div17,
437 Div18,
438 Div19,
439 Div20,
440 Div21,
441 Div22,
442 Div23,
443 Div24,
444 Div25,
445 Div26,
446 Div27,
447 Div28,
448 Div29,
449 Div30,
450 Div31,
451 Div32,
452 Div33,
453 Div34,
454 Div35,
455 Div36,
456 Div37,
457 Div38,
458 Div39,
459 Div40,
460 Div41,
461 Div42,
462 Div43,
463 Div44,
464 Div45,
465 Div46,
466 Div47,
467 Div48,
468 Div49,
469 Div50,
470 Div51,
471 Div52,
472 Div53,
473 Div54,
474 Div55,
475 Div56,
476 Div57,
477 Div58,
478 Div59,
479 Div60,
480 Div61,
481 Div62,
482 Div63,
483}
484
485impl MasterClockDivider {
486 #[cfg(any(sai_v1, sai_v2))]
487 const fn mckdiv(&self) -> u8 {
488 match self {
489 MasterClockDivider::MasterClockDisabled => 0,
490 MasterClockDivider::Div1 => 0,
491 MasterClockDivider::Div2 => 1,
492 MasterClockDivider::Div4 => 2,
493 MasterClockDivider::Div6 => 3,
494 MasterClockDivider::Div8 => 4,
495 MasterClockDivider::Div10 => 5,
496 MasterClockDivider::Div12 => 6,
497 MasterClockDivider::Div14 => 7,
498 MasterClockDivider::Div16 => 8,
499 MasterClockDivider::Div18 => 9,
500 MasterClockDivider::Div20 => 10,
501 MasterClockDivider::Div22 => 11,
502 MasterClockDivider::Div24 => 12,
503 MasterClockDivider::Div26 => 13,
504 MasterClockDivider::Div28 => 14,
505 MasterClockDivider::Div30 => 15,
506 }
507 }
508
509 #[cfg(any(sai_v1_4pdm, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
510 const fn mckdiv(&self) -> u8 {
511 match self {
512 MasterClockDivider::MasterClockDisabled => 0,
513 MasterClockDivider::Div1 => 1,
514 MasterClockDivider::Div2 => 2,
515 MasterClockDivider::Div3 => 3,
516 MasterClockDivider::Div4 => 4,
517 MasterClockDivider::Div5 => 5,
518 MasterClockDivider::Div6 => 6,
519 MasterClockDivider::Div7 => 7,
520 MasterClockDivider::Div8 => 8,
521 MasterClockDivider::Div9 => 9,
522 MasterClockDivider::Div10 => 10,
523 MasterClockDivider::Div11 => 11,
524 MasterClockDivider::Div12 => 12,
525 MasterClockDivider::Div13 => 13,
526 MasterClockDivider::Div14 => 14,
527 MasterClockDivider::Div15 => 15,
528 MasterClockDivider::Div16 => 16,
529 MasterClockDivider::Div17 => 17,
530 MasterClockDivider::Div18 => 18,
531 MasterClockDivider::Div19 => 19,
532 MasterClockDivider::Div20 => 20,
533 MasterClockDivider::Div21 => 21,
534 MasterClockDivider::Div22 => 22,
535 MasterClockDivider::Div23 => 23,
536 MasterClockDivider::Div24 => 24,
537 MasterClockDivider::Div25 => 25,
538 MasterClockDivider::Div26 => 26,
539 MasterClockDivider::Div27 => 27,
540 MasterClockDivider::Div28 => 28,
541 MasterClockDivider::Div29 => 29,
542 MasterClockDivider::Div30 => 30,
543 MasterClockDivider::Div31 => 31,
544 MasterClockDivider::Div32 => 32,
545 MasterClockDivider::Div33 => 33,
546 MasterClockDivider::Div34 => 34,
547 MasterClockDivider::Div35 => 35,
548 MasterClockDivider::Div36 => 36,
549 MasterClockDivider::Div37 => 37,
550 MasterClockDivider::Div38 => 38,
551 MasterClockDivider::Div39 => 39,
552 MasterClockDivider::Div40 => 40,
553 MasterClockDivider::Div41 => 41,
554 MasterClockDivider::Div42 => 42,
555 MasterClockDivider::Div43 => 43,
556 MasterClockDivider::Div44 => 44,
557 MasterClockDivider::Div45 => 45,
558 MasterClockDivider::Div46 => 46,
559 MasterClockDivider::Div47 => 47,
560 MasterClockDivider::Div48 => 48,
561 MasterClockDivider::Div49 => 49,
562 MasterClockDivider::Div50 => 50,
563 MasterClockDivider::Div51 => 51,
564 MasterClockDivider::Div52 => 52,
565 MasterClockDivider::Div53 => 53,
566 MasterClockDivider::Div54 => 54,
567 MasterClockDivider::Div55 => 55,
568 MasterClockDivider::Div56 => 56,
569 MasterClockDivider::Div57 => 57,
570 MasterClockDivider::Div58 => 58,
571 MasterClockDivider::Div59 => 59,
572 MasterClockDivider::Div60 => 60,
573 MasterClockDivider::Div61 => 61,
574 MasterClockDivider::Div62 => 62,
575 MasterClockDivider::Div63 => 63,
576 }
577 }
578}
579
580/// [`SAI`] configuration. 373/// [`SAI`] configuration.
581#[allow(missing_docs)] 374#[allow(missing_docs)]
582#[non_exhaustive] 375#[non_exhaustive]
@@ -601,8 +394,7 @@ pub struct Config {
601 pub frame_length: u8, 394 pub frame_length: u8,
602 pub clock_strobe: ClockStrobe, 395 pub clock_strobe: ClockStrobe,
603 pub output_drive: OutputDrive, 396 pub output_drive: OutputDrive,
604 pub master_clock_divider: MasterClockDivider, 397 pub master_clock_divider: Option<MasterClockDivider>,
605 pub nodiv: bool,
606 pub is_high_impedance_on_inactive_slot: bool, 398 pub is_high_impedance_on_inactive_slot: bool,
607 pub fifo_threshold: FifoThreshold, 399 pub fifo_threshold: FifoThreshold,
608 pub companding: Companding, 400 pub companding: Companding,
@@ -631,8 +423,7 @@ impl Default for Config {
631 frame_sync_active_level_length: word::U7(16), 423 frame_sync_active_level_length: word::U7(16),
632 frame_sync_definition: FrameSyncDefinition::ChannelIdentification, 424 frame_sync_definition: FrameSyncDefinition::ChannelIdentification,
633 frame_length: 32, 425 frame_length: 32,
634 master_clock_divider: MasterClockDivider::MasterClockDisabled, 426 master_clock_divider: None,
635 nodiv: false,
636 clock_strobe: ClockStrobe::Rising, 427 clock_strobe: ClockStrobe::Rising,
637 output_drive: OutputDrive::Immediately, 428 output_drive: OutputDrive::Immediately,
638 is_high_impedance_on_inactive_slot: false, 429 is_high_impedance_on_inactive_slot: false,
@@ -652,7 +443,6 @@ impl Config {
652 } 443 }
653} 444}
654 445
655#[cfg(not(gpdma))]
656enum RingBuffer<'d, W: word::Word> { 446enum RingBuffer<'d, W: word::Word> {
657 Writable(WritableRingBuffer<'d, W>), 447 Writable(WritableRingBuffer<'d, W>),
658 Readable(ReadableRingBuffer<'d, W>), 448 Readable(ReadableRingBuffer<'d, W>),
@@ -679,7 +469,6 @@ fn get_af_types(mode: Mode, tx_rx: TxRx) -> (AfType, AfType) {
679 ) 469 )
680} 470}
681 471
682#[cfg(not(gpdma))]
683fn get_ring_buffer<'d, T: Instance, W: word::Word>( 472fn get_ring_buffer<'d, T: Instance, W: word::Word>(
684 dma: Peri<'d, impl Channel>, 473 dma: Peri<'d, impl Channel>,
685 dma_buf: &'d mut [W], 474 dma_buf: &'d mut [W],
@@ -711,7 +500,7 @@ fn update_synchronous_config(config: &mut Config) {
711 config.sync_input = SyncInput::Internal; 500 config.sync_input = SyncInput::Internal;
712 } 501 }
713 502
714 #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 503 #[cfg(any(sai_v3, sai_v4))]
715 { 504 {
716 //this must either be Internal or External 505 //this must either be Internal or External
717 //The asynchronous sub-block on the same SAI needs to enable sync_output 506 //The asynchronous sub-block on the same SAI needs to enable sync_output
@@ -750,14 +539,10 @@ pub struct Sai<'d, T: Instance, W: word::Word> {
750 fs: Option<Peri<'d, AnyPin>>, 539 fs: Option<Peri<'d, AnyPin>>,
751 sck: Option<Peri<'d, AnyPin>>, 540 sck: Option<Peri<'d, AnyPin>>,
752 mclk: Option<Peri<'d, AnyPin>>, 541 mclk: Option<Peri<'d, AnyPin>>,
753 #[cfg(gpdma)]
754 ring_buffer: PhantomData<W>,
755 #[cfg(not(gpdma))]
756 ring_buffer: RingBuffer<'d, W>, 542 ring_buffer: RingBuffer<'d, W>,
757 sub_block: WhichSubBlock, 543 sub_block: WhichSubBlock,
758} 544}
759 545
760#[cfg(not(gpdma))]
761impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { 546impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
762 /// Create a new SAI driver in asynchronous mode with MCLK. 547 /// Create a new SAI driver in asynchronous mode with MCLK.
763 /// 548 ///
@@ -770,14 +555,10 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
770 mclk: Peri<'d, impl MclkPin<T, S>>, 555 mclk: Peri<'d, impl MclkPin<T, S>>,
771 dma: Peri<'d, impl Channel + Dma<T, S>>, 556 dma: Peri<'d, impl Channel + Dma<T, S>>,
772 dma_buf: &'d mut [W], 557 dma_buf: &'d mut [W],
773 mut config: Config, 558 config: Config,
774 ) -> Self { 559 ) -> Self {
775 let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); 560 let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx);
776 mclk.set_as_af(mclk.af_num(), ck_af_type); 561 set_as_af!(mclk, ck_af_type);
777
778 if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
779 config.master_clock_divider = MasterClockDivider::Div1;
780 }
781 562
782 Self::new_asynchronous(peri, sck, sd, fs, dma, dma_buf, config) 563 Self::new_asynchronous(peri, sck, sd, fs, dma, dma_buf, config)
783 } 564 }
@@ -797,9 +578,9 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
797 let peri = peri.peri; 578 let peri = peri.peri;
798 579
799 let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); 580 let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx);
800 sd.set_as_af(sd.af_num(), sd_af_type); 581 set_as_af!(sd, sd_af_type);
801 sck.set_as_af(sck.af_num(), ck_af_type); 582 set_as_af!(sck, ck_af_type);
802 fs.set_as_af(fs.af_num(), ck_af_type); 583 set_as_af!(fs, ck_af_type);
803 584
804 let sub_block = S::WHICH; 585 let sub_block = S::WHICH;
805 let request = dma.request(); 586 let request = dma.request();
@@ -831,7 +612,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
831 let peri = peri.peri; 612 let peri = peri.peri;
832 613
833 let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx); 614 let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx);
834 sd.set_as_af(sd.af_num(), sd_af_type); 615 set_as_af!(sd, sd_af_type);
835 616
836 let sub_block = S::WHICH; 617 let sub_block = S::WHICH;
837 let request = dma.request(); 618 let request = dma.request();
@@ -860,14 +641,11 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
860 ) -> Self { 641 ) -> Self {
861 let ch = T::REGS.ch(sub_block as usize); 642 let ch = T::REGS.ch(sub_block as usize);
862 643
863 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 644 ch.cr1().modify(|w| w.set_saien(false));
864 {
865 ch.cr1().modify(|w| w.set_saien(false));
866 }
867 645
868 ch.cr2().modify(|w| w.set_fflush(true)); 646 ch.cr2().modify(|w| w.set_fflush(true));
869 647
870 #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 648 #[cfg(any(sai_v3, sai_v4))]
871 { 649 {
872 if let SyncInput::External(i) = config.sync_input { 650 if let SyncInput::External(i) = config.sync_input {
873 T::REGS.gcr().modify(|w| { 651 T::REGS.gcr().modify(|w| {
@@ -886,55 +664,52 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
886 } 664 }
887 } 665 }
888 666
889 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 667 ch.cr1().modify(|w| {
890 { 668 w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) {
891 ch.cr1().modify(|w| { 669 TxRx::Transmitter
892 w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) { 670 } else {
893 TxRx::Transmitter 671 TxRx::Receiver
894 } else { 672 }));
895 TxRx::Receiver 673 w.set_prtcfg(config.protocol.prtcfg());
896 })); 674 w.set_ds(config.data_size.ds());
897 w.set_prtcfg(config.protocol.prtcfg()); 675 w.set_lsbfirst(config.bit_order.lsbfirst());
898 w.set_ds(config.data_size.ds()); 676 w.set_ckstr(config.clock_strobe.ckstr());
899 w.set_lsbfirst(config.bit_order.lsbfirst()); 677 w.set_syncen(config.sync_input.syncen());
900 w.set_ckstr(config.clock_strobe.ckstr()); 678 w.set_mono(config.stereo_mono.mono());
901 w.set_syncen(config.sync_input.syncen()); 679 w.set_outdriv(config.output_drive.outdriv());
902 w.set_mono(config.stereo_mono.mono()); 680 w.set_mckdiv(config.master_clock_divider.unwrap_or(MasterClockDivider::DIV1));
903 w.set_outdriv(config.output_drive.outdriv()); 681 w.set_nodiv(config.master_clock_divider.is_none());
904 w.set_mckdiv(config.master_clock_divider.mckdiv().into()); 682 w.set_dmaen(true);
905 w.set_nodiv(config.nodiv); 683 });
906 w.set_dmaen(true); 684
907 }); 685 ch.cr2().modify(|w| {
908 686 w.set_fth(config.fifo_threshold.fth());
909 ch.cr2().modify(|w| { 687 w.set_comp(config.companding.comp());
910 w.set_fth(config.fifo_threshold.fth()); 688 w.set_cpl(config.complement_format.cpl());
911 w.set_comp(config.companding.comp()); 689 w.set_muteval(config.mute_value.muteval());
912 w.set_cpl(config.complement_format.cpl()); 690 w.set_mutecnt(config.mute_detection_counter.0 as u8);
913 w.set_muteval(config.mute_value.muteval()); 691 w.set_tris(config.is_high_impedance_on_inactive_slot);
914 w.set_mutecnt(config.mute_detection_counter.0 as u8); 692 });
915 w.set_tris(config.is_high_impedance_on_inactive_slot); 693
916 }); 694 ch.frcr().modify(|w| {
917 695 w.set_fsoff(config.frame_sync_offset.fsoff());
918 ch.frcr().modify(|w| { 696 w.set_fspol(config.frame_sync_polarity.fspol());
919 w.set_fsoff(config.frame_sync_offset.fsoff()); 697 w.set_fsdef(config.frame_sync_definition.fsdef());
920 w.set_fspol(config.frame_sync_polarity.fspol()); 698 w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1);
921 w.set_fsdef(config.frame_sync_definition.fsdef()); 699 w.set_frl(config.frame_length - 1);
922 w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); 700 });
923 w.set_frl(config.frame_length - 1); 701
924 }); 702 ch.slotr().modify(|w| {
925 703 w.set_nbslot(config.slot_count.0 as u8 - 1);
926 ch.slotr().modify(|w| { 704 w.set_slotsz(config.slot_size.slotsz());
927 w.set_nbslot(config.slot_count.0 as u8 - 1); 705 w.set_fboff(config.first_bit_offset.0 as u8);
928 w.set_slotsz(config.slot_size.slotsz()); 706 w.set_sloten(vals::Sloten::from_bits(config.slot_enable as u16));
929 w.set_fboff(config.first_bit_offset.0 as u8); 707 });
930 w.set_sloten(vals::Sloten::from_bits(config.slot_enable as u16)); 708
931 }); 709 ch.cr1().modify(|w| w.set_saien(true));
932 710
933 ch.cr1().modify(|w| w.set_saien(true)); 711 if ch.cr1().read().saien() == false {
934 712 panic!("SAI failed to enable. Check that config is valid (frame length, slot count, etc)");
935 if ch.cr1().read().saien() == false {
936 panic!("SAI failed to enable. Check that config is valid (frame length, slot count, etc)");
937 }
938 } 713 }
939 714
940 Self { 715 Self {
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs
index 6e5d735d7..ccbd16cbf 100644
--- a/embassy-stm32/src/sdmmc/mod.rs
+++ b/embassy-stm32/src/sdmmc/mod.rs
@@ -428,9 +428,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
428 config: Config, 428 config: Config,
429 ) -> Self { 429 ) -> Self {
430 critical_section::with(|_| { 430 critical_section::with(|_| {
431 clk.set_as_af(clk.af_num(), CLK_AF); 431 set_as_af!(clk, CLK_AF);
432 cmd.set_as_af(cmd.af_num(), CMD_AF); 432 set_as_af!(cmd, CMD_AF);
433 d0.set_as_af(d0.af_num(), DATA_AF); 433 set_as_af!(d0, DATA_AF);
434 }); 434 });
435 435
436 Self::new_inner( 436 Self::new_inner(
@@ -464,12 +464,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
464 config: Config, 464 config: Config,
465 ) -> Self { 465 ) -> Self {
466 critical_section::with(|_| { 466 critical_section::with(|_| {
467 clk.set_as_af(clk.af_num(), CLK_AF); 467 set_as_af!(clk, CLK_AF);
468 cmd.set_as_af(cmd.af_num(), CMD_AF); 468 set_as_af!(cmd, CMD_AF);
469 d0.set_as_af(d0.af_num(), DATA_AF); 469 set_as_af!(d0, DATA_AF);
470 d1.set_as_af(d1.af_num(), DATA_AF); 470 set_as_af!(d1, DATA_AF);
471 d2.set_as_af(d2.af_num(), DATA_AF); 471 set_as_af!(d2, DATA_AF);
472 d3.set_as_af(d3.af_num(), DATA_AF); 472 set_as_af!(d3, DATA_AF);
473 }); 473 });
474 474
475 Self::new_inner( 475 Self::new_inner(
@@ -510,16 +510,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
510 config: Config, 510 config: Config,
511 ) -> Self { 511 ) -> Self {
512 critical_section::with(|_| { 512 critical_section::with(|_| {
513 clk.set_as_af(clk.af_num(), CLK_AF); 513 set_as_af!(clk, CLK_AF);
514 cmd.set_as_af(cmd.af_num(), CMD_AF); 514 set_as_af!(cmd, CMD_AF);
515 d0.set_as_af(d0.af_num(), DATA_AF); 515 set_as_af!(d0, DATA_AF);
516 d1.set_as_af(d1.af_num(), DATA_AF); 516 set_as_af!(d1, DATA_AF);
517 d2.set_as_af(d2.af_num(), DATA_AF); 517 set_as_af!(d2, DATA_AF);
518 d3.set_as_af(d3.af_num(), DATA_AF); 518 set_as_af!(d3, DATA_AF);
519 d4.set_as_af(d4.af_num(), DATA_AF); 519 set_as_af!(d4, DATA_AF);
520 d5.set_as_af(d5.af_num(), DATA_AF); 520 set_as_af!(d5, DATA_AF);
521 d6.set_as_af(d6.af_num(), DATA_AF); 521 set_as_af!(d6, DATA_AF);
522 d7.set_as_af(d7.af_num(), DATA_AF); 522 set_as_af!(d7, DATA_AF);
523 }); 523 });
524 524
525 Self::new_inner( 525 Self::new_inner(
@@ -552,9 +552,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
552 config: Config, 552 config: Config,
553 ) -> Self { 553 ) -> Self {
554 critical_section::with(|_| { 554 critical_section::with(|_| {
555 clk.set_as_af(clk.af_num(), CLK_AF); 555 set_as_af!(clk, CLK_AF);
556 cmd.set_as_af(cmd.af_num(), CMD_AF); 556 set_as_af!(cmd, CMD_AF);
557 d0.set_as_af(d0.af_num(), DATA_AF); 557 set_as_af!(d0, DATA_AF);
558 }); 558 });
559 559
560 Self::new_inner( 560 Self::new_inner(
@@ -586,12 +586,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
586 config: Config, 586 config: Config,
587 ) -> Self { 587 ) -> Self {
588 critical_section::with(|_| { 588 critical_section::with(|_| {
589 clk.set_as_af(clk.af_num(), CLK_AF); 589 set_as_af!(clk, CLK_AF);
590 cmd.set_as_af(cmd.af_num(), CMD_AF); 590 set_as_af!(cmd, CMD_AF);
591 d0.set_as_af(d0.af_num(), DATA_AF); 591 set_as_af!(d0, DATA_AF);
592 d1.set_as_af(d1.af_num(), DATA_AF); 592 set_as_af!(d1, DATA_AF);
593 d2.set_as_af(d2.af_num(), DATA_AF); 593 set_as_af!(d2, DATA_AF);
594 d3.set_as_af(d3.af_num(), DATA_AF); 594 set_as_af!(d3, DATA_AF);
595 }); 595 });
596 596
597 Self::new_inner( 597 Self::new_inner(
@@ -630,16 +630,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
630 config: Config, 630 config: Config,
631 ) -> Self { 631 ) -> Self {
632 critical_section::with(|_| { 632 critical_section::with(|_| {
633 clk.set_as_af(clk.af_num(), CLK_AF); 633 set_as_af!(clk, CLK_AF);
634 cmd.set_as_af(cmd.af_num(), CMD_AF); 634 set_as_af!(cmd, CMD_AF);
635 d0.set_as_af(d0.af_num(), DATA_AF); 635 set_as_af!(d0, DATA_AF);
636 d1.set_as_af(d1.af_num(), DATA_AF); 636 set_as_af!(d1, DATA_AF);
637 d2.set_as_af(d2.af_num(), DATA_AF); 637 set_as_af!(d2, DATA_AF);
638 d3.set_as_af(d3.af_num(), DATA_AF); 638 set_as_af!(d3, DATA_AF);
639 d4.set_as_af(d4.af_num(), DATA_AF); 639 set_as_af!(d4, DATA_AF);
640 d5.set_as_af(d5.af_num(), DATA_AF); 640 set_as_af!(d5, DATA_AF);
641 d6.set_as_af(d6.af_num(), DATA_AF); 641 set_as_af!(d6, DATA_AF);
642 d7.set_as_af(d7.af_num(), DATA_AF); 642 set_as_af!(d7, DATA_AF);
643 }); 643 });
644 644
645 Self::new_inner( 645 Self::new_inner(
diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs
index 9c42217f0..b0a32d5d1 100644
--- a/embassy-stm32/src/spdifrx/mod.rs
+++ b/embassy-stm32/src/spdifrx/mod.rs
@@ -8,9 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker;
8 8
9use crate::dma::ringbuffer::Error as RingbufferError; 9use crate::dma::ringbuffer::Error as RingbufferError;
10pub use crate::dma::word; 10pub use crate::dma::word;
11#[cfg(not(gpdma))] 11use crate::dma::{Channel, ReadableRingBuffer, TransferOptions};
12use crate::dma::ReadableRingBuffer;
13use crate::dma::{Channel, TransferOptions};
14use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; 12use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _};
15use crate::interrupt::typelevel::Interrupt; 13use crate::interrupt::typelevel::Interrupt;
16use crate::pac::spdifrx::Spdifrx as Regs; 14use crate::pac::spdifrx::Spdifrx as Regs;
@@ -37,7 +35,7 @@ macro_rules! new_spdifrx_pin {
37 ($name:ident, $af_type:expr) => {{ 35 ($name:ident, $af_type:expr) => {{
38 let pin = $name; 36 let pin = $name;
39 let input_sel = pin.input_sel(); 37 let input_sel = pin.input_sel();
40 pin.set_as_af(pin.af_num(), $af_type); 38 set_as_af!(pin, $af_type);
41 (Some(pin.into()), input_sel) 39 (Some(pin.into()), input_sel)
42 }}; 40 }};
43} 41}
@@ -58,7 +56,6 @@ macro_rules! impl_spdifrx_pin {
58/// Ring-buffered SPDIFRX driver. 56/// Ring-buffered SPDIFRX driver.
59/// 57///
60/// Data is read by DMAs and stored in a ring buffer. 58/// Data is read by DMAs and stored in a ring buffer.
61#[cfg(not(gpdma))]
62pub struct Spdifrx<'d, T: Instance> { 59pub struct Spdifrx<'d, T: Instance> {
63 _peri: Peri<'d, T>, 60 _peri: Peri<'d, T>,
64 spdifrx_in: Option<Peri<'d, AnyPin>>, 61 spdifrx_in: Option<Peri<'d, AnyPin>>,
@@ -118,7 +115,6 @@ impl Default for Config {
118 } 115 }
119} 116}
120 117
121#[cfg(not(gpdma))]
122impl<'d, T: Instance> Spdifrx<'d, T> { 118impl<'d, T: Instance> Spdifrx<'d, T> {
123 fn dma_opts() -> TransferOptions { 119 fn dma_opts() -> TransferOptions {
124 TransferOptions { 120 TransferOptions {
@@ -236,7 +232,6 @@ impl<'d, T: Instance> Spdifrx<'d, T> {
236 } 232 }
237} 233}
238 234
239#[cfg(not(gpdma))]
240impl<'d, T: Instance> Drop for Spdifrx<'d, T> { 235impl<'d, T: Instance> Drop for Spdifrx<'d, T> {
241 fn drop(&mut self) { 236 fn drop(&mut self) {
242 T::info().regs.cr().modify(|cr| cr.set_spdifen(0x00)); 237 T::info().regs.cr().modify(|cr| cr.set_spdifen(0x00));
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 4c5308eba..c5373a54d 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -174,7 +174,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
174 self.info.rcc.enable_and_reset(); 174 self.info.rcc.enable_and_reset();
175 175
176 let regs = self.info.regs; 176 let regs = self.info.regs;
177 #[cfg(any(spi_v1, spi_f1))] 177 #[cfg(any(spi_v1, spi_v2))]
178 { 178 {
179 regs.cr2().modify(|w| { 179 regs.cr2().modify(|w| {
180 w.set_ssoe(false); 180 w.set_ssoe(false);
@@ -198,7 +198,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
198 w.set_dff(<u8 as SealedWord>::CONFIG) 198 w.set_dff(<u8 as SealedWord>::CONFIG)
199 }); 199 });
200 } 200 }
201 #[cfg(spi_v2)] 201 #[cfg(spi_v3)]
202 { 202 {
203 regs.cr2().modify(|w| { 203 regs.cr2().modify(|w| {
204 let (ds, frxth) = <u8 as SealedWord>::CONFIG; 204 let (ds, frxth) = <u8 as SealedWord>::CONFIG;
@@ -220,7 +220,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
220 w.set_spe(true); 220 w.set_spe(true);
221 }); 221 });
222 } 222 }
223 #[cfg(any(spi_v3, spi_v4, spi_v5))] 223 #[cfg(any(spi_v4, spi_v5, spi_v6))]
224 { 224 {
225 regs.ifcr().write(|w| w.0 = 0xffff_ffff); 225 regs.ifcr().write(|w| w.0 = 0xffff_ffff);
226 regs.cfg2().modify(|w| { 226 regs.cfg2().modify(|w| {
@@ -274,7 +274,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
274 } 274 }
275 } 275 }
276 276
277 #[cfg(any(spi_v1, spi_f1, spi_v2))] 277 #[cfg(any(spi_v1, spi_v2, spi_v3))]
278 self.info.regs.cr1().modify(|w| { 278 self.info.regs.cr1().modify(|w| {
279 w.set_cpha(cpha); 279 w.set_cpha(cpha);
280 w.set_cpol(cpol); 280 w.set_cpol(cpol);
@@ -282,7 +282,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
282 w.set_lsbfirst(lsbfirst); 282 w.set_lsbfirst(lsbfirst);
283 }); 283 });
284 284
285 #[cfg(any(spi_v3, spi_v4, spi_v5))] 285 #[cfg(any(spi_v4, spi_v5, spi_v6))]
286 { 286 {
287 self.info.regs.cr1().modify(|w| { 287 self.info.regs.cr1().modify(|w| {
288 w.set_spe(false); 288 w.set_spe(false);
@@ -306,11 +306,11 @@ impl<'d, M: PeriMode> Spi<'d, M> {
306 306
307 /// Get current SPI configuration. 307 /// Get current SPI configuration.
308 pub fn get_current_config(&self) -> Config { 308 pub fn get_current_config(&self) -> Config {
309 #[cfg(any(spi_v1, spi_f1, spi_v2))] 309 #[cfg(any(spi_v1, spi_v2, spi_v3))]
310 let cfg = self.info.regs.cr1().read(); 310 let cfg = self.info.regs.cr1().read();
311 #[cfg(any(spi_v3, spi_v4, spi_v5))] 311 #[cfg(any(spi_v4, spi_v5, spi_v6))]
312 let cfg = self.info.regs.cfg2().read(); 312 let cfg = self.info.regs.cfg2().read();
313 #[cfg(any(spi_v3, spi_v4, spi_v5))] 313 #[cfg(any(spi_v4, spi_v5, spi_v6))]
314 let cfg1 = self.info.regs.cfg1().read(); 314 let cfg1 = self.info.regs.cfg1().read();
315 315
316 let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { 316 let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW {
@@ -335,9 +335,9 @@ impl<'d, M: PeriMode> Spi<'d, M> {
335 Some(pin) => pin.pull(), 335 Some(pin) => pin.pull(),
336 }; 336 };
337 337
338 #[cfg(any(spi_v1, spi_f1, spi_v2))] 338 #[cfg(any(spi_v1, spi_v2, spi_v3))]
339 let br = cfg.br(); 339 let br = cfg.br();
340 #[cfg(any(spi_v3, spi_v4, spi_v5))] 340 #[cfg(any(spi_v4, spi_v5, spi_v6))]
341 let br = cfg1.mbr(); 341 let br = cfg1.mbr();
342 342
343 let frequency = compute_frequency(self.kernel_clock, br); 343 let frequency = compute_frequency(self.kernel_clock, br);
@@ -360,16 +360,16 @@ impl<'d, M: PeriMode> Spi<'d, M> {
360 w.set_spe(false); 360 w.set_spe(false);
361 }); 361 });
362 362
363 #[cfg(any(spi_v1, spi_f1))] 363 #[cfg(any(spi_v1, spi_v2))]
364 self.info.regs.cr1().modify(|reg| { 364 self.info.regs.cr1().modify(|reg| {
365 reg.set_dff(word_size); 365 reg.set_dff(word_size);
366 }); 366 });
367 #[cfg(spi_v2)] 367 #[cfg(spi_v3)]
368 self.info.regs.cr2().modify(|w| { 368 self.info.regs.cr2().modify(|w| {
369 w.set_frxth(word_size.1); 369 w.set_frxth(word_size.1);
370 w.set_ds(word_size.0); 370 w.set_ds(word_size.0);
371 }); 371 });
372 #[cfg(any(spi_v3, spi_v4, spi_v5))] 372 #[cfg(any(spi_v4, spi_v5, spi_v6))]
373 self.info.regs.cfg1().modify(|w| { 373 self.info.regs.cfg1().modify(|w| {
374 w.set_dsize(word_size); 374 w.set_dsize(word_size);
375 }); 375 });
@@ -380,7 +380,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
380 /// Blocking write. 380 /// Blocking write.
381 pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> { 381 pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {
382 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? 382 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
383 #[cfg(any(spi_v3, spi_v4, spi_v5))] 383 #[cfg(any(spi_v4, spi_v5, spi_v6))]
384 self.info.regs.cr1().modify(|w| w.set_spe(false)); 384 self.info.regs.cr1().modify(|w| w.set_spe(false));
385 self.set_word_size(W::CONFIG); 385 self.set_word_size(W::CONFIG);
386 self.info.regs.cr1().modify(|w| w.set_spe(true)); 386 self.info.regs.cr1().modify(|w| w.set_spe(true));
@@ -391,7 +391,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
391 // This is the case when the SPI has been created with `new_(blocking_?)txonly_nosck`. 391 // This is the case when the SPI has been created with `new_(blocking_?)txonly_nosck`.
392 // See https://github.com/embassy-rs/embassy/issues/2902 392 // See https://github.com/embassy-rs/embassy/issues/2902
393 // This is not documented as an errata by ST, and I've been unable to find anything online... 393 // This is not documented as an errata by ST, and I've been unable to find anything online...
394 #[cfg(not(any(spi_v1, spi_f1)))] 394 #[cfg(not(any(spi_v1, spi_v2)))]
395 write_word(self.info.regs, *word)?; 395 write_word(self.info.regs, *word)?;
396 396
397 // if we're doing tx only, after writing the last byte to FIFO we have to wait 397 // if we're doing tx only, after writing the last byte to FIFO we have to wait
@@ -401,14 +401,14 @@ impl<'d, M: PeriMode> Spi<'d, M> {
401 // Luckily this doesn't affect SPIv2+. 401 // Luckily this doesn't affect SPIv2+.
402 // See http://efton.sk/STM32/gotcha/g68.html 402 // See http://efton.sk/STM32/gotcha/g68.html
403 // ST doesn't seem to document this in errata sheets (?) 403 // ST doesn't seem to document this in errata sheets (?)
404 #[cfg(any(spi_v1, spi_f1))] 404 #[cfg(any(spi_v1, spi_v2))]
405 transfer_word(self.info.regs, *word)?; 405 transfer_word(self.info.regs, *word)?;
406 } 406 }
407 407
408 // wait until last word is transmitted. (except on v1, see above) 408 // wait until last word is transmitted. (except on v1, see above)
409 #[cfg(not(any(spi_v1, spi_f1, spi_v2)))] 409 #[cfg(not(any(spi_v1, spi_v2, spi_v3)))]
410 while !self.info.regs.sr().read().txc() {} 410 while !self.info.regs.sr().read().txc() {}
411 #[cfg(spi_v2)] 411 #[cfg(spi_v3)]
412 while self.info.regs.sr().read().bsy() {} 412 while self.info.regs.sr().read().bsy() {}
413 413
414 Ok(()) 414 Ok(())
@@ -417,7 +417,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
417 /// Blocking read. 417 /// Blocking read.
418 pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { 418 pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
419 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? 419 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
420 #[cfg(any(spi_v3, spi_v4, spi_v5))] 420 #[cfg(any(spi_v4, spi_v5, spi_v6))]
421 self.info.regs.cr1().modify(|w| w.set_spe(false)); 421 self.info.regs.cr1().modify(|w| w.set_spe(false));
422 self.set_word_size(W::CONFIG); 422 self.set_word_size(W::CONFIG);
423 self.info.regs.cr1().modify(|w| w.set_spe(true)); 423 self.info.regs.cr1().modify(|w| w.set_spe(true));
@@ -433,7 +433,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
433 /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. 433 /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.
434 pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { 434 pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
435 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? 435 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
436 #[cfg(any(spi_v3, spi_v4, spi_v5))] 436 #[cfg(any(spi_v4, spi_v5, spi_v6))]
437 self.info.regs.cr1().modify(|w| w.set_spe(false)); 437 self.info.regs.cr1().modify(|w| w.set_spe(false));
438 self.set_word_size(W::CONFIG); 438 self.set_word_size(W::CONFIG);
439 self.info.regs.cr1().modify(|w| w.set_spe(true)); 439 self.info.regs.cr1().modify(|w| w.set_spe(true));
@@ -452,7 +452,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
452 /// If `write` is shorter it is padded with zero bytes. 452 /// If `write` is shorter it is padded with zero bytes.
453 pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { 453 pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
454 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? 454 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
455 #[cfg(any(spi_v3, spi_v4, spi_v5))] 455 #[cfg(any(spi_v4, spi_v5, spi_v6))]
456 self.info.regs.cr1().modify(|w| w.set_spe(false)); 456 self.info.regs.cr1().modify(|w| w.set_spe(false));
457 self.set_word_size(W::CONFIG); 457 self.set_word_size(W::CONFIG);
458 self.info.regs.cr1().modify(|w| w.set_spe(true)); 458 self.info.regs.cr1().modify(|w| w.set_spe(true));
@@ -471,11 +471,11 @@ impl<'d, M: PeriMode> Spi<'d, M> {
471 471
472impl<'d> Spi<'d, Blocking> { 472impl<'d> Spi<'d, Blocking> {
473 /// Create a new blocking SPI driver. 473 /// Create a new blocking SPI driver.
474 pub fn new_blocking<T: Instance>( 474 pub fn new_blocking<T: Instance, #[cfg(afio)] A>(
475 peri: Peri<'d, T>, 475 peri: Peri<'d, T>,
476 sck: Peri<'d, impl SckPin<T>>, 476 sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,
477 mosi: Peri<'d, impl MosiPin<T>>, 477 mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
478 miso: Peri<'d, impl MisoPin<T>>, 478 miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,
479 config: Config, 479 config: Config,
480 ) -> Self { 480 ) -> Self {
481 Self::new_inner( 481 Self::new_inner(
@@ -490,10 +490,10 @@ impl<'d> Spi<'d, Blocking> {
490 } 490 }
491 491
492 /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI). 492 /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI).
493 pub fn new_blocking_rxonly<T: Instance>( 493 pub fn new_blocking_rxonly<T: Instance, #[cfg(afio)] A>(
494 peri: Peri<'d, T>, 494 peri: Peri<'d, T>,
495 sck: Peri<'d, impl SckPin<T>>, 495 sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,
496 miso: Peri<'d, impl MisoPin<T>>, 496 miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,
497 config: Config, 497 config: Config,
498 ) -> Self { 498 ) -> Self {
499 Self::new_inner( 499 Self::new_inner(
@@ -508,10 +508,10 @@ impl<'d> Spi<'d, Blocking> {
508 } 508 }
509 509
510 /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO). 510 /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO).
511 pub fn new_blocking_txonly<T: Instance>( 511 pub fn new_blocking_txonly<T: Instance, #[cfg(afio)] A>(
512 peri: Peri<'d, T>, 512 peri: Peri<'d, T>,
513 sck: Peri<'d, impl SckPin<T>>, 513 sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,
514 mosi: Peri<'d, impl MosiPin<T>>, 514 mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
515 config: Config, 515 config: Config,
516 ) -> Self { 516 ) -> Self {
517 Self::new_inner( 517 Self::new_inner(
@@ -528,9 +528,9 @@ impl<'d> Spi<'d, Blocking> {
528 /// Create a new SPI driver, in TX-only mode, without SCK pin. 528 /// Create a new SPI driver, in TX-only mode, without SCK pin.
529 /// 529 ///
530 /// This can be useful for bit-banging non-SPI protocols. 530 /// This can be useful for bit-banging non-SPI protocols.
531 pub fn new_blocking_txonly_nosck<T: Instance>( 531 pub fn new_blocking_txonly_nosck<T: Instance, #[cfg(afio)] A>(
532 peri: Peri<'d, T>, 532 peri: Peri<'d, T>,
533 mosi: Peri<'d, impl MosiPin<T>>, 533 mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
534 config: Config, 534 config: Config,
535 ) -> Self { 535 ) -> Self {
536 Self::new_inner( 536 Self::new_inner(
@@ -547,11 +547,11 @@ impl<'d> Spi<'d, Blocking> {
547 547
548impl<'d> Spi<'d, Async> { 548impl<'d> Spi<'d, Async> {
549 /// Create a new SPI driver. 549 /// Create a new SPI driver.
550 pub fn new<T: Instance>( 550 pub fn new<T: Instance, #[cfg(afio)] A>(
551 peri: Peri<'d, T>, 551 peri: Peri<'d, T>,
552 sck: Peri<'d, impl SckPin<T>>, 552 sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,
553 mosi: Peri<'d, impl MosiPin<T>>, 553 mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
554 miso: Peri<'d, impl MisoPin<T>>, 554 miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,
555 tx_dma: Peri<'d, impl TxDma<T>>, 555 tx_dma: Peri<'d, impl TxDma<T>>,
556 rx_dma: Peri<'d, impl RxDma<T>>, 556 rx_dma: Peri<'d, impl RxDma<T>>,
557 config: Config, 557 config: Config,
@@ -568,11 +568,11 @@ impl<'d> Spi<'d, Async> {
568 } 568 }
569 569
570 /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). 570 /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI).
571 pub fn new_rxonly<T: Instance>( 571 pub fn new_rxonly<T: Instance, #[cfg(afio)] A>(
572 peri: Peri<'d, T>, 572 peri: Peri<'d, T>,
573 sck: Peri<'d, impl SckPin<T>>, 573 sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,
574 miso: Peri<'d, impl MisoPin<T>>, 574 miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,
575 #[cfg(any(spi_v1, spi_f1, spi_v2))] tx_dma: Peri<'d, impl TxDma<T>>, 575 #[cfg(any(spi_v1, spi_v2, spi_v3))] tx_dma: Peri<'d, impl TxDma<T>>,
576 rx_dma: Peri<'d, impl RxDma<T>>, 576 rx_dma: Peri<'d, impl RxDma<T>>,
577 config: Config, 577 config: Config,
578 ) -> Self { 578 ) -> Self {
@@ -581,9 +581,9 @@ impl<'d> Spi<'d, Async> {
581 new_pin!(sck, config.sck_af()), 581 new_pin!(sck, config.sck_af()),
582 None, 582 None,
583 new_pin!(miso, AfType::input(config.miso_pull)), 583 new_pin!(miso, AfType::input(config.miso_pull)),
584 #[cfg(any(spi_v1, spi_f1, spi_v2))] 584 #[cfg(any(spi_v1, spi_v2, spi_v3))]
585 new_dma!(tx_dma), 585 new_dma!(tx_dma),
586 #[cfg(any(spi_v3, spi_v4, spi_v5))] 586 #[cfg(any(spi_v4, spi_v5, spi_v6))]
587 None, 587 None,
588 new_dma!(rx_dma), 588 new_dma!(rx_dma),
589 config, 589 config,
@@ -591,10 +591,10 @@ impl<'d> Spi<'d, Async> {
591 } 591 }
592 592
593 /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). 593 /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO).
594 pub fn new_txonly<T: Instance>( 594 pub fn new_txonly<T: Instance, #[cfg(afio)] A>(
595 peri: Peri<'d, T>, 595 peri: Peri<'d, T>,
596 sck: Peri<'d, impl SckPin<T>>, 596 sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,
597 mosi: Peri<'d, impl MosiPin<T>>, 597 mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
598 tx_dma: Peri<'d, impl TxDma<T>>, 598 tx_dma: Peri<'d, impl TxDma<T>>,
599 config: Config, 599 config: Config,
600 ) -> Self { 600 ) -> Self {
@@ -612,9 +612,9 @@ impl<'d> Spi<'d, Async> {
612 /// Create a new SPI driver, in TX-only mode, without SCK pin. 612 /// Create a new SPI driver, in TX-only mode, without SCK pin.
613 /// 613 ///
614 /// This can be useful for bit-banging non-SPI protocols. 614 /// This can be useful for bit-banging non-SPI protocols.
615 pub fn new_txonly_nosck<T: Instance>( 615 pub fn new_txonly_nosck<T: Instance, #[cfg(afio)] A>(
616 peri: Peri<'d, T>, 616 peri: Peri<'d, T>,
617 mosi: Peri<'d, impl MosiPin<T>>, 617 mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
618 tx_dma: Peri<'d, impl TxDma<T>>, 618 tx_dma: Peri<'d, impl TxDma<T>>,
619 config: Config, 619 config: Config,
620 ) -> Self { 620 ) -> Self {
@@ -677,7 +677,7 @@ impl<'d> Spi<'d, Async> {
677 self.info.regs.cr1().modify(|w| { 677 self.info.regs.cr1().modify(|w| {
678 w.set_spe(true); 678 w.set_spe(true);
679 }); 679 });
680 #[cfg(any(spi_v3, spi_v4, spi_v5))] 680 #[cfg(any(spi_v4, spi_v5, spi_v6))]
681 self.info.regs.cr1().modify(|w| { 681 self.info.regs.cr1().modify(|w| {
682 w.set_cstart(true); 682 w.set_cstart(true);
683 }); 683 });
@@ -690,7 +690,7 @@ impl<'d> Spi<'d, Async> {
690 } 690 }
691 691
692 /// SPI read, using DMA. 692 /// SPI read, using DMA.
693 #[cfg(any(spi_v3, spi_v4, spi_v5))] 693 #[cfg(any(spi_v4, spi_v5, spi_v6))]
694 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 694 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
695 if data.is_empty() { 695 if data.is_empty() {
696 return Ok(()); 696 return Ok(());
@@ -710,7 +710,7 @@ impl<'d> Spi<'d, Async> {
710 prev 710 prev
711 }); 711 });
712 712
713 #[cfg(spi_v3)] 713 #[cfg(spi_v4)]
714 let i2scfg = regs.i2scfgr().modify(|w| { 714 let i2scfg = regs.i2scfgr().modify(|w| {
715 w.i2smod().then(|| { 715 w.i2smod().then(|| {
716 let prev = w.i2scfg(); 716 let prev = w.i2scfg();
@@ -766,7 +766,7 @@ impl<'d> Spi<'d, Async> {
766 w.set_tsize(0); 766 w.set_tsize(0);
767 }); 767 });
768 768
769 #[cfg(spi_v3)] 769 #[cfg(spi_v4)]
770 if let Some(i2scfg) = i2scfg { 770 if let Some(i2scfg) = i2scfg {
771 regs.i2scfgr().modify(|w| { 771 regs.i2scfgr().modify(|w| {
772 w.set_i2scfg(i2scfg); 772 w.set_i2scfg(i2scfg);
@@ -777,7 +777,7 @@ impl<'d> Spi<'d, Async> {
777 } 777 }
778 778
779 /// SPI read, using DMA. 779 /// SPI read, using DMA.
780 #[cfg(any(spi_v1, spi_f1, spi_v2))] 780 #[cfg(any(spi_v1, spi_v2, spi_v3))]
781 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 781 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
782 if data.is_empty() { 782 if data.is_empty() {
783 return Ok(()); 783 return Ok(());
@@ -790,7 +790,7 @@ impl<'d> Spi<'d, Async> {
790 self.set_word_size(W::CONFIG); 790 self.set_word_size(W::CONFIG);
791 791
792 // SPIv3 clears rxfifo on SPE=0 792 // SPIv3 clears rxfifo on SPE=0
793 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 793 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
794 flush_rx_fifo(self.info.regs); 794 flush_rx_fifo(self.info.regs);
795 795
796 set_rxdmaen(self.info.regs, true); 796 set_rxdmaen(self.info.regs, true);
@@ -813,7 +813,7 @@ impl<'d> Spi<'d, Async> {
813 self.info.regs.cr1().modify(|w| { 813 self.info.regs.cr1().modify(|w| {
814 w.set_spe(true); 814 w.set_spe(true);
815 }); 815 });
816 #[cfg(any(spi_v3, spi_v4, spi_v5))] 816 #[cfg(any(spi_v4, spi_v5, spi_v6))]
817 self.info.regs.cr1().modify(|w| { 817 self.info.regs.cr1().modify(|w| {
818 w.set_cstart(true); 818 w.set_cstart(true);
819 }); 819 });
@@ -838,7 +838,7 @@ impl<'d> Spi<'d, Async> {
838 self.set_word_size(W::CONFIG); 838 self.set_word_size(W::CONFIG);
839 839
840 // SPIv3 clears rxfifo on SPE=0 840 // SPIv3 clears rxfifo on SPE=0
841 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 841 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
842 flush_rx_fifo(self.info.regs); 842 flush_rx_fifo(self.info.regs);
843 843
844 set_rxdmaen(self.info.regs, true); 844 set_rxdmaen(self.info.regs, true);
@@ -858,7 +858,7 @@ impl<'d> Spi<'d, Async> {
858 self.info.regs.cr1().modify(|w| { 858 self.info.regs.cr1().modify(|w| {
859 w.set_spe(true); 859 w.set_spe(true);
860 }); 860 });
861 #[cfg(any(spi_v3, spi_v4, spi_v5))] 861 #[cfg(any(spi_v4, spi_v5, spi_v6))]
862 self.info.regs.cr1().modify(|w| { 862 self.info.regs.cr1().modify(|w| {
863 w.set_cstart(true); 863 w.set_cstart(true);
864 }); 864 });
@@ -898,9 +898,9 @@ impl<'d, M: PeriMode> Drop for Spi<'d, M> {
898 } 898 }
899} 899}
900 900
901#[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 901#[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
902use vals::Br; 902use vals::Br;
903#[cfg(any(spi_v3, spi_v4, spi_v5))] 903#[cfg(any(spi_v4, spi_v5, spi_v6))]
904use vals::Mbr as Br; 904use vals::Mbr as Br;
905 905
906fn compute_baud_rate(kernel_clock: Hertz, freq: Hertz) -> Br { 906fn compute_baud_rate(kernel_clock: Hertz, freq: Hertz) -> Br {
@@ -941,21 +941,21 @@ pub(crate) trait RegsExt {
941 941
942impl RegsExt for Regs { 942impl RegsExt for Regs {
943 fn tx_ptr<W>(&self) -> *mut W { 943 fn tx_ptr<W>(&self) -> *mut W {
944 #[cfg(any(spi_v1, spi_f1))] 944 #[cfg(any(spi_v1, spi_v2))]
945 let dr = self.dr(); 945 let dr = self.dr();
946 #[cfg(spi_v2)] 946 #[cfg(spi_v3)]
947 let dr = self.dr16(); 947 let dr = self.dr16();
948 #[cfg(any(spi_v3, spi_v4, spi_v5))] 948 #[cfg(any(spi_v4, spi_v5, spi_v6))]
949 let dr = self.txdr32(); 949 let dr = self.txdr32();
950 dr.as_ptr() as *mut W 950 dr.as_ptr() as *mut W
951 } 951 }
952 952
953 fn rx_ptr<W>(&self) -> *mut W { 953 fn rx_ptr<W>(&self) -> *mut W {
954 #[cfg(any(spi_v1, spi_f1))] 954 #[cfg(any(spi_v1, spi_v2))]
955 let dr = self.dr(); 955 let dr = self.dr();
956 #[cfg(spi_v2)] 956 #[cfg(spi_v3)]
957 let dr = self.dr16(); 957 let dr = self.dr16();
958 #[cfg(any(spi_v3, spi_v4, spi_v5))] 958 #[cfg(any(spi_v4, spi_v5, spi_v6))]
959 let dr = self.rxdr32(); 959 let dr = self.rxdr32();
960 dr.as_ptr() as *mut W 960 dr.as_ptr() as *mut W
961 } 961 }
@@ -965,22 +965,22 @@ fn check_error_flags(sr: regs::Sr, ovr: bool) -> Result<(), Error> {
965 if sr.ovr() && ovr { 965 if sr.ovr() && ovr {
966 return Err(Error::Overrun); 966 return Err(Error::Overrun);
967 } 967 }
968 #[cfg(not(any(spi_f1, spi_v3, spi_v4, spi_v5)))] 968 #[cfg(not(any(spi_v1, spi_v4, spi_v5, spi_v6)))]
969 if sr.fre() { 969 if sr.fre() {
970 return Err(Error::Framing); 970 return Err(Error::Framing);
971 } 971 }
972 #[cfg(any(spi_v3, spi_v4, spi_v5))] 972 #[cfg(any(spi_v4, spi_v5, spi_v6))]
973 if sr.tifre() { 973 if sr.tifre() {
974 return Err(Error::Framing); 974 return Err(Error::Framing);
975 } 975 }
976 if sr.modf() { 976 if sr.modf() {
977 return Err(Error::ModeFault); 977 return Err(Error::ModeFault);
978 } 978 }
979 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 979 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
980 if sr.crcerr() { 980 if sr.crcerr() {
981 return Err(Error::Crc); 981 return Err(Error::Crc);
982 } 982 }
983 #[cfg(any(spi_v3, spi_v4, spi_v5))] 983 #[cfg(any(spi_v4, spi_v5, spi_v6))]
984 if sr.crce() { 984 if sr.crce() {
985 return Err(Error::Crc); 985 return Err(Error::Crc);
986 } 986 }
@@ -994,11 +994,11 @@ fn spin_until_tx_ready(regs: Regs, ovr: bool) -> Result<(), Error> {
994 994
995 check_error_flags(sr, ovr)?; 995 check_error_flags(sr, ovr)?;
996 996
997 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 997 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
998 if sr.txe() { 998 if sr.txe() {
999 return Ok(()); 999 return Ok(());
1000 } 1000 }
1001 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1001 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1002 if sr.txp() { 1002 if sr.txp() {
1003 return Ok(()); 1003 return Ok(());
1004 } 1004 }
@@ -1011,11 +1011,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
1011 1011
1012 check_error_flags(sr, true)?; 1012 check_error_flags(sr, true)?;
1013 1013
1014 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1014 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1015 if sr.rxne() { 1015 if sr.rxne() {
1016 return Ok(()); 1016 return Ok(());
1017 } 1017 }
1018 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1018 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1019 if sr.rxp() { 1019 if sr.rxp() {
1020 return Ok(()); 1020 return Ok(());
1021 } 1021 }
@@ -1023,46 +1023,46 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
1023} 1023}
1024 1024
1025pub(crate) fn flush_rx_fifo(regs: Regs) { 1025pub(crate) fn flush_rx_fifo(regs: Regs) {
1026 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1026 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1027 while regs.sr().read().rxne() { 1027 while regs.sr().read().rxne() {
1028 #[cfg(not(spi_v2))] 1028 #[cfg(not(spi_v3))]
1029 let _ = regs.dr().read(); 1029 let _ = regs.dr().read();
1030 #[cfg(spi_v2)] 1030 #[cfg(spi_v3)]
1031 let _ = regs.dr16().read(); 1031 let _ = regs.dr16().read();
1032 } 1032 }
1033 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1033 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1034 while regs.sr().read().rxp() { 1034 while regs.sr().read().rxp() {
1035 let _ = regs.rxdr32().read(); 1035 let _ = regs.rxdr32().read();
1036 } 1036 }
1037} 1037}
1038 1038
1039pub(crate) fn set_txdmaen(regs: Regs, val: bool) { 1039pub(crate) fn set_txdmaen(regs: Regs, val: bool) {
1040 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1040 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1041 regs.cr2().modify(|reg| { 1041 regs.cr2().modify(|reg| {
1042 reg.set_txdmaen(val); 1042 reg.set_txdmaen(val);
1043 }); 1043 });
1044 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1044 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1045 regs.cfg1().modify(|reg| { 1045 regs.cfg1().modify(|reg| {
1046 reg.set_txdmaen(val); 1046 reg.set_txdmaen(val);
1047 }); 1047 });
1048} 1048}
1049 1049
1050pub(crate) fn set_rxdmaen(regs: Regs, val: bool) { 1050pub(crate) fn set_rxdmaen(regs: Regs, val: bool) {
1051 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1051 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1052 regs.cr2().modify(|reg| { 1052 regs.cr2().modify(|reg| {
1053 reg.set_rxdmaen(val); 1053 reg.set_rxdmaen(val);
1054 }); 1054 });
1055 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1055 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1056 regs.cfg1().modify(|reg| { 1056 regs.cfg1().modify(|reg| {
1057 reg.set_rxdmaen(val); 1057 reg.set_rxdmaen(val);
1058 }); 1058 });
1059} 1059}
1060 1060
1061fn finish_dma(regs: Regs) { 1061fn finish_dma(regs: Regs) {
1062 #[cfg(spi_v2)] 1062 #[cfg(spi_v3)]
1063 while regs.sr().read().ftlvl().to_bits() > 0 {} 1063 while regs.sr().read().ftlvl().to_bits() > 0 {}
1064 1064
1065 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1065 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1066 { 1066 {
1067 if regs.cr2().read().tsize() == 0 { 1067 if regs.cr2().read().tsize() == 0 {
1068 while !regs.sr().read().txc() {} 1068 while !regs.sr().read().txc() {}
@@ -1070,7 +1070,7 @@ fn finish_dma(regs: Regs) {
1070 while !regs.sr().read().eot() {} 1070 while !regs.sr().read().eot() {}
1071 } 1071 }
1072 } 1072 }
1073 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1073 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1074 while regs.sr().read().bsy() {} 1074 while regs.sr().read().bsy() {}
1075 1075
1076 // Disable the spi peripheral 1076 // Disable the spi peripheral
@@ -1080,12 +1080,12 @@ fn finish_dma(regs: Regs) {
1080 1080
1081 // The peripheral automatically disables the DMA stream on completion without error, 1081 // The peripheral automatically disables the DMA stream on completion without error,
1082 // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. 1082 // but it does not clear the RXDMAEN/TXDMAEN flag in CR2.
1083 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1083 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1084 regs.cr2().modify(|reg| { 1084 regs.cr2().modify(|reg| {
1085 reg.set_txdmaen(false); 1085 reg.set_txdmaen(false);
1086 reg.set_rxdmaen(false); 1086 reg.set_rxdmaen(false);
1087 }); 1087 });
1088 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1088 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1089 regs.cfg1().modify(|reg| { 1089 regs.cfg1().modify(|reg| {
1090 reg.set_txdmaen(false); 1090 reg.set_txdmaen(false);
1091 reg.set_rxdmaen(false); 1091 reg.set_rxdmaen(false);
@@ -1098,7 +1098,7 @@ fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
1098 unsafe { 1098 unsafe {
1099 ptr::write_volatile(regs.tx_ptr(), tx_word); 1099 ptr::write_volatile(regs.tx_ptr(), tx_word);
1100 1100
1101 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1101 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1102 regs.cr1().modify(|reg| reg.set_cstart(true)); 1102 regs.cr1().modify(|reg| reg.set_cstart(true));
1103 } 1103 }
1104 1104
@@ -1117,7 +1117,7 @@ fn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> {
1117 unsafe { 1117 unsafe {
1118 ptr::write_volatile(regs.tx_ptr(), tx_word); 1118 ptr::write_volatile(regs.tx_ptr(), tx_word);
1119 1119
1120 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1120 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1121 regs.cr1().modify(|reg| reg.set_cstart(true)); 1121 regs.cr1().modify(|reg| reg.set_cstart(true));
1122 } 1122 }
1123 Ok(()) 1123 Ok(())
@@ -1225,7 +1225,7 @@ macro_rules! impl_word {
1225 }; 1225 };
1226} 1226}
1227 1227
1228#[cfg(any(spi_v1, spi_f1))] 1228#[cfg(any(spi_v1, spi_v2))]
1229mod word_impl { 1229mod word_impl {
1230 use super::*; 1230 use super::*;
1231 1231
@@ -1235,7 +1235,7 @@ mod word_impl {
1235 impl_word!(u16, vals::Dff::BITS16); 1235 impl_word!(u16, vals::Dff::BITS16);
1236} 1236}
1237 1237
1238#[cfg(spi_v2)] 1238#[cfg(spi_v3)]
1239mod word_impl { 1239mod word_impl {
1240 use super::*; 1240 use super::*;
1241 1241
@@ -1256,7 +1256,7 @@ mod word_impl {
1256 impl_word!(u16, (vals::Ds::BITS16, vals::Frxth::HALF)); 1256 impl_word!(u16, (vals::Ds::BITS16, vals::Frxth::HALF));
1257} 1257}
1258 1258
1259#[cfg(any(spi_v3, spi_v4, spi_v5))] 1259#[cfg(any(spi_v4, spi_v5, spi_v6))]
1260mod word_impl { 1260mod word_impl {
1261 use super::*; 1261 use super::*;
1262 1262
@@ -1309,13 +1309,13 @@ impl State {
1309 1309
1310peri_trait!(); 1310peri_trait!();
1311 1311
1312pin_trait!(SckPin, Instance); 1312pin_trait!(SckPin, Instance, @A);
1313pin_trait!(MosiPin, Instance); 1313pin_trait!(MosiPin, Instance, @A);
1314pin_trait!(MisoPin, Instance); 1314pin_trait!(MisoPin, Instance, @A);
1315pin_trait!(CsPin, Instance); 1315pin_trait!(CsPin, Instance, @A);
1316pin_trait!(MckPin, Instance); 1316pin_trait!(MckPin, Instance, @A);
1317pin_trait!(CkPin, Instance); 1317pin_trait!(CkPin, Instance, @A);
1318pin_trait!(WsPin, Instance); 1318pin_trait!(WsPin, Instance, @A);
1319dma_trait!(RxDma, Instance); 1319dma_trait!(RxDma, Instance);
1320dma_trait!(TxDma, Instance); 1320dma_trait!(TxDma, Instance);
1321 1321
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index b291fc155..484aae1d0 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -16,23 +16,24 @@ use crate::Peri;
16/// Complementary PWM pin wrapper. 16/// Complementary PWM pin wrapper.
17/// 17///
18/// This wraps a pin to make it usable with PWM. 18/// This wraps a pin to make it usable with PWM.
19pub struct ComplementaryPwmPin<'d, T, C> { 19pub struct ComplementaryPwmPin<'d, T, C, #[cfg(afio)] A> {
20 _pin: Peri<'d, AnyPin>, 20 #[allow(unused)]
21 phantom: PhantomData<(T, C)>, 21 pin: Peri<'d, AnyPin>,
22 phantom: PhantomData<if_afio!((T, C, A))>,
22} 23}
23 24
24impl<'d, T: AdvancedInstance4Channel, C: TimerChannel> ComplementaryPwmPin<'d, T, C> { 25impl<'d, T: AdvancedInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(ComplementaryPwmPin<'d, T, C, A>) {
25 /// Create a new complementary PWM pin instance. 26 /// Create a new complementary PWM pin instance.
26 pub fn new(pin: Peri<'d, impl TimerComplementaryPin<T, C>>, output_type: OutputType) -> Self { 27 pub fn new(pin: Peri<'d, if_afio!(impl TimerComplementaryPin<T, C, A>)>, output_type: OutputType) -> Self {
27 critical_section::with(|_| { 28 critical_section::with(|_| {
28 pin.set_low(); 29 pin.set_low();
29 pin.set_as_af( 30 set_as_af!(
30 pin.af_num(), 31 pin,
31 crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh), 32 crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh)
32 ); 33 );
33 }); 34 });
34 ComplementaryPwmPin { 35 ComplementaryPwmPin {
35 _pin: pin.into(), 36 pin: pin.into(),
36 phantom: PhantomData, 37 phantom: PhantomData,
37 } 38 }
38 } 39 }
@@ -54,17 +55,17 @@ pub enum IdlePolarity {
54 55
55impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { 56impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
56 /// Create a new complementary PWM driver. 57 /// Create a new complementary PWM driver.
57 #[allow(clippy::too_many_arguments)] 58 #[allow(clippy::too_many_arguments, unused)]
58 pub fn new( 59 pub fn new<#[cfg(afio)] A>(
59 tim: Peri<'d, T>, 60 tim: Peri<'d, T>,
60 _ch1: Option<PwmPin<'d, T, Ch1>>, 61 ch1: Option<if_afio!(PwmPin<'d, T, Ch1, A>)>,
61 _ch1n: Option<ComplementaryPwmPin<'d, T, Ch1>>, 62 ch1n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch1, A>)>,
62 _ch2: Option<PwmPin<'d, T, Ch2>>, 63 ch2: Option<if_afio!(PwmPin<'d, T, Ch2, A>)>,
63 _ch2n: Option<ComplementaryPwmPin<'d, T, Ch2>>, 64 ch2n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch2, A>)>,
64 _ch3: Option<PwmPin<'d, T, Ch3>>, 65 ch3: Option<if_afio!(PwmPin<'d, T, Ch3, A>)>,
65 _ch3n: Option<ComplementaryPwmPin<'d, T, Ch3>>, 66 ch3n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch3, A>)>,
66 _ch4: Option<PwmPin<'d, T, Ch4>>, 67 ch4: Option<if_afio!(PwmPin<'d, T, Ch4, A>)>,
67 _ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>, 68 ch4n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch4, A>)>,
68 freq: Hertz, 69 freq: Hertz,
69 counting_mode: CountingMode, 70 counting_mode: CountingMode,
70 ) -> Self { 71 ) -> Self {
@@ -185,6 +186,16 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
185 self.inner.set_complementary_output_polarity(channel, polarity); 186 self.inner.set_complementary_output_polarity(channel, polarity);
186 } 187 }
187 188
189 /// Set the main output polarity for a given channel.
190 pub fn set_main_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
191 self.inner.set_output_polarity(channel, polarity);
192 }
193
194 /// Set the complementary output polarity for a given channel.
195 pub fn set_complementary_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
196 self.inner.set_complementary_output_polarity(channel, polarity);
197 }
198
188 /// Set the dead time as a proportion of max_duty 199 /// Set the dead time as a proportion of max_duty
189 pub fn set_dead_time(&mut self, value: u16) { 200 pub fn set_dead_time(&mut self, value: u16) {
190 let (ckd, value) = compute_dead_time_value(value); 201 let (ckd, value) = compute_dead_time_value(value);
@@ -192,6 +203,66 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
192 self.inner.set_dead_time_clock_division(ckd); 203 self.inner.set_dead_time_clock_division(ckd);
193 self.inner.set_dead_time_value(value); 204 self.inner.set_dead_time_value(value);
194 } 205 }
206
207 /// Generate a sequence of PWM waveform
208 ///
209 /// Note:
210 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
211 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
212 #[allow(clippy::let_unit_value)] // eg. stm32f334
213 let req = dma.request();
214
215 let original_duty_state = self.inner.get_compare_value(channel);
216 let original_enable_state = self.inner.get_channel_enable_state(channel);
217 let original_update_dma_state = self.inner.get_update_dma_state();
218
219 if !original_update_dma_state {
220 self.inner.enable_update_dma(true);
221 }
222
223 if !original_enable_state {
224 self.inner.enable_channel(channel, true);
225 }
226
227 unsafe {
228 #[cfg(not(any(bdma, gpdma)))]
229 use crate::dma::{Burst, FifoThreshold};
230 use crate::dma::{Transfer, TransferOptions};
231
232 let dma_transfer_option = TransferOptions {
233 #[cfg(not(any(bdma, gpdma)))]
234 fifo_threshold: Some(FifoThreshold::Full),
235 #[cfg(not(any(bdma, gpdma)))]
236 mburst: Burst::Incr8,
237 ..Default::default()
238 };
239
240 Transfer::new_write(
241 dma,
242 req,
243 duty,
244 self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16,
245 dma_transfer_option,
246 )
247 .await
248 };
249
250 // restore output compare state
251 if !original_enable_state {
252 self.inner.enable_channel(channel, false);
253 }
254
255 self.inner.set_compare_value(channel, original_duty_state);
256
257 // Since DMA is closed before timer update event trigger DMA is turn off,
258 // this can almost always trigger a DMA FIFO error.
259 //
260 // optional TODO:
261 // clean FEIF after disable UDE
262 if !original_update_dma_state {
263 self.inner.enable_update_dma(false);
264 }
265 }
195} 266}
196 267
197impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> { 268impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> {
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs
index dda33e7f1..7a25e6c21 100644
--- a/embassy-stm32/src/timer/input_capture.rs
+++ b/embassy-stm32/src/timer/input_capture.rs
@@ -17,16 +17,17 @@ use crate::Peri;
17/// Capture pin wrapper. 17/// Capture pin wrapper.
18/// 18///
19/// This wraps a pin to make it usable with capture. 19/// This wraps a pin to make it usable with capture.
20pub struct CapturePin<'d, T, C> { 20pub struct CapturePin<'d, T, C, #[cfg(afio)] A> {
21 _pin: Peri<'d, AnyPin>, 21 #[allow(unused)]
22 phantom: PhantomData<(T, C)>, 22 pin: Peri<'d, AnyPin>,
23 phantom: PhantomData<if_afio!((T, C, A))>,
23} 24}
24impl<'d, T: GeneralInstance4Channel, C: TimerChannel> CapturePin<'d, T, C> { 25impl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(CapturePin<'d, T, C, A>) {
25 /// Create a new capture pin instance. 26 /// Create a new capture pin instance.
26 pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, pull: Pull) -> Self { 27 pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pull: Pull) -> Self {
27 pin.set_as_af(pin.af_num(), AfType::input(pull)); 28 set_as_af!(pin, AfType::input(pull));
28 CapturePin { 29 CapturePin {
29 _pin: pin.into(), 30 pin: pin.into(),
30 phantom: PhantomData, 31 phantom: PhantomData,
31 } 32 }
32 } 33 }
@@ -39,12 +40,13 @@ pub struct InputCapture<'d, T: GeneralInstance4Channel> {
39 40
40impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { 41impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {
41 /// Create a new input capture driver. 42 /// Create a new input capture driver.
42 pub fn new( 43 #[allow(unused)]
44 pub fn new<#[cfg(afio)] A>(
43 tim: Peri<'d, T>, 45 tim: Peri<'d, T>,
44 _ch1: Option<CapturePin<'d, T, Ch1>>, 46 ch1: Option<if_afio!(CapturePin<'d, T, Ch1, A>)>,
45 _ch2: Option<CapturePin<'d, T, Ch2>>, 47 ch2: Option<if_afio!(CapturePin<'d, T, Ch2, A>)>,
46 _ch3: Option<CapturePin<'d, T, Ch3>>, 48 ch3: Option<if_afio!(CapturePin<'d, T, Ch3, A>)>,
47 _ch4: Option<CapturePin<'d, T, Ch4>>, 49 ch4: Option<if_afio!(CapturePin<'d, T, Ch4, A>)>,
48 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, 50 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
49 freq: Hertz, 51 freq: Hertz,
50 counting_mode: CountingMode, 52 counting_mode: CountingMode,
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index 7062f5f4c..b09bc7166 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -223,15 +223,15 @@ pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + Ad
223/// Advanced 16-bit timer with 4 channels instance. 223/// Advanced 16-bit timer with 4 channels instance.
224pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} 224pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {}
225 225
226pin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel); 226pin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel, @A);
227pin_trait!(ExternalTriggerPin, GeneralInstance4Channel); 227pin_trait!(ExternalTriggerPin, GeneralInstance4Channel, @A);
228 228
229pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel); 229pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel, @A);
230 230
231pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput); 231pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput, @A);
232 232
233pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput); 233pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput, @A);
234pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput); 234pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput, @A);
235 235
236// Update Event trigger DMA for every timer 236// Update Event trigger DMA for every timer
237dma_trait!(UpDma, BasicInstance); 237dma_trait!(UpDma, BasicInstance);
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs
index 498d9c082..a75b41bd7 100644
--- a/embassy-stm32/src/timer/one_pulse.rs
+++ b/embassy-stm32/src/timer/one_pulse.rs
@@ -15,6 +15,7 @@ use crate::gpio::{AfType, AnyPin, Pull};
15use crate::interrupt::typelevel::{Binding, Interrupt}; 15use crate::interrupt::typelevel::{Binding, Interrupt};
16use crate::pac::timer::vals::Etp; 16use crate::pac::timer::vals::Etp;
17use crate::time::Hertz; 17use crate::time::Hertz;
18use crate::timer::TimerChannel;
18use crate::Peri; 19use crate::Peri;
19 20
20/// External input marker type. 21/// External input marker type.
@@ -42,7 +43,8 @@ impl From<ExternalTriggerPolarity> for Etp {
42/// 43///
43/// This wraps a pin to make it usable as a timer trigger. 44/// This wraps a pin to make it usable as a timer trigger.
44pub struct TriggerPin<'d, T, C> { 45pub struct TriggerPin<'d, T, C> {
45 _pin: Peri<'d, AnyPin>, 46 #[allow(unused)]
47 pin: Peri<'d, AnyPin>,
46 phantom: PhantomData<(T, C)>, 48 phantom: PhantomData<(T, C)>,
47} 49}
48 50
@@ -60,60 +62,23 @@ impl SealedTriggerSource for Ch1 {}
60impl SealedTriggerSource for Ch2 {} 62impl SealedTriggerSource for Ch2 {}
61impl SealedTriggerSource for Ext {} 63impl SealedTriggerSource for Ext {}
62 64
63trait SealedTimerTriggerPin<T, S>: crate::gpio::Pin {} 65impl<'d, T: GeneralInstance4Channel, C: TriggerSource + TimerChannel> TriggerPin<'d, T, C> {
64 66 /// Create a new Channel trigger pin instance.
65/// Marker trait for a trigger pin. 67 pub fn new<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pull: Pull) -> Self {
66#[expect(private_bounds)] 68 set_as_af!(pin, AfType::input(pull));
67// TODO: find better naming scheme than prefixing all pin traits with "Timer". 69 TriggerPin {
68// The trait name cannot conflict with the corresponding type's name. 70 pin: pin.into(),
69// Applies to other timer submodules as well. 71 phantom: PhantomData,
70pub trait TimerTriggerPin<T, S>: SealedTimerTriggerPin<T, S> { 72 }
71 /// Get the AF number needed to use this pin as a trigger source.
72 fn af_num(&self) -> u8;
73}
74
75impl<T, P, C> TimerTriggerPin<T, C> for P
76where
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
86impl<T, P> TimerTriggerPin<T, Ext> for P
87where
88 T: GeneralInstance4Channel,
89 P: ExternalTriggerPin<T>,
90{
91 fn af_num(&self) -> u8 {
92 ExternalTriggerPin::af_num(self)
93 } 73 }
94} 74}
95 75
96impl<T, P, C> SealedTimerTriggerPin<T, C> for P 76impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ext> {
97where 77 /// Create a new external trigger pin instance.
98 T: GeneralInstance4Channel, 78 pub fn new_external<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl ExternalTriggerPin<T, A>)>, pull: Pull) -> Self {
99 P: TimerPin<T, C>, 79 set_as_af!(pin, AfType::input(pull));
100 C: super::TimerChannel + TriggerSource,
101{
102}
103
104impl<T, P> SealedTimerTriggerPin<T, Ext> for P
105where
106 T: GeneralInstance4Channel,
107 P: ExternalTriggerPin<T>,
108{
109}
110
111impl<'d, T: GeneralInstance4Channel, C: TriggerSource> TriggerPin<'d, T, C> {
112 /// "Create a new Ch1 trigger pin instance.
113 pub fn new(pin: Peri<'d, impl TimerTriggerPin<T, C>>, pull: Pull) -> Self {
114 pin.set_as_af(pin.af_num(), AfType::input(pull));
115 TriggerPin { 80 TriggerPin {
116 _pin: pin.into(), 81 pin: pin.into(),
117 phantom: PhantomData, 82 phantom: PhantomData,
118 } 83 }
119 } 84 }
@@ -131,9 +96,10 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
131 /// 96 ///
132 /// The pulse is triggered by a channel 1 input pin on both rising and 97 /// The pulse is triggered by a channel 1 input pin on both rising and
133 /// falling edges. Channel 1 will unusable as an output. 98 /// falling edges. Channel 1 will unusable as an output.
99 #[allow(unused)]
134 pub fn new_ch1_edge_detect( 100 pub fn new_ch1_edge_detect(
135 tim: Peri<'d, T>, 101 tim: Peri<'d, T>,
136 _pin: TriggerPin<'d, T, Ch1>, 102 pin: TriggerPin<'d, T, Ch1>,
137 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, 103 _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
138 freq: Hertz, 104 freq: Hertz,
139 pulse_end: u32, 105 pulse_end: u32,
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs
index 1e55f2919..159b5a177 100644
--- a/embassy-stm32/src/timer/pwm_input.rs
+++ b/embassy-stm32/src/timer/pwm_input.rs
@@ -18,15 +18,25 @@ pub struct PwmInput<'d, T: GeneralInstance4Channel> {
18 18
19impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { 19impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
20 /// Create a new PWM input driver. 20 /// Create a new PWM input driver.
21 pub fn new_ch1(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin<T, Ch1>>, pull: Pull, freq: Hertz) -> Self { 21 pub fn new_ch1<#[cfg(afio)] A>(
22 pin.set_as_af(pin.af_num(), AfType::input(pull)); 22 tim: Peri<'d, T>,
23 pin: Peri<'d, if_afio!(impl TimerPin<T, Ch1, A>)>,
24 pull: Pull,
25 freq: Hertz,
26 ) -> Self {
27 set_as_af!(pin, AfType::input(pull));
23 28
24 Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) 29 Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2)
25 } 30 }
26 31
27 /// Create a new PWM input driver. 32 /// Create a new PWM input driver.
28 pub fn new_ch2(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin<T, Ch2>>, pull: Pull, freq: Hertz) -> Self { 33 pub fn new_ch2<#[cfg(afio)] A>(
29 pin.set_as_af(pin.af_num(), AfType::input(pull)); 34 tim: Peri<'d, T>,
35 pin: Peri<'d, if_afio!(impl TimerPin<T, Ch2, A>)>,
36 pull: Pull,
37 freq: Hertz,
38 ) -> Self {
39 set_as_af!(pin, AfType::input(pull));
30 40
31 Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) 41 Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1)
32 } 42 }
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs
index eabe1b22a..82b5968b0 100644
--- a/embassy-stm32/src/timer/qei.rs
+++ b/embassy-stm32/src/timer/qei.rs
@@ -20,20 +20,21 @@ pub enum Direction {
20} 20}
21 21
22/// Wrapper for using a pin with QEI. 22/// Wrapper for using a pin with QEI.
23pub struct QeiPin<'d, T, Channel> { 23pub struct QeiPin<'d, T, Channel, #[cfg(afio)] A> {
24 _pin: Peri<'d, AnyPin>, 24 #[allow(unused)]
25 phantom: PhantomData<(T, Channel)>, 25 pin: Peri<'d, AnyPin>,
26 phantom: PhantomData<if_afio!((T, Channel, A))>,
26} 27}
27 28
28impl<'d, T: GeneralInstance4Channel, C: QeiChannel> QeiPin<'d, T, C> { 29impl<'d, T: GeneralInstance4Channel, C: QeiChannel, #[cfg(afio)] A> if_afio!(QeiPin<'d, T, C, A>) {
29 /// Create a new QEI pin instance. 30 /// Create a new QEI pin instance.
30 pub fn new(pin: Peri<'d, impl TimerPin<T, C>>) -> Self { 31 pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>) -> Self {
31 critical_section::with(|_| { 32 critical_section::with(|_| {
32 pin.set_low(); 33 pin.set_low();
33 pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); 34 set_as_af!(pin, AfType::input(Pull::None));
34 }); 35 });
35 QeiPin { 36 QeiPin {
36 _pin: pin.into(), 37 pin: pin.into(),
37 phantom: PhantomData, 38 phantom: PhantomData,
38 } 39 }
39 } 40 }
@@ -58,7 +59,12 @@ pub struct Qei<'d, T: GeneralInstance4Channel> {
58 59
59impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { 60impl<'d, T: GeneralInstance4Channel> Qei<'d, T> {
60 /// Create a new quadrature decoder driver. 61 /// Create a new quadrature decoder driver.
61 pub fn new(tim: Peri<'d, T>, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { 62 #[allow(unused)]
63 pub fn new<#[cfg(afio)] A>(
64 tim: Peri<'d, T>,
65 ch1: if_afio!(QeiPin<'d, T, Ch1, A>),
66 ch2: if_afio!(QeiPin<'d, T, Ch2, A>),
67 ) -> Self {
62 Self::new_inner(tim) 68 Self::new_inner(tim)
63 } 69 }
64 70
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index c04b1ab97..e6165e42b 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -14,9 +14,10 @@ use crate::Peri;
14/// PWM pin wrapper. 14/// PWM pin wrapper.
15/// 15///
16/// This wraps a pin to make it usable with PWM. 16/// This wraps a pin to make it usable with PWM.
17pub struct PwmPin<'d, T, C> { 17pub struct PwmPin<'d, T, C, #[cfg(afio)] A> {
18 _pin: Peri<'d, AnyPin>, 18 #[allow(unused)]
19 phantom: PhantomData<(T, C)>, 19 pub(crate) pin: Peri<'d, AnyPin>,
20 phantom: PhantomData<if_afio!((T, C, A))>,
20} 21}
21 22
22/// PWM pin config 23/// PWM pin config
@@ -34,33 +35,33 @@ pub struct PwmPinConfig {
34 pub pull: Pull, 35 pub pull: Pull,
35} 36}
36 37
37impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> { 38impl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(PwmPin<'d, T, C, A>) {
38 /// Create a new PWM pin instance. 39 /// Create a new PWM pin instance.
39 pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, output_type: OutputType) -> Self { 40 pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, output_type: OutputType) -> Self {
40 critical_section::with(|_| { 41 critical_section::with(|_| {
41 pin.set_low(); 42 pin.set_low();
42 pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); 43 set_as_af!(pin, AfType::output(output_type, Speed::VeryHigh));
43 }); 44 });
44 PwmPin { 45 PwmPin {
45 _pin: pin.into(), 46 pin: pin.into(),
46 phantom: PhantomData, 47 phantom: PhantomData,
47 } 48 }
48 } 49 }
49 50
50 /// Create a new PWM pin instance with config. 51 /// Create a new PWM pin instance with a specific configuration.
51 pub fn new_with_config(pin: Peri<'d, impl TimerPin<T, C>>, pin_config: PwmPinConfig) -> Self { 52 pub fn new_with_config(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pin_config: PwmPinConfig) -> Self {
52 critical_section::with(|_| { 53 critical_section::with(|_| {
53 pin.set_low(); 54 pin.set_low();
54 pin.set_as_af( 55 #[cfg(gpio_v1)]
55 pin.af_num(), 56 set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed));
56 #[cfg(gpio_v1)] 57 #[cfg(gpio_v2)]
57 AfType::output(pin_config.output_type, pin_config.speed), 58 set_as_af!(
58 #[cfg(gpio_v2)] 59 pin,
59 AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), 60 AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull)
60 ); 61 );
61 }); 62 });
62 PwmPin { 63 PwmPin {
63 _pin: pin.into(), 64 pin: pin.into(),
64 phantom: PhantomData, 65 phantom: PhantomData,
65 } 66 }
66 } 67 }
@@ -178,12 +179,13 @@ pub struct SimplePwm<'d, T: GeneralInstance4Channel> {
178 179
179impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { 180impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
180 /// Create a new simple PWM driver. 181 /// Create a new simple PWM driver.
181 pub fn new( 182 #[allow(unused)]
183 pub fn new<#[cfg(afio)] A>(
182 tim: Peri<'d, T>, 184 tim: Peri<'d, T>,
183 _ch1: Option<PwmPin<'d, T, Ch1>>, 185 ch1: Option<if_afio!(PwmPin<'d, T, Ch1, A>)>,
184 _ch2: Option<PwmPin<'d, T, Ch2>>, 186 ch2: Option<if_afio!(PwmPin<'d, T, Ch2, A>)>,
185 _ch3: Option<PwmPin<'d, T, Ch3>>, 187 ch3: Option<if_afio!(PwmPin<'d, T, Ch3, A>)>,
186 _ch4: Option<PwmPin<'d, T, Ch4>>, 188 ch4: Option<if_afio!(PwmPin<'d, T, Ch4, A>)>,
187 freq: Hertz, 189 freq: Hertz,
188 counting_mode: CountingMode, 190 counting_mode: CountingMode,
189 ) -> Self { 191 ) -> Self {
diff --git a/embassy-stm32/src/tsc/pin_groups.rs b/embassy-stm32/src/tsc/pin_groups.rs
index 6f914a94e..84421f7ff 100644
--- a/embassy-stm32/src/tsc/pin_groups.rs
+++ b/embassy-stm32/src/tsc/pin_groups.rs
@@ -427,7 +427,7 @@ macro_rules! impl_set_io {
427 pub fn $method<Role: pin_roles::Role>(&mut self, pin: Peri<'d, impl $trait<T>>) -> IOPinWithRole<$group, Role> { 427 pub fn $method<Role: pin_roles::Role>(&mut self, pin: Peri<'d, impl $trait<T>>) -> IOPinWithRole<$group, Role> {
428 critical_section::with(|_| { 428 critical_section::with(|_| {
429 pin.set_low(); 429 pin.set_low();
430 pin.set_as_af(pin.af_num(), AfType::output(Role::output_type(), Speed::VeryHigh)); 430 set_as_af!(pin, AfType::output(Role::output_type(), Speed::VeryHigh));
431 let tsc_io_pin = trait_to_io_pin!($trait); 431 let tsc_io_pin = trait_to_io_pin!($trait);
432 let new_pin = Pin { 432 let new_pin = Pin {
433 _pin: pin.into(), 433 _pin: pin.into(),
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs
index 0a80adb8f..18aff4fbd 100644
--- a/embassy-stm32/src/ucpd.rs
+++ b/embassy-stm32/src/ucpd.rs
@@ -490,14 +490,14 @@ impl<'d, T: Instance> PdPhy<'d, T> {
490 let sr = r.sr().read(); 490 let sr = r.sr().read();
491 491
492 if sr.rxhrstdet() { 492 if sr.rxhrstdet() {
493 dma.request_stop(); 493 dma.request_pause();
494 494
495 // Clean and re-enable hard reset receive interrupt. 495 // Clean and re-enable hard reset receive interrupt.
496 r.icr().write(|w| w.set_rxhrstdetcf(true)); 496 r.icr().write(|w| w.set_rxhrstdetcf(true));
497 r.imr().modify(|w| w.set_rxhrstdetie(true)); 497 r.imr().modify(|w| w.set_rxhrstdetie(true));
498 Poll::Ready(Err(RxError::HardReset)) 498 Poll::Ready(Err(RxError::HardReset))
499 } else if sr.rxmsgend() { 499 } else if sr.rxmsgend() {
500 dma.request_stop(); 500 dma.request_pause();
501 // Should be read immediately on interrupt. 501 // Should be read immediately on interrupt.
502 rxpaysz = r.rx_payszr().read().rxpaysz().into(); 502 rxpaysz = r.rx_payszr().read().rxpaysz().into();
503 503
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs
index 729440c46..c734eed49 100644
--- a/embassy-stm32/src/usart/buffered.rs
+++ b/embassy-stm32/src/usart/buffered.rs
@@ -208,10 +208,10 @@ impl<'d> SetConfig for BufferedUartTx<'d> {
208 208
209impl<'d> BufferedUart<'d> { 209impl<'d> BufferedUart<'d> {
210 /// Create a new bidirectional buffered UART driver 210 /// Create a new bidirectional buffered UART driver
211 pub fn new<T: Instance>( 211 pub fn new<T: Instance, #[cfg(afio)] A>(
212 peri: Peri<'d, T>, 212 peri: Peri<'d, T>,
213 rx: Peri<'d, impl RxPin<T>>, 213 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
214 tx: Peri<'d, impl TxPin<T>>, 214 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
215 tx_buffer: &'d mut [u8], 215 tx_buffer: &'d mut [u8],
216 rx_buffer: &'d mut [u8], 216 rx_buffer: &'d mut [u8],
217 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 217 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
@@ -231,12 +231,12 @@ impl<'d> BufferedUart<'d> {
231 } 231 }
232 232
233 /// Create a new bidirectional buffered UART driver with request-to-send and clear-to-send pins 233 /// Create a new bidirectional buffered UART driver with request-to-send and clear-to-send pins
234 pub fn new_with_rtscts<T: Instance>( 234 pub fn new_with_rtscts<T: Instance, #[cfg(afio)] A>(
235 peri: Peri<'d, T>, 235 peri: Peri<'d, T>,
236 rx: Peri<'d, impl RxPin<T>>, 236 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
237 tx: Peri<'d, impl TxPin<T>>, 237 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
238 rts: Peri<'d, impl RtsPin<T>>, 238 rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,
239 cts: Peri<'d, impl CtsPin<T>>, 239 cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>,
240 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 240 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
241 tx_buffer: &'d mut [u8], 241 tx_buffer: &'d mut [u8],
242 rx_buffer: &'d mut [u8], 242 rx_buffer: &'d mut [u8],
@@ -256,11 +256,11 @@ impl<'d> BufferedUart<'d> {
256 } 256 }
257 257
258 /// Create a new bidirectional buffered UART driver with only the RTS pin as the DE pin 258 /// Create a new bidirectional buffered UART driver with only the RTS pin as the DE pin
259 pub fn new_with_rts_as_de<T: Instance>( 259 pub fn new_with_rts_as_de<T: Instance, #[cfg(afio)] A>(
260 peri: Peri<'d, T>, 260 peri: Peri<'d, T>,
261 rx: Peri<'d, impl RxPin<T>>, 261 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
262 tx: Peri<'d, impl TxPin<T>>, 262 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
263 rts: Peri<'d, impl RtsPin<T>>, 263 rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,
264 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 264 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
265 tx_buffer: &'d mut [u8], 265 tx_buffer: &'d mut [u8],
266 rx_buffer: &'d mut [u8], 266 rx_buffer: &'d mut [u8],
@@ -280,11 +280,11 @@ impl<'d> BufferedUart<'d> {
280 } 280 }
281 281
282 /// Create a new bidirectional buffered UART driver with only the request-to-send pin 282 /// Create a new bidirectional buffered UART driver with only the request-to-send pin
283 pub fn new_with_rts<T: Instance>( 283 pub fn new_with_rts<T: Instance, #[cfg(afio)] A>(
284 peri: Peri<'d, T>, 284 peri: Peri<'d, T>,
285 rx: Peri<'d, impl RxPin<T>>, 285 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
286 tx: Peri<'d, impl TxPin<T>>, 286 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
287 rts: Peri<'d, impl RtsPin<T>>, 287 rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,
288 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 288 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
289 tx_buffer: &'d mut [u8], 289 tx_buffer: &'d mut [u8],
290 rx_buffer: &'d mut [u8], 290 rx_buffer: &'d mut [u8],
@@ -305,11 +305,11 @@ impl<'d> BufferedUart<'d> {
305 305
306 /// Create a new bidirectional buffered UART driver with a driver-enable pin 306 /// Create a new bidirectional buffered UART driver with a driver-enable pin
307 #[cfg(not(any(usart_v1, usart_v2)))] 307 #[cfg(not(any(usart_v1, usart_v2)))]
308 pub fn new_with_de<T: Instance>( 308 pub fn new_with_de<T: Instance, #[cfg(afio)] A>(
309 peri: Peri<'d, T>, 309 peri: Peri<'d, T>,
310 rx: Peri<'d, impl RxPin<T>>, 310 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
311 tx: Peri<'d, impl TxPin<T>>, 311 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
312 de: Peri<'d, impl DePin<T>>, 312 de: Peri<'d, if_afio!(impl DePin<T, A>)>,
313 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 313 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
314 tx_buffer: &'d mut [u8], 314 tx_buffer: &'d mut [u8],
315 rx_buffer: &'d mut [u8], 315 rx_buffer: &'d mut [u8],
@@ -340,9 +340,9 @@ impl<'d> BufferedUart<'d> {
340 /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict 340 /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
341 /// on the line must be managed by software (for instance by using a centralized arbiter). 341 /// on the line must be managed by software (for instance by using a centralized arbiter).
342 #[doc(alias("HDSEL"))] 342 #[doc(alias("HDSEL"))]
343 pub fn new_half_duplex<T: Instance>( 343 pub fn new_half_duplex<T: Instance, #[cfg(afio)] A>(
344 peri: Peri<'d, T>, 344 peri: Peri<'d, T>,
345 tx: Peri<'d, impl TxPin<T>>, 345 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
346 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 346 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
347 tx_buffer: &'d mut [u8], 347 tx_buffer: &'d mut [u8],
348 rx_buffer: &'d mut [u8], 348 rx_buffer: &'d mut [u8],
@@ -379,9 +379,9 @@ impl<'d> BufferedUart<'d> {
379 /// on the line must be managed by software (for instance by using a centralized arbiter). 379 /// on the line must be managed by software (for instance by using a centralized arbiter).
380 #[cfg(not(any(usart_v1, usart_v2)))] 380 #[cfg(not(any(usart_v1, usart_v2)))]
381 #[doc(alias("HDSEL"))] 381 #[doc(alias("HDSEL"))]
382 pub fn new_half_duplex_on_rx<T: Instance>( 382 pub fn new_half_duplex_on_rx<T: Instance, #[cfg(afio)] A>(
383 peri: Peri<'d, T>, 383 peri: Peri<'d, T>,
384 rx: Peri<'d, impl RxPin<T>>, 384 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
385 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 385 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
386 tx_buffer: &'d mut [u8], 386 tx_buffer: &'d mut [u8],
387 rx_buffer: &'d mut [u8], 387 rx_buffer: &'d mut [u8],
@@ -692,6 +692,8 @@ impl<'d> BufferedUartTx<'d> {
692 fn blocking_write(&self, buf: &[u8]) -> Result<usize, Error> { 692 fn blocking_write(&self, buf: &[u8]) -> Result<usize, Error> {
693 loop { 693 loop {
694 let state = self.state; 694 let state = self.state;
695 state.tx_done.store(false, Ordering::Release);
696
695 let empty = state.tx_buf.is_empty(); 697 let empty = state.tx_buf.is_empty();
696 698
697 let mut tx_writer = unsafe { state.tx_buf.writer() }; 699 let mut tx_writer = unsafe { state.tx_buf.writer() };
@@ -713,7 +715,7 @@ impl<'d> BufferedUartTx<'d> {
713 fn blocking_flush(&self) -> Result<(), Error> { 715 fn blocking_flush(&self) -> Result<(), Error> {
714 loop { 716 loop {
715 let state = self.state; 717 let state = self.state;
716 if state.tx_buf.is_empty() { 718 if state.tx_done.load(Ordering::Acquire) {
717 return Ok(()); 719 return Ok(());
718 } 720 }
719 } 721 }
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index 5bece6d66..ff211e0c9 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -429,9 +429,9 @@ impl<'d, M: Mode> SetConfig for UartRx<'d, M> {
429 429
430impl<'d> UartTx<'d, Async> { 430impl<'d> UartTx<'d, Async> {
431 /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. 431 /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power.
432 pub fn new<T: Instance>( 432 pub fn new<T: Instance, #[cfg(afio)] A>(
433 peri: Peri<'d, T>, 433 peri: Peri<'d, T>,
434 tx: Peri<'d, impl TxPin<T>>, 434 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
435 tx_dma: Peri<'d, impl TxDma<T>>, 435 tx_dma: Peri<'d, impl TxDma<T>>,
436 config: Config, 436 config: Config,
437 ) -> Result<Self, ConfigError> { 437 ) -> Result<Self, ConfigError> {
@@ -439,10 +439,10 @@ impl<'d> UartTx<'d, Async> {
439 } 439 }
440 440
441 /// Create a new tx-only UART with a clear-to-send pin 441 /// Create a new tx-only UART with a clear-to-send pin
442 pub fn new_with_cts<T: Instance>( 442 pub fn new_with_cts<T: Instance, #[cfg(afio)] A>(
443 peri: Peri<'d, T>, 443 peri: Peri<'d, T>,
444 tx: Peri<'d, impl TxPin<T>>, 444 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
445 cts: Peri<'d, impl CtsPin<T>>, 445 cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>,
446 tx_dma: Peri<'d, impl TxDma<T>>, 446 tx_dma: Peri<'d, impl TxDma<T>>,
447 config: Config, 447 config: Config,
448 ) -> Result<Self, ConfigError> { 448 ) -> Result<Self, ConfigError> {
@@ -482,19 +482,19 @@ impl<'d> UartTx<'d, Blocking> {
482 /// Create a new blocking tx-only UART with no hardware flow control. 482 /// Create a new blocking tx-only UART with no hardware flow control.
483 /// 483 ///
484 /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. 484 /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power.
485 pub fn new_blocking<T: Instance>( 485 pub fn new_blocking<T: Instance, #[cfg(afio)] A>(
486 peri: Peri<'d, T>, 486 peri: Peri<'d, T>,
487 tx: Peri<'d, impl TxPin<T>>, 487 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
488 config: Config, 488 config: Config,
489 ) -> Result<Self, ConfigError> { 489 ) -> Result<Self, ConfigError> {
490 Self::new_inner(peri, new_pin!(tx, config.tx_af()), None, None, config) 490 Self::new_inner(peri, new_pin!(tx, config.tx_af()), None, None, config)
491 } 491 }
492 492
493 /// Create a new blocking tx-only UART with a clear-to-send pin 493 /// Create a new blocking tx-only UART with a clear-to-send pin
494 pub fn new_blocking_with_cts<T: Instance>( 494 pub fn new_blocking_with_cts<T: Instance, #[cfg(afio)] A>(
495 peri: Peri<'d, T>, 495 peri: Peri<'d, T>,
496 tx: Peri<'d, impl TxPin<T>>, 496 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
497 cts: Peri<'d, impl CtsPin<T>>, 497 cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>,
498 config: Config, 498 config: Config,
499 ) -> Result<Self, ConfigError> { 499 ) -> Result<Self, ConfigError> {
500 Self::new_inner( 500 Self::new_inner(
@@ -662,10 +662,10 @@ impl<'d> UartRx<'d, Async> {
662 /// Create a new rx-only UART with no hardware flow control. 662 /// Create a new rx-only UART with no hardware flow control.
663 /// 663 ///
664 /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. 664 /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power.
665 pub fn new<T: Instance>( 665 pub fn new<T: Instance, #[cfg(afio)] A>(
666 peri: Peri<'d, T>, 666 peri: Peri<'d, T>,
667 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 667 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
668 rx: Peri<'d, impl RxPin<T>>, 668 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
669 rx_dma: Peri<'d, impl RxDma<T>>, 669 rx_dma: Peri<'d, impl RxDma<T>>,
670 config: Config, 670 config: Config,
671 ) -> Result<Self, ConfigError> { 671 ) -> Result<Self, ConfigError> {
@@ -673,11 +673,11 @@ impl<'d> UartRx<'d, Async> {
673 } 673 }
674 674
675 /// Create a new rx-only UART with a request-to-send pin 675 /// Create a new rx-only UART with a request-to-send pin
676 pub fn new_with_rts<T: Instance>( 676 pub fn new_with_rts<T: Instance, #[cfg(afio)] A>(
677 peri: Peri<'d, T>, 677 peri: Peri<'d, T>,
678 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 678 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
679 rx: Peri<'d, impl RxPin<T>>, 679 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
680 rts: Peri<'d, impl RtsPin<T>>, 680 rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,
681 rx_dma: Peri<'d, impl RxDma<T>>, 681 rx_dma: Peri<'d, impl RxDma<T>>,
682 config: Config, 682 config: Config,
683 ) -> Result<Self, ConfigError> { 683 ) -> Result<Self, ConfigError> {
@@ -913,19 +913,19 @@ impl<'d> UartRx<'d, Blocking> {
913 /// Create a new rx-only UART with no hardware flow control. 913 /// Create a new rx-only UART with no hardware flow control.
914 /// 914 ///
915 /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. 915 /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power.
916 pub fn new_blocking<T: Instance>( 916 pub fn new_blocking<T: Instance, #[cfg(afio)] A>(
917 peri: Peri<'d, T>, 917 peri: Peri<'d, T>,
918 rx: Peri<'d, impl RxPin<T>>, 918 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
919 config: Config, 919 config: Config,
920 ) -> Result<Self, ConfigError> { 920 ) -> Result<Self, ConfigError> {
921 Self::new_inner(peri, new_pin!(rx, config.rx_af()), None, None, config) 921 Self::new_inner(peri, new_pin!(rx, config.rx_af()), None, None, config)
922 } 922 }
923 923
924 /// Create a new rx-only UART with a request-to-send pin 924 /// Create a new rx-only UART with a request-to-send pin
925 pub fn new_blocking_with_rts<T: Instance>( 925 pub fn new_blocking_with_rts<T: Instance, #[cfg(afio)] A>(
926 peri: Peri<'d, T>, 926 peri: Peri<'d, T>,
927 rx: Peri<'d, impl RxPin<T>>, 927 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
928 rts: Peri<'d, impl RtsPin<T>>, 928 rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,
929 config: Config, 929 config: Config,
930 ) -> Result<Self, ConfigError> { 930 ) -> Result<Self, ConfigError> {
931 Self::new_inner( 931 Self::new_inner(
@@ -1109,10 +1109,10 @@ fn drop_tx_rx(info: &Info, state: &State) {
1109 1109
1110impl<'d> Uart<'d, Async> { 1110impl<'d> Uart<'d, Async> {
1111 /// Create a new bidirectional UART 1111 /// Create a new bidirectional UART
1112 pub fn new<T: Instance>( 1112 pub fn new<T: Instance, #[cfg(afio)] A>(
1113 peri: Peri<'d, T>, 1113 peri: Peri<'d, T>,
1114 rx: Peri<'d, impl RxPin<T>>, 1114 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
1115 tx: Peri<'d, impl TxPin<T>>, 1115 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
1116 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 1116 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
1117 tx_dma: Peri<'d, impl TxDma<T>>, 1117 tx_dma: Peri<'d, impl TxDma<T>>,
1118 rx_dma: Peri<'d, impl RxDma<T>>, 1118 rx_dma: Peri<'d, impl RxDma<T>>,
@@ -1132,13 +1132,13 @@ impl<'d> Uart<'d, Async> {
1132 } 1132 }
1133 1133
1134 /// Create a new bidirectional UART with request-to-send and clear-to-send pins 1134 /// Create a new bidirectional UART with request-to-send and clear-to-send pins
1135 pub fn new_with_rtscts<T: Instance>( 1135 pub fn new_with_rtscts<T: Instance, #[cfg(afio)] A>(
1136 peri: Peri<'d, T>, 1136 peri: Peri<'d, T>,
1137 rx: Peri<'d, impl RxPin<T>>, 1137 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
1138 tx: Peri<'d, impl TxPin<T>>, 1138 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
1139 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 1139 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
1140 rts: Peri<'d, impl RtsPin<T>>, 1140 rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,
1141 cts: Peri<'d, impl CtsPin<T>>, 1141 cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>,
1142 tx_dma: Peri<'d, impl TxDma<T>>, 1142 tx_dma: Peri<'d, impl TxDma<T>>,
1143 rx_dma: Peri<'d, impl RxDma<T>>, 1143 rx_dma: Peri<'d, impl RxDma<T>>,
1144 config: Config, 1144 config: Config,
@@ -1158,12 +1158,12 @@ impl<'d> Uart<'d, Async> {
1158 1158
1159 #[cfg(not(any(usart_v1, usart_v2)))] 1159 #[cfg(not(any(usart_v1, usart_v2)))]
1160 /// Create a new bidirectional UART with a driver-enable pin 1160 /// Create a new bidirectional UART with a driver-enable pin
1161 pub fn new_with_de<T: Instance>( 1161 pub fn new_with_de<T: Instance, #[cfg(afio)] A>(
1162 peri: Peri<'d, T>, 1162 peri: Peri<'d, T>,
1163 rx: Peri<'d, impl RxPin<T>>, 1163 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
1164 tx: Peri<'d, impl TxPin<T>>, 1164 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
1165 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 1165 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
1166 de: Peri<'d, impl DePin<T>>, 1166 de: Peri<'d, if_afio!(impl DePin<T, A>)>,
1167 tx_dma: Peri<'d, impl TxDma<T>>, 1167 tx_dma: Peri<'d, impl TxDma<T>>,
1168 rx_dma: Peri<'d, impl RxDma<T>>, 1168 rx_dma: Peri<'d, impl RxDma<T>>,
1169 config: Config, 1169 config: Config,
@@ -1193,9 +1193,9 @@ impl<'d> Uart<'d, Async> {
1193 /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict 1193 /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
1194 /// on the line must be managed by software (for instance by using a centralized arbiter). 1194 /// on the line must be managed by software (for instance by using a centralized arbiter).
1195 #[doc(alias("HDSEL"))] 1195 #[doc(alias("HDSEL"))]
1196 pub fn new_half_duplex<T: Instance>( 1196 pub fn new_half_duplex<T: Instance, #[cfg(afio)] A>(
1197 peri: Peri<'d, T>, 1197 peri: Peri<'d, T>,
1198 tx: Peri<'d, impl TxPin<T>>, 1198 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
1199 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 1199 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
1200 tx_dma: Peri<'d, impl TxDma<T>>, 1200 tx_dma: Peri<'d, impl TxDma<T>>,
1201 rx_dma: Peri<'d, impl RxDma<T>>, 1201 rx_dma: Peri<'d, impl RxDma<T>>,
@@ -1232,9 +1232,9 @@ impl<'d> Uart<'d, Async> {
1232 /// on the line must be managed by software (for instance by using a centralized arbiter). 1232 /// on the line must be managed by software (for instance by using a centralized arbiter).
1233 #[cfg(not(any(usart_v1, usart_v2)))] 1233 #[cfg(not(any(usart_v1, usart_v2)))]
1234 #[doc(alias("HDSEL"))] 1234 #[doc(alias("HDSEL"))]
1235 pub fn new_half_duplex_on_rx<T: Instance>( 1235 pub fn new_half_duplex_on_rx<T: Instance, #[cfg(afio)] A>(
1236 peri: Peri<'d, T>, 1236 peri: Peri<'d, T>,
1237 rx: Peri<'d, impl RxPin<T>>, 1237 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
1238 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 1238 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
1239 tx_dma: Peri<'d, impl TxDma<T>>, 1239 tx_dma: Peri<'d, impl TxDma<T>>,
1240 rx_dma: Peri<'d, impl RxDma<T>>, 1240 rx_dma: Peri<'d, impl RxDma<T>>,
@@ -1280,10 +1280,10 @@ impl<'d> Uart<'d, Async> {
1280 1280
1281impl<'d> Uart<'d, Blocking> { 1281impl<'d> Uart<'d, Blocking> {
1282 /// Create a new blocking bidirectional UART. 1282 /// Create a new blocking bidirectional UART.
1283 pub fn new_blocking<T: Instance>( 1283 pub fn new_blocking<T: Instance, #[cfg(afio)] A>(
1284 peri: Peri<'d, T>, 1284 peri: Peri<'d, T>,
1285 rx: Peri<'d, impl RxPin<T>>, 1285 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
1286 tx: Peri<'d, impl TxPin<T>>, 1286 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
1287 config: Config, 1287 config: Config,
1288 ) -> Result<Self, ConfigError> { 1288 ) -> Result<Self, ConfigError> {
1289 Self::new_inner( 1289 Self::new_inner(
@@ -1300,12 +1300,12 @@ impl<'d> Uart<'d, Blocking> {
1300 } 1300 }
1301 1301
1302 /// Create a new bidirectional UART with request-to-send and clear-to-send pins 1302 /// Create a new bidirectional UART with request-to-send and clear-to-send pins
1303 pub fn new_blocking_with_rtscts<T: Instance>( 1303 pub fn new_blocking_with_rtscts<T: Instance, #[cfg(afio)] A>(
1304 peri: Peri<'d, T>, 1304 peri: Peri<'d, T>,
1305 rx: Peri<'d, impl RxPin<T>>, 1305 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
1306 tx: Peri<'d, impl TxPin<T>>, 1306 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
1307 rts: Peri<'d, impl RtsPin<T>>, 1307 rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,
1308 cts: Peri<'d, impl CtsPin<T>>, 1308 cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>,
1309 config: Config, 1309 config: Config,
1310 ) -> Result<Self, ConfigError> { 1310 ) -> Result<Self, ConfigError> {
1311 Self::new_inner( 1311 Self::new_inner(
@@ -1323,11 +1323,11 @@ impl<'d> Uart<'d, Blocking> {
1323 1323
1324 #[cfg(not(any(usart_v1, usart_v2)))] 1324 #[cfg(not(any(usart_v1, usart_v2)))]
1325 /// Create a new bidirectional UART with a driver-enable pin 1325 /// Create a new bidirectional UART with a driver-enable pin
1326 pub fn new_blocking_with_de<T: Instance>( 1326 pub fn new_blocking_with_de<T: Instance, #[cfg(afio)] A>(
1327 peri: Peri<'d, T>, 1327 peri: Peri<'d, T>,
1328 rx: Peri<'d, impl RxPin<T>>, 1328 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
1329 tx: Peri<'d, impl TxPin<T>>, 1329 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
1330 de: Peri<'d, impl DePin<T>>, 1330 de: Peri<'d, if_afio!(impl DePin<T, A>)>,
1331 config: Config, 1331 config: Config,
1332 ) -> Result<Self, ConfigError> { 1332 ) -> Result<Self, ConfigError> {
1333 Self::new_inner( 1333 Self::new_inner(
@@ -1354,9 +1354,9 @@ impl<'d> Uart<'d, Blocking> {
1354 /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict 1354 /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
1355 /// on the line must be managed by software (for instance by using a centralized arbiter). 1355 /// on the line must be managed by software (for instance by using a centralized arbiter).
1356 #[doc(alias("HDSEL"))] 1356 #[doc(alias("HDSEL"))]
1357 pub fn new_blocking_half_duplex<T: Instance>( 1357 pub fn new_blocking_half_duplex<T: Instance, #[cfg(afio)] A>(
1358 peri: Peri<'d, T>, 1358 peri: Peri<'d, T>,
1359 tx: Peri<'d, impl TxPin<T>>, 1359 tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,
1360 mut config: Config, 1360 mut config: Config,
1361 readback: HalfDuplexReadback, 1361 readback: HalfDuplexReadback,
1362 ) -> Result<Self, ConfigError> { 1362 ) -> Result<Self, ConfigError> {
@@ -1390,9 +1390,9 @@ impl<'d> Uart<'d, Blocking> {
1390 /// on the line must be managed by software (for instance by using a centralized arbiter). 1390 /// on the line must be managed by software (for instance by using a centralized arbiter).
1391 #[cfg(not(any(usart_v1, usart_v2)))] 1391 #[cfg(not(any(usart_v1, usart_v2)))]
1392 #[doc(alias("HDSEL"))] 1392 #[doc(alias("HDSEL"))]
1393 pub fn new_blocking_half_duplex_on_rx<T: Instance>( 1393 pub fn new_blocking_half_duplex_on_rx<T: Instance, #[cfg(afio)] A>(
1394 peri: Peri<'d, T>, 1394 peri: Peri<'d, T>,
1395 rx: Peri<'d, impl RxPin<T>>, 1395 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,
1396 mut config: Config, 1396 mut config: Config,
1397 readback: HalfDuplexReadback, 1397 readback: HalfDuplexReadback,
1398 ) -> Result<Self, ConfigError> { 1398 ) -> Result<Self, ConfigError> {
@@ -1965,9 +1965,7 @@ pub use buffered::*;
1965pub use crate::usart::buffered::InterruptHandler as BufferedInterruptHandler; 1965pub use crate::usart::buffered::InterruptHandler as BufferedInterruptHandler;
1966mod buffered; 1966mod buffered;
1967 1967
1968#[cfg(not(gpdma))]
1969mod ringbuffered; 1968mod ringbuffered;
1970#[cfg(not(gpdma))]
1971pub use ringbuffered::RingBufferedUartRx; 1969pub use ringbuffered::RingBufferedUartRx;
1972 1970
1973#[cfg(any(usart_v1, usart_v2))] 1971#[cfg(any(usart_v1, usart_v2))]
@@ -2057,12 +2055,12 @@ pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
2057 type Interrupt: interrupt::typelevel::Interrupt; 2055 type Interrupt: interrupt::typelevel::Interrupt;
2058} 2056}
2059 2057
2060pin_trait!(RxPin, Instance); 2058pin_trait!(RxPin, Instance, @A);
2061pin_trait!(TxPin, Instance); 2059pin_trait!(TxPin, Instance, @A);
2062pin_trait!(CtsPin, Instance); 2060pin_trait!(CtsPin, Instance, @A);
2063pin_trait!(RtsPin, Instance); 2061pin_trait!(RtsPin, Instance, @A);
2064pin_trait!(CkPin, Instance); 2062pin_trait!(CkPin, Instance, @A);
2065pin_trait!(DePin, Instance); 2063pin_trait!(DePin, Instance, @A);
2066 2064
2067dma_trait!(TxDma, Instance); 2065dma_trait!(TxDma, Instance);
2068dma_trait!(RxDma, Instance); 2066dma_trait!(RxDma, Instance);
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs
index 1d4a44896..5f4e87834 100644
--- a/embassy-stm32/src/usart/ringbuffered.rs
+++ b/embassy-stm32/src/usart/ringbuffered.rs
@@ -381,7 +381,7 @@ impl ReadReady for RingBufferedUartRx<'_> {
381 crate::dma::ringbuffer::Error::Overrun => Self::Error::Overrun, 381 crate::dma::ringbuffer::Error::Overrun => Self::Error::Overrun,
382 crate::dma::ringbuffer::Error::DmaUnsynced => { 382 crate::dma::ringbuffer::Error::DmaUnsynced => {
383 error!( 383 error!(
384 "Ringbuffer error: DmaUNsynced, driver implementation is 384 "Ringbuffer error: DmaUNsynced, driver implementation is
385 probably bugged please open an issue" 385 probably bugged please open an issue"
386 ); 386 );
387 // we report this as overrun since its recoverable in the same way 387 // we report this as overrun since its recoverable in the same way
diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs
index 1c3b99b93..5ce81b131 100644
--- a/embassy-stm32/src/usb/otg.rs
+++ b/embassy-stm32/src/usb/otg.rs
@@ -34,7 +34,7 @@ macro_rules! config_ulpi_pins {
34 ($($pin:ident),*) => { 34 ($($pin:ident),*) => {
35 critical_section::with(|_| { 35 critical_section::with(|_| {
36 $( 36 $(
37 $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 37 set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));
38 )* 38 )*
39 }) 39 })
40 }; 40 };
@@ -68,8 +68,8 @@ impl<'d, T: Instance> Driver<'d, T> {
68 ep_out_buffer: &'d mut [u8], 68 ep_out_buffer: &'d mut [u8],
69 config: Config, 69 config: Config,
70 ) -> Self { 70 ) -> Self {
71 dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 71 set_as_af!(dp, AfType::output(OutputType::PushPull, Speed::VeryHigh));
72 dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 72 set_as_af!(dm, AfType::output(OutputType::PushPull, Speed::VeryHigh));
73 73
74 let regs = T::regs(); 74 let regs = T::regs();
75 75
@@ -107,8 +107,8 @@ impl<'d, T: Instance> Driver<'d, T> {
107 // For STM32U5 High speed pins need to be left in analog mode 107 // For STM32U5 High speed pins need to be left in analog mode
108 #[cfg(not(any(all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))] 108 #[cfg(not(any(all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))]
109 { 109 {
110 _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 110 set_as_af!(_dp, AfType::output(OutputType::PushPull, Speed::VeryHigh));
111 _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 111 set_as_af!(_dm, AfType::output(OutputType::PushPull, Speed::VeryHigh));
112 } 112 }
113 113
114 let instance = OtgInstance { 114 let instance = OtgInstance {
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs
index 54596aeae..9e08d99b3 100644
--- a/embassy-stm32/src/usb/usb.rs
+++ b/embassy-stm32/src/usb/usb.rs
@@ -298,7 +298,7 @@ impl<'d, T: Instance> Driver<'d, T> {
298 ) -> Self { 298 ) -> Self {
299 { 299 {
300 use crate::gpio::{AfType, OutputType, Speed}; 300 use crate::gpio::{AfType, OutputType, Speed};
301 sof.set_as_af(sof.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 301 set_as_af!(sof, AfType::output(OutputType::PushPull, Speed::VeryHigh));
302 } 302 }
303 303
304 Self::new(_usb, _irq, dp, dm) 304 Self::new(_usb, _irq, dp, dm)
@@ -329,8 +329,8 @@ impl<'d, T: Instance> Driver<'d, T> {
329 #[cfg(not(stm32l1))] 329 #[cfg(not(stm32l1))]
330 { 330 {
331 use crate::gpio::{AfType, OutputType, Speed}; 331 use crate::gpio::{AfType, OutputType, Speed};
332 dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 332 set_as_af!(dp, AfType::output(OutputType::PushPull, Speed::VeryHigh));
333 dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 333 set_as_af!(dm, AfType::output(OutputType::PushPull, Speed::VeryHigh));
334 } 334 }
335 #[cfg(stm32l1)] 335 #[cfg(stm32l1)]
336 let _ = (dp, dm); // suppress "unused" warnings. 336 let _ = (dp, dm); // suppress "unused" warnings.
diff --git a/embassy-stm32/src/xspi/enums.rs b/embassy-stm32/src/xspi/enums.rs
index c96641180..2e510fada 100644
--- a/embassy-stm32/src/xspi/enums.rs
+++ b/embassy-stm32/src/xspi/enums.rs
@@ -22,6 +22,7 @@ impl Into<u8> for XspiMode {
22 22
23/// Xspi lane width 23/// Xspi lane width
24#[derive(Copy, Clone)] 24#[derive(Copy, Clone)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
25pub enum XspiWidth { 26pub enum XspiWidth {
26 /// None 27 /// None
27 NONE, 28 NONE,
@@ -50,6 +51,7 @@ impl Into<u8> for XspiWidth {
50/// Wrap Size 51/// Wrap Size
51#[allow(missing_docs)] 52#[allow(missing_docs)]
52#[derive(Copy, Clone)] 53#[derive(Copy, Clone)]
54#[cfg_attr(feature = "defmt", derive(defmt::Format))]
53pub enum WrapSize { 55pub enum WrapSize {
54 None, 56 None,
55 _16Bytes, 57 _16Bytes,
@@ -73,6 +75,7 @@ impl Into<u8> for WrapSize {
73/// Memory Type 75/// Memory Type
74#[allow(missing_docs)] 76#[allow(missing_docs)]
75#[derive(Copy, Clone)] 77#[derive(Copy, Clone)]
78#[cfg_attr(feature = "defmt", derive(defmt::Format))]
76pub enum MemoryType { 79pub enum MemoryType {
77 Micron, 80 Micron,
78 Macronix, 81 Macronix,
@@ -98,6 +101,7 @@ impl Into<u8> for MemoryType {
98/// Xspi memory size. 101/// Xspi memory size.
99#[allow(missing_docs)] 102#[allow(missing_docs)]
100#[derive(Copy, Clone)] 103#[derive(Copy, Clone)]
104#[cfg_attr(feature = "defmt", derive(defmt::Format))]
101pub enum MemorySize { 105pub enum MemorySize {
102 _1KiB, 106 _1KiB,
103 _2KiB, 107 _2KiB,
@@ -158,6 +162,7 @@ impl Into<u8> for MemorySize {
158 162
159/// Xspi Address size 163/// Xspi Address size
160#[derive(Copy, Clone)] 164#[derive(Copy, Clone)]
165#[cfg_attr(feature = "defmt", derive(defmt::Format))]
161pub enum AddressSize { 166pub enum AddressSize {
162 /// 8-bit address 167 /// 8-bit address
163 _8bit, 168 _8bit,
@@ -183,6 +188,7 @@ impl Into<u8> for AddressSize {
183/// Time the Chip Select line stays high. 188/// Time the Chip Select line stays high.
184#[allow(missing_docs)] 189#[allow(missing_docs)]
185#[derive(Copy, Clone)] 190#[derive(Copy, Clone)]
191#[cfg_attr(feature = "defmt", derive(defmt::Format))]
186pub enum ChipSelectHighTime { 192pub enum ChipSelectHighTime {
187 _1Cycle, 193 _1Cycle,
188 _2Cycle, 194 _2Cycle,
@@ -212,6 +218,7 @@ impl Into<u8> for ChipSelectHighTime {
212/// FIFO threshold. 218/// FIFO threshold.
213#[allow(missing_docs)] 219#[allow(missing_docs)]
214#[derive(Copy, Clone)] 220#[derive(Copy, Clone)]
221#[cfg_attr(feature = "defmt", derive(defmt::Format))]
215pub enum FIFOThresholdLevel { 222pub enum FIFOThresholdLevel {
216 _1Bytes, 223 _1Bytes,
217 _2Bytes, 224 _2Bytes,
@@ -289,6 +296,7 @@ impl Into<u8> for FIFOThresholdLevel {
289/// Dummy cycle count 296/// Dummy cycle count
290#[allow(missing_docs)] 297#[allow(missing_docs)]
291#[derive(Copy, Clone)] 298#[derive(Copy, Clone)]
299#[cfg_attr(feature = "defmt", derive(defmt::Format))]
292pub enum DummyCycles { 300pub enum DummyCycles {
293 _0, 301 _0,
294 _1, 302 _1,
diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs
index 60ccf3c97..901569f64 100644
--- a/embassy-stm32/src/xspi/mod.rs
+++ b/embassy-stm32/src/xspi/mod.rs
@@ -23,6 +23,7 @@ use crate::{peripherals, Peri};
23 23
24/// XPSI driver config. 24/// XPSI driver config.
25#[derive(Clone, Copy)] 25#[derive(Clone, Copy)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct Config { 27pub struct Config {
27 /// Fifo threshold used by the peripheral to generate the interrupt indicating data 28 /// Fifo threshold used by the peripheral to generate the interrupt indicating data
28 /// or space is available in the FIFO 29 /// or space is available in the FIFO
@@ -80,6 +81,8 @@ impl Default for Config {
80} 81}
81 82
82/// XSPI transfer configuration. 83/// XSPI transfer configuration.
84#[derive(Clone, Copy)]
85#[cfg_attr(feature = "defmt", derive(defmt::Format))]
83pub struct TransferConfig { 86pub struct TransferConfig {
84 /// Instruction width (IMODE) 87 /// Instruction width (IMODE)
85 pub iwidth: XspiWidth, 88 pub iwidth: XspiWidth,
@@ -110,7 +113,7 @@ pub struct TransferConfig {
110 113
111 /// Data width (DMODE) 114 /// Data width (DMODE)
112 pub dwidth: XspiWidth, 115 pub dwidth: XspiWidth,
113 /// Data buffer 116 /// Data Double Transfer rate enable
114 pub ddtr: bool, 117 pub ddtr: bool,
115 118
116 /// Number of dummy cycles (DCYC) 119 /// Number of dummy cycles (DCYC)
@@ -424,11 +427,6 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> {
424 // Configure alternate bytes 427 // Configure alternate bytes
425 if let Some(ab) = command.alternate_bytes { 428 if let Some(ab) = command.alternate_bytes {
426 T::REGS.abr().write(|v| v.set_alternate(ab)); 429 T::REGS.abr().write(|v| v.set_alternate(ab));
427 T::REGS.ccr().modify(|w| {
428 w.set_abmode(CcrAbmode::from_bits(command.abwidth.into()));
429 w.set_abdtr(command.abdtr);
430 w.set_absize(CcrAbsize::from_bits(command.absize.into()));
431 })
432 } else { 430 } else {
433 T::REGS.ccr().modify(|w| { 431 T::REGS.ccr().modify(|w| {
434 // disable alternate bytes 432 // disable alternate bytes
@@ -445,14 +443,14 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> {
445 if let Some(data_length) = data_len { 443 if let Some(data_length) = data_len {
446 T::REGS.dlr().write(|v| { 444 T::REGS.dlr().write(|v| {
447 v.set_dl((data_length - 1) as u32); 445 v.set_dl((data_length - 1) as u32);
448 }) 446 });
449 } else { 447 } else {
450 T::REGS.dlr().write(|v| { 448 T::REGS.dlr().write(|v| {
451 v.set_dl((0) as u32); 449 v.set_dl((0) as u32);
452 }) 450 });
453 } 451 }
454 452
455 // Configure instruction/address/data modes 453 // Configure instruction/address/alternate bytes/data modes
456 T::REGS.ccr().modify(|w| { 454 T::REGS.ccr().modify(|w| {
457 w.set_imode(CcrImode::from_bits(command.iwidth.into())); 455 w.set_imode(CcrImode::from_bits(command.iwidth.into()));
458 w.set_idtr(command.idtr); 456 w.set_idtr(command.idtr);
@@ -462,6 +460,10 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> {
462 w.set_addtr(command.addtr); 460 w.set_addtr(command.addtr);
463 w.set_adsize(CcrAdsize::from_bits(command.adsize.into())); 461 w.set_adsize(CcrAdsize::from_bits(command.adsize.into()));
464 462
463 w.set_abmode(CcrAbmode::from_bits(command.abwidth.into()));
464 w.set_abdtr(command.abdtr);
465 w.set_absize(CcrAbsize::from_bits(command.absize.into()));
466
465 w.set_dmode(CcrDmode::from_bits(command.dwidth.into())); 467 w.set_dmode(CcrDmode::from_bits(command.dwidth.into()));
466 w.set_ddtr(command.ddtr); 468 w.set_ddtr(command.ddtr);
467 }); 469 });
diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md
index a53d5f5b1..242b8b7ab 100644
--- a/embassy-sync/CHANGELOG.md
+++ b/embassy-sync/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10- Fix wakers getting dropped by `Signal::reset` 10- Fix wakers getting dropped by `Signal::reset`
11- Remove `Sized` trait bound from `MutexGuard::map`
11 12
12## 0.7.2 - 2025-08-26 13## 0.7.2 - 2025-08-26
13 14
diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml
index 30a27c13f..6494da727 100644
--- a/embassy-sync/Cargo.toml
+++ b/embassy-sync/Cargo.toml
@@ -17,6 +17,8 @@ categories = [
17[package.metadata.embassy] 17[package.metadata.embassy]
18build = [ 18build = [
19 {target = "thumbv6m-none-eabi", features = ["defmt"]}, 19 {target = "thumbv6m-none-eabi", features = ["defmt"]},
20 # Xtensa builds
21 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt"]},
20] 22]
21 23
22[package.metadata.embassy_docs] 24[package.metadata.embassy_docs]
diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs
index 4ce6dd987..aea682899 100644
--- a/embassy-sync/src/mutex.rs
+++ b/embassy-sync/src/mutex.rs
@@ -187,7 +187,7 @@ where
187 T: ?Sized, 187 T: ?Sized,
188{ 188{
189 /// Returns a locked view over a portion of the locked data. 189 /// Returns a locked view over a portion of the locked data.
190 pub fn map<U>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { 190 pub fn map<U: ?Sized>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> {
191 let mutex = this.mutex; 191 let mutex = this.mutex;
192 let value = fun(unsafe { &mut *this.mutex.inner.get() }); 192 let value = fun(unsafe { &mut *this.mutex.inner.get() });
193 // Don't run the `drop` method for MutexGuard. The ownership of the underlying 193 // Don't run the `drop` method for MutexGuard. The ownership of the underlying
@@ -279,7 +279,7 @@ where
279 T: ?Sized, 279 T: ?Sized,
280{ 280{
281 /// Returns a locked view over a portion of the locked data. 281 /// Returns a locked view over a portion of the locked data.
282 pub fn map<U>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { 282 pub fn map<U: ?Sized>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> {
283 let state = this.state; 283 let state = this.state;
284 let value = fun(unsafe { &mut *this.value }); 284 let value = fun(unsafe { &mut *this.value });
285 // Don't run the `drop` method for MutexGuard. The ownership of the underlying 285 // Don't run the `drop` method for MutexGuard. The ownership of the underlying
diff --git a/embassy-time-queue-utils/Cargo.toml b/embassy-time-queue-utils/Cargo.toml
index e1abf1cd8..13da62874 100644
--- a/embassy-time-queue-utils/Cargo.toml
+++ b/embassy-time-queue-utils/Cargo.toml
@@ -56,6 +56,9 @@ _generic-queue = []
56build = [ 56build = [
57 {target = "thumbv6m-none-eabi", features = []}, 57 {target = "thumbv6m-none-eabi", features = []},
58 {target = "thumbv6m-none-eabi", features = ["generic-queue-8"]}, 58 {target = "thumbv6m-none-eabi", features = ["generic-queue-8"]},
59 # Xtensa builds
60 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = []},
61 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["generic-queue-8"]},
59] 62]
60 63
61[package.metadata.embassy_docs] 64[package.metadata.embassy_docs]
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml
index 6ebf0a468..2d7c3c1fa 100644
--- a/embassy-time/Cargo.toml
+++ b/embassy-time/Cargo.toml
@@ -17,6 +17,8 @@ categories = [
17[package.metadata.embassy] 17[package.metadata.embassy]
18build = [ 18build = [
19 {target = "thumbv6m-none-eabi", features = ["defmt", "defmt-timestamp-uptime", "mock-driver"]}, 19 {target = "thumbv6m-none-eabi", features = ["defmt", "defmt-timestamp-uptime", "mock-driver"]},
20 # Xtensa builds
21 {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt", "defmt-timestamp-uptime", "mock-driver"]},
20] 22]
21 23
22[package.metadata.embassy_docs] 24[package.metadata.embassy_docs]
diff --git a/embassy-usb-dfu/CHANGELOG.md b/embassy-usb-dfu/CHANGELOG.md
index 7e5adb1f2..0088e66fe 100644
--- a/embassy-usb-dfu/CHANGELOG.md
+++ b/embassy-usb-dfu/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- changed: Do not reset in the GetStatus request 11- changed: Do not reset in the GetStatus request
12- Allow enabling the `application` and `dfu` feature at the same time
12 13
13## 0.2.0 - 2025-08-27 14## 0.2.0 - 2025-08-27
14 15
diff --git a/embassy-usb-dfu/src/application.rs b/embassy-usb-dfu/src/application.rs
index 4b7b72073..78eb2c083 100644
--- a/embassy-usb-dfu/src/application.rs
+++ b/embassy-usb-dfu/src/application.rs
@@ -1,3 +1,4 @@
1//! Application part of DFU logic
1use embassy_boot::BlockingFirmwareState; 2use embassy_boot::BlockingFirmwareState;
2use embassy_time::{Duration, Instant}; 3use embassy_time::{Duration, Instant};
3use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; 4use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
diff --git a/embassy-usb-dfu/src/dfu.rs b/embassy-usb-dfu/src/dfu.rs
index 3a390a37a..7c28d04cf 100644
--- a/embassy-usb-dfu/src/dfu.rs
+++ b/embassy-usb-dfu/src/dfu.rs
@@ -1,3 +1,4 @@
1//! DFU bootloader part of DFU logic
1use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError}; 2use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError};
2use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; 3use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
3use embassy_usb::driver::Driver; 4use embassy_usb::driver::Driver;
diff --git a/embassy-usb-dfu/src/lib.rs b/embassy-usb-dfu/src/lib.rs
index 54ffa7276..e9f4278b6 100644
--- a/embassy-usb-dfu/src/lib.rs
+++ b/embassy-usb-dfu/src/lib.rs
@@ -6,21 +6,15 @@ mod fmt;
6pub mod consts; 6pub mod consts;
7 7
8#[cfg(feature = "dfu")] 8#[cfg(feature = "dfu")]
9mod dfu; 9pub mod dfu;
10#[cfg(feature = "dfu")] 10#[cfg(all(feature = "dfu", not(feature = "application")))]
11pub use self::dfu::*; 11pub use self::dfu::*;
12 12
13#[cfg(feature = "application")] 13#[cfg(feature = "application")]
14mod application; 14pub mod application;
15#[cfg(feature = "application")] 15#[cfg(all(feature = "application", not(feature = "dfu")))]
16pub use self::application::*; 16pub use self::application::*;
17 17
18#[cfg(any(
19 all(feature = "dfu", feature = "application"),
20 not(any(feature = "dfu", feature = "application"))
21))]
22compile_error!("usb-dfu must be compiled with exactly one of `dfu`, or `application` features");
23
24/// Provides a platform-agnostic interface for initiating a system reset. 18/// Provides a platform-agnostic interface for initiating a system reset.
25/// 19///
26/// This crate exposes `ResetImmediate` when compiled with cortex-m or esp32c3 support, which immediately issues a 20/// This crate exposes `ResetImmediate` when compiled with cortex-m or esp32c3 support, which immediately issues a
diff --git a/embassy-usb-driver/CHANGELOG.md b/embassy-usb-driver/CHANGELOG.md
index 15875e087..71768d7e5 100644
--- a/embassy-usb-driver/CHANGELOG.md
+++ b/embassy-usb-driver/CHANGELOG.md
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Add `EndpointOut::read_data()` and `EndpointIn::write_data()` provided methods.
12
11## 0.2.0 - 2025-07-16 13## 0.2.0 - 2025-07-16
12 14
13- Make USB endpoint allocator methods accept an optional `EndpointAddress`. 15- Make USB endpoint allocator methods accept an optional `EndpointAddress`.
diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs
index 99616f1ec..3ad96c61d 100644
--- a/embassy-usb-driver/src/lib.rs
+++ b/embassy-usb-driver/src/lib.rs
@@ -236,6 +236,22 @@ pub trait EndpointOut: Endpoint {
236 /// 236 ///
237 /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. 237 /// This should also clear any NAK flags and prepare the endpoint to receive the next packet.
238 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>; 238 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>;
239
240 /// Read until the buffer is full or we receive a short packet from the USB host returning the
241 /// actual length of the entire data block.
242 ///
243 /// This should also clear any NAK flags and prepare the endpoint to receive the next packet or
244 /// data block.
245 async fn read_transfer(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
246 let mut n = 0;
247 loop {
248 let i = self.read(&mut buf[n..]).await?;
249 n += i;
250 if i < self.info().max_packet_size as usize {
251 return Ok(n);
252 }
253 }
254 }
239} 255}
240 256
241/// USB control pipe trait. 257/// USB control pipe trait.
@@ -349,6 +365,20 @@ pub trait ControlPipe {
349pub trait EndpointIn: Endpoint { 365pub trait EndpointIn: Endpoint {
350 /// Write a single packet of data to the endpoint. 366 /// Write a single packet of data to the endpoint.
351 async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>; 367 async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>;
368
369 /// Write all the data from buf to the endpoint one wMaxPacketSize chunk at a time.
370 ///
371 /// If the buffer size is evenly divisible by wMaxPacketSize, this will also ensure the
372 /// terminating zero-length-packet is transmitted.
373 async fn write_transfer(&mut self, buf: &[u8], needs_zlp: bool) -> Result<(), EndpointError> {
374 for chunk in buf.chunks(self.info().max_packet_size as usize) {
375 self.write(chunk).await?;
376 }
377 if needs_zlp && buf.len() % self.info().max_packet_size as usize == 0 {
378 self.write(&[]).await?;
379 }
380 Ok(())
381 }
352} 382}
353 383
354#[derive(Copy, Clone, Eq, PartialEq, Debug)] 384#[derive(Copy, Clone, Eq, PartialEq, Debug)]
diff --git a/embassy-usb-synopsys-otg/CHANGELOG.md b/embassy-usb-synopsys-otg/CHANGELOG.md
index 9ca90f5e9..45353d907 100644
--- a/embassy-usb-synopsys-otg/CHANGELOG.md
+++ b/embassy-usb-synopsys-otg/CHANGELOG.md
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10 10
11## 0.3.1 - 2025-08-26 11## 0.3.1 - 2025-08-26
12 12
13- Improve receive performance, more efficient copy from FIFO
14
13## 0.3.0 - 2025-07-22 15## 0.3.0 - 2025-07-22
14 16
15- Bump `embassy-usb-driver` to v0.2.0 17- Bump `embassy-usb-driver` to v0.2.0
diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs
index 9d74c046d..6b4a87bdf 100644
--- a/embassy-usb-synopsys-otg/src/lib.rs
+++ b/embassy-usb-synopsys-otg/src/lib.rs
@@ -76,10 +76,16 @@ pub unsafe fn on_interrupt<const MAX_EP_COUNT: usize>(r: Otg, state: &State<MAX_
76 let buf = 76 let buf =
77 unsafe { core::slice::from_raw_parts_mut(*state.ep_states[ep_num].out_buffer.get(), len) }; 77 unsafe { core::slice::from_raw_parts_mut(*state.ep_states[ep_num].out_buffer.get(), len) };
78 78
79 for chunk in buf.chunks_mut(4) { 79 let mut chunks = buf.chunks_exact_mut(4);
80 for chunk in &mut chunks {
80 // RX FIFO is shared so always read from fifo(0) 81 // RX FIFO is shared so always read from fifo(0)
81 let data = r.fifo(0).read().0; 82 let data = r.fifo(0).read().0;
82 chunk.copy_from_slice(&data.to_ne_bytes()[0..chunk.len()]); 83 chunk.copy_from_slice(&data.to_ne_bytes());
84 }
85 let rem = chunks.into_remainder();
86 if !rem.is_empty() {
87 let data = r.fifo(0).read().0;
88 rem.copy_from_slice(&data.to_ne_bytes()[0..rem.len()]);
83 } 89 }
84 90
85 state.ep_states[ep_num].out_size.store(len as u16, Ordering::Release); 91 state.ep_states[ep_num].out_size.store(len as u16, Ordering::Release);
@@ -1229,23 +1235,19 @@ impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> {
1229 }); 1235 });
1230 1236
1231 // Write data to FIFO 1237 // Write data to FIFO
1232 let chunks = buf.chunks_exact(4);
1233 // Stash the last partial chunk
1234 let rem = chunks.remainder();
1235 let last_chunk = (!rem.is_empty()).then(|| {
1236 let mut tmp = [0u8; 4];
1237 tmp[0..rem.len()].copy_from_slice(rem);
1238 u32::from_ne_bytes(tmp)
1239 });
1240
1241 let fifo = self.regs.fifo(index); 1238 let fifo = self.regs.fifo(index);
1242 for chunk in chunks { 1239 let mut chunks = buf.chunks_exact(4);
1240 for chunk in &mut chunks {
1243 let val = u32::from_ne_bytes(chunk.try_into().unwrap()); 1241 let val = u32::from_ne_bytes(chunk.try_into().unwrap());
1244 fifo.write_value(regs::Fifo(val)); 1242 fifo.write_value(regs::Fifo(val));
1245 } 1243 }
1246 // Write any last chunk 1244 // Write any last chunk
1247 if let Some(val) = last_chunk { 1245 let rem = chunks.remainder();
1248 fifo.write_value(regs::Fifo(val)); 1246 if !rem.is_empty() {
1247 let mut tmp = [0u8; 4];
1248 tmp[0..rem.len()].copy_from_slice(rem);
1249 let tmp = u32::from_ne_bytes(tmp);
1250 fifo.write_value(regs::Fifo(tmp));
1249 } 1251 }
1250 }); 1252 });
1251 1253
diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml
index 79b27f269..579748595 100644
--- a/examples/lpc55s69/Cargo.toml
+++ b/examples/lpc55s69/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7publish = false 7publish = false
8 8
9[dependencies] 9[dependencies]
10embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55", "rt", "defmt", "time-driver-rtc"] } 10embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55-core0", "rt", "defmt", "time-driver-rtc"] }
11embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } 11embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] }
12embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 12embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
13embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } 13embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] }
diff --git a/examples/mspm0c1104/Cargo.toml b/examples/mspm0c1104/Cargo.toml
index 4daddbbb4..21434106a 100644
--- a/examples/mspm0c1104/Cargo.toml
+++ b/examples/mspm0c1104/Cargo.toml
@@ -33,7 +33,6 @@ lto = true
33codegen-units = 1 33codegen-units = 1
34 34
35[package.metadata.embassy] 35[package.metadata.embassy]
36skip = true # TODO: remove when we find a way to decrease the defmt buffer size in ci.
37build = [ 36build = [
38 { target = "thumbv6m-none-eabi", artifact-dir = "out/examples/mspm0c1104" } 37 { target = "thumbv6m-none-eabi", artifact-dir = "out/examples/mspm0c1104", env = { DEFMT_RTT_BUFFER_SIZE = "72" }}
39] 38]
diff --git a/examples/mspm0g3507/src/bin/adc.rs b/examples/mspm0g3507/src/bin/adc.rs
new file mode 100644
index 000000000..ceccc7c02
--- /dev/null
+++ b/examples/mspm0g3507/src/bin/adc.rs
@@ -0,0 +1,39 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_mspm0::adc::{self, Adc, Vrsel};
7use embassy_mspm0::{bind_interrupts, peripherals, Config};
8use embassy_time::Timer;
9use {defmt_rtt as _, panic_halt as _};
10
11bind_interrupts!(struct Irqs {
12 ADC0 => adc::InterruptHandler<peripherals::ADC0>;
13});
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) -> ! {
17 info!("Hello world!");
18 let p = embassy_mspm0::init(Config::default());
19
20 // Configure adc with sequence 0 to 1
21 let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs);
22 let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PB20.into(), Vrsel::VddaVssa)];
23 let mut readings = [0u16; 2];
24
25 loop {
26 let r = adc.read_channel(&p.PA27).await;
27 info!("Raw adc PA27: {}", r);
28 // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be
29 // approximated to voltage (~0.0008 per step).
30 let mut x = r as u32;
31 x = x * 8;
32 info!("Adc voltage PA27: {},{:#04}", x / 10_000, x % 10_000);
33 // Read a sequence of channels
34 adc.read_sequence(sequence.into_iter(), &mut readings).await;
35 info!("Raw adc sequence: {}", readings);
36
37 Timer::after_millis(400).await;
38 }
39}
diff --git a/examples/mspm0l1306/src/bin/adc.rs b/examples/mspm0l1306/src/bin/adc.rs
new file mode 100644
index 000000000..2806b98cc
--- /dev/null
+++ b/examples/mspm0l1306/src/bin/adc.rs
@@ -0,0 +1,39 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_mspm0::adc::{self, Adc, Vrsel};
7use embassy_mspm0::{bind_interrupts, peripherals, Config};
8use embassy_time::Timer;
9use {defmt_rtt as _, panic_halt as _};
10
11bind_interrupts!(struct Irqs {
12 ADC0 => adc::InterruptHandler<peripherals::ADC0>;
13});
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) -> ! {
17 info!("Hello world!");
18 let p = embassy_mspm0::init(Config::default());
19
20 // Configure adc with sequence 0 to 1
21 let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs);
22 let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PA20.into(), Vrsel::VddaVssa)];
23 let mut readings = [0u16; 2];
24
25 loop {
26 let r = adc.read_channel(&p.PA27).await;
27 info!("Raw adc PA27: {}", r);
28 // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be
29 // approximated to voltage (~0.0008 per step).
30 let mut x = r as u32;
31 x = x * 8;
32 info!("Adc voltage PA27: {},{:#04}", x / 10_000, x % 10_000);
33 // Read a sequence of channels
34 adc.read_sequence(sequence.into_iter(), &mut readings).await;
35 info!("Raw adc sequence: {}", readings);
36
37 Timer::after_millis(400).await;
38 }
39}
diff --git a/examples/nrf52840-edf/.cargo/config.toml b/examples/nrf52840-edf/.cargo/config.toml
new file mode 100644
index 000000000..e0b9ce59e
--- /dev/null
+++ b/examples/nrf52840-edf/.cargo/config.toml
@@ -0,0 +1,9 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
3runner = "probe-rs run --chip nRF52840_xxAA"
4
5[build]
6target = "thumbv7em-none-eabi"
7
8[env]
9DEFMT_LOG = "debug"
diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml
new file mode 100644
index 000000000..1e8803233
--- /dev/null
+++ b/examples/nrf52840-edf/Cargo.toml
@@ -0,0 +1,27 @@
1[package]
2edition = "2021"
3name = "embassy-nrf52840-edf-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6publish = false
7
8[dependencies]
9# NOTE: "scheduler-deadline" and "embassy-time-driver" features are enabled
10embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "scheduler-deadline", "embassy-time-driver"] }
11embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
13
14defmt = "1.0.1"
15defmt-rtt = "1.0.0"
16
17cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
18cortex-m-rt = "0.7.0"
19panic-probe = { version = "1.0.0", features = ["print-defmt"] }
20
21[profile.release]
22debug = 2
23
24[package.metadata.embassy]
25build = [
26 { target = "thumbv7em-none-eabi", artifact-dir = "out/examples/nrf52840-edf" }
27]
diff --git a/examples/nrf52840-edf/build.rs b/examples/nrf52840-edf/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/nrf52840-edf/build.rs
@@ -0,0 +1,35 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35}
diff --git a/examples/nrf52840-edf/memory.x b/examples/nrf52840-edf/memory.x
new file mode 100644
index 000000000..15b492bce
--- /dev/null
+++ b/examples/nrf52840-edf/memory.x
@@ -0,0 +1,12 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
5 RAM : ORIGIN = 0x20000000, LENGTH = 256K
6
7 /* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */
8 /*
9 FLASH : ORIGIN = 0x00027000, LENGTH = 868K
10 RAM : ORIGIN = 0x20020000, LENGTH = 128K
11 */
12}
diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs
new file mode 100644
index 000000000..d888e17d1
--- /dev/null
+++ b/examples/nrf52840-edf/src/bin/basic.rs
@@ -0,0 +1,194 @@
1//! Basic side-by-side example of the Earliest Deadline First scheduler
2//!
3//! This test spawns a number of background "ambient system load" workers
4//! that are constantly working, and runs two sets of trials.
5//!
6//! The first trial runs with no deadline set, so our trial task is at the
7//! same prioritization level as the background worker tasks.
8//!
9//! The second trial sets a deadline, meaning that it will be given higher
10//! scheduling priority than background tasks, that have no deadline set
11
12#![no_std]
13#![no_main]
14
15use core::sync::atomic::{compiler_fence, Ordering};
16
17use defmt::unwrap;
18use embassy_executor::Spawner;
19use embassy_time::{Duration, Instant, Timer};
20use {defmt_rtt as _, panic_probe as _};
21
22#[embassy_executor::main]
23async fn main(spawner: Spawner) {
24 embassy_nrf::init(Default::default());
25
26 // Enable flash cache to remove some flash latency jitter
27 compiler_fence(Ordering::SeqCst);
28 embassy_nrf::pac::NVMC.icachecnf().write(|w| {
29 w.set_cacheen(true);
30 });
31 compiler_fence(Ordering::SeqCst);
32
33 //
34 // Baseline system load tunables
35 //
36
37 // how many load tasks? More load tasks means more tasks contending
38 // for the runqueue
39 let tasks = 32;
40 // how long should each task work for? The longer the working time,
41 // the longer the max jitter possible, even when a task is prioritized,
42 // as EDF is still cooperative and not pre-emptive
43 //
44 // 33 ticks ~= 1ms
45 let work_time_ticks = 33;
46 // what fraction, 1/denominator, should the system be busy?
47 // bigger number means **less** busy
48 //
49 // 2 => 50%
50 // 4 => 25%
51 // 10 => 10%
52 let denominator = 2;
53
54 // Total time window, so each worker is working 1/denominator
55 // amount of the total time
56 let time_window = work_time_ticks * u64::from(tasks) * denominator;
57
58 // Spawn all of our load workers!
59 for i in 0..tasks {
60 spawner.spawn(unwrap!(load_task(i, work_time_ticks, time_window)));
61 }
62
63 // Let all the tasks spin up
64 defmt::println!("Spinning up load tasks...");
65 Timer::after_secs(1).await;
66
67 //
68 // Trial task worker tunables
69 //
70
71 // How many steps should the workers under test run?
72 // More steps means more chances to have to wait for other tasks
73 // in line ahead of us.
74 let num_steps = 100;
75
76 // How many ticks should the worker take working on each step?
77 //
78 // 33 ticks ~= 1ms
79 let work_ticks = 33;
80 // How many ticks should the worker wait on each step?
81 //
82 // 66 ticks ~= 2ms
83 let idle_ticks = 66;
84
85 // How many times to repeat each trial?
86 let trials = 3;
87
88 // The total time a trial would take, in a perfect unloaded system
89 let theoretical = (num_steps * work_ticks) + (num_steps * idle_ticks);
90
91 defmt::println!("");
92 defmt::println!("Starting UNPRIORITIZED worker trials");
93 for _ in 0..trials {
94 //
95 // UNPRIORITIZED worker
96 //
97 defmt::println!("");
98 defmt::println!("Starting unprioritized worker");
99 let start = Instant::now();
100 for _ in 0..num_steps {
101 let now = Instant::now();
102 while now.elapsed().as_ticks() < work_ticks {}
103 Timer::after_ticks(idle_ticks).await;
104 }
105 let elapsed = start.elapsed().as_ticks();
106 defmt::println!(
107 "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}",
108 theoretical,
109 elapsed
110 );
111 let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0;
112 defmt::println!("Took {=f32}% of ideal time", ratio);
113 Timer::after_millis(500).await;
114 }
115
116 Timer::after_secs(1).await;
117
118 defmt::println!("");
119 defmt::println!("Starting PRIORITIZED worker trials");
120 for _ in 0..trials {
121 //
122 // PRIORITIZED worker
123 //
124 defmt::println!("");
125 defmt::println!("Starting prioritized worker");
126 let start = Instant::now();
127 // Set the deadline to ~2x the theoretical time. In practice, setting any deadline
128 // here elevates the current task above all other worker tasks.
129 let meta = embassy_executor::Metadata::for_current_task().await;
130 meta.set_deadline_after(theoretical * 2);
131
132 // Perform the trial
133 for _ in 0..num_steps {
134 let now = Instant::now();
135 while now.elapsed().as_ticks() < work_ticks {}
136 Timer::after_ticks(idle_ticks).await;
137 }
138
139 let elapsed = start.elapsed().as_ticks();
140 defmt::println!(
141 "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}",
142 theoretical,
143 elapsed
144 );
145 let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0;
146 defmt::println!("Took {=f32}% of ideal time", ratio);
147
148 // Unset the deadline, deadlines are not automatically cleared, and if our
149 // deadline is in the past, then we get very high priority!
150 meta.unset_deadline();
151
152 Timer::after_millis(500).await;
153 }
154
155 defmt::println!("");
156 defmt::println!("Trials Complete.");
157}
158
159#[embassy_executor::task(pool_size = 32)]
160async fn load_task(id: u32, ticks_on: u64, ttl_ticks: u64) {
161 let mut last_print = Instant::now();
162 let mut last_tick = last_print;
163 let mut variance = 0;
164 let mut max_variance = 0;
165 loop {
166 let tgt = last_tick + Duration::from_ticks(ttl_ticks);
167 assert!(tgt > Instant::now(), "fell too behind!");
168
169 Timer::at(tgt).await;
170 let now = Instant::now();
171 // How late are we from the target?
172 let var = now.duration_since(tgt).as_ticks();
173 max_variance = max_variance.max(var);
174 variance += var;
175
176 // blocking work
177 while now.elapsed().as_ticks() < ticks_on {}
178
179 if last_print.elapsed() >= Duration::from_secs(1) {
180 defmt::trace!(
181 "Task {=u32} variance ticks (1s): {=u64}, max: {=u64}, act: {=u64}",
182 id,
183 variance,
184 max_variance,
185 ticks_on,
186 );
187 max_variance = 0;
188 variance = 0;
189 last_print = Instant::now();
190 }
191
192 last_tick = tgt;
193 }
194}
diff --git a/examples/nrf52840-rtic/src/bin/blinky.rs b/examples/nrf52840-rtic/src/bin/blinky.rs
index 719e22729..2adac7e0a 100644
--- a/examples/nrf52840-rtic/src/bin/blinky.rs
+++ b/examples/nrf52840-rtic/src/bin/blinky.rs
@@ -1,6 +1,5 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3#![feature(type_alias_impl_trait)]
4 3
5use {defmt_rtt as _, panic_probe as _}; 4use {defmt_rtt as _, panic_probe as _};
6 5
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index a9339bcd3..452e83b7e 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -10,8 +10,8 @@ embassy-futures = { version = "0.1.2", path = "../../embassy-futures" }
10embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 11embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
12embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 13embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "net-driver"] }
14embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } 14embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet","udp", "medium-ieee802154", "proto-ipv6"] }
15embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } 15embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] }
16embedded-io = { version = "0.6.0", features = ["defmt-03"] } 16embedded-io = { version = "0.6.0", features = ["defmt-03"] }
17embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } 17embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
diff --git a/examples/nrf52840/src/bin/nfct.rs b/examples/nrf52840/src/bin/nfct.rs
index d559d006a..fafa37f48 100644
--- a/examples/nrf52840/src/bin/nfct.rs
+++ b/examples/nrf52840/src/bin/nfct.rs
@@ -1,11 +1,12 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use defmt::*; 4use defmt::{todo, *};
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_nrf::config::HfclkSource; 6use embassy_nrf::config::HfclkSource;
7use embassy_nrf::nfct::{Config as NfcConfig, NfcId, NfcT}; 7use embassy_nrf::nfct::{Config as NfcConfig, NfcId, NfcT};
8use embassy_nrf::{bind_interrupts, nfct}; 8use embassy_nrf::{bind_interrupts, nfct};
9use iso14443_4::{Card, IsoDep};
9use {defmt_rtt as _, embassy_nrf as _, panic_probe as _}; 10use {defmt_rtt as _, embassy_nrf as _, panic_probe as _};
10 11
11bind_interrupts!(struct Irqs { 12bind_interrupts!(struct Irqs {
@@ -30,12 +31,28 @@ async fn main(_spawner: Spawner) {
30 31
31 let mut buf = [0u8; 256]; 32 let mut buf = [0u8; 256];
32 33
34 let cc = &[
35 0x00, 0x0f, /* CCEN_HI, CCEN_LOW */
36 0x20, /* VERSION */
37 0x00, 0x7f, /* MLe_HI, MLe_LOW */
38 0x00, 0x7f, /* MLc_HI, MLc_LOW */
39 /* TLV */
40 0x04, 0x06, 0xe1, 0x04, 0x00, 0x7f, 0x00, 0x00,
41 ];
42
43 let ndef = &[
44 0x00, 0x10, 0xd1, 0x1, 0xc, 0x55, 0x4, 0x65, 0x6d, 0x62, 0x61, 0x73, 0x73, 0x79, 0x2e, 0x64, 0x65, 0x76,
45 ];
46 let mut selected: &[u8] = cc;
47
33 loop { 48 loop {
34 info!("activating"); 49 info!("activating");
35 nfc.activate().await; 50 nfc.activate().await;
51 info!("activated!");
52
53 let mut nfc = IsoDep::new(iso14443_3::Logger(&mut nfc));
36 54
37 loop { 55 loop {
38 info!("rxing");
39 let n = match nfc.receive(&mut buf).await { 56 let n = match nfc.receive(&mut buf).await {
40 Ok(n) => n, 57 Ok(n) => n,
41 Err(e) => { 58 Err(e) => {
@@ -44,25 +61,51 @@ async fn main(_spawner: Spawner) {
44 } 61 }
45 }; 62 };
46 let req = &buf[..n]; 63 let req = &buf[..n];
47 info!("received frame {:02x}", req); 64 info!("iso-dep rx {:02x}", req);
48 65
49 let mut deselect = false; 66 let Ok(apdu) = Apdu::parse(req) else {
50 let resp = match req { 67 error!("apdu parse error");
51 [0xe0, ..] => { 68 break;
52 info!("Got RATS, tx'ing ATS"); 69 };
53 &[0x06, 0x77, 0x77, 0x81, 0x02, 0x80][..] 70
71 info!("apdu: {:?}", apdu);
72
73 let resp = match (apdu.cla, apdu.ins, apdu.p1, apdu.p2) {
74 (0, 0xa4, 4, 0) => {
75 info!("select app");
76 &[0x90, 0x00][..]
54 } 77 }
55 [0xc2] => { 78 (0, 0xa4, 0, 12) => {
56 info!("Got deselect!"); 79 info!("select df");
57 deselect = true; 80 match apdu.data {
58 &[0xc2] 81 [0xe1, 0x03] => {
82 selected = cc;
83 &[0x90, 0x00][..]
84 }
85 [0xe1, 0x04] => {
86 selected = ndef;
87 &[0x90, 0x00][..]
88 }
89 _ => todo!(), // return NOT FOUND
90 }
91 }
92 (0, 0xb0, p1, p2) => {
93 info!("read");
94 let offs = u16::from_be_bytes([p1 & 0x7f, p2]) as usize;
95 let len = if apdu.le == 0 { usize::MAX } else { apdu.le as usize };
96 let n = len.min(selected.len() - offs);
97 buf[..n].copy_from_slice(&selected[offs..][..n]);
98 buf[n..][..2].copy_from_slice(&[0x90, 0x00]);
99 &buf[..n + 2]
59 } 100 }
60 _ => { 101 _ => {
61 info!("Got unknown command!"); 102 info!("Got unknown command!");
62 &[0xFF] 103 &[0xFF, 0xFF]
63 } 104 }
64 }; 105 };
65 106
107 info!("iso-dep tx {:02x}", resp);
108
66 match nfc.transmit(resp).await { 109 match nfc.transmit(resp).await {
67 Ok(()) => {} 110 Ok(()) => {}
68 Err(e) => { 111 Err(e) => {
@@ -70,10 +113,211 @@ async fn main(_spawner: Spawner) {
70 break; 113 break;
71 } 114 }
72 } 115 }
116 }
117 }
118}
73 119
74 if deselect { 120#[derive(Debug, Clone, defmt::Format)]
75 break; 121struct Apdu<'a> {
122 pub cla: u8,
123 pub ins: u8,
124 pub p1: u8,
125 pub p2: u8,
126 pub data: &'a [u8],
127 pub le: u16,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
131struct ApduParseError;
132
133impl<'a> Apdu<'a> {
134 pub fn parse(apdu: &'a [u8]) -> Result<Self, ApduParseError> {
135 if apdu.len() < 4 {
136 return Err(ApduParseError);
137 }
138
139 let (data, le) = match apdu.len() - 4 {
140 0 => (&[][..], 0),
141 1 => (&[][..], apdu[4]),
142 n if n == 1 + apdu[4] as usize && apdu[4] != 0 => (&apdu[5..][..apdu[4] as usize], 0),
143 n if n == 2 + apdu[4] as usize && apdu[4] != 0 => (&apdu[5..][..apdu[4] as usize], apdu[apdu.len() - 1]),
144 _ => return Err(ApduParseError),
145 };
146
147 Ok(Apdu {
148 cla: apdu[0],
149 ins: apdu[1],
150 p1: apdu[2],
151 p2: apdu[3],
152 data,
153 le: le as _,
154 })
155 }
156}
157
158mod iso14443_3 {
159 use core::future::Future;
160
161 use defmt::info;
162 use embassy_nrf::nfct::{Error, NfcT};
163
164 pub trait Card {
165 type Error;
166 async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
167 async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error>;
168 }
169
170 impl<'a, T: Card> Card for &'a mut T {
171 type Error = T::Error;
172
173 fn receive(&mut self, buf: &mut [u8]) -> impl Future<Output = Result<usize, Self::Error>> {
174 T::receive(self, buf)
175 }
176
177 fn transmit(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Self::Error>> {
178 T::transmit(self, buf)
179 }
180 }
181
182 impl<'a> Card for NfcT<'a> {
183 type Error = Error;
184
185 fn receive(&mut self, buf: &mut [u8]) -> impl Future<Output = Result<usize, Self::Error>> {
186 self.receive(buf)
187 }
188
189 fn transmit(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Self::Error>> {
190 self.transmit(buf)
191 }
192 }
193
194 pub struct Logger<T: Card>(pub T);
195
196 impl<T: Card> Card for Logger<T> {
197 type Error = T::Error;
198
199 async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
200 let n = T::receive(&mut self.0, buf).await?;
201 info!("<- {:02x}", &buf[..n]);
202 Ok(n)
203 }
204
205 fn transmit(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Self::Error>> {
206 info!("-> {:02x}", buf);
207 T::transmit(&mut self.0, buf)
208 }
209 }
210}
211
212mod iso14443_4 {
213 use defmt::info;
214
215 use crate::iso14443_3;
216
217 #[derive(defmt::Format)]
218 pub enum Error<T> {
219 Deselected,
220 Protocol,
221 Lower(T),
222 }
223
224 pub trait Card {
225 type Error;
226 async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
227 async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error>;
228 }
229
230 pub struct IsoDep<T: iso14443_3::Card> {
231 nfc: T,
232
233 /// Block count spin bit: 0 or 1
234 block_num: u8,
235
236 /// true if deselected. This is permanent, you must create another IsoDep
237 /// instance if we get selected again.
238 deselected: bool,
239
240 /// last response, in case we need to retransmit.
241 resp: [u8; 256],
242 resp_len: usize,
243 }
244
245 impl<T: iso14443_3::Card> IsoDep<T> {
246 pub fn new(nfc: T) -> Self {
247 Self {
248 nfc,
249 block_num: 1,
250 deselected: false,
251 resp: [0u8; 256],
252 resp_len: 0,
76 } 253 }
77 } 254 }
78 } 255 }
256
257 impl<T: iso14443_3::Card> Card for IsoDep<T> {
258 type Error = Error<T::Error>;
259
260 async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
261 if self.deselected {
262 return Err(Error::Deselected);
263 }
264
265 let mut temp = [0u8; 256];
266
267 loop {
268 let n = self.nfc.receive(&mut temp).await.map_err(Error::Lower)?;
269 assert!(n != 0);
270 match temp[0] {
271 0x02 | 0x03 => {
272 self.block_num ^= 0x01;
273 assert!(temp[0] == 0x02 | self.block_num);
274 buf[..n - 1].copy_from_slice(&temp[1..n]);
275 return Ok(n - 1);
276 }
277 0xb2 | 0xb3 => {
278 if temp[0] & 0x01 != self.block_num {
279 info!("Got NAK, transmitting ACK.");
280 let resp = &[0xA2 | self.block_num];
281 self.nfc.transmit(resp).await.map_err(Error::Lower)?;
282 } else {
283 info!("Got NAK, retransmitting.");
284 let resp: &[u8] = &self.resp[..self.resp_len];
285 self.nfc.transmit(resp).await.map_err(Error::Lower)?;
286 }
287 }
288 0xe0 => {
289 info!("Got RATS, tx'ing ATS");
290 let resp = &[0x06, 0x77, 0x77, 0x81, 0x02, 0x80];
291 self.nfc.transmit(resp).await.map_err(Error::Lower)?;
292 }
293 0xc2 => {
294 info!("Got deselect!");
295 self.deselected = true;
296 let resp = &[0xC2];
297 self.nfc.transmit(resp).await.map_err(Error::Lower)?;
298 return Err(Error::Deselected);
299 }
300 _ => {
301 info!("Got unknown command {:02x}!", temp[0]);
302 return Err(Error::Protocol);
303 }
304 };
305 }
306 }
307
308 async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
309 if self.deselected {
310 return Err(Error::Deselected);
311 }
312
313 self.resp[0] = 0x02 | self.block_num;
314 self.resp[1..][..buf.len()].copy_from_slice(buf);
315 self.resp_len = 1 + buf.len();
316
317 let resp: &[u8] = &self.resp[..self.resp_len];
318 self.nfc.transmit(resp).await.map_err(Error::Lower)?;
319
320 Ok(())
321 }
322 }
79} 323}
diff --git a/examples/nrf52840/src/bin/sixlowpan.rs b/examples/nrf52840/src/bin/sixlowpan.rs
new file mode 100644
index 000000000..00a597366
--- /dev/null
+++ b/examples/nrf52840/src/bin/sixlowpan.rs
@@ -0,0 +1,120 @@
1#![no_std]
2#![no_main]
3
4use core::net::Ipv6Addr;
5
6use defmt::{info, unwrap, warn};
7use embassy_executor::Spawner;
8use embassy_net::udp::{PacketMetadata, UdpMetadata, UdpSocket};
9use embassy_net::{IpAddress, IpEndpoint, IpListenEndpoint, Ipv6Cidr, StackResources, StaticConfigV6};
10use embassy_nrf::config::{Config, HfclkSource};
11use embassy_nrf::rng::Rng;
12use embassy_nrf::{bind_interrupts, embassy_net_802154_driver as net, peripherals, radio};
13use embassy_time::Delay;
14use embedded_hal_async::delay::DelayNs;
15use static_cell::StaticCell;
16use {defmt_rtt as _, panic_probe as _};
17
18bind_interrupts!(struct Irqs {
19 RADIO => radio::InterruptHandler<peripherals::RADIO>;
20 RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;
21});
22
23#[embassy_executor::task]
24async fn ieee802154_task(runner: net::Runner<'static, peripherals::RADIO>) -> ! {
25 runner.run().await
26}
27
28#[embassy_executor::task]
29async fn net_task(mut runner: embassy_net::Runner<'static, net::Device<'static>>) -> ! {
30 runner.run().await
31}
32
33#[embassy_executor::main]
34async fn main(spawner: Spawner) {
35 let mut config = Config::default();
36 // Necessary to run the radio nrf52840 v1.11 5.4.1
37 config.hfclk_source = HfclkSource::ExternalXtal;
38 let p = embassy_nrf::init(config);
39
40 let mac_addr: [u8; 8] = [2, 3, 4, 5, 6, 7, 8, 9];
41 static NRF802154_STATE: StaticCell<net::State<20, 20>> = StaticCell::new();
42 let (device, runner) = net::new(mac_addr, p.RADIO, Irqs, NRF802154_STATE.init(net::State::new()))
43 .await
44 .unwrap();
45
46 spawner.spawn(unwrap!(ieee802154_task(runner)));
47
48 // Swap these when flashing a second board
49 let peer = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xd701, 0xda3f, 0x3955, 0x82a4);
50 let local = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xd701, 0xda3f, 0x3955, 0x82a5);
51
52 let config = embassy_net::Config::ipv6_static(StaticConfigV6 {
53 address: Ipv6Cidr::new(local, 64),
54 gateway: None,
55 dns_servers: Default::default(),
56 });
57
58 // Generate random seed
59 let mut rng = Rng::new(p.RNG, Irqs);
60 let mut seed = [0; 8];
61 rng.blocking_fill_bytes(&mut seed);
62 let seed = u64::from_le_bytes(seed);
63
64 // Init network stack
65 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
66 let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);
67
68 spawner.spawn(unwrap!(net_task(runner)));
69
70 let mut rx_buffer = [0; 2096];
71 let mut tx_buffer = [0; 2096];
72 let mut tx_m_buffer = [PacketMetadata::EMPTY; 5];
73 let mut rx_m_buffer = [PacketMetadata::EMPTY; 5];
74
75 let mut delay = Delay;
76 loop {
77 let mut socket = UdpSocket::new(
78 stack,
79 &mut tx_m_buffer,
80 &mut rx_buffer,
81 &mut rx_m_buffer,
82 &mut tx_buffer,
83 );
84 socket
85 .bind(IpListenEndpoint {
86 addr: Some(IpAddress::Ipv6(local)),
87 port: 1234,
88 })
89 .unwrap();
90 let rep = UdpMetadata {
91 endpoint: IpEndpoint {
92 addr: IpAddress::Ipv6(peer),
93 port: 1234,
94 },
95 local_address: Some(IpAddress::Ipv6(local)),
96 meta: Default::default(),
97 };
98
99 info!("Listening on {:?} UDP:1234...", local);
100
101 let mut recv_buf = [0; 12];
102 loop {
103 delay.delay_ms(2000).await;
104 if socket.may_recv() {
105 let n = match socket.recv_from(&mut recv_buf).await {
106 Ok((0, _)) => panic!(),
107 Ok((n, _)) => n,
108 Err(e) => {
109 warn!("read error: {:?}", e);
110 break;
111 }
112 };
113 info!("Received {:02x}", &recv_buf[..n]);
114 }
115
116 info!("Sending");
117 socket.send_to(b"Hello World", rep).await.unwrap();
118 }
119 }
120}
diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs
new file mode 100644
index 000000000..f51df2df9
--- /dev/null
+++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs
@@ -0,0 +1,155 @@
1//! This example implements a TCP echo server on port 1234 and using DHCP.
2//! Send it some data, you should see it echoed back and printed in the console.
3//!
4//! Example written for the [`WIZnet W55RP20-EVB-Pico`](https://docs.wiznet.io/Product/ioNIC/W55RP20/w55rp20-evb-pico) board.
5//! Note: the W55RP20 is a single package that contains both a RP2040 and the Wiznet W5500 ethernet
6//! controller
7
8#![no_std]
9#![no_main]
10
11use defmt::*;
12use embassy_executor::Spawner;
13use embassy_futures::yield_now;
14use embassy_net::{Stack, StackResources};
15use embassy_net_wiznet::chip::W5500;
16use embassy_net_wiznet::*;
17use embassy_rp::clocks::RoscRng;
18use embassy_rp::gpio::{Input, Level, Output, Pull};
19use embassy_rp::peripherals::PIO0;
20use embassy_rp::pio_programs::spi::Spi;
21use embassy_rp::spi::{Async, Config as SpiConfig};
22use embassy_rp::{bind_interrupts, pio};
23use embassy_time::{Delay, Duration};
24use embedded_hal_bus::spi::ExclusiveDevice;
25use embedded_io_async::Write;
26use static_cell::StaticCell;
27use {defmt_rtt as _, panic_probe as _};
28
29bind_interrupts!(struct Irqs {
30 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
31});
32
33#[embassy_executor::task]
34async fn ethernet_task(
35 runner: Runner<
36 'static,
37 W5500,
38 ExclusiveDevice<Spi<'static, PIO0, 0, Async>, Output<'static>, Delay>,
39 Input<'static>,
40 Output<'static>,
41 >,
42) -> ! {
43 runner.run().await
44}
45
46#[embassy_executor::task]
47async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
48 runner.run().await
49}
50
51#[embassy_executor::main]
52async fn main(spawner: Spawner) {
53 let p = embassy_rp::init(Default::default());
54 let mut rng = RoscRng;
55 let mut led = Output::new(p.PIN_19, Level::Low);
56
57 // The W55RP20 uses a PIO unit for SPI communication, once the SPI bus has been formed using a
58 // PIO statemachine everything else is generally unchanged from the other examples that use the W5500
59 let mosi = p.PIN_23;
60 let miso = p.PIN_22;
61 let clk = p.PIN_21;
62
63 let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs);
64
65 // Construct an SPI driver backed by a PIO state machine
66 let mut spi_cfg = SpiConfig::default();
67 spi_cfg.frequency = 12_500_000; // The PIO SPI program is much less stable than the actual SPI
68 // peripheral, use higher speeds at your peril
69 let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg);
70
71 // Further control pins
72 let cs = Output::new(p.PIN_20, Level::High);
73 let w5500_int = Input::new(p.PIN_24, Pull::Up);
74 let w5500_reset = Output::new(p.PIN_25, Level::High);
75
76 let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];
77 static STATE: StaticCell<State<8, 8>> = StaticCell::new();
78 let state = STATE.init(State::<8, 8>::new());
79 let (device, runner) = embassy_net_wiznet::new(
80 mac_addr,
81 state,
82 ExclusiveDevice::new(spi, cs, Delay),
83 w5500_int,
84 w5500_reset,
85 )
86 .await
87 .unwrap();
88 spawner.spawn(unwrap!(ethernet_task(runner)));
89
90 // Generate random seed
91 let seed = rng.next_u64();
92
93 // Init network stack
94 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
95 let (stack, runner) = embassy_net::new(
96 device,
97 embassy_net::Config::dhcpv4(Default::default()),
98 RESOURCES.init(StackResources::new()),
99 seed,
100 );
101
102 // Launch network task
103 spawner.spawn(unwrap!(net_task(runner)));
104
105 info!("Waiting for DHCP...");
106 let cfg = wait_for_config(stack).await;
107 let local_addr = cfg.address.address();
108 info!("IP address: {:?}", local_addr);
109
110 let mut rx_buffer = [0; 4096];
111 let mut tx_buffer = [0; 4096];
112 let mut buf = [0; 4096];
113 loop {
114 let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
115 socket.set_timeout(Some(Duration::from_secs(10)));
116
117 led.set_low();
118 info!("Listening on TCP:1234...");
119 if let Err(e) = socket.accept(1234).await {
120 warn!("accept error: {:?}", e);
121 continue;
122 }
123 info!("Received connection from {:?}", socket.remote_endpoint());
124 led.set_high();
125
126 loop {
127 let n = match socket.read(&mut buf).await {
128 Ok(0) => {
129 warn!("read EOF");
130 break;
131 }
132 Ok(n) => n,
133 Err(e) => {
134 warn!("{:?}", e);
135 break;
136 }
137 };
138 info!("rxd {}", core::str::from_utf8(&buf[..n]).unwrap());
139
140 if let Err(e) = socket.write_all(&buf[..n]).await {
141 warn!("write error: {:?}", e);
142 break;
143 }
144 }
145 }
146}
147
148async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
149 loop {
150 if let Some(config) = stack.config_v4() {
151 return config.clone();
152 }
153 yield_now().await;
154 }
155}
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs
index 379e2b8f9..102f13c45 100644
--- a/examples/rp/src/bin/pio_onewire.rs
+++ b/examples/rp/src/bin/pio_onewire.rs
@@ -1,4 +1,5 @@
1//! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors. 1//! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors.
2//! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example.
2 3
3#![no_std] 4#![no_std]
4#![no_main] 5#![no_main]
diff --git a/examples/rp/src/bin/pio_onewire_parasite.rs b/examples/rp/src/bin/pio_onewire_parasite.rs
new file mode 100644
index 000000000..fd076dee0
--- /dev/null
+++ b/examples/rp/src/bin/pio_onewire_parasite.rs
@@ -0,0 +1,89 @@
1//! This example shows how you can use PIO to read one or more `DS18B20`
2//! one-wire temperature sensors using parasite power.
3//! It applies a strong pullup during conversion, see "Powering the DS18B20" in the datasheet.
4//! For externally powered sensors, use the pio_onewire.rs example.
5
6#![no_std]
7#![no_main]
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{InterruptHandler, Pio};
13use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch};
14use embassy_time::Duration;
15use heapless::Vec;
16use {defmt_rtt as _, panic_probe as _};
17
18bind_interrupts!(struct Irqs {
19 PIO0_IRQ_0 => InterruptHandler<PIO0>;
20});
21
22#[embassy_executor::main]
23async fn main(_spawner: Spawner) {
24 let p = embassy_rp::init(Default::default());
25 let mut pio = Pio::new(p.PIO0, Irqs);
26
27 let prg = PioOneWireProgram::new(&mut pio.common);
28 let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);
29
30 info!("Starting onewire search");
31
32 let mut devices = Vec::<u64, 10>::new();
33 let mut search = PioOneWireSearch::new();
34 for _ in 0..10 {
35 if !search.is_finished() {
36 if let Some(address) = search.next(&mut onewire).await {
37 if crc8(&address.to_le_bytes()) == 0 {
38 info!("Found address: {:x}", address);
39 let _ = devices.push(address);
40 } else {
41 warn!("Found invalid address: {:x}", address);
42 }
43 }
44 }
45 }
46
47 info!("Search done, found {} devices", devices.len());
48
49 loop {
50 // Read all devices one by one
51 for device in &devices {
52 onewire.reset().await;
53 onewire.write_bytes(&[0x55]).await; // Match rom
54 onewire.write_bytes(&device.to_le_bytes()).await;
55 // 750 ms delay required for default 12-bit resolution.
56 onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await;
57
58 onewire.reset().await;
59 onewire.write_bytes(&[0x55]).await; // Match rom
60 onewire.write_bytes(&device.to_le_bytes()).await;
61 onewire.write_bytes(&[0xBE]).await; // Read scratchpad
62
63 let mut data = [0; 9];
64 onewire.read_bytes(&mut data).await;
65 if crc8(&data) == 0 {
66 let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.;
67 info!("Read device {:x}: {} deg C", device, temp);
68 } else {
69 warn!("Reading device {:x} failed. {:02x}", device, data);
70 }
71 }
72 }
73}
74
75fn crc8(data: &[u8]) -> u8 {
76 let mut crc = 0;
77 for b in data {
78 let mut data_byte = *b;
79 for _ in 0..8 {
80 let temp = (crc ^ data_byte) & 0x01;
81 crc >>= 1;
82 if temp != 0 {
83 crc ^= 0x8C;
84 }
85 data_byte >>= 1;
86 }
87 }
88 crc
89}
diff --git a/examples/rp/src/bin/pio_spi.rs b/examples/rp/src/bin/pio_spi.rs
new file mode 100644
index 000000000..4218327ec
--- /dev/null
+++ b/examples/rp/src/bin/pio_spi.rs
@@ -0,0 +1,48 @@
1//! This example shows how to use a PIO state machine as an additional SPI
2//! (Serial Peripheral Interface) on the RP2040 chip. No specific hardware is
3//! specified in this example.
4//!
5//! If you connect pin 6 and 7 you should get the same data back.
6
7#![no_std]
8#![no_main]
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio_programs::spi::Spi;
14use embassy_rp::spi::Config;
15use embassy_rp::{bind_interrupts, pio};
16use embassy_time::Timer;
17use {defmt_rtt as _, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
21});
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let p = embassy_rp::init(Default::default());
26 info!("Hello World!");
27
28 // These pins are routed to different hardware SPI peripherals, but we can
29 // use them together regardless
30 let mosi = p.PIN_6; // SPI0 SCLK
31 let miso = p.PIN_7; // SPI0 MOSI
32 let clk = p.PIN_8; // SPI1 MISO
33
34 let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs);
35
36 // Construct an SPI driver backed by a PIO state machine
37 let mut spi = Spi::new_blocking(&mut common, sm0, clk, mosi, miso, Config::default());
38
39 loop {
40 let tx_buf = [1_u8, 2, 3, 4, 5, 6];
41 let mut rx_buf = [0_u8; 6];
42
43 spi.blocking_transfer(&mut rx_buf, &tx_buf).unwrap();
44 info!("{:?}", rx_buf);
45
46 Timer::after_secs(1).await;
47 }
48}
diff --git a/examples/rp/src/bin/pio_spi_async.rs b/examples/rp/src/bin/pio_spi_async.rs
new file mode 100644
index 000000000..18b57d26e
--- /dev/null
+++ b/examples/rp/src/bin/pio_spi_async.rs
@@ -0,0 +1,57 @@
1//! This example shows how to use a PIO state machine as an additional SPI
2//! (Serial Peripheral Interface) on the RP2040 chip. No specific hardware is
3//! specified in this example.
4//!
5//! If you connect pin 6 and 7 you should get the same data back.
6
7#![no_std]
8#![no_main]
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio_programs::spi::Spi;
14use embassy_rp::spi::Config;
15use embassy_rp::{bind_interrupts, pio};
16use embassy_time::Timer;
17use {defmt_rtt as _, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
21});
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let p = embassy_rp::init(Default::default());
26 info!("Hello World!");
27
28 // These pins are routed to different hardware SPI peripherals, but we can
29 // use them together regardless
30 let mosi = p.PIN_6; // SPI0 SCLK
31 let miso = p.PIN_7; // SPI0 MOSI
32 let clk = p.PIN_8; // SPI1 MISO
33
34 let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs);
35
36 // Construct an SPI driver backed by a PIO state machine
37 let mut spi = Spi::new(
38 &mut common,
39 sm0,
40 clk,
41 mosi,
42 miso,
43 p.DMA_CH0,
44 p.DMA_CH1,
45 Config::default(),
46 );
47
48 loop {
49 let tx_buf = [1_u8, 2, 3, 4, 5, 6];
50 let mut rx_buf = [0_u8; 6];
51
52 spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
53 info!("{:?}", rx_buf);
54
55 Timer::after_secs(1).await;
56 }
57}
diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs
index e9a5e43a8..1692bdf36 100644
--- a/examples/rp/src/bin/rtc.rs
+++ b/examples/rp/src/bin/rtc.rs
@@ -5,16 +5,22 @@
5 5
6use defmt::*; 6use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
8use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; 9use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc};
9use embassy_time::Timer; 10use embassy_time::Timer;
10use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
11 12
13// Bind the RTC interrupt to the handler
14bind_interrupts!(struct Irqs {
15 RTC_IRQ => embassy_rp::rtc::InterruptHandler;
16});
17
12#[embassy_executor::main] 18#[embassy_executor::main]
13async fn main(_spawner: Spawner) { 19async fn main(_spawner: Spawner) {
14 let p = embassy_rp::init(Default::default()); 20 let p = embassy_rp::init(Default::default());
15 info!("Wait for 20s"); 21 info!("Wait for 20s");
16 22
17 let mut rtc = Rtc::new(p.RTC); 23 let mut rtc = Rtc::new(p.RTC, Irqs);
18 24
19 if !rtc.is_running() { 25 if !rtc.is_running() {
20 info!("Start RTC"); 26 info!("Start RTC");
diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs
new file mode 100644
index 000000000..94b5fbd27
--- /dev/null
+++ b/examples/rp/src/bin/rtc_alarm.rs
@@ -0,0 +1,66 @@
1//! This example shows how to use RTC (Real Time Clock) for scheduling alarms and reacting to them.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_futures::select::{select, Either};
9use embassy_rp::bind_interrupts;
10use embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc};
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14// Bind the RTC interrupt to the handler
15bind_interrupts!(struct Irqs {
16 RTC_IRQ => embassy_rp::rtc::InterruptHandler;
17});
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 let p = embassy_rp::init(Default::default());
22 let mut rtc = Rtc::new(p.RTC, Irqs);
23
24 if !rtc.is_running() {
25 info!("Start RTC");
26 let now = DateTime {
27 year: 2000,
28 month: 1,
29 day: 1,
30 day_of_week: DayOfWeek::Saturday,
31 hour: 0,
32 minute: 0,
33 second: 0,
34 };
35 rtc.set_datetime(now).unwrap();
36 }
37
38 loop {
39 // Wait for 5 seconds or until the alarm is triggered
40 match select(Timer::after_secs(5), rtc.wait_for_alarm()).await {
41 // Timer expired
42 Either::First(_) => {
43 let dt = rtc.now().unwrap();
44 info!(
45 "Now: {}-{:02}-{:02} {}:{:02}:{:02}",
46 dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
47 );
48
49 // See if the alarm is already scheduled, if not, schedule it
50 if rtc.alarm_scheduled().is_none() {
51 info!("Scheduling alarm for 30 seconds from now");
52 rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60));
53 info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap());
54 }
55 }
56 // Alarm triggered
57 Either::Second(_) => {
58 let dt = rtc.now().unwrap();
59 info!(
60 "ALARM TRIGGERED! Now: {}-{:02}-{:02} {}:{:02}:{:02}",
61 dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
62 );
63 }
64 }
65 }
66}
diff --git a/examples/rp235x/src/bin/pio_i2s_rx.rs b/examples/rp235x/src/bin/pio_i2s_rx.rs
new file mode 100644
index 000000000..c3f505b13
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_i2s_rx.rs
@@ -0,0 +1,81 @@
1//! This example shows receiving audio from a connected I2S microphone (or other audio source)
2//! using the PIO module of the RP235x.
3//!
4//!
5//! Connect the i2s microphone as follows:
6//! bclk : GPIO 18
7//! lrc : GPIO 19
8//! din : GPIO 20
9//! Then hold down the boot select button to begin receiving audio. Received I2S words will be written to
10//! buffers for the left and right channels for use in your application, whether that's storage or
11//! further processing
12//!
13//! Note the const USE_ONBOARD_PULLDOWN is by default set to false, meaning an external
14//! pull-down resistor is being used on the data pin if required by the mic being used.
15
16#![no_std]
17#![no_main]
18use core::mem;
19
20use defmt::*;
21use embassy_executor::Spawner;
22use embassy_rp::bind_interrupts;
23use embassy_rp::peripherals::PIO0;
24use embassy_rp::pio::{InterruptHandler, Pio};
25use embassy_rp::pio_programs::i2s::{PioI2sIn, PioI2sInProgram};
26use static_cell::StaticCell;
27use {defmt_rtt as _, panic_probe as _};
28
29bind_interrupts!(struct Irqs {
30 PIO0_IRQ_0 => InterruptHandler<PIO0>;
31});
32
33const SAMPLE_RATE: u32 = 48_000;
34const BIT_DEPTH: u32 = 16;
35const CHANNELS: u32 = 2;
36const USE_ONBOARD_PULLDOWN: bool = false; // whether or not to use the onboard pull-down resistor,
37 // which has documented issues on many RP235x boards
38#[embassy_executor::main]
39async fn main(_spawner: Spawner) {
40 let p = embassy_rp::init(Default::default());
41
42 // Setup pio state machine for i2s input
43 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
44
45 let bit_clock_pin = p.PIN_18;
46 let left_right_clock_pin = p.PIN_19;
47 let data_pin = p.PIN_20;
48
49 let program = PioI2sInProgram::new(&mut common);
50 let mut i2s = PioI2sIn::new(
51 &mut common,
52 sm0,
53 p.DMA_CH0,
54 USE_ONBOARD_PULLDOWN,
55 data_pin,
56 bit_clock_pin,
57 left_right_clock_pin,
58 SAMPLE_RATE,
59 BIT_DEPTH,
60 CHANNELS,
61 &program,
62 );
63
64 // create two audio buffers (back and front) which will take turns being
65 // filled with new audio data from the PIO fifo using DMA
66 const BUFFER_SIZE: usize = 960;
67 static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new();
68 let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]);
69 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
70
71 loop {
72 // trigger transfer of front buffer data to the pio fifo
73 // but don't await the returned future, yet
74 let dma_future = i2s.read(front_buffer);
75 // now await the dma future. once the dma finishes, the next buffer needs to be queued
76 // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us
77 dma_future.await;
78 info!("Received I2S data word: {:?}", &front_buffer);
79 mem::swap(&mut back_buffer, &mut front_buffer);
80 }
81}
diff --git a/examples/rp235x/src/bin/pio_onewire.rs b/examples/rp235x/src/bin/pio_onewire.rs
index 991510851..102f13c45 100644
--- a/examples/rp235x/src/bin/pio_onewire.rs
+++ b/examples/rp235x/src/bin/pio_onewire.rs
@@ -1,4 +1,5 @@
1//! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor. 1//! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors.
2//! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example.
2 3
3#![no_std] 4#![no_std]
4#![no_main] 5#![no_main]
@@ -6,9 +7,10 @@ use defmt::*;
6use embassy_executor::Spawner; 7use embassy_executor::Spawner;
7use embassy_rp::bind_interrupts; 8use embassy_rp::bind_interrupts;
8use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
9use embassy_rp::pio::{self, InterruptHandler, Pio}; 10use embassy_rp::pio::{InterruptHandler, Pio};
10use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram}; 11use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch};
11use embassy_time::Timer; 12use embassy_time::Timer;
13use heapless::Vec;
12use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
13 15
14bind_interrupts!(struct Irqs { 16bind_interrupts!(struct Irqs {
@@ -21,63 +23,66 @@ async fn main(_spawner: Spawner) {
21 let mut pio = Pio::new(p.PIO0, Irqs); 23 let mut pio = Pio::new(p.PIO0, Irqs);
22 24
23 let prg = PioOneWireProgram::new(&mut pio.common); 25 let prg = PioOneWireProgram::new(&mut pio.common);
24 let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); 26 let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);
25 27
26 let mut sensor = Ds18b20::new(onewire); 28 info!("Starting onewire search");
27 29
28 loop { 30 let mut devices = Vec::<u64, 10>::new();
29 sensor.start().await; // Start a new measurement 31 let mut search = PioOneWireSearch::new();
30 Timer::after_secs(1).await; // Allow 1s for the measurement to finish 32 for _ in 0..10 {
31 match sensor.temperature().await { 33 if !search.is_finished() {
32 Ok(temp) => info!("temp = {:?} deg C", temp), 34 if let Some(address) = search.next(&mut onewire).await {
33 _ => error!("sensor error"), 35 if crc8(&address.to_le_bytes()) == 0 {
36 info!("Found addres: {:x}", address);
37 let _ = devices.push(address);
38 } else {
39 warn!("Found invalid address: {:x}", address);
40 }
41 }
34 } 42 }
35 Timer::after_secs(1).await;
36 } 43 }
37}
38 44
39/// DS18B20 temperature sensor driver 45 info!("Search done, found {} devices", devices.len());
40pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> {
41 wire: PioOneWire<'d, PIO, SM>,
42}
43 46
44impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { 47 loop {
45 pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self { 48 onewire.reset().await;
46 Self { wire } 49 // Skip rom and trigger conversion, we can trigger all devices on the bus immediately
47 } 50 onewire.write_bytes(&[0xCC, 0x44]).await;
48 51
49 /// Calculate CRC8 of the data 52 Timer::after_secs(1).await; // Allow 1s for the measurement to finish
50 fn crc8(data: &[u8]) -> u8 { 53
51 let mut temp; 54 // Read all devices one by one
52 let mut data_byte; 55 for device in &devices {
53 let mut crc = 0; 56 onewire.reset().await;
54 for b in data { 57 onewire.write_bytes(&[0x55]).await; // Match rom
55 data_byte = *b; 58 onewire.write_bytes(&device.to_le_bytes()).await;
56 for _ in 0..8 { 59 onewire.write_bytes(&[0xBE]).await; // Read scratchpad
57 temp = (crc ^ data_byte) & 0x01; 60
58 crc >>= 1; 61 let mut data = [0; 9];
59 if temp != 0 { 62 onewire.read_bytes(&mut data).await;
60 crc ^= 0x8C; 63 if crc8(&data) == 0 {
61 } 64 let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.;
62 data_byte >>= 1; 65 info!("Read device {:x}: {} deg C", device, temp);
66 } else {
67 warn!("Reading device {:x} failed", device);
63 } 68 }
64 } 69 }
65 crc 70 Timer::after_secs(1).await;
66 }
67
68 /// Start a new measurement. Allow at least 1000ms before getting `temperature`.
69 pub async fn start(&mut self) {
70 self.wire.write_bytes(&[0xCC, 0x44]).await;
71 } 71 }
72}
72 73
73 /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. 74fn crc8(data: &[u8]) -> u8 {
74 pub async fn temperature(&mut self) -> Result<f32, ()> { 75 let mut crc = 0;
75 self.wire.write_bytes(&[0xCC, 0xBE]).await; 76 for b in data {
76 let mut data = [0; 9]; 77 let mut data_byte = *b;
77 self.wire.read_bytes(&mut data).await; 78 for _ in 0..8 {
78 match Self::crc8(&data) == 0 { 79 let temp = (crc ^ data_byte) & 0x01;
79 true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), 80 crc >>= 1;
80 false => Err(()), 81 if temp != 0 {
82 crc ^= 0x8C;
83 }
84 data_byte >>= 1;
81 } 85 }
82 } 86 }
87 crc
83} 88}
diff --git a/examples/rp235x/src/bin/pio_onewire_parasite.rs b/examples/rp235x/src/bin/pio_onewire_parasite.rs
new file mode 100644
index 000000000..fd076dee0
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_onewire_parasite.rs
@@ -0,0 +1,89 @@
1//! This example shows how you can use PIO to read one or more `DS18B20`
2//! one-wire temperature sensors using parasite power.
3//! It applies a strong pullup during conversion, see "Powering the DS18B20" in the datasheet.
4//! For externally powered sensors, use the pio_onewire.rs example.
5
6#![no_std]
7#![no_main]
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{InterruptHandler, Pio};
13use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch};
14use embassy_time::Duration;
15use heapless::Vec;
16use {defmt_rtt as _, panic_probe as _};
17
18bind_interrupts!(struct Irqs {
19 PIO0_IRQ_0 => InterruptHandler<PIO0>;
20});
21
22#[embassy_executor::main]
23async fn main(_spawner: Spawner) {
24 let p = embassy_rp::init(Default::default());
25 let mut pio = Pio::new(p.PIO0, Irqs);
26
27 let prg = PioOneWireProgram::new(&mut pio.common);
28 let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);
29
30 info!("Starting onewire search");
31
32 let mut devices = Vec::<u64, 10>::new();
33 let mut search = PioOneWireSearch::new();
34 for _ in 0..10 {
35 if !search.is_finished() {
36 if let Some(address) = search.next(&mut onewire).await {
37 if crc8(&address.to_le_bytes()) == 0 {
38 info!("Found address: {:x}", address);
39 let _ = devices.push(address);
40 } else {
41 warn!("Found invalid address: {:x}", address);
42 }
43 }
44 }
45 }
46
47 info!("Search done, found {} devices", devices.len());
48
49 loop {
50 // Read all devices one by one
51 for device in &devices {
52 onewire.reset().await;
53 onewire.write_bytes(&[0x55]).await; // Match rom
54 onewire.write_bytes(&device.to_le_bytes()).await;
55 // 750 ms delay required for default 12-bit resolution.
56 onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await;
57
58 onewire.reset().await;
59 onewire.write_bytes(&[0x55]).await; // Match rom
60 onewire.write_bytes(&device.to_le_bytes()).await;
61 onewire.write_bytes(&[0xBE]).await; // Read scratchpad
62
63 let mut data = [0; 9];
64 onewire.read_bytes(&mut data).await;
65 if crc8(&data) == 0 {
66 let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.;
67 info!("Read device {:x}: {} deg C", device, temp);
68 } else {
69 warn!("Reading device {:x} failed. {:02x}", device, data);
70 }
71 }
72 }
73}
74
75fn crc8(data: &[u8]) -> u8 {
76 let mut crc = 0;
77 for b in data {
78 let mut data_byte = *b;
79 for _ in 0..8 {
80 let temp = (crc ^ data_byte) & 0x01;
81 crc >>= 1;
82 if temp != 0 {
83 crc ^= 0x8C;
84 }
85 data_byte >>= 1;
86 }
87 }
88 crc
89}
diff --git a/examples/rp235x/src/bin/psram.rs b/examples/rp235x/src/bin/psram.rs
new file mode 100644
index 000000000..716ac7695
--- /dev/null
+++ b/examples/rp235x/src/bin/psram.rs
@@ -0,0 +1,49 @@
1//! This example tests an APS6404L PSRAM chip connected to the RP235x
2//! It fills the PSRAM with alternating patterns and reads back a value
3//!
4//! In this example, the PSRAM CS is connected to Pin 0.
5
6#![no_std]
7#![no_main]
8
9use core::slice;
10
11use defmt::*;
12use embassy_executor::Spawner;
13use embassy_time::Timer;
14use {defmt_rtt as _, panic_probe as _};
15
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) {
18 let config = embassy_rp::config::Config::default();
19 let p = embassy_rp::init(config);
20 let psram_config = embassy_rp::psram::Config::aps6404l();
21 let psram = embassy_rp::psram::Psram::new(embassy_rp::qmi_cs1::QmiCs1::new(p.QMI_CS1, p.PIN_0), psram_config);
22
23 let Ok(psram) = psram else {
24 error!("PSRAM not found");
25 loop {
26 Timer::after_secs(1).await;
27 }
28 };
29
30 let psram_slice = unsafe {
31 let psram_ptr = psram.base_address();
32 let slice: &'static mut [u8] = slice::from_raw_parts_mut(psram_ptr, psram.size() as usize);
33 slice
34 };
35
36 loop {
37 psram_slice.fill(0x55);
38 info!("PSRAM filled with 0x55");
39 let at_addr = psram_slice[0x100];
40 info!("Read from PSRAM at address 0x100: 0x{:02x}", at_addr);
41 Timer::after_secs(1).await;
42
43 psram_slice.fill(0xAA);
44 info!("PSRAM filled with 0xAA");
45 let at_addr = psram_slice[0x100];
46 info!("Read from PSRAM at address 0x100: 0x{:02x}", at_addr);
47 Timer::after_secs(1).await;
48 }
49}
diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs
index d747a43c2..b5b26938d 100644
--- a/examples/stm32f1/src/bin/input_capture.rs
+++ b/examples/stm32f1/src/bin/input_capture.rs
@@ -3,7 +3,7 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 6use embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed};
7use embassy_stm32::time::khz; 7use embassy_stm32::time::khz;
8use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; 8use embassy_stm32::timer::input_capture::{CapturePin, InputCapture};
9use embassy_stm32::timer::{self, Channel}; 9use embassy_stm32::timer::{self, Channel};
@@ -40,7 +40,8 @@ async fn main(spawner: Spawner) {
40 spawner.spawn(unwrap!(blinky(p.PC13))); 40 spawner.spawn(unwrap!(blinky(p.PC13)));
41 41
42 let ch3 = CapturePin::new(p.PA2, Pull::None); 42 let ch3 = CapturePin::new(p.PA2, Pull::None);
43 let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); 43 let mut ic =
44 InputCapture::new::<AfioRemap<0>>(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default());
44 45
45 loop { 46 loop {
46 info!("wait for rising edge"); 47 info!("wait for rising edge");
diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs
index 63b899767..9ae747018 100644
--- a/examples/stm32f1/src/bin/pwm_input.rs
+++ b/examples/stm32f1/src/bin/pwm_input.rs
@@ -3,7 +3,7 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::gpio::{Level, Output, Pull, Speed}; 6use embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed};
7use embassy_stm32::time::khz; 7use embassy_stm32::time::khz;
8use embassy_stm32::timer::pwm_input::PwmInput; 8use embassy_stm32::timer::pwm_input::PwmInput;
9use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; 9use embassy_stm32::{bind_interrupts, peripherals, timer, Peri};
@@ -38,7 +38,7 @@ async fn main(spawner: Spawner) {
38 38
39 spawner.spawn(unwrap!(blinky(p.PC13))); 39 spawner.spawn(unwrap!(blinky(p.PC13)));
40 40
41 let mut pwm_input = PwmInput::new_ch1(p.TIM2, p.PA0, Pull::None, khz(10)); 41 let mut pwm_input = PwmInput::new_ch1::<AfioRemap<0>>(p.TIM2, p.PA0, Pull::None, khz(10));
42 pwm_input.enable(); 42 pwm_input.enable();
43 43
44 loop { 44 loop {
diff --git a/examples/stm32f7/src/bin/qspi.rs b/examples/stm32f7/src/bin/qspi.rs
index ab29ddeff..80652b865 100644
--- a/examples/stm32f7/src/bin/qspi.rs
+++ b/examples/stm32f7/src/bin/qspi.rs
@@ -273,14 +273,14 @@ async fn main(_spawner: Spawner) -> ! {
273 let p = embassy_stm32::init(config); 273 let p = embassy_stm32::init(config);
274 info!("Embassy initialized"); 274 info!("Embassy initialized");
275 275
276 let config = QspiCfg { 276 let mut config = QspiCfg::default();
277 memory_size: MemorySize::_8MiB, 277 config.memory_size = MemorySize::_8MiB;
278 address_size: AddressSize::_24bit, 278 config.address_size = AddressSize::_24bit;
279 prescaler: 16, 279 config.prescaler = 16;
280 cs_high_time: ChipSelectHighTime::_1Cycle, 280 config.cs_high_time = ChipSelectHighTime::_1Cycle;
281 fifo_threshold: FIFOThresholdLevel::_16Bytes, 281 config.fifo_threshold = FIFOThresholdLevel::_16Bytes;
282 sample_shifting: SampleShifting::None, 282 config.sample_shifting = SampleShifting::None;
283 }; 283
284 let driver = Qspi::new_bank1( 284 let driver = Qspi::new_bank1(
285 p.QUADSPI, p.PF8, p.PF9, p.PE2, p.PF6, p.PF10, p.PB10, p.DMA2_CH7, config, 285 p.QUADSPI, p.PF8, p.PF9, p.PE2, p.PF6, p.PF10, p.PB10, p.DMA2_CH7, config,
286 ); 286 );
diff --git a/examples/stm32g0/src/bin/adc.rs b/examples/stm32g0/src/bin/adc.rs
index 6c7f3b48a..7d8653ef2 100644
--- a/examples/stm32g0/src/bin/adc.rs
+++ b/examples/stm32g0/src/bin/adc.rs
@@ -3,7 +3,7 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::adc::{Adc, SampleTime}; 6use embassy_stm32::adc::{Adc, Clock, Presc, SampleTime};
7use embassy_time::Timer; 7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
@@ -12,7 +12,7 @@ async fn main(_spawner: Spawner) {
12 let p = embassy_stm32::init(Default::default()); 12 let p = embassy_stm32::init(Default::default());
13 info!("Hello World!"); 13 info!("Hello World!");
14 14
15 let mut adc = Adc::new(p.ADC1); 15 let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 });
16 adc.set_sample_time(SampleTime::CYCLES79_5); 16 adc.set_sample_time(SampleTime::CYCLES79_5);
17 let mut pin = p.PA1; 17 let mut pin = p.PA1;
18 18
diff --git a/examples/stm32g0/src/bin/adc_dma.rs b/examples/stm32g0/src/bin/adc_dma.rs
index d7515933c..8266a6d83 100644
--- a/examples/stm32g0/src/bin/adc_dma.rs
+++ b/examples/stm32g0/src/bin/adc_dma.rs
@@ -3,7 +3,7 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; 6use embassy_stm32::adc::{Adc, AdcChannel as _, Clock, Presc, SampleTime};
7use embassy_time::Timer; 7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
@@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) {
17 17
18 info!("Hello World!"); 18 info!("Hello World!");
19 19
20 let mut adc = Adc::new(p.ADC1); 20 let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 });
21 21
22 let mut dma = p.DMA1_CH1; 22 let mut dma = p.DMA1_CH1;
23 let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); 23 let mut vrefint_channel = adc.enable_vrefint().degrade_adc();
diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs
index 9c5dd872a..834d1cd4a 100644
--- a/examples/stm32g0/src/bin/adc_oversampling.rs
+++ b/examples/stm32g0/src/bin/adc_oversampling.rs
@@ -7,7 +7,7 @@
7 7
8use defmt::*; 8use defmt::*;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::adc::{Adc, SampleTime}; 10use embassy_stm32::adc::{Adc, Clock, Ovsr, Ovss, Presc, SampleTime};
11use embassy_time::Timer; 11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
13 13
@@ -16,23 +16,12 @@ async fn main(_spawner: Spawner) {
16 let p = embassy_stm32::init(Default::default()); 16 let p = embassy_stm32::init(Default::default());
17 info!("Adc oversample test"); 17 info!("Adc oversample test");
18 18
19 let mut adc = Adc::new(p.ADC1); 19 let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 });
20 adc.set_sample_time(SampleTime::CYCLES1_5); 20 adc.set_sample_time(SampleTime::CYCLES1_5);
21 let mut pin = p.PA1; 21 let mut pin = p.PA1;
22 22
23 // From https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf 23 adc.set_oversampling_ratio(Ovsr::MUL16);
24 // page373 15.8 Oversampler 24 adc.set_oversampling_shift(Ovss::NO_SHIFT);
25 // Table 76. Maximum output results vs N and M. Grayed values indicates truncation
26 // 0x00 oversampling ratio X2
27 // 0x01 oversampling ratio X4
28 // 0x02 oversampling ratio X8
29 // 0x03 oversampling ratio X16
30 // 0x04 oversampling ratio X32
31 // 0x05 oversampling ratio X64
32 // 0x06 oversampling ratio X128
33 // 0x07 oversampling ratio X256
34 adc.set_oversampling_ratio(0x03);
35 adc.set_oversampling_shift(0b0000);
36 adc.oversampling_enable(true); 25 adc.oversampling_enable(true);
37 26
38 loop { 27 loop {
diff --git a/examples/stm32h5/src/bin/sai.rs b/examples/stm32h5/src/bin/sai.rs
new file mode 100644
index 000000000..0e182f9cf
--- /dev/null
+++ b/examples/stm32h5/src/bin/sai.rs
@@ -0,0 +1,52 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_stm32::{sai, Config};
7use {defmt_rtt as _, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 info!("Hello world.");
12
13 let mut config = Config::default();
14 {
15 use embassy_stm32::rcc::*;
16
17 config.rcc.pll2 = Some(Pll {
18 source: PllSource::HSI,
19 prediv: PllPreDiv::DIV16,
20 mul: PllMul::MUL32,
21 divp: Some(PllDiv::DIV16), // 8 MHz SAI clock
22 divq: None,
23 divr: None,
24 });
25
26 config.rcc.mux.sai1sel = mux::Saisel::PLL2_P;
27 }
28 let p = embassy_stm32::init(config);
29
30 let mut write_buffer = [0u16; 1024];
31 let (_, sai_b) = sai::split_subblocks(p.SAI1);
32
33 let mut sai_b = sai::Sai::new_asynchronous(
34 sai_b,
35 p.PF8,
36 p.PE3,
37 p.PF9,
38 p.GPDMA1_CH0,
39 &mut write_buffer,
40 Default::default(),
41 );
42
43 // Populate arbitrary data.
44 let mut data = [0u16; 256];
45 for (index, sample) in data.iter_mut().enumerate() {
46 *sample = index as u16;
47 }
48
49 loop {
50 sai_b.write(&data).await.unwrap();
51 }
52}
diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs
index 01937593a..847b70c85 100644
--- a/examples/stm32h7/src/bin/sai.rs
+++ b/examples/stm32h7/src/bin/sai.rs
@@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) {
63 tx_config.tx_rx = TxRx::Transmitter; 63 tx_config.tx_rx = TxRx::Transmitter;
64 tx_config.sync_output = true; 64 tx_config.sync_output = true;
65 tx_config.clock_strobe = ClockStrobe::Falling; 65 tx_config.clock_strobe = ClockStrobe::Falling;
66 tx_config.master_clock_divider = mclk_div; 66 tx_config.master_clock_divider = Some(mclk_div);
67 tx_config.stereo_mono = StereoMono::Stereo; 67 tx_config.stereo_mono = StereoMono::Stereo;
68 tx_config.data_size = DataSize::Data24; 68 tx_config.data_size = DataSize::Data24;
69 tx_config.bit_order = BitOrder::MsbFirst; 69 tx_config.bit_order = BitOrder::MsbFirst;
@@ -119,71 +119,7 @@ async fn main(_spawner: Spawner) {
119 } 119 }
120} 120}
121 121
122const fn mclk_div_from_u8(v: u8) -> MasterClockDivider { 122fn mclk_div_from_u8(v: u8) -> MasterClockDivider {
123 match v { 123 assert!((1..=63).contains(&v));
124 1 => MasterClockDivider::Div1, 124 MasterClockDivider::from_bits(v)
125 2 => MasterClockDivider::Div2,
126 3 => MasterClockDivider::Div3,
127 4 => MasterClockDivider::Div4,
128 5 => MasterClockDivider::Div5,
129 6 => MasterClockDivider::Div6,
130 7 => MasterClockDivider::Div7,
131 8 => MasterClockDivider::Div8,
132 9 => MasterClockDivider::Div9,
133 10 => MasterClockDivider::Div10,
134 11 => MasterClockDivider::Div11,
135 12 => MasterClockDivider::Div12,
136 13 => MasterClockDivider::Div13,
137 14 => MasterClockDivider::Div14,
138 15 => MasterClockDivider::Div15,
139 16 => MasterClockDivider::Div16,
140 17 => MasterClockDivider::Div17,
141 18 => MasterClockDivider::Div18,
142 19 => MasterClockDivider::Div19,
143 20 => MasterClockDivider::Div20,
144 21 => MasterClockDivider::Div21,
145 22 => MasterClockDivider::Div22,
146 23 => MasterClockDivider::Div23,
147 24 => MasterClockDivider::Div24,
148 25 => MasterClockDivider::Div25,
149 26 => MasterClockDivider::Div26,
150 27 => MasterClockDivider::Div27,
151 28 => MasterClockDivider::Div28,
152 29 => MasterClockDivider::Div29,
153 30 => MasterClockDivider::Div30,
154 31 => MasterClockDivider::Div31,
155 32 => MasterClockDivider::Div32,
156 33 => MasterClockDivider::Div33,
157 34 => MasterClockDivider::Div34,
158 35 => MasterClockDivider::Div35,
159 36 => MasterClockDivider::Div36,
160 37 => MasterClockDivider::Div37,
161 38 => MasterClockDivider::Div38,
162 39 => MasterClockDivider::Div39,
163 40 => MasterClockDivider::Div40,
164 41 => MasterClockDivider::Div41,
165 42 => MasterClockDivider::Div42,
166 43 => MasterClockDivider::Div43,
167 44 => MasterClockDivider::Div44,
168 45 => MasterClockDivider::Div45,
169 46 => MasterClockDivider::Div46,
170 47 => MasterClockDivider::Div47,
171 48 => MasterClockDivider::Div48,
172 49 => MasterClockDivider::Div49,
173 50 => MasterClockDivider::Div50,
174 51 => MasterClockDivider::Div51,
175 52 => MasterClockDivider::Div52,
176 53 => MasterClockDivider::Div53,
177 54 => MasterClockDivider::Div54,
178 55 => MasterClockDivider::Div55,
179 56 => MasterClockDivider::Div56,
180 57 => MasterClockDivider::Div57,
181 58 => MasterClockDivider::Div58,
182 59 => MasterClockDivider::Div59,
183 60 => MasterClockDivider::Div60,
184 61 => MasterClockDivider::Div61,
185 62 => MasterClockDivider::Div62,
186 63 => MasterClockDivider::Div63,
187 _ => panic!(),
188 }
189} 125}
diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs
index 6d29e8a4d..b75a03ae8 100644
--- a/examples/stm32h723/src/bin/spdifrx.rs
+++ b/examples/stm32h723/src/bin/spdifrx.rs
@@ -168,7 +168,7 @@ fn new_sai_transmitter<'d>(
168 sai_config.slot_enable = 0xFFFF; // All slots 168 sai_config.slot_enable = 0xFFFF; // All slots
169 sai_config.data_size = sai::DataSize::Data32; 169 sai_config.data_size = sai::DataSize::Data32;
170 sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; 170 sai_config.frame_length = (CHANNEL_COUNT * 32) as u8;
171 sai_config.master_clock_divider = hal::sai::MasterClockDivider::MasterClockDisabled; 171 sai_config.master_clock_divider = None;
172 172
173 let (sub_block_tx, _) = hal::sai::split_subblocks(sai); 173 let (sub_block_tx, _) = hal::sai::split_subblocks(sai);
174 Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config) 174 Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config)
diff --git a/examples/stm32h742/src/bin/qspi.rs b/examples/stm32h742/src/bin/qspi.rs
index 50e37ec52..9e79d7089 100644
--- a/examples/stm32h742/src/bin/qspi.rs
+++ b/examples/stm32h742/src/bin/qspi.rs
@@ -266,14 +266,14 @@ async fn main(_spawner: Spawner) -> ! {
266 let p = embassy_stm32::init(config); 266 let p = embassy_stm32::init(config);
267 info!("Embassy initialized"); 267 info!("Embassy initialized");
268 268
269 let config = QspiCfg { 269 let mut config = QspiCfg::default();
270 memory_size: MemorySize::_8MiB, 270 config.memory_size = MemorySize::_8MiB;
271 address_size: AddressSize::_24bit, 271 config.address_size = AddressSize::_24bit;
272 prescaler: 16, 272 config.prescaler = 16;
273 cs_high_time: ChipSelectHighTime::_1Cycle, 273 config.cs_high_time = ChipSelectHighTime::_1Cycle;
274 fifo_threshold: FIFOThresholdLevel::_16Bytes, 274 config.fifo_threshold = FIFOThresholdLevel::_16Bytes;
275 sample_shifting: SampleShifting::None, 275 config.sample_shifting = SampleShifting::None;
276 }; 276
277 let driver = Qspi::new_blocking_bank1(p.QUADSPI, p.PD11, p.PD12, p.PE2, p.PD13, p.PB2, p.PB10, config); 277 let driver = Qspi::new_blocking_bank1(p.QUADSPI, p.PD11, p.PD12, p.PE2, p.PD13, p.PB2, p.PB10, config);
278 let mut flash = FlashMemory::new(driver); 278 let mut flash = FlashMemory::new(driver);
279 let flash_id = flash.read_id(); 279 let flash_id = flash.read_id();
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index 90f57a2d8..d42cdac15 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -11,6 +11,8 @@ embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = ["
11embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 11embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
12embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 12embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
13embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 13embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
14embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] }
15embassy-futures = { version = "0.1.2", path = "../../embassy-futures" }
14 16
15defmt = "1.0.1" 17defmt = "1.0.1"
16defmt-rtt = "1.0.0" 18defmt-rtt = "1.0.0"
diff --git a/examples/stm32l0/src/bin/usb_serial.rs b/examples/stm32l0/src/bin/usb_serial.rs
new file mode 100644
index 000000000..fdb1aeb59
--- /dev/null
+++ b/examples/stm32l0/src/bin/usb_serial.rs
@@ -0,0 +1,95 @@
1#![no_std]
2#![no_main]
3
4use defmt::{panic, *};
5use embassy_executor::Spawner;
6use embassy_futures::join::join;
7use embassy_stm32::usb::{self, Driver, Instance};
8use embassy_stm32::{bind_interrupts, peripherals};
9use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
10use embassy_usb::driver::EndpointError;
11use embassy_usb::Builder;
12use {defmt_rtt as _, panic_probe as _};
13
14bind_interrupts!(struct Irqs {
15 USB => usb::InterruptHandler<peripherals::USB>;
16});
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 let mut config = embassy_stm32::Config::default();
21 {
22 use embassy_stm32::rcc::*;
23 config.rcc.hsi = true;
24 config.rcc.pll = Some(Pll {
25 source: PllSource::HSI,
26 mul: PllMul::MUL6, // PLLVCO = 16*6 = 96Mhz
27 div: PllDiv::DIV3, // 32Mhz clock (16 * 6 / 3)
28 });
29 config.rcc.sys = Sysclk::PLL1_R;
30 }
31
32 let p = embassy_stm32::init(config);
33
34 info!("Hello World!");
35
36 let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
37
38 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
39 config.manufacturer = Some("Embassy");
40 config.product = Some("USB-Serial Example");
41 config.serial_number = Some("123456");
42
43 let mut config_descriptor = [0; 256];
44 let mut bos_descriptor = [0; 256];
45 let mut control_buf = [0; 64];
46
47 let mut state = State::new();
48
49 let mut builder = Builder::new(
50 driver,
51 config,
52 &mut config_descriptor,
53 &mut bos_descriptor,
54 &mut [], // no msos descriptors
55 &mut control_buf,
56 );
57
58 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
59
60 let mut usb = builder.build();
61
62 let usb_fut = usb.run();
63
64 let echo_fut = async {
65 loop {
66 class.wait_connection().await;
67 info!("Connected");
68 let _ = echo(&mut class).await;
69 info!("Disconnected");
70 }
71 };
72
73 join(usb_fut, echo_fut).await;
74}
75
76struct Disconnected {}
77
78impl From<EndpointError> for Disconnected {
79 fn from(val: EndpointError) -> Self {
80 match val {
81 EndpointError::BufferOverflow => panic!("Buffer overflow"),
82 EndpointError::Disabled => Disconnected {},
83 }
84 }
85}
86
87async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
88 let mut buf = [0; 64];
89 loop {
90 let n = class.read_packet(&mut buf).await?;
91 let data = &buf[..n];
92 info!("data: {:x}", data);
93 class.write_packet(data).await?;
94 }
95}
diff --git a/examples/stm32l432/src/bin/qspi_mmap.rs b/examples/stm32l432/src/bin/qspi_mmap.rs
index 075458fe5..feabdd532 100644
--- a/examples/stm32l432/src/bin/qspi_mmap.rs
+++ b/examples/stm32l432/src/bin/qspi_mmap.rs
@@ -246,14 +246,14 @@ const MEMORY_ADDR: u32 = 0x00000000 as u32;
246async fn main(_spawner: Spawner) { 246async fn main(_spawner: Spawner) {
247 let p = embassy_stm32::init(Default::default()); 247 let p = embassy_stm32::init(Default::default());
248 248
249 let config = qspi::Config { 249 let mut config = qspi::Config::default();
250 memory_size: MemorySize::_16MiB, 250 config.memory_size = MemorySize::_16MiB;
251 address_size: AddressSize::_24bit, 251 config.address_size = AddressSize::_24bit;
252 prescaler: 200, 252 config.prescaler = 200;
253 cs_high_time: ChipSelectHighTime::_1Cycle, 253 config.cs_high_time = ChipSelectHighTime::_1Cycle;
254 fifo_threshold: FIFOThresholdLevel::_16Bytes, 254 config.fifo_threshold = FIFOThresholdLevel::_16Bytes;
255 sample_shifting: SampleShifting::None, 255 config.sample_shifting = SampleShifting::None;
256 }; 256
257 let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config); 257 let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config);
258 let mut flash = FlashMemory::new(driver); 258 let mut flash = FlashMemory::new(driver);
259 let mut wr_buf = [0u8; 256]; 259 let mut wr_buf = [0u8; 256];
diff --git a/examples/stm32wl/src/bin/adc.rs b/examples/stm32wl/src/bin/adc.rs
new file mode 100644
index 000000000..118f02ae1
--- /dev/null
+++ b/examples/stm32wl/src/bin/adc.rs
@@ -0,0 +1,39 @@
1#![no_std]
2#![no_main]
3
4use core::mem::MaybeUninit;
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_stm32::adc::{Adc, CkModePclk, Clock, SampleTime};
9use embassy_stm32::SharedData;
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12
13static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) {
17 let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA);
18 info!("Hello World!");
19
20 let mut adc = Adc::new_with_clock(p.ADC1, Clock::Sync { div: CkModePclk::DIV1 });
21 adc.set_sample_time(SampleTime::CYCLES79_5);
22 let mut pin = p.PB2;
23
24 let mut vrefint = adc.enable_vrefint();
25 let vrefint_sample = adc.blocking_read(&mut vrefint);
26 let convert_to_millivolts = |sample| {
27 // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf
28 // 6.3.3 Embedded internal reference voltage
29 const VREFINT_MV: u32 = 1212; // mV
30
31 (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16
32 };
33
34 loop {
35 let v = adc.blocking_read(&mut pin);
36 info!("--> {} - {} mV", v, convert_to_millivolts(v));
37 Timer::after_millis(100).await;
38 }
39}
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
index 809346bed..19461520a 100644
--- a/tests/rp/Cargo.toml
+++ b/tests/rp/Cargo.toml
@@ -64,6 +64,12 @@ name = "float"
64path = "src/bin/float.rs" 64path = "src/bin/float.rs"
65required-features = [ "rp2040",] 65required-features = [ "rp2040",]
66 66
67# RTC is only available on RP2040
68[[bin]]
69name = "rtc"
70path = "src/bin/rtc.rs"
71required-features = [ "rp2040",]
72
67[profile.dev] 73[profile.dev]
68debug = 2 74debug = 2
69debug-assertions = true 75debug-assertions = true
diff --git a/tests/rp/src/bin/rtc.rs b/tests/rp/src/bin/rtc.rs
new file mode 100644
index 000000000..c66981d95
--- /dev/null
+++ b/tests/rp/src/bin/rtc.rs
@@ -0,0 +1,125 @@
1#![no_std]
2#![no_main]
3#[cfg(feature = "rp2040")]
4teleprobe_meta::target!(b"rpi-pico");
5
6use defmt::{assert, *};
7use embassy_executor::Spawner;
8use embassy_futures::select::{select, Either};
9use embassy_rp::bind_interrupts;
10use embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc};
11use embassy_time::{Duration, Instant, Timer};
12use {defmt_rtt as _, panic_probe as _};
13
14// Bind the RTC interrupt to the handler
15bind_interrupts!(struct Irqs {
16 RTC_IRQ => embassy_rp::rtc::InterruptHandler;
17});
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 let p = embassy_rp::init(Default::default());
22 let mut rtc = Rtc::new(p.RTC, Irqs);
23
24 info!("RTC test started");
25
26 // Initialize RTC if not running
27 if !rtc.is_running() {
28 info!("Starting RTC");
29 let now = DateTime {
30 year: 2000,
31 month: 1,
32 day: 1,
33 day_of_week: DayOfWeek::Saturday,
34 hour: 0,
35 minute: 0,
36 second: 0,
37 };
38 rtc.set_datetime(now).unwrap();
39 Timer::after_millis(100).await;
40 }
41
42 // Test 1: Basic RTC functionality - read current time
43 let initial_time = rtc.now().unwrap();
44 info!(
45 "Initial time: {}-{:02}-{:02} {}:{:02}:{:02}",
46 initial_time.year,
47 initial_time.month,
48 initial_time.day,
49 initial_time.hour,
50 initial_time.minute,
51 initial_time.second
52 );
53
54 // Test 2: Schedule and wait for alarm
55 info!("Testing alarm scheduling");
56
57 // Wait until we're at a predictable second, then schedule for a future second
58 loop {
59 let current = rtc.now().unwrap();
60 if current.second <= 55 {
61 break;
62 }
63 Timer::after_millis(100).await;
64 }
65
66 // Now schedule alarm for 3 seconds from current time
67 let current_time = rtc.now().unwrap();
68 let alarm_second = (current_time.second + 3) % 60;
69 let alarm_filter = DateTimeFilter::default().second(alarm_second);
70
71 info!("Scheduling alarm for second: {}", alarm_second);
72 rtc.schedule_alarm(alarm_filter);
73
74 // Verify alarm is scheduled
75 let scheduled = rtc.alarm_scheduled();
76 assert!(scheduled.is_some(), "Alarm should be scheduled");
77 info!("Alarm scheduled successfully: {}", scheduled.unwrap());
78
79 // Wait for alarm with timeout
80 let alarm_start = Instant::now();
81 match select(Timer::after_secs(5), rtc.wait_for_alarm()).await {
82 Either::First(_) => {
83 core::panic!("Alarm timeout - alarm should have triggered by now");
84 }
85 Either::Second(_) => {
86 let alarm_duration = Instant::now() - alarm_start;
87 info!("ALARM TRIGGERED after {:?}", alarm_duration);
88
89 // Verify timing is reasonable (should be around 3 seconds)
90 assert!(
91 alarm_duration >= Duration::from_secs(2) && alarm_duration <= Duration::from_secs(4),
92 "Alarm timing incorrect: {:?}",
93 alarm_duration
94 );
95 }
96 }
97
98 // Test 3: Verify RTC is still running and time has advanced
99 let final_time = rtc.now().unwrap();
100 info!(
101 "Final time: {}-{:02}-{:02} {}:{:02}:{:02}",
102 final_time.year, final_time.month, final_time.day, final_time.hour, final_time.minute, final_time.second
103 );
104
105 // Verify time has advanced (allowing for minute/hour rollover)
106 let time_diff = if final_time.second >= initial_time.second {
107 final_time.second - initial_time.second
108 } else {
109 60 - initial_time.second + final_time.second
110 };
111
112 assert!(time_diff >= 3, "RTC should have advanced by at least 3 seconds");
113 info!("Time advanced by {} seconds", time_diff);
114
115 // Test 4: Verify alarm is no longer scheduled after triggering
116 let post_alarm_scheduled = rtc.alarm_scheduled();
117 assert!(
118 post_alarm_scheduled.is_none(),
119 "Alarm should not be scheduled after triggering"
120 );
121 info!("Alarm correctly cleared after triggering");
122
123 info!("Test OK");
124 cortex_m::asm::bkpt();
125}
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index aeca67659..891ec93fd 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -9,7 +9,9 @@ publish = false
9[features] 9[features]
10stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] 10stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"]
11stm32c071rb = ["embassy-stm32/stm32c071rb", "cm0", "not-gpdma"] 11stm32c071rb = ["embassy-stm32/stm32c071rb", "cm0", "not-gpdma"]
12stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma"] 12stm32f100rd = ["embassy-stm32/stm32f100rd", "spi-v1", "not-gpdma", "afio", "afio-value-line"]
13stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma", "afio"]
14stm32f107vc = ["embassy-stm32/stm32f107vc", "spi-v1", "not-gpdma", "afio", "afio-connectivity-line"]
13stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "eth", "rng"] 15stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "eth", "rng"]
14stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] 16stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"]
15stm32f429zi = ["embassy-stm32/stm32f429zi", "spi-v1", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"] 17stm32f429zi = ["embassy-stm32/stm32f429zi", "spi-v1", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"]
@@ -59,6 +61,9 @@ cordic = ["dep:num-traits"]
59dual-bank = ["embassy-stm32/dual-bank"] 61dual-bank = ["embassy-stm32/dual-bank"]
60single-bank = ["embassy-stm32/single-bank"] 62single-bank = ["embassy-stm32/single-bank"]
61eeprom = [] 63eeprom = []
64afio = []
65afio-connectivity-line = []
66afio-value-line = []
62 67
63cm0 = ["portable-atomic/unsafe-assume-single-core"] 68cm0 = ["portable-atomic/unsafe-assume-single-core"]
64 69
@@ -99,6 +104,11 @@ num-traits = { version="0.2", default-features = false,features = ["libm"], opti
99# BEGIN TESTS 104# BEGIN TESTS
100# Generated by gen_test.py. DO NOT EDIT. 105# Generated by gen_test.py. DO NOT EDIT.
101[[bin]] 106[[bin]]
107name = "afio"
108path = "src/bin/afio.rs"
109required-features = [ "afio",]
110
111[[bin]]
102name = "can" 112name = "can"
103path = "src/bin/can.rs" 113path = "src/bin/can.rs"
104required-features = [ "can",] 114required-features = [ "can",]
diff --git a/tests/stm32/src/bin/afio.rs b/tests/stm32/src/bin/afio.rs
new file mode 100644
index 000000000..cc44dc59c
--- /dev/null
+++ b/tests/stm32/src/bin/afio.rs
@@ -0,0 +1,1156 @@
1// required-features: afio
2#![no_std]
3#![no_main]
4#[path = "../common.rs"]
5mod common;
6
7use common::*;
8use embassy_executor::Spawner;
9use embassy_stm32::gpio::{AfioRemap, OutputType, Pull};
10use embassy_stm32::pac::AFIO;
11use embassy_stm32::time::khz;
12use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin};
13use embassy_stm32::timer::input_capture::{CapturePin, InputCapture};
14use embassy_stm32::timer::pwm_input::PwmInput;
15use embassy_stm32::timer::qei::{Qei, QeiPin};
16use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
17use embassy_stm32::usart::{Uart, UartRx, UartTx};
18use embassy_stm32::{bind_interrupts, Peripherals};
19
20#[cfg(not(feature = "afio-connectivity-line"))]
21bind_interrupts!(struct Irqs {
22 USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;
23 TIM1_CC => embassy_stm32::timer::CaptureCompareInterruptHandler<embassy_stm32::peripherals::TIM1>;
24});
25
26#[cfg(feature = "afio-connectivity-line")]
27bind_interrupts!(struct Irqs {
28 CAN1_RX0 => embassy_stm32::can::Rx0InterruptHandler<embassy_stm32::peripherals::CAN1>;
29 CAN1_RX1 => embassy_stm32::can::Rx1InterruptHandler<embassy_stm32::peripherals::CAN1>;
30 CAN1_SCE => embassy_stm32::can::SceInterruptHandler<embassy_stm32::peripherals::CAN1>;
31 CAN1_TX => embassy_stm32::can::TxInterruptHandler<embassy_stm32::peripherals::CAN1>;
32
33 CAN2_RX0 => embassy_stm32::can::Rx0InterruptHandler<embassy_stm32::peripherals::CAN2>;
34 CAN2_RX1 => embassy_stm32::can::Rx1InterruptHandler<embassy_stm32::peripherals::CAN2>;
35 CAN2_SCE => embassy_stm32::can::SceInterruptHandler<embassy_stm32::peripherals::CAN2>;
36 CAN2_TX => embassy_stm32::can::TxInterruptHandler<embassy_stm32::peripherals::CAN2>;
37
38 ETH => embassy_stm32::eth::InterruptHandler;
39 USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;
40 TIM1_CC => embassy_stm32::timer::CaptureCompareInterruptHandler<embassy_stm32::peripherals::TIM1>;
41});
42
43#[embassy_executor::main]
44async fn main(_spawner: Spawner) {
45 let mut p = init();
46 info!("Hello World!");
47
48 // USART3
49 {
50 // no remap RX/TX/RTS/CTS
51 afio_registers_set_remap();
52 Uart::new_blocking_with_rtscts(
53 p.USART3.reborrow(),
54 p.PB11.reborrow(),
55 p.PB10.reborrow(),
56 p.PB14.reborrow(),
57 p.PB13.reborrow(),
58 Default::default(),
59 )
60 .unwrap();
61 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);
62 }
63 {
64 // no remap RX/TX
65 afio_registers_set_remap();
66 Uart::new_blocking(
67 p.USART3.reborrow(),
68 p.PB11.reborrow(),
69 p.PB10.reborrow(),
70 Default::default(),
71 )
72 .unwrap();
73 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);
74 }
75 {
76 // no remap TX
77 afio_registers_set_remap();
78 Uart::new_blocking_half_duplex(
79 p.USART3.reborrow(),
80 p.PB10.reborrow(),
81 Default::default(),
82 embassy_stm32::usart::HalfDuplexReadback::NoReadback,
83 )
84 .unwrap();
85 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);
86 }
87 {
88 // no remap TX async
89 afio_registers_set_remap();
90 UartTx::new(
91 p.USART3.reborrow(),
92 p.PB10.reborrow(),
93 p.DMA1_CH2.reborrow(),
94 Default::default(),
95 )
96 .unwrap();
97 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);
98 }
99 {
100 // no remap TX/CTS async
101 afio_registers_set_remap();
102 UartTx::new_with_cts(
103 p.USART3.reborrow(),
104 p.PB10.reborrow(),
105 p.PB13.reborrow(),
106 p.DMA1_CH2.reborrow(),
107 Default::default(),
108 )
109 .unwrap();
110 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);
111 }
112 {
113 // no remap RX async
114 afio_registers_set_remap();
115 UartRx::new(
116 p.USART3.reborrow(),
117 Irqs,
118 p.PB11.reborrow(),
119 p.DMA1_CH3.reborrow(),
120 Default::default(),
121 )
122 .unwrap();
123 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);
124 }
125 {
126 // no remap RX async
127 afio_registers_set_remap();
128 UartRx::new_with_rts(
129 p.USART3.reborrow(),
130 Irqs,
131 p.PB11.reborrow(),
132 p.PB14.reborrow(),
133 p.DMA1_CH3.reborrow(),
134 Default::default(),
135 )
136 .unwrap();
137 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);
138 }
139 {
140 // no remap RX/TX async
141 afio_registers_set_remap();
142 Uart::new(
143 p.USART3.reborrow(),
144 p.PB11.reborrow(),
145 p.PB10.reborrow(),
146 Irqs,
147 p.DMA1_CH2.reborrow(),
148 p.DMA1_CH3.reborrow(),
149 Default::default(),
150 )
151 .unwrap();
152 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);
153 }
154 {
155 // no remap RX/TX/RTS/CTS async
156 afio_registers_set_remap();
157 Uart::new_with_rtscts(
158 p.USART3.reborrow(),
159 p.PB11.reborrow(),
160 p.PB10.reborrow(),
161 Irqs,
162 p.PB14.reborrow(),
163 p.PB13.reborrow(),
164 p.DMA1_CH2.reborrow(),
165 p.DMA1_CH3.reborrow(),
166 Default::default(),
167 )
168 .unwrap();
169 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);
170 }
171
172 // TIM1
173 {
174 // no remap
175 afio_registers_set_remap();
176 SimplePwm::new::<AfioRemap<0>>(
177 p.TIM1.reborrow(),
178 Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)),
179 Some(PwmPin::new(p.PA9.reborrow(), OutputType::PushPull)),
180 Some(PwmPin::new(p.PA10.reborrow(), OutputType::PushPull)),
181 Some(PwmPin::new(p.PA11.reborrow(), OutputType::PushPull)),
182 khz(10),
183 Default::default(),
184 );
185 defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 0);
186 }
187 {
188 // no remap
189 afio_registers_set_remap();
190 SimplePwm::new::<AfioRemap<0>>(
191 p.TIM1.reborrow(),
192 Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)),
193 None,
194 None,
195 None,
196 khz(10),
197 Default::default(),
198 );
199 defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 0);
200 }
201 {
202 // partial remap
203 reset_afio_registers();
204 ComplementaryPwm::new::<AfioRemap<1>>(
205 p.TIM1.reborrow(),
206 Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)),
207 None,
208 None,
209 None,
210 None,
211 None,
212 None,
213 None,
214 khz(10),
215 Default::default(),
216 );
217 defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1);
218 }
219 {
220 // partial remap
221 reset_afio_registers();
222 ComplementaryPwm::new::<AfioRemap<1>>(
223 p.TIM1.reborrow(),
224 Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)),
225 Some(ComplementaryPwmPin::new(p.PA7.reborrow(), OutputType::PushPull)),
226 Some(PwmPin::new(p.PA9.reborrow(), OutputType::PushPull)),
227 Some(ComplementaryPwmPin::new(p.PB0.reborrow(), OutputType::PushPull)),
228 Some(PwmPin::new(p.PA10.reborrow(), OutputType::PushPull)),
229 None, // pin does not exist on medium-density devices
230 Some(PwmPin::new(p.PA11.reborrow(), OutputType::PushPull)),
231 None, // signal does not exist
232 khz(10),
233 Default::default(),
234 );
235 defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1);
236 }
237 {
238 // partial remap
239 reset_afio_registers();
240 InputCapture::new::<AfioRemap<1>>(
241 p.TIM1.reborrow(),
242 Some(CapturePin::new(p.PA8.reborrow(), Pull::Down)),
243 None,
244 None,
245 None,
246 Irqs,
247 khz(10),
248 Default::default(),
249 );
250 defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1);
251 }
252 {
253 // partial remap
254 reset_afio_registers();
255 PwmInput::new_ch1::<AfioRemap<1>>(p.TIM1.reborrow(), p.PA8.reborrow(), Pull::Down, khz(10));
256 defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1);
257 }
258 {
259 // partial remap
260 reset_afio_registers();
261 Qei::new::<AfioRemap<1>>(
262 p.TIM1.reborrow(),
263 QeiPin::new(p.PA8.reborrow()),
264 QeiPin::new(p.PA9.reborrow()),
265 );
266 defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1);
267 }
268
269 // TIM2
270 {
271 // no remap
272 afio_registers_set_remap();
273 SimplePwm::new::<AfioRemap<0>>(
274 p.TIM2.reborrow(),
275 Some(PwmPin::new(p.PA0.reborrow(), OutputType::PushPull)),
276 Some(PwmPin::new(p.PA1.reborrow(), OutputType::PushPull)),
277 Some(PwmPin::new(p.PA2.reborrow(), OutputType::PushPull)),
278 Some(PwmPin::new(p.PA3.reborrow(), OutputType::PushPull)),
279 khz(10),
280 Default::default(),
281 );
282 defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 0);
283 }
284 {
285 // partial remap 1
286 reset_afio_registers();
287 SimplePwm::new::<AfioRemap<1>>(
288 p.TIM2.reborrow(),
289 Some(PwmPin::new(p.PA15.reborrow(), OutputType::PushPull)),
290 Some(PwmPin::new(p.PB3.reborrow(), OutputType::PushPull)),
291 Some(PwmPin::new(p.PA2.reborrow(), OutputType::PushPull)),
292 Some(PwmPin::new(p.PA3.reborrow(), OutputType::PushPull)),
293 khz(10),
294 Default::default(),
295 );
296 defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 1);
297 }
298 {
299 // partial remap 2
300 reset_afio_registers();
301 SimplePwm::new::<AfioRemap<2>>(
302 p.TIM2.reborrow(),
303 Some(PwmPin::new(p.PA0.reborrow(), OutputType::PushPull)),
304 Some(PwmPin::new(p.PA1.reborrow(), OutputType::PushPull)),
305 Some(PwmPin::new(p.PB10.reborrow(), OutputType::PushPull)),
306 Some(PwmPin::new(p.PB11.reborrow(), OutputType::PushPull)),
307 khz(10),
308 Default::default(),
309 );
310 defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 2);
311 }
312 {
313 // full remap
314 reset_afio_registers();
315 SimplePwm::new::<AfioRemap<3>>(
316 p.TIM2.reborrow(),
317 Some(PwmPin::new(p.PA15.reborrow(), OutputType::PushPull)),
318 Some(PwmPin::new(p.PB3.reborrow(), OutputType::PushPull)),
319 Some(PwmPin::new(p.PB10.reborrow(), OutputType::PushPull)),
320 Some(PwmPin::new(p.PB11.reborrow(), OutputType::PushPull)),
321 khz(10),
322 Default::default(),
323 );
324 defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 3);
325 }
326
327 connectivity_line::run(&mut p);
328 value_line::run(&mut p);
329
330 info!("Test OK");
331 cortex_m::asm::bkpt();
332}
333
334#[cfg(feature = "afio-connectivity-line")]
335mod connectivity_line {
336 use embassy_stm32::can::Can;
337 use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue};
338 use embassy_stm32::i2s::I2S;
339 use embassy_stm32::spi::Spi;
340
341 use super::*;
342
343 pub fn run(p: &mut Peripherals) {
344 // USART3
345 {
346 // partial remap RX/TX/RTS/CTS
347 reset_afio_registers();
348 Uart::new_blocking_with_rtscts(
349 p.USART3.reborrow(),
350 p.PC11.reborrow(),
351 p.PC10.reborrow(),
352 p.PB14.reborrow(),
353 p.PB13.reborrow(),
354 Default::default(),
355 )
356 .unwrap();
357 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);
358 }
359 {
360 // partial remap RX/TX
361 reset_afio_registers();
362 Uart::new_blocking(
363 p.USART3.reborrow(),
364 p.PC11.reborrow(),
365 p.PC10.reborrow(),
366 Default::default(),
367 )
368 .unwrap();
369 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);
370 }
371 {
372 // partial remap TX
373 reset_afio_registers();
374 Uart::new_blocking_half_duplex(
375 p.USART3.reborrow(),
376 p.PC10.reborrow(),
377 Default::default(),
378 embassy_stm32::usart::HalfDuplexReadback::NoReadback,
379 )
380 .unwrap();
381 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);
382 }
383 {
384 // partial remap TX async
385 reset_afio_registers();
386 UartTx::new(
387 p.USART3.reborrow(),
388 p.PC10.reborrow(),
389 p.DMA1_CH2.reborrow(),
390 Default::default(),
391 )
392 .unwrap();
393 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);
394 }
395 {
396 // partial remap TX/CTS async
397 reset_afio_registers();
398 UartTx::new_with_cts(
399 p.USART3.reborrow(),
400 p.PC10.reborrow(),
401 p.PB13.reborrow(),
402 p.DMA1_CH2.reborrow(),
403 Default::default(),
404 )
405 .unwrap();
406 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);
407 }
408 {
409 // partial remap RX async
410 reset_afio_registers();
411 UartRx::new(
412 p.USART3.reborrow(),
413 Irqs,
414 p.PC11.reborrow(),
415 p.DMA1_CH3.reborrow(),
416 Default::default(),
417 )
418 .unwrap();
419 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);
420 }
421 {
422 // partial remap RX async
423 reset_afio_registers();
424 UartRx::new_with_rts(
425 p.USART3.reborrow(),
426 Irqs,
427 p.PC11.reborrow(),
428 p.PB14.reborrow(),
429 p.DMA1_CH3.reborrow(),
430 Default::default(),
431 )
432 .unwrap();
433 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);
434 }
435 {
436 // partial remap RX/TX async
437 reset_afio_registers();
438 Uart::new(
439 p.USART3.reborrow(),
440 p.PC11.reborrow(),
441 p.PC10.reborrow(),
442 Irqs,
443 p.DMA1_CH2.reborrow(),
444 p.DMA1_CH3.reborrow(),
445 Default::default(),
446 )
447 .unwrap();
448 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);
449 }
450 {
451 // partial remap RX/TX/RTS/CTS async
452 reset_afio_registers();
453 Uart::new_with_rtscts(
454 p.USART3.reborrow(),
455 p.PC11.reborrow(),
456 p.PC10.reborrow(),
457 Irqs,
458 p.PB14.reborrow(),
459 p.PB13.reborrow(),
460 p.DMA1_CH2.reborrow(),
461 p.DMA1_CH3.reborrow(),
462 Default::default(),
463 )
464 .unwrap();
465 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);
466 }
467 {
468 // full remap RX/TX/RTS/CTS
469 reset_afio_registers();
470 Uart::new_blocking_with_rtscts(
471 p.USART3.reborrow(),
472 p.PD9.reborrow(),
473 p.PD8.reborrow(),
474 p.PD12.reborrow(),
475 p.PD11.reborrow(),
476 Default::default(),
477 )
478 .unwrap();
479 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);
480 }
481 {
482 // full remap RX/TX
483 reset_afio_registers();
484 Uart::new_blocking(
485 p.USART3.reborrow(),
486 p.PD9.reborrow(),
487 p.PD8.reborrow(),
488 Default::default(),
489 )
490 .unwrap();
491 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);
492 }
493 {
494 // full remap TX
495 reset_afio_registers();
496 Uart::new_blocking_half_duplex(
497 p.USART3.reborrow(),
498 p.PD8.reborrow(),
499 Default::default(),
500 embassy_stm32::usart::HalfDuplexReadback::NoReadback,
501 )
502 .unwrap();
503 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);
504 }
505 {
506 // full remap TX async
507 reset_afio_registers();
508 UartTx::new(
509 p.USART3.reborrow(),
510 p.PD8.reborrow(),
511 p.DMA1_CH2.reborrow(),
512 Default::default(),
513 )
514 .unwrap();
515 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);
516 }
517 {
518 // full remap TX/CTS async
519 reset_afio_registers();
520 UartTx::new_with_cts(
521 p.USART3.reborrow(),
522 p.PD8.reborrow(),
523 p.PD11.reborrow(),
524 p.DMA1_CH2.reborrow(),
525 Default::default(),
526 )
527 .unwrap();
528 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);
529 }
530 {
531 // full remap RX async
532 reset_afio_registers();
533 UartRx::new(
534 p.USART3.reborrow(),
535 Irqs,
536 p.PD9.reborrow(),
537 p.DMA1_CH3.reborrow(),
538 Default::default(),
539 )
540 .unwrap();
541 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);
542 }
543 {
544 // full remap RX async
545 reset_afio_registers();
546 UartRx::new_with_rts(
547 p.USART3.reborrow(),
548 Irqs,
549 p.PD9.reborrow(),
550 p.PD12.reborrow(),
551 p.DMA1_CH3.reborrow(),
552 Default::default(),
553 )
554 .unwrap();
555 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);
556 }
557 {
558 // full remap RX/TX async
559 reset_afio_registers();
560 Uart::new(
561 p.USART3.reborrow(),
562 p.PD9.reborrow(),
563 p.PD8.reborrow(),
564 Irqs,
565 p.DMA1_CH2.reborrow(),
566 p.DMA1_CH3.reborrow(),
567 Default::default(),
568 )
569 .unwrap();
570 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);
571 }
572 {
573 // full remap RX/TX/RTS/CTS async
574 reset_afio_registers();
575 Uart::new_with_rtscts(
576 p.USART3.reborrow(),
577 p.PD9.reborrow(),
578 p.PD8.reborrow(),
579 Irqs,
580 p.PD12.reborrow(),
581 p.PD11.reborrow(),
582 p.DMA1_CH2.reborrow(),
583 p.DMA1_CH3.reborrow(),
584 Default::default(),
585 )
586 .unwrap();
587 defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);
588 }
589
590 // SPI3
591 {
592 // no remap SCK/MISO/MOSI
593 afio_registers_set_remap();
594 Spi::new_blocking(
595 p.SPI3.reborrow(),
596 p.PB3.reborrow(),
597 p.PB5.reborrow(),
598 p.PB4.reborrow(),
599 Default::default(),
600 );
601 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);
602 }
603 {
604 // no remap SCK/MOSI
605 afio_registers_set_remap();
606 Spi::new_blocking_txonly(
607 p.SPI3.reborrow(),
608 p.PB3.reborrow(),
609 p.PB5.reborrow(),
610 Default::default(),
611 );
612 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);
613 }
614 {
615 // no remap MOSI
616 afio_registers_set_remap();
617 Spi::new_blocking_txonly_nosck(p.SPI3.reborrow(), p.PB5.reborrow(), Default::default());
618 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);
619 }
620 {
621 // no remap SCK/MISO
622 afio_registers_set_remap();
623 Spi::new_blocking_rxonly(
624 p.SPI3.reborrow(),
625 p.PB3.reborrow(),
626 p.PB4.reborrow(),
627 Default::default(),
628 );
629 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);
630 }
631 {
632 // remap SCK/MISO/MOSI
633 reset_afio_registers();
634 Spi::new_blocking(
635 p.SPI3.reborrow(),
636 p.PC10.reborrow(),
637 p.PC12.reborrow(),
638 p.PC11.reborrow(),
639 Default::default(),
640 );
641
642 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);
643 }
644 {
645 // remap SCK/MOSI
646 reset_afio_registers();
647 Spi::new_blocking_txonly(
648 p.SPI3.reborrow(),
649 p.PC10.reborrow(),
650 p.PC12.reborrow(),
651 Default::default(),
652 );
653 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);
654 }
655 {
656 // remap MOSI
657 reset_afio_registers();
658 Spi::new_blocking_txonly_nosck(p.SPI3.reborrow(), p.PB5.reborrow(), Default::default());
659 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);
660 }
661 {
662 // remap SCK/MISO
663 reset_afio_registers();
664 Spi::new_blocking_rxonly(
665 p.SPI3.reborrow(),
666 p.PC10.reborrow(),
667 p.PC11.reborrow(),
668 Default::default(),
669 );
670 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);
671 }
672
673 // I2S3
674 {
675 // no remap SD/WS/CK/MCK
676 afio_registers_set_remap();
677 I2S::new_txonly(
678 p.SPI3.reborrow(),
679 p.PB5.reborrow(),
680 p.PA15.reborrow(),
681 p.PB3.reborrow(),
682 p.PC7.reborrow(),
683 p.DMA2_CH2.reborrow(),
684 &mut [0u16; 0],
685 Default::default(),
686 );
687 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);
688 }
689 {
690 // no remap SD/WS/CK
691 afio_registers_set_remap();
692 I2S::new_txonly_nomck(
693 p.SPI3.reborrow(),
694 p.PB5.reborrow(),
695 p.PA15.reborrow(),
696 p.PB3.reborrow(),
697 p.DMA2_CH2.reborrow(),
698 &mut [0u16; 0],
699 Default::default(),
700 );
701 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);
702 }
703 {
704 // no remap SD/WS/CK/MCK
705 afio_registers_set_remap();
706 I2S::new_rxonly(
707 p.SPI3.reborrow(),
708 p.PB4.reborrow(),
709 p.PA15.reborrow(),
710 p.PB3.reborrow(),
711 p.PC7.reborrow(),
712 p.DMA2_CH1.reborrow(),
713 &mut [0u16; 0],
714 Default::default(),
715 );
716 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);
717 }
718 {
719 // remap SD/WS/CK/MCK
720 reset_afio_registers();
721 I2S::new_txonly(
722 p.SPI3.reborrow(),
723 p.PC12.reborrow(),
724 p.PA4.reborrow(),
725 p.PC10.reborrow(),
726 p.PC7.reborrow(),
727 p.DMA2_CH2.reborrow(),
728 &mut [0u16; 0],
729 Default::default(),
730 );
731 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);
732 }
733 {
734 // remap SD/WS/CK
735 reset_afio_registers();
736 I2S::new_txonly_nomck(
737 p.SPI3.reborrow(),
738 p.PC12.reborrow(),
739 p.PA4.reborrow(),
740 p.PC10.reborrow(),
741 p.DMA2_CH2.reborrow(),
742 &mut [0u16; 0],
743 Default::default(),
744 );
745 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);
746 }
747 {
748 // remap SD/WS/CK/MCK
749 reset_afio_registers();
750 I2S::new_rxonly(
751 p.SPI3.reborrow(),
752 p.PC11.reborrow(),
753 p.PA4.reborrow(),
754 p.PC10.reborrow(),
755 p.PC7.reborrow(),
756 p.DMA2_CH1.reborrow(),
757 &mut [0u16; 0],
758 Default::default(),
759 );
760 defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);
761 }
762
763 // CAN2
764 {
765 // no remap
766 afio_registers_set_remap();
767 Can::new(p.CAN2.reborrow(), p.PB12.reborrow(), p.PB13.reborrow(), Irqs);
768 defmt::assert_eq!(AFIO.mapr().read().can2_remap(), false);
769 }
770 {
771 // remap
772 reset_afio_registers();
773 Can::new(p.CAN2.reborrow(), p.PB5.reborrow(), p.PB6.reborrow(), Irqs);
774 defmt::assert_eq!(AFIO.mapr().read().can2_remap(), true);
775 }
776
777 // Ethernet
778 {
779 // no remap RMII
780 afio_registers_set_remap();
781 Ethernet::new(
782 &mut PacketQueue::<1, 1>::new(),
783 p.ETH.reborrow(),
784 Irqs,
785 p.PA1.reborrow(),
786 p.PA2.reborrow(),
787 p.PC1.reborrow(),
788 p.PA7.reborrow(),
789 p.PC4.reborrow(),
790 p.PC5.reborrow(),
791 p.PB12.reborrow(),
792 p.PB13.reborrow(),
793 p.PB11.reborrow(),
794 GenericPhy::new_auto(),
795 Default::default(),
796 );
797 defmt::assert_eq!(AFIO.mapr().read().eth_remap(), false);
798 }
799 {
800 // no remap MII
801 afio_registers_set_remap();
802 Ethernet::new_mii(
803 &mut PacketQueue::<1, 1>::new(),
804 p.ETH.reborrow(),
805 Irqs,
806 p.PA1.reborrow(),
807 p.PC3.reborrow(),
808 p.PA2.reborrow(),
809 p.PC1.reborrow(),
810 p.PA7.reborrow(),
811 p.PC4.reborrow(),
812 p.PC5.reborrow(),
813 p.PB0.reborrow(),
814 p.PB1.reborrow(),
815 p.PB12.reborrow(),
816 p.PB13.reborrow(),
817 p.PC2.reborrow(),
818 p.PB8.reborrow(),
819 p.PB11.reborrow(),
820 GenericPhy::new_auto(),
821 Default::default(),
822 );
823 defmt::assert_eq!(AFIO.mapr().read().eth_remap(), false);
824 }
825 {
826 // remap RMII
827 reset_afio_registers();
828 Ethernet::new(
829 &mut PacketQueue::<1, 1>::new(),
830 p.ETH.reborrow(),
831 Irqs,
832 p.PA1.reborrow(),
833 p.PA2.reborrow(),
834 p.PC1.reborrow(),
835 p.PD8.reborrow(),
836 p.PD9.reborrow(),
837 p.PD10.reborrow(),
838 p.PB12.reborrow(),
839 p.PB13.reborrow(),
840 p.PB11.reborrow(),
841 GenericPhy::new_auto(),
842 Default::default(),
843 );
844 defmt::assert_eq!(AFIO.mapr().read().eth_remap(), true);
845 }
846 {
847 // remap MII
848 reset_afio_registers();
849 Ethernet::new_mii(
850 &mut PacketQueue::<1, 1>::new(),
851 p.ETH.reborrow(),
852 Irqs,
853 p.PA1.reborrow(),
854 p.PC3.reborrow(),
855 p.PA2.reborrow(),
856 p.PC1.reborrow(),
857 p.PD8.reborrow(),
858 p.PD9.reborrow(),
859 p.PD10.reborrow(),
860 p.PD11.reborrow(),
861 p.PD12.reborrow(),
862 p.PB12.reborrow(),
863 p.PB13.reborrow(),
864 p.PC2.reborrow(),
865 p.PB8.reborrow(),
866 p.PB11.reborrow(),
867 GenericPhy::new_auto(),
868 Default::default(),
869 );
870 defmt::assert_eq!(AFIO.mapr().read().eth_remap(), true);
871 }
872
873 // CAN1
874 {
875 // no remap
876 afio_registers_set_remap();
877 Can::new(p.CAN1.reborrow(), p.PA11.reborrow(), p.PA12.reborrow(), Irqs);
878 defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 0);
879 }
880 {
881 // partial remap
882 reset_afio_registers();
883 Can::new(p.CAN1.reborrow(), p.PB8.reborrow(), p.PB9.reborrow(), Irqs);
884 defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 2);
885 }
886 {
887 // full remap
888 reset_afio_registers();
889 Can::new(p.CAN1.reborrow(), p.PD0.reborrow(), p.PD1.reborrow(), Irqs);
890 defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 3);
891 }
892
893 // USART2
894 {
895 // no remap RX/TX/RTS/CTS
896 afio_registers_set_remap();
897 Uart::new_blocking_with_rtscts(
898 p.USART2.reborrow(),
899 p.PA3.reborrow(),
900 p.PA2.reborrow(),
901 p.PA1.reborrow(),
902 p.PA0.reborrow(),
903 Default::default(),
904 )
905 .unwrap();
906 defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false);
907 }
908 {
909 // no remap RX/TX
910 afio_registers_set_remap();
911 Uart::new_blocking(
912 p.USART2.reborrow(),
913 p.PA3.reborrow(),
914 p.PA2.reborrow(),
915 Default::default(),
916 )
917 .unwrap();
918 defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false);
919 }
920 {
921 // no remap TX
922 afio_registers_set_remap();
923 Uart::new_blocking_half_duplex(
924 p.USART2.reborrow(),
925 p.PA2.reborrow(),
926 Default::default(),
927 embassy_stm32::usart::HalfDuplexReadback::NoReadback,
928 )
929 .unwrap();
930 defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false);
931 }
932 {
933 // full remap RX/TX/RTS/CTS
934 reset_afio_registers();
935 Uart::new_blocking_with_rtscts(
936 p.USART2.reborrow(),
937 p.PD6.reborrow(),
938 p.PD5.reborrow(),
939 p.PD4.reborrow(),
940 p.PD3.reborrow(),
941 Default::default(),
942 )
943 .unwrap();
944 defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false);
945 }
946 {
947 // full remap RX/TX
948 reset_afio_registers();
949 Uart::new_blocking(
950 p.USART2.reborrow(),
951 p.PD6.reborrow(),
952 p.PD5.reborrow(),
953 Default::default(),
954 )
955 .unwrap();
956 defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false);
957 }
958 {
959 // full remap TX
960 reset_afio_registers();
961 Uart::new_blocking_half_duplex(
962 p.USART2.reborrow(),
963 p.PD5.reborrow(),
964 Default::default(),
965 embassy_stm32::usart::HalfDuplexReadback::NoReadback,
966 )
967 .unwrap();
968 defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), true);
969 }
970
971 // USART1
972 {
973 // no remap RX/TX/RTS/CTS
974 afio_registers_set_remap();
975 Uart::new_blocking_with_rtscts(
976 p.USART1.reborrow(),
977 p.PA10.reborrow(),
978 p.PA9.reborrow(),
979 p.PA12.reborrow(),
980 p.PA11.reborrow(),
981 Default::default(),
982 )
983 .unwrap();
984 defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false);
985 }
986 {
987 // no remap RX/TX
988 afio_registers_set_remap();
989 Uart::new_blocking(
990 p.USART1.reborrow(),
991 p.PA10.reborrow(),
992 p.PA9.reborrow(),
993 Default::default(),
994 )
995 .unwrap();
996 defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false);
997 }
998 {
999 // no remap TX
1000 afio_registers_set_remap();
1001 Uart::new_blocking_half_duplex(
1002 p.USART1.reborrow(),
1003 p.PA9.reborrow(),
1004 Default::default(),
1005 embassy_stm32::usart::HalfDuplexReadback::NoReadback,
1006 )
1007 .unwrap();
1008 defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false);
1009 }
1010 {
1011 // remap RX/TX/RTS/CTS
1012 reset_afio_registers();
1013 Uart::new_blocking_with_rtscts(
1014 p.USART1.reborrow(),
1015 p.PB7.reborrow(),
1016 p.PB6.reborrow(),
1017 p.PA12.reborrow(),
1018 p.PA11.reborrow(),
1019 Default::default(),
1020 )
1021 .unwrap();
1022 defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true);
1023 }
1024 {
1025 // remap RX/TX
1026 reset_afio_registers();
1027 Uart::new_blocking(
1028 p.USART1.reborrow(),
1029 p.PB7.reborrow(),
1030 p.PB6.reborrow(),
1031 Default::default(),
1032 )
1033 .unwrap();
1034 defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true);
1035 }
1036 {
1037 // remap TX
1038 reset_afio_registers();
1039 Uart::new_blocking_half_duplex(
1040 p.USART1.reborrow(),
1041 p.PB6.reborrow(),
1042 Default::default(),
1043 embassy_stm32::usart::HalfDuplexReadback::NoReadback,
1044 )
1045 .unwrap();
1046 defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true);
1047 }
1048
1049 // TIM1
1050 {
1051 // full remap
1052 reset_afio_registers();
1053 SimplePwm::new(
1054 p.TIM1.reborrow(),
1055 Some(PwmPin::new(p.PE9.reborrow(), OutputType::PushPull)),
1056 Some(PwmPin::new(p.PE11.reborrow(), OutputType::PushPull)),
1057 None,
1058 None,
1059 khz(10),
1060 Default::default(),
1061 );
1062 defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 3);
1063 }
1064 }
1065}
1066
1067#[cfg(feature = "afio-value-line")]
1068mod value_line {
1069 use super::*;
1070
1071 pub fn run(p: &mut Peripherals) {
1072 // TIM13
1073 {
1074 // no remap
1075 reset_afio_registers();
1076 SimplePwm::new(
1077 p.TIM13.reborrow(),
1078 Some(PwmPin::new(p.PC8.reborrow(), OutputType::PushPull)),
1079 None,
1080 None,
1081 None,
1082 khz(10),
1083 Default::default(),
1084 );
1085 defmt::assert_eq!(AFIO.mapr2().read().tim13_remap(), false);
1086 }
1087 {
1088 // remap
1089 reset_afio_registers();
1090 SimplePwm::new(
1091 p.TIM13.reborrow(),
1092 Some(PwmPin::new(p.PB0.reborrow(), OutputType::PushPull)),
1093 None,
1094 None,
1095 None,
1096 khz(10),
1097 Default::default(),
1098 );
1099 defmt::assert_eq!(AFIO.mapr2().read().tim13_remap(), true);
1100 }
1101 }
1102}
1103
1104#[cfg(not(feature = "afio-connectivity-line"))]
1105mod connectivity_line {
1106 use super::*;
1107
1108 pub fn run(_: &mut Peripherals) {}
1109}
1110
1111#[cfg(not(feature = "afio-value-line"))]
1112mod value_line {
1113 use super::*;
1114
1115 pub fn run(_: &mut Peripherals) {}
1116}
1117
1118fn reset_afio_registers() {
1119 set_afio_registers(false, 0);
1120}
1121
1122fn afio_registers_set_remap() {
1123 set_afio_registers(true, 1);
1124}
1125
1126fn set_afio_registers(bool_val: bool, num_val: u8) {
1127 AFIO.mapr().modify(|w| {
1128 w.set_swj_cfg(embassy_stm32::pac::afio::vals::SwjCfg::NO_OP);
1129 w.set_can1_remap(num_val);
1130 w.set_can2_remap(bool_val);
1131 w.set_eth_remap(bool_val);
1132 w.set_i2c1_remap(bool_val);
1133 w.set_spi1_remap(bool_val);
1134 w.set_spi3_remap(bool_val);
1135 w.set_tim1_remap(num_val);
1136 w.set_tim2_remap(num_val);
1137 w.set_tim3_remap(num_val);
1138 w.set_tim4_remap(bool_val);
1139 w.set_usart1_remap(bool_val);
1140 w.set_usart2_remap(bool_val);
1141 w.set_usart3_remap(num_val);
1142 });
1143
1144 AFIO.mapr2().modify(|w| {
1145 w.set_cec_remap(bool_val);
1146 w.set_tim9_remap(bool_val);
1147 w.set_tim10_remap(bool_val);
1148 w.set_tim11_remap(bool_val);
1149 w.set_tim12_remap(bool_val);
1150 w.set_tim13_remap(bool_val);
1151 w.set_tim14_remap(bool_val);
1152 w.set_tim15_remap(bool_val);
1153 w.set_tim16_remap(bool_val);
1154 w.set_tim17_remap(bool_val);
1155 });
1156}
diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs
index cb63b3374..f800769ab 100644
--- a/tests/stm32/src/common.rs
+++ b/tests/stm32/src/common.rs
@@ -103,7 +103,7 @@ define_peris!(
103 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, 103 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,
104 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;}, 104 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
105); 105);
106#[cfg(feature = "stm32f103c8")] 106#[cfg(any(feature = "stm32f100rd", feature = "stm32f103c8", feature = "stm32f107vc"))]
107define_peris!( 107define_peris!(
108 UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, 108 UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5,
109 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, 109 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,