aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-12-11 09:08:50 -0600
committerGitHub <[email protected]>2025-12-11 09:08:50 -0600
commit1aadce76847a7686bf66e3a6532e18e05042f78a (patch)
treeca42d80e784a6746065bd4e0c676db3ef0f10b93
parent52a9b08f0ca13d23bfb039c884a9101997c10567 (diff)
parentf650afc33b2d6b39116f27c6545c5f2d9e3c7d06 (diff)
Merge branch 'main' into main
-rwxr-xr-x.github/ci/book.sh2
-rwxr-xr-x.github/ci/build-nightly.sh2
-rwxr-xr-x.github/ci/build-xtensa.sh2
-rwxr-xr-x.github/ci/build.sh2
-rwxr-xr-x.github/ci/doc.sh4
-rwxr-xr-x.github/ci/janitor.sh2
-rw-r--r--.github/workflows/changelog.yml724
-rw-r--r--README.md2
-rw-r--r--cyw43/CHANGELOG.md2
-rw-r--r--cyw43/src/control.rs13
-rw-r--r--docs/pages/mcxa.adoc18
-rw-r--r--docs/pages/overview.adoc6
-rw-r--r--embassy-mcxa/.gitignore19
-rw-r--r--embassy-mcxa/Cargo.toml69
-rw-r--r--embassy-mcxa/README.md5
-rw-r--r--embassy-mcxa/src/adc.rs752
-rw-r--r--embassy-mcxa/src/clkout.rs169
-rw-r--r--embassy-mcxa/src/clocks/config.rs204
-rw-r--r--embassy-mcxa/src/clocks/mod.rs955
-rw-r--r--embassy-mcxa/src/clocks/periph_helpers.rs499
-rw-r--r--embassy-mcxa/src/config.rs27
-rw-r--r--embassy-mcxa/src/crc.rs758
-rw-r--r--embassy-mcxa/src/dma.rs2602
-rw-r--r--embassy-mcxa/src/gpio.rs1062
-rw-r--r--embassy-mcxa/src/i2c/controller.rs742
-rw-r--r--embassy-mcxa/src/i2c/mod.rs194
-rw-r--r--embassy-mcxa/src/interrupt.rs536
-rw-r--r--embassy-mcxa/src/lib.rs447
-rw-r--r--embassy-mcxa/src/lpuart/buffered.rs780
-rw-r--r--embassy-mcxa/src/lpuart/mod.rs1783
-rw-r--r--embassy-mcxa/src/ostimer.rs745
-rw-r--r--embassy-mcxa/src/rtc.rs453
-rw-r--r--embassy-net-esp-hosted/CHANGELOG.md1
-rw-r--r--embassy-net-esp-hosted/src/control.rs40
-rw-r--r--embassy-net-esp-hosted/src/ioctl.rs27
-rw-r--r--embassy-net-esp-hosted/src/lib.rs5
-rw-r--r--embassy-net-esp-hosted/src/proto.rs12
-rw-r--r--embassy-net-nrf91/Cargo.toml2
-rw-r--r--embassy-nrf/CHANGELOG.md3
-rw-r--r--embassy-nrf/Cargo.toml23
-rw-r--r--embassy-nrf/src/chips/nrf54l05_app.rs746
-rw-r--r--embassy-nrf/src/chips/nrf54l10_app.rs746
-rw-r--r--embassy-nrf/src/chips/nrf54lm20_app.rs841
-rw-r--r--embassy-nrf/src/cracen.rs30
-rw-r--r--embassy-nrf/src/gpiote.rs8
-rw-r--r--embassy-nrf/src/lib.rs32
-rw-r--r--embassy-nrf/src/saadc.rs1
-rw-r--r--embassy-stm32-wpan/CHANGELOG.md1
-rw-r--r--embassy-stm32-wpan/Cargo.toml80
-rw-r--r--embassy-stm32-wpan/src/lib.rs203
-rw-r--r--embassy-stm32-wpan/src/sub/mod.rs6
-rw-r--r--embassy-stm32-wpan/src/wb55/channels.rs (renamed from embassy-stm32-wpan/src/channels.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/cmd.rs (renamed from embassy-stm32-wpan/src/cmd.rs)2
-rw-r--r--embassy-stm32-wpan/src/wb55/consts.rs (renamed from embassy-stm32-wpan/src/consts.rs)2
-rw-r--r--embassy-stm32-wpan/src/wb55/evt.rs (renamed from embassy-stm32-wpan/src/evt.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/fmt.rs (renamed from embassy-stm32-wpan/src/fmt.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/lhci.rs (renamed from embassy-stm32-wpan/src/lhci.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/commands.rs (renamed from embassy-stm32-wpan/src/mac/commands.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/consts.rs (renamed from embassy-stm32-wpan/src/mac/consts.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/control.rs (renamed from embassy-stm32-wpan/src/mac/control.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/driver.rs (renamed from embassy-stm32-wpan/src/mac/driver.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/event.rs (renamed from embassy-stm32-wpan/src/mac/event.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/indications.rs (renamed from embassy-stm32-wpan/src/mac/indications.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/macros.rs (renamed from embassy-stm32-wpan/src/mac/macros.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/mod.rs (renamed from embassy-stm32-wpan/src/mac/mod.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/opcodes.rs (renamed from embassy-stm32-wpan/src/mac/opcodes.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/responses.rs (renamed from embassy-stm32-wpan/src/mac/responses.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/runner.rs (renamed from embassy-stm32-wpan/src/mac/runner.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/typedefs.rs (renamed from embassy-stm32-wpan/src/mac/typedefs.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mod.rs198
-rw-r--r--embassy-stm32-wpan/src/wb55/shci.rs (renamed from embassy-stm32-wpan/src/shci.rs)2
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/ble.rs (renamed from embassy-stm32-wpan/src/sub/ble.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/mac.rs (renamed from embassy-stm32-wpan/src/sub/mac.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/mm.rs (renamed from embassy-stm32-wpan/src/sub/mm.rs)6
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/mod.rs6
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/sys.rs (renamed from embassy-stm32-wpan/src/sub/sys.rs)8
-rw-r--r--embassy-stm32-wpan/src/wb55/tables.rs (renamed from embassy-stm32-wpan/src/tables.rs)14
-rw-r--r--embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs (renamed from embassy-stm32-wpan/src/unsafe_linked_list.rs)0
-rw-r--r--embassy-stm32-wpan/src/wba/bindings.rs1
-rw-r--r--embassy-stm32-wpan/src/wba/linklayer_plat.rs645
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/ll_sys_cs.rs77
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/ll_sys_dp_slp.rs163
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/ll_sys_intf.rs199
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/ll_sys_startup.rs125
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/ll_version.rs115
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/mod.rs5
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys_if.rs416
-rw-r--r--embassy-stm32-wpan/src/wba/mac_sys_if.rs188
-rw-r--r--embassy-stm32-wpan/src/wba/mod.rs6
-rw-r--r--embassy-stm32-wpan/src/wba/util_seq.rs243
-rw-r--r--embassy-stm32/CHANGELOG.md6
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/src/adc/adc4.rs207
-rw-r--r--embassy-stm32/src/adc/c0.rs64
-rw-r--r--embassy-stm32/src/adc/g4.rs121
-rw-r--r--embassy-stm32/src/adc/injected.rs16
-rw-r--r--embassy-stm32/src/adc/mod.rs254
-rw-r--r--embassy-stm32/src/adc/ringbuffered.rs48
-rw-r--r--embassy-stm32/src/adc/v2.rs60
-rw-r--r--embassy-stm32/src/adc/v3.rs91
-rw-r--r--embassy-stm32/src/adc/v4.rs76
-rw-r--r--embassy-stm32/src/dma/gpdma/mod.rs6
-rw-r--r--embassy-stm32/src/dma/mod.rs44
-rw-r--r--embassy-stm32/src/dma/util.rs58
-rw-r--r--embassy-stm32/src/dma/word.rs4
-rw-r--r--embassy-stm32/src/exti.rs2
-rw-r--r--embassy-stm32/src/fmt.rs10
-rw-r--r--embassy-stm32/src/gpio.rs6
-rw-r--r--embassy-stm32/src/i2c/mod.rs2
-rw-r--r--embassy-stm32/src/i2c/v1.rs7
-rw-r--r--embassy-stm32/src/i2c/v2.rs10
-rw-r--r--embassy-stm32/src/low_power.rs61
-rw-r--r--embassy-stm32/src/qspi/mod.rs4
-rw-r--r--embassy-stm32/src/rcc/mod.rs22
-rw-r--r--embassy-stm32/src/rcc/n6.rs20
-rw-r--r--embassy-stm32/src/sai/mod.rs10
-rw-r--r--embassy-stm32/src/sdmmc/mod.rs1352
-rw-r--r--embassy-stm32/src/sdmmc/sd.rs693
-rw-r--r--embassy-stm32/src/sdmmc/sdio.rs177
-rw-r--r--embassy-stm32/src/spi/mod.rs109
-rw-r--r--embassy-stm32/src/time.rs2
-rw-r--r--embassy-stm32/src/time_driver.rs6
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs90
-rw-r--r--embassy-stm32/src/timer/input_capture.rs2
-rw-r--r--embassy-stm32/src/timer/low_level.rs235
-rw-r--r--embassy-stm32/src/timer/mod.rs34
-rw-r--r--embassy-stm32/src/timer/one_pulse.rs12
-rw-r--r--embassy-stm32/src/timer/pwm_input.rs14
-rw-r--r--embassy-stm32/src/timer/ringbuffered.rs29
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs82
-rw-r--r--embassy-stm32/src/usart/mod.rs8
-rw-r--r--embassy-time/src/timer.rs2
-rw-r--r--embassy-usb/CHANGELOG.md2
-rw-r--r--embassy-usb/src/class/cdc_acm.rs17
-rw-r--r--examples/boot/application/nrf/Cargo.toml14
-rw-r--r--examples/boot/application/nrf/README.md2
-rw-r--r--examples/boot/application/nrf/src/bin/a.rs25
-rw-r--r--examples/boot/application/nrf/src/bin/b.rs4
-rw-r--r--examples/boot/bootloader/nrf/Cargo.toml4
-rw-r--r--examples/boot/bootloader/nrf/src/main.rs3
-rw-r--r--examples/mcxa/.cargo/config.toml17
-rw-r--r--examples/mcxa/.gitignore1
-rw-r--r--examples/mcxa/Cargo.toml34
-rw-r--r--examples/mcxa/build.rs20
-rw-r--r--examples/mcxa/memory.x5
-rw-r--r--examples/mcxa/src/bin/adc_interrupt.rs73
-rw-r--r--examples/mcxa/src/bin/adc_polling.rs72
-rw-r--r--examples/mcxa/src/bin/blinky.rs36
-rw-r--r--examples/mcxa/src/bin/button.rs23
-rw-r--r--examples/mcxa/src/bin/button_async.rs29
-rw-r--r--examples/mcxa/src/bin/clkout.rs69
-rw-r--r--examples/mcxa/src/bin/crc.rs154
-rw-r--r--examples/mcxa/src/bin/dma_mem_to_mem.rs118
-rw-r--r--examples/mcxa/src/bin/dma_scatter_gather_builder.rs130
-rw-r--r--examples/mcxa/src/bin/dma_wrap_transfer.rs184
-rw-r--r--examples/mcxa/src/bin/hello.rs119
-rw-r--r--examples/mcxa/src/bin/i2c-async.rs39
-rw-r--r--examples/mcxa/src/bin/i2c-blocking.rs31
-rw-r--r--examples/mcxa/src/bin/i2c-scan-blocking.rs41
-rw-r--r--examples/mcxa/src/bin/lpuart_buffered.rs62
-rw-r--r--examples/mcxa/src/bin/lpuart_dma.rs68
-rw-r--r--examples/mcxa/src/bin/lpuart_polling.rs47
-rw-r--r--examples/mcxa/src/bin/lpuart_ring_buffer.rs115
-rw-r--r--examples/mcxa/src/bin/raw_dma_channel_link.rs278
-rw-r--r--examples/mcxa/src/bin/raw_dma_interleave_transfer.rs141
-rw-r--r--examples/mcxa/src/bin/raw_dma_memset.rs129
-rw-r--r--examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs244
-rw-r--r--examples/mcxa/src/bin/raw_dma_scatter_gather.rs165
-rw-r--r--examples/mcxa/src/bin/rtc_alarm.rs47
-rw-r--r--examples/nrf54lm20/.cargo/config.toml9
-rw-r--r--examples/nrf54lm20/Cargo.toml37
-rw-r--r--examples/nrf54lm20/build.rs35
-rw-r--r--examples/nrf54lm20/memory.x5
-rw-r--r--examples/nrf54lm20/src/bin/blinky.rs23
-rw-r--r--examples/nrf54lm20/src/bin/gpio.rs23
-rw-r--r--examples/nrf54lm20/src/bin/gpiote_channel.rs49
-rw-r--r--examples/nrf54lm20/src/bin/gpiote_port.rs33
-rw-r--r--examples/nrf54lm20/src/bin/timer.rs30
-rw-r--r--examples/rp235x/src/bin/pio_i2s.rs4
-rw-r--r--examples/stm32f4/src/bin/pwm_complementary.rs2
-rw-r--r--examples/stm32f4/src/bin/sdmmc.rs38
-rw-r--r--examples/stm32f4/src/bin/ws2812_pwm.rs2
-rw-r--r--examples/stm32f7/src/bin/sdmmc.rs11
-rw-r--r--examples/stm32h5/src/bin/adc_dma.rs2
-rw-r--r--examples/stm32h7/src/bin/sai.rs2
-rw-r--r--examples/stm32h7/src/bin/sdmmc.rs13
-rw-r--r--examples/stm32h723/src/bin/spdifrx.rs1
-rw-r--r--examples/stm32n6/Cargo.toml2
-rw-r--r--examples/stm32n6/src/bin/crc.rs31
-rw-r--r--examples/stm32n6/src/bin/hash.rs78
-rw-r--r--examples/stm32wb/Cargo.toml4
-rw-r--r--examples/stm32wba/Cargo.toml8
-rw-r--r--examples/stm32wba/src/bin/mac_ffd.rs58
-rw-r--r--examples/stm32wba/src/bin/rtc.rs62
-rw-r--r--examples/stm32wba6/src/bin/blinky.rs33
-rw-r--r--examples/stm32wba6/src/bin/rtc.rs62
-rwxr-xr-xfmtall.sh13
-rw-r--r--tests/stm32/Cargo.toml6
-rw-r--r--tests/stm32/src/bin/sdmmc.rs92
-rw-r--r--tests/stm32/src/bin/usart.rs44
-rw-r--r--tests/stm32/src/bin/usart_dma.rs18
201 files changed, 23747 insertions, 2513 deletions
diff --git a/.github/ci/book.sh b/.github/ci/book.sh
index 6c300bf09..a39f0dac7 100755
--- a/.github/ci/book.sh
+++ b/.github/ci/book.sh
@@ -9,7 +9,7 @@ set -euxo pipefail
9make -C docs 9make -C docs
10 10
11export KUBECONFIG=/ci/secrets/kubeconfig.yml 11export KUBECONFIG=/ci/secrets/kubeconfig.yml
12POD=$(kubectl -n embassy get po -l app=website -o jsonpath={.items[0].metadata.name}) 12POD=$(kubectl get po -l app=website -o jsonpath={.items[0].metadata.name})
13 13
14mkdir -p build 14mkdir -p build
15mv docs/book build/book 15mv docs/book build/book
diff --git a/.github/ci/build-nightly.sh b/.github/ci/build-nightly.sh
index 82e9436f3..04fbd2353 100755
--- a/.github/ci/build-nightly.sh
+++ b/.github/ci/build-nightly.sh
@@ -23,7 +23,7 @@ fi
23hashtime restore /ci/cache/filetime.json || true 23hashtime restore /ci/cache/filetime.json || true
24hashtime save /ci/cache/filetime.json 24hashtime save /ci/cache/filetime.json
25 25
26cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 26cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev c60400e213f7eb0296581183140ec147dd7a848b
27 27
28./ci-nightly.sh 28./ci-nightly.sh
29 29
diff --git a/.github/ci/build-xtensa.sh b/.github/ci/build-xtensa.sh
index 3f74b4a5a..f07816861 100755
--- a/.github/ci/build-xtensa.sh
+++ b/.github/ci/build-xtensa.sh
@@ -25,7 +25,7 @@ fi
25hashtime restore /ci/cache/filetime.json || true 25hashtime restore /ci/cache/filetime.json || true
26hashtime save /ci/cache/filetime.json 26hashtime save /ci/cache/filetime.json
27 27
28cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 28cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev c60400e213f7eb0296581183140ec147dd7a848b
29 29
30./ci-xtensa.sh 30./ci-xtensa.sh
31 31
diff --git a/.github/ci/build.sh b/.github/ci/build.sh
index 3c196f72b..cd3006c49 100755
--- a/.github/ci/build.sh
+++ b/.github/ci/build.sh
@@ -28,7 +28,7 @@ fi
28hashtime restore /ci/cache/filetime.json || true 28hashtime restore /ci/cache/filetime.json || true
29hashtime save /ci/cache/filetime.json 29hashtime save /ci/cache/filetime.json
30 30
31cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 31cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev c60400e213f7eb0296581183140ec147dd7a848b
32 32
33./ci.sh 33./ci.sh
34 34
diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh
index 535fc5262..85eed9f80 100755
--- a/.github/ci/doc.sh
+++ b/.github/ci/doc.sh
@@ -12,11 +12,11 @@ export CARGO_TARGET_DIR=/ci/cache/target
12export PATH=$CARGO_HOME/bin:$PATH 12export PATH=$CARGO_HOME/bin:$PATH
13mv rust-toolchain-nightly.toml rust-toolchain.toml 13mv rust-toolchain-nightly.toml rust-toolchain.toml
14 14
15cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 15cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev c60400e213f7eb0296581183140ec147dd7a848b
16 16
17cargo embassy-devtool doc -o webroot 17cargo embassy-devtool doc -o webroot
18 18
19export KUBECONFIG=/ci/secrets/kubeconfig.yml 19export KUBECONFIG=/ci/secrets/kubeconfig.yml
20POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) 20POD=$(kubectl get po -l app=docserver -o jsonpath={.items[0].metadata.name})
21kubectl cp webroot/crates $POD:/data 21kubectl cp webroot/crates $POD:/data
22kubectl cp webroot/static $POD:/data 22kubectl cp webroot/static $POD:/data
diff --git a/.github/ci/janitor.sh b/.github/ci/janitor.sh
index bc43075bd..9679308f2 100755
--- a/.github/ci/janitor.sh
+++ b/.github/ci/janitor.sh
@@ -9,7 +9,7 @@ export 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 10export PATH=$CARGO_HOME/bin:$PATH
11 11
12cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 12cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev c60400e213f7eb0296581183140ec147dd7a848b
13 13
14cargo embassy-devtool check-crlf 14cargo embassy-devtool check-crlf
15cargo embassy-devtool check-manifest 15cargo embassy-devtool check-manifest
diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml
index 8ee143cad..0cb66ded5 100644
--- a/.github/workflows/changelog.yml
+++ b/.github/workflows/changelog.yml
@@ -1,362 +1,362 @@
1name: Changelog check 1# name: Changelog check
2 2#
3on: 3# on:
4 pull_request: 4# pull_request:
5 # We will not track changes for the following packages/directories. 5# # We will not track changes for the following packages/directories.
6 paths-ignore: 6# paths-ignore:
7 - "/examples/" 7# - "/examples/"
8 - "/docs/" 8# - "/docs/"
9 - "/out/" 9# - "/out/"
10 - "/tests/" 10# - "/tests/"
11 - "/release/" 11# - "/release/"
12 - "/cyw43-firmware/" 12# - "/cyw43-firmware/"
13 # Run on labeled/unlabeled in addition to defaults to detect 13# # Run on labeled/unlabeled in addition to defaults to detect
14 # adding/removing skip-changelog labels. 14# # adding/removing skip-changelog labels.
15 types: [opened, reopened, labeled, unlabeled, synchronize] 15# types: [opened, reopened, labeled, unlabeled, synchronize]
16 16#
17jobs: 17# jobs:
18 changelog: 18# changelog:
19 runs-on: ubuntu-latest 19# runs-on: ubuntu-latest
20 20#
21 steps: 21# steps:
22 - name: Checkout sources 22# - name: Checkout sources
23 uses: actions/checkout@v4 23# uses: actions/checkout@v4
24 24#
25 - name: Check which package is modified 25# - name: Check which package is modified
26 uses: dorny/paths-filter@v3 26# uses: dorny/paths-filter@v3
27 id: changes 27# id: changes
28 with: 28# with:
29 filters: | 29# filters: |
30 cyw43: 30# cyw43:
31 - 'cyw43/**' 31# - 'cyw43/**'
32 cyw43-pio: 32# cyw43-pio:
33 - 'cyw43-pio/**' 33# - 'cyw43-pio/**'
34 embassy-boot: 34# embassy-boot:
35 - 'embassy-boot/**' 35# - 'embassy-boot/**'
36 embassy-boot-nrf: 36# embassy-boot-nrf:
37 - 'embassy-boot-nrf/**' 37# - 'embassy-boot-nrf/**'
38 embassy-boot-rp: 38# embassy-boot-rp:
39 - 'embassy-boot-rp/**' 39# - 'embassy-boot-rp/**'
40 embassy-boot-stm32: 40# embassy-boot-stm32:
41 - 'embassy-boot-stm32/**' 41# - 'embassy-boot-stm32/**'
42 embassy-embedded-hal: 42# embassy-embedded-hal:
43 - 'embassy-embedded-hal/**' 43# - 'embassy-embedded-hal/**'
44 embassy-executor: 44# embassy-executor:
45 - 'embassy-executor/**' 45# - 'embassy-executor/**'
46 embassy-executor-macros: 46# embassy-executor-macros:
47 - 'embassy-executor-macros/**' 47# - 'embassy-executor-macros/**'
48 embassy-executor-timer-queue: 48# embassy-executor-timer-queue:
49 - 'embassy-executor-timer-queue/**' 49# - 'embassy-executor-timer-queue/**'
50 embassy-futures: 50# embassy-futures:
51 - 'embassy-futures/**' 51# - 'embassy-futures/**'
52 embassy-imxrt: 52# embassy-imxrt:
53 - 'embassy-imxrt/**' 53# - 'embassy-imxrt/**'
54 embassy-mspm0: 54# embassy-mspm0:
55 - 'embassy-mspm0/**' 55# - 'embassy-mspm0/**'
56 embassy-net: 56# embassy-net:
57 - 'embassy-net/**' 57# - 'embassy-net/**'
58 embassy-net-adin1110: 58# embassy-net-adin1110:
59 - 'embassy-net-adin1110/**' 59# - 'embassy-net-adin1110/**'
60 embassy-net-driver: 60# embassy-net-driver:
61 - 'embassy-net-driver/**' 61# - 'embassy-net-driver/**'
62 embassy-net-driver-channel: 62# embassy-net-driver-channel:
63 - 'embassy-net-driver-channel/**' 63# - 'embassy-net-driver-channel/**'
64 embassy-net-enc28j60: 64# embassy-net-enc28j60:
65 - 'embassy-net-enc28j60/**' 65# - 'embassy-net-enc28j60/**'
66 embassy-net-esp-hosted: 66# embassy-net-esp-hosted:
67 - 'embassy-net-esp-hosted/**' 67# - 'embassy-net-esp-hosted/**'
68 embassy-net-nrf91: 68# embassy-net-nrf91:
69 - 'embassy-net-nrf91/**' 69# - 'embassy-net-nrf91/**'
70 embassy-net-ppp: 70# embassy-net-ppp:
71 - 'embassy-net-ppp/**' 71# - 'embassy-net-ppp/**'
72 embassy-net-tuntap: 72# embassy-net-tuntap:
73 - 'embassy-net-tuntap/**' 73# - 'embassy-net-tuntap/**'
74 embassy-net-wiznet: 74# embassy-net-wiznet:
75 - 'embassy-net-wiznet/**' 75# - 'embassy-net-wiznet/**'
76 embassy-nrf: 76# embassy-nrf:
77 - 'embassy-nrf/**' 77# - 'embassy-nrf/**'
78 embassy-nxp: 78# embassy-nxp:
79 - 'embassy-nxp/**' 79# - 'embassy-nxp/**'
80 embassy-rp: 80# embassy-rp:
81 - 'embassy-rp/**' 81# - 'embassy-rp/**'
82 embassy-stm32: 82# embassy-stm32:
83 - 'embassy-stm32/**' 83# - 'embassy-stm32/**'
84 embassy-stm32-wpan: 84# embassy-stm32-wpan:
85 - 'embassy-stm32-wpan/**' 85# - 'embassy-stm32-wpan/**'
86 embassy-sync: 86# embassy-sync:
87 - 'embassy-sync/**' 87# - 'embassy-sync/**'
88 embassy-time: 88# embassy-time:
89 - 'embassy-time/**' 89# - 'embassy-time/**'
90 embassy-time-driver: 90# embassy-time-driver:
91 - 'embassy-time-driver/**' 91# - 'embassy-time-driver/**'
92 embassy-time-queue-utils: 92# embassy-time-queue-utils:
93 - 'embassy-time-queue-utils/**' 93# - 'embassy-time-queue-utils/**'
94 embassy-usb: 94# embassy-usb:
95 - 'embassy-usb/**' 95# - 'embassy-usb/**'
96 embassy-usb-dfu: 96# embassy-usb-dfu:
97 - 'embassy-usb-dfu/**' 97# - 'embassy-usb-dfu/**'
98 embassy-usb-driver: 98# embassy-usb-driver:
99 - 'embassy-usb-driver/**' 99# - 'embassy-usb-driver/**'
100 embassy-usb-logger: 100# embassy-usb-logger:
101 - 'embassy-usb-logger/**' 101# - 'embassy-usb-logger/**'
102 embassy-usb-synopsys-otg: 102# embassy-usb-synopsys-otg:
103 - 'embassy-usb-synopsys-otg/**' 103# - 'embassy-usb-synopsys-otg/**'
104 - name: Check that changelog updated (cyw43) 104# - name: Check that changelog updated (cyw43)
105 if: steps.changes.outputs.cyw43 == 'true' 105# if: steps.changes.outputs.cyw43 == 'true'
106 uses: dangoslen/changelog-enforcer@v3 106# uses: dangoslen/changelog-enforcer@v3
107 with: 107# with:
108 changeLogPath: cyw43/CHANGELOG.md 108# changeLogPath: cyw43/CHANGELOG.md
109 skipLabels: "skip-changelog" 109# skipLabels: "skip-changelog"
110 missingUpdateErrorMessage: "Please add a changelog entry in the cyw43/CHANGELOG.md file." 110# missingUpdateErrorMessage: "Please add a changelog entry in the cyw43/CHANGELOG.md file."
111 - name: Check that changelog updated (cyw43-pio) 111# - name: Check that changelog updated (cyw43-pio)
112 if: steps.changes.outputs.cyw43-pio == 'true' 112# if: steps.changes.outputs.cyw43-pio == 'true'
113 uses: dangoslen/changelog-enforcer@v3 113# uses: dangoslen/changelog-enforcer@v3
114 with: 114# with:
115 changeLogPath: cyw43-pio/CHANGELOG.md 115# changeLogPath: cyw43-pio/CHANGELOG.md
116 skipLabels: "skip-changelog" 116# skipLabels: "skip-changelog"
117 missingUpdateErrorMessage: "Please add a changelog entry in the cyw43-pio/CHANGELOG.md file." 117# missingUpdateErrorMessage: "Please add a changelog entry in the cyw43-pio/CHANGELOG.md file."
118 - name: Check that changelog updated (embassy-boot) 118# - name: Check that changelog updated (embassy-boot)
119 if: steps.changes.outputs.embassy-boot == 'true' 119# if: steps.changes.outputs.embassy-boot == 'true'
120 uses: dangoslen/changelog-enforcer@v3 120# uses: dangoslen/changelog-enforcer@v3
121 with: 121# with:
122 changeLogPath: embassy-boot/CHANGELOG.md 122# changeLogPath: embassy-boot/CHANGELOG.md
123 skipLabels: "skip-changelog" 123# skipLabels: "skip-changelog"
124 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-boot/CHANGELOG.md file." 124# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-boot/CHANGELOG.md file."
125 - name: Check that changelog updated (embassy-boot-nrf) 125# - name: Check that changelog updated (embassy-boot-nrf)
126 if: steps.changes.outputs.embassy-boot-nrf == 'true' 126# if: steps.changes.outputs.embassy-boot-nrf == 'true'
127 uses: dangoslen/changelog-enforcer@v3 127# uses: dangoslen/changelog-enforcer@v3
128 with: 128# with:
129 changeLogPath: embassy-boot-nrf/CHANGELOG.md 129# changeLogPath: embassy-boot-nrf/CHANGELOG.md
130 skipLabels: "skip-changelog" 130# skipLabels: "skip-changelog"
131 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-boot-nrf/CHANGELOG.md file." 131# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-boot-nrf/CHANGELOG.md file."
132 - name: Check that changelog updated (embassy-boot-rp) 132# - name: Check that changelog updated (embassy-boot-rp)
133 if: steps.changes.outputs.embassy-boot-rp == 'true' 133# if: steps.changes.outputs.embassy-boot-rp == 'true'
134 uses: dangoslen/changelog-enforcer@v3 134# uses: dangoslen/changelog-enforcer@v3
135 with: 135# with:
136 changeLogPath: embassy-boot-rp/CHANGELOG.md 136# changeLogPath: embassy-boot-rp/CHANGELOG.md
137 skipLabels: "skip-changelog" 137# skipLabels: "skip-changelog"
138 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-boot-rp/CHANGELOG.md file." 138# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-boot-rp/CHANGELOG.md file."
139 - name: Check that changelog updated (embassy-boot-stm32) 139# - name: Check that changelog updated (embassy-boot-stm32)
140 if: steps.changes.outputs.embassy-boot-stm32 == 'true' 140# if: steps.changes.outputs.embassy-boot-stm32 == 'true'
141 uses: dangoslen/changelog-enforcer@v3 141# uses: dangoslen/changelog-enforcer@v3
142 with: 142# with:
143 changeLogPath: embassy-boot-stm32/CHANGELOG.md 143# changeLogPath: embassy-boot-stm32/CHANGELOG.md
144 skipLabels: "skip-changelog" 144# skipLabels: "skip-changelog"
145 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-boot-stm32/CHANGELOG.md file." 145# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-boot-stm32/CHANGELOG.md file."
146 - name: Check that changelog updated (embassy-embedded-hal) 146# - name: Check that changelog updated (embassy-embedded-hal)
147 if: steps.changes.outputs.embassy-embedded-hal == 'true' 147# if: steps.changes.outputs.embassy-embedded-hal == 'true'
148 uses: dangoslen/changelog-enforcer@v3 148# uses: dangoslen/changelog-enforcer@v3
149 with: 149# with:
150 changeLogPath: embassy-embedded-hal/CHANGELOG.md 150# changeLogPath: embassy-embedded-hal/CHANGELOG.md
151 skipLabels: "skip-changelog" 151# skipLabels: "skip-changelog"
152 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-embedded-hal/CHANGELOG.md file." 152# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-embedded-hal/CHANGELOG.md file."
153 - name: Check that changelog updated (embassy-executor) 153# - name: Check that changelog updated (embassy-executor)
154 if: steps.changes.outputs.embassy-executor == 'true' 154# if: steps.changes.outputs.embassy-executor == 'true'
155 uses: dangoslen/changelog-enforcer@v3 155# uses: dangoslen/changelog-enforcer@v3
156 with: 156# with:
157 changeLogPath: embassy-executor/CHANGELOG.md 157# changeLogPath: embassy-executor/CHANGELOG.md
158 skipLabels: "skip-changelog" 158# skipLabels: "skip-changelog"
159 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-executor/CHANGELOG.md file." 159# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-executor/CHANGELOG.md file."
160 - name: Check that changelog updated (embassy-executor-macros) 160# - name: Check that changelog updated (embassy-executor-macros)
161 if: steps.changes.outputs.embassy-executor-macros == 'true' 161# if: steps.changes.outputs.embassy-executor-macros == 'true'
162 uses: dangoslen/changelog-enforcer@v3 162# uses: dangoslen/changelog-enforcer@v3
163 with: 163# with:
164 changeLogPath: embassy-executor/CHANGELOG.md 164# changeLogPath: embassy-executor/CHANGELOG.md
165 skipLabels: "skip-changelog" 165# skipLabels: "skip-changelog"
166 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-executor/CHANGELOG.md file." 166# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-executor/CHANGELOG.md file."
167 - name: Check that changelog updated (embassy-executor-timer-queue) 167# - name: Check that changelog updated (embassy-executor-timer-queue)
168 if: steps.changes.outputs.embassy-executor-timer-queue == 'true' 168# if: steps.changes.outputs.embassy-executor-timer-queue == 'true'
169 uses: dangoslen/changelog-enforcer@v3 169# uses: dangoslen/changelog-enforcer@v3
170 with: 170# with:
171 changeLogPath: embassy-executor-timer-queue/CHANGELOG.md 171# changeLogPath: embassy-executor-timer-queue/CHANGELOG.md
172 skipLabels: "skip-changelog" 172# skipLabels: "skip-changelog"
173 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-executor-timer-queue/CHANGELOG.md file." 173# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-executor-timer-queue/CHANGELOG.md file."
174 - name: Check that changelog updated (embassy-futures) 174# - name: Check that changelog updated (embassy-futures)
175 if: steps.changes.outputs.embassy-futures == 'true' 175# if: steps.changes.outputs.embassy-futures == 'true'
176 uses: dangoslen/changelog-enforcer@v3 176# uses: dangoslen/changelog-enforcer@v3
177 with: 177# with:
178 changeLogPath: embassy-futures/CHANGELOG.md 178# changeLogPath: embassy-futures/CHANGELOG.md
179 skipLabels: "skip-changelog" 179# skipLabels: "skip-changelog"
180 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-futures/CHANGELOG.md file." 180# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-futures/CHANGELOG.md file."
181 - name: Check that changelog updated (embassy-imxrt) 181# - name: Check that changelog updated (embassy-imxrt)
182 if: steps.changes.outputs.embassy-imxrt == 'true' 182# if: steps.changes.outputs.embassy-imxrt == 'true'
183 uses: dangoslen/changelog-enforcer@v3 183# uses: dangoslen/changelog-enforcer@v3
184 with: 184# with:
185 changeLogPath: embassy-imxrt/CHANGELOG.md 185# changeLogPath: embassy-imxrt/CHANGELOG.md
186 skipLabels: "skip-changelog" 186# skipLabels: "skip-changelog"
187 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-imxrt/CHANGELOG.md file." 187# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-imxrt/CHANGELOG.md file."
188 - name: Check that changelog updated (embassy-mspm0) 188# - name: Check that changelog updated (embassy-mspm0)
189 if: steps.changes.outputs.embassy-mspm0 == 'true' 189# if: steps.changes.outputs.embassy-mspm0 == 'true'
190 uses: dangoslen/changelog-enforcer@v3 190# uses: dangoslen/changelog-enforcer@v3
191 with: 191# with:
192 changeLogPath: embassy-mspm0/CHANGELOG.md 192# changeLogPath: embassy-mspm0/CHANGELOG.md
193 skipLabels: "skip-changelog" 193# skipLabels: "skip-changelog"
194 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-mspm0/CHANGELOG.md file." 194# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-mspm0/CHANGELOG.md file."
195 - name: Check that changelog updated (embassy-net) 195# - name: Check that changelog updated (embassy-net)
196 if: steps.changes.outputs.embassy-net == 'true' 196# if: steps.changes.outputs.embassy-net == 'true'
197 uses: dangoslen/changelog-enforcer@v3 197# uses: dangoslen/changelog-enforcer@v3
198 with: 198# with:
199 changeLogPath: embassy-net/CHANGELOG.md 199# changeLogPath: embassy-net/CHANGELOG.md
200 skipLabels: "skip-changelog" 200# skipLabels: "skip-changelog"
201 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net/CHANGELOG.md file." 201# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net/CHANGELOG.md file."
202 - name: Check that changelog updated (embassy-net-adin1110) 202# - name: Check that changelog updated (embassy-net-adin1110)
203 if: steps.changes.outputs.embassy-net-adin1110 == 'true' 203# if: steps.changes.outputs.embassy-net-adin1110 == 'true'
204 uses: dangoslen/changelog-enforcer@v3 204# uses: dangoslen/changelog-enforcer@v3
205 with: 205# with:
206 changeLogPath: embassy-net-adin1110/CHANGELOG.md 206# changeLogPath: embassy-net-adin1110/CHANGELOG.md
207 skipLabels: "skip-changelog" 207# skipLabels: "skip-changelog"
208 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-adin1110/CHANGELOG.md file." 208# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-adin1110/CHANGELOG.md file."
209 - name: Check that changelog updated (embassy-net-driver) 209# - name: Check that changelog updated (embassy-net-driver)
210 if: steps.changes.outputs.embassy-net-driver == 'true' 210# if: steps.changes.outputs.embassy-net-driver == 'true'
211 uses: dangoslen/changelog-enforcer@v3 211# uses: dangoslen/changelog-enforcer@v3
212 with: 212# with:
213 changeLogPath: embassy-net-driver/CHANGELOG.md 213# changeLogPath: embassy-net-driver/CHANGELOG.md
214 skipLabels: "skip-changelog" 214# skipLabels: "skip-changelog"
215 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-driver/CHANGELOG.md file." 215# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-driver/CHANGELOG.md file."
216 - name: Check that changelog updated (embassy-net-driver-channel) 216# - name: Check that changelog updated (embassy-net-driver-channel)
217 if: steps.changes.outputs.embassy-net-driver-channel == 'true' 217# if: steps.changes.outputs.embassy-net-driver-channel == 'true'
218 uses: dangoslen/changelog-enforcer@v3 218# uses: dangoslen/changelog-enforcer@v3
219 with: 219# with:
220 changeLogPath: embassy-net-driver-channel/CHANGELOG.md 220# changeLogPath: embassy-net-driver-channel/CHANGELOG.md
221 skipLabels: "skip-changelog" 221# skipLabels: "skip-changelog"
222 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-driver-channel/CHANGELOG.md file." 222# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-driver-channel/CHANGELOG.md file."
223 - name: Check that changelog updated (embassy-net-enc28j60) 223# - name: Check that changelog updated (embassy-net-enc28j60)
224 if: steps.changes.outputs.embassy-net-enc28j60 == 'true' 224# if: steps.changes.outputs.embassy-net-enc28j60 == 'true'
225 uses: dangoslen/changelog-enforcer@v3 225# uses: dangoslen/changelog-enforcer@v3
226 with: 226# with:
227 changeLogPath: embassy-net-enc28j60/CHANGELOG.md 227# changeLogPath: embassy-net-enc28j60/CHANGELOG.md
228 skipLabels: "skip-changelog" 228# skipLabels: "skip-changelog"
229 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-enc28j60/CHANGELOG.md file." 229# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-enc28j60/CHANGELOG.md file."
230 - name: Check that changelog updated (embassy-net-esp-hosted) 230# - name: Check that changelog updated (embassy-net-esp-hosted)
231 if: steps.changes.outputs.embassy-net-esp-hosted == 'true' 231# if: steps.changes.outputs.embassy-net-esp-hosted == 'true'
232 uses: dangoslen/changelog-enforcer@v3 232# uses: dangoslen/changelog-enforcer@v3
233 with: 233# with:
234 changeLogPath: embassy-net-esp-hosted/CHANGELOG.md 234# changeLogPath: embassy-net-esp-hosted/CHANGELOG.md
235 skipLabels: "skip-changelog" 235# skipLabels: "skip-changelog"
236 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-esp-hosted/CHANGELOG.md file." 236# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-esp-hosted/CHANGELOG.md file."
237 - name: Check that changelog updated (embassy-net-nrf91) 237# - name: Check that changelog updated (embassy-net-nrf91)
238 if: steps.changes.outputs.embassy-net-nrf91 == 'true' 238# if: steps.changes.outputs.embassy-net-nrf91 == 'true'
239 uses: dangoslen/changelog-enforcer@v3 239# uses: dangoslen/changelog-enforcer@v3
240 with: 240# with:
241 changeLogPath: embassy-net-nrf91/CHANGELOG.md 241# changeLogPath: embassy-net-nrf91/CHANGELOG.md
242 skipLabels: "skip-changelog" 242# skipLabels: "skip-changelog"
243 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-nrf91/CHANGELOG.md file." 243# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-nrf91/CHANGELOG.md file."
244 - name: Check that changelog updated (embassy-net-ppp) 244# - name: Check that changelog updated (embassy-net-ppp)
245 if: steps.changes.outputs.embassy-net-ppp == 'true' 245# if: steps.changes.outputs.embassy-net-ppp == 'true'
246 uses: dangoslen/changelog-enforcer@v3 246# uses: dangoslen/changelog-enforcer@v3
247 with: 247# with:
248 changeLogPath: embassy-net-ppp/CHANGELOG.md 248# changeLogPath: embassy-net-ppp/CHANGELOG.md
249 skipLabels: "skip-changelog" 249# skipLabels: "skip-changelog"
250 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-ppp/CHANGELOG.md file." 250# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-ppp/CHANGELOG.md file."
251 - name: Check that changelog updated (embassy-net-tuntap) 251# - name: Check that changelog updated (embassy-net-tuntap)
252 if: steps.changes.outputs.embassy-net-tuntap == 'true' 252# if: steps.changes.outputs.embassy-net-tuntap == 'true'
253 uses: dangoslen/changelog-enforcer@v3 253# uses: dangoslen/changelog-enforcer@v3
254 with: 254# with:
255 changeLogPath: embassy-net-tuntap/CHANGELOG.md 255# changeLogPath: embassy-net-tuntap/CHANGELOG.md
256 skipLabels: "skip-changelog" 256# skipLabels: "skip-changelog"
257 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-tuntap/CHANGELOG.md file." 257# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-tuntap/CHANGELOG.md file."
258 - name: Check that changelog updated (embassy-net-wiznet) 258# - name: Check that changelog updated (embassy-net-wiznet)
259 if: steps.changes.outputs.embassy-net-wiznet == 'true' 259# if: steps.changes.outputs.embassy-net-wiznet == 'true'
260 uses: dangoslen/changelog-enforcer@v3 260# uses: dangoslen/changelog-enforcer@v3
261 with: 261# with:
262 changeLogPath: embassy-net-wiznet/CHANGELOG.md 262# changeLogPath: embassy-net-wiznet/CHANGELOG.md
263 skipLabels: "skip-changelog" 263# skipLabels: "skip-changelog"
264 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-wiznet/CHANGELOG.md file." 264# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-net-wiznet/CHANGELOG.md file."
265 - name: Check that changelog updated (embassy-nrf) 265# - name: Check that changelog updated (embassy-nrf)
266 if: steps.changes.outputs.embassy-nrf == 'true' 266# if: steps.changes.outputs.embassy-nrf == 'true'
267 uses: dangoslen/changelog-enforcer@v3 267# uses: dangoslen/changelog-enforcer@v3
268 with: 268# with:
269 changeLogPath: embassy-nrf/CHANGELOG.md 269# changeLogPath: embassy-nrf/CHANGELOG.md
270 skipLabels: "skip-changelog" 270# skipLabels: "skip-changelog"
271 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-nrf/CHANGELOG.md file." 271# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-nrf/CHANGELOG.md file."
272 - name: Check that changelog updated (embassy-nxp) 272# - name: Check that changelog updated (embassy-nxp)
273 if: steps.changes.outputs.embassy-nxp == 'true' 273# if: steps.changes.outputs.embassy-nxp == 'true'
274 uses: dangoslen/changelog-enforcer@v3 274# uses: dangoslen/changelog-enforcer@v3
275 with: 275# with:
276 changeLogPath: embassy-nxp/CHANGELOG.md 276# changeLogPath: embassy-nxp/CHANGELOG.md
277 skipLabels: "skip-changelog" 277# skipLabels: "skip-changelog"
278 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-nxp/CHANGELOG.md file." 278# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-nxp/CHANGELOG.md file."
279 - name: Check that changelog updated (embassy-rp) 279# - name: Check that changelog updated (embassy-rp)
280 if: steps.changes.outputs.embassy-rp == 'true' 280# if: steps.changes.outputs.embassy-rp == 'true'
281 uses: dangoslen/changelog-enforcer@v3 281# uses: dangoslen/changelog-enforcer@v3
282 with: 282# with:
283 changeLogPath: embassy-rp/CHANGELOG.md 283# changeLogPath: embassy-rp/CHANGELOG.md
284 skipLabels: "skip-changelog" 284# skipLabels: "skip-changelog"
285 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-rp/CHANGELOG.md file." 285# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-rp/CHANGELOG.md file."
286 - name: Check that changelog updated (embassy-stm32) 286# - name: Check that changelog updated (embassy-stm32)
287 if: steps.changes.outputs.embassy-stm32 == 'true' 287# if: steps.changes.outputs.embassy-stm32 == 'true'
288 uses: dangoslen/changelog-enforcer@v3 288# uses: dangoslen/changelog-enforcer@v3
289 with: 289# with:
290 changeLogPath: embassy-stm32/CHANGELOG.md 290# changeLogPath: embassy-stm32/CHANGELOG.md
291 skipLabels: "skip-changelog" 291# skipLabels: "skip-changelog"
292 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-stm32/CHANGELOG.md file." 292# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-stm32/CHANGELOG.md file."
293 - name: Check that changelog updated (embassy-stm32-wpan) 293# - name: Check that changelog updated (embassy-stm32-wpan)
294 if: steps.changes.outputs.embassy-stm32-wpan == 'true' 294# if: steps.changes.outputs.embassy-stm32-wpan == 'true'
295 uses: dangoslen/changelog-enforcer@v3 295# uses: dangoslen/changelog-enforcer@v3
296 with: 296# with:
297 changeLogPath: embassy-stm32-wpan/CHANGELOG.md 297# changeLogPath: embassy-stm32-wpan/CHANGELOG.md
298 skipLabels: "skip-changelog" 298# skipLabels: "skip-changelog"
299 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-stm32-wpan/CHANGELOG.md file." 299# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-stm32-wpan/CHANGELOG.md file."
300 - name: Check that changelog updated (embassy-sync) 300# - name: Check that changelog updated (embassy-sync)
301 if: steps.changes.outputs.embassy-sync == 'true' 301# if: steps.changes.outputs.embassy-sync == 'true'
302 uses: dangoslen/changelog-enforcer@v3 302# uses: dangoslen/changelog-enforcer@v3
303 with: 303# with:
304 changeLogPath: embassy-sync/CHANGELOG.md 304# changeLogPath: embassy-sync/CHANGELOG.md
305 skipLabels: "skip-changelog" 305# skipLabels: "skip-changelog"
306 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-sync/CHANGELOG.md file." 306# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-sync/CHANGELOG.md file."
307 - name: Check that changelog updated (embassy-time) 307# - name: Check that changelog updated (embassy-time)
308 if: steps.changes.outputs.embassy-time == 'true' 308# if: steps.changes.outputs.embassy-time == 'true'
309 uses: dangoslen/changelog-enforcer@v3 309# uses: dangoslen/changelog-enforcer@v3
310 with: 310# with:
311 changeLogPath: embassy-time/CHANGELOG.md 311# changeLogPath: embassy-time/CHANGELOG.md
312 skipLabels: "skip-changelog" 312# skipLabels: "skip-changelog"
313 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-time/CHANGELOG.md file." 313# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-time/CHANGELOG.md file."
314 - name: Check that changelog updated (embassy-time-driver) 314# - name: Check that changelog updated (embassy-time-driver)
315 if: steps.changes.outputs.embassy-time-driver == 'true' 315# if: steps.changes.outputs.embassy-time-driver == 'true'
316 uses: dangoslen/changelog-enforcer@v3 316# uses: dangoslen/changelog-enforcer@v3
317 with: 317# with:
318 changeLogPath: embassy-time-driver/CHANGELOG.md 318# changeLogPath: embassy-time-driver/CHANGELOG.md
319 skipLabels: "skip-changelog" 319# skipLabels: "skip-changelog"
320 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-time-driver/CHANGELOG.md file." 320# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-time-driver/CHANGELOG.md file."
321 - name: Check that changelog updated (embassy-time-queue-utils) 321# - name: Check that changelog updated (embassy-time-queue-utils)
322 if: steps.changes.outputs.embassy-time-queue-utils == 'true' 322# if: steps.changes.outputs.embassy-time-queue-utils == 'true'
323 uses: dangoslen/changelog-enforcer@v3 323# uses: dangoslen/changelog-enforcer@v3
324 with: 324# with:
325 changeLogPath: embassy-time-queue-utils/CHANGELOG.md 325# changeLogPath: embassy-time-queue-utils/CHANGELOG.md
326 skipLabels: "skip-changelog" 326# skipLabels: "skip-changelog"
327 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-time-queue-utils/CHANGELOG.md file." 327# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-time-queue-utils/CHANGELOG.md file."
328 - name: Check that changelog updated (embassy-usb) 328# - name: Check that changelog updated (embassy-usb)
329 if: steps.changes.outputs.embassy-usb == 'true' 329# if: steps.changes.outputs.embassy-usb == 'true'
330 uses: dangoslen/changelog-enforcer@v3 330# uses: dangoslen/changelog-enforcer@v3
331 with: 331# with:
332 changeLogPath: embassy-usb/CHANGELOG.md 332# changeLogPath: embassy-usb/CHANGELOG.md
333 skipLabels: "skip-changelog" 333# skipLabels: "skip-changelog"
334 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-usb/CHANGELOG.md file." 334# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-usb/CHANGELOG.md file."
335 - name: Check that changelog updated (embassy-usb-dfu) 335# - name: Check that changelog updated (embassy-usb-dfu)
336 if: steps.changes.outputs.embassy-usb-dfu == 'true' 336# if: steps.changes.outputs.embassy-usb-dfu == 'true'
337 uses: dangoslen/changelog-enforcer@v3 337# uses: dangoslen/changelog-enforcer@v3
338 with: 338# with:
339 changeLogPath: embassy-usb-dfu/CHANGELOG.md 339# changeLogPath: embassy-usb-dfu/CHANGELOG.md
340 skipLabels: "skip-changelog" 340# skipLabels: "skip-changelog"
341 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-usb-dfu/CHANGELOG.md file." 341# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-usb-dfu/CHANGELOG.md file."
342 - name: Check that changelog updated (embassy-usb-driver) 342# - name: Check that changelog updated (embassy-usb-driver)
343 if: steps.changes.outputs.embassy-usb-driver == 'true' 343# if: steps.changes.outputs.embassy-usb-driver == 'true'
344 uses: dangoslen/changelog-enforcer@v3 344# uses: dangoslen/changelog-enforcer@v3
345 with: 345# with:
346 changeLogPath: embassy-usb-driver/CHANGELOG.md 346# changeLogPath: embassy-usb-driver/CHANGELOG.md
347 skipLabels: "skip-changelog" 347# skipLabels: "skip-changelog"
348 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-usb-driver/CHANGELOG.md file." 348# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-usb-driver/CHANGELOG.md file."
349 - name: Check that changelog updated (embassy-usb-logger) 349# - name: Check that changelog updated (embassy-usb-logger)
350 if: steps.changes.outputs.embassy-usb-logger == 'true' 350# if: steps.changes.outputs.embassy-usb-logger == 'true'
351 uses: dangoslen/changelog-enforcer@v3 351# uses: dangoslen/changelog-enforcer@v3
352 with: 352# with:
353 changeLogPath: embassy-usb-logger/CHANGELOG.md 353# changeLogPath: embassy-usb-logger/CHANGELOG.md
354 skipLabels: "skip-changelog" 354# skipLabels: "skip-changelog"
355 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-usb-logger/CHANGELOG.md file." 355# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-usb-logger/CHANGELOG.md file."
356 - name: Check that changelog updated (embassy-usb-synopsys-otg) 356# - name: Check that changelog updated (embassy-usb-synopsys-otg)
357 if: steps.changes.outputs.embassy-usb-synopsys-otg == 'true' 357# if: steps.changes.outputs.embassy-usb-synopsys-otg == 'true'
358 uses: dangoslen/changelog-enforcer@v3 358# uses: dangoslen/changelog-enforcer@v3
359 with: 359# with:
360 changeLogPath: embassy-usb-synopsys-otg/CHANGELOG.md 360# changeLogPath: embassy-usb-synopsys-otg/CHANGELOG.md
361 skipLabels: "skip-changelog" 361# skipLabels: "skip-changelog"
362 missingUpdateErrorMessage: "Please add a changelog entry in the embassy-usb-synopsys-otg/CHANGELOG.md file." 362# missingUpdateErrorMessage: "Please add a changelog entry in the embassy-usb-synopsys-otg/CHANGELOG.md file."
diff --git a/README.md b/README.md
index 950d19a6d..1d6f3499d 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ Rust's [async/await](https://rust-lang.github.io/async-book/) allows for unprece
38 38
39- **Bluetooth** 39- **Bluetooth**
40 - The [trouble](https://github.com/embassy-rs/trouble) crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the [bt-hci](https://github.com/embassy-rs/bt-hci) traits (currently 40 - The [trouble](https://github.com/embassy-rs/trouble) crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the [bt-hci](https://github.com/embassy-rs/bt-hci) traits (currently
41 `nRF52`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported). 41 `nRF52`, `nrf54`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported).
42 - The [nrf-softdevice](https://github.com/embassy-rs/nrf-softdevice) crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. 42 - The [nrf-softdevice](https://github.com/embassy-rs/nrf-softdevice) crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
43 - The [embassy-stm32-wpan](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan) crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers. 43 - The [embassy-stm32-wpan](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan) crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers.
44 44
diff --git a/cyw43/CHANGELOG.md b/cyw43/CHANGELOG.md
index 9fe341357..4f0dc896b 100644
--- a/cyw43/CHANGELOG.md
+++ b/cyw43/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- Reset WPA security before creating secure AP
12
11## 0.6.0 - 2025-11-27 13## 0.6.0 - 2025-11-27
12 14
13- Updated documentation for Control::join() #4678 15- Updated documentation for Control::join() #4678
diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs
index 49e3faee4..07fa1955e 100644
--- a/cyw43/src/control.rs
+++ b/cyw43/src/control.rs
@@ -436,6 +436,9 @@ impl<'a> Control<'a> {
436 // Set wifi up again 436 // Set wifi up again
437 self.up().await; 437 self.up().await;
438 438
439 // Disable authentication
440 self.ioctl_set_u32(Ioctl::SetAuth, 0, AUTH_OPEN).await;
441
439 // Turn on AP mode 442 // Turn on AP mode
440 self.ioctl_set_u32(Ioctl::SetAp, 0, 1).await; 443 self.ioctl_set_u32(Ioctl::SetAp, 0, 1).await;
441 444
@@ -470,8 +473,6 @@ impl<'a> Control<'a> {
470 pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes()); 473 pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes());
471 self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut pfi.to_bytes()) 474 self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut pfi.to_bytes())
472 .await; 475 .await;
473 } else {
474 self.ioctl_set_u32(Ioctl::SetAuth, 0, 0).await;
475 } 476 }
476 477
477 // Change mutlicast rate from 1 Mbps to 11 Mbps 478 // Change mutlicast rate from 1 Mbps to 11 Mbps
@@ -547,6 +548,14 @@ impl<'a> Control<'a> {
547 n 548 n
548 } 549 }
549 550
551 /// Retrieve the latest RSSI value
552 pub async fn get_rssi(&mut self) -> i32 {
553 let mut rssi_buf = [0u8; 4];
554 let n = self.ioctl(IoctlType::Get, Ioctl::GetRssi, 0, &mut rssi_buf).await;
555 assert_eq!(n, 4);
556 i32::from_ne_bytes(rssi_buf)
557 }
558
550 async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { 559 async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) {
551 let mut buf = [0; 8]; 560 let mut buf = [0; 8];
552 buf[0..4].copy_from_slice(&val1.to_le_bytes()); 561 buf[0..4].copy_from_slice(&val1.to_le_bytes());
diff --git a/docs/pages/mcxa.adoc b/docs/pages/mcxa.adoc
new file mode 100644
index 000000000..e2284f45f
--- /dev/null
+++ b/docs/pages/mcxa.adoc
@@ -0,0 +1,18 @@
1= Embassy MCX-A HAL
2
3The link: link:https://github.com/embassy-rs/embassy/tree/main/embassy-mcxa[Embassy MCX-A HAL] is based on the following PAC (Peripheral Access Crate):
4
5* link:https://github.com/OpenDevicePartnership/mcxa-pac[mcxa-pac]
6
7== Peripherals
8
9The following peripherals have a HAL implementation at present
10
11* Clocks
12* GPIO
13* ADC
14* CLKOUT
15* I2C
16* LPUart
17* OSTimer
18* RTC
diff --git a/docs/pages/overview.adoc b/docs/pages/overview.adoc
index 18eaaeb75..894789d33 100644
--- a/docs/pages/overview.adoc
+++ b/docs/pages/overview.adoc
@@ -27,7 +27,7 @@ Embassy provides implementations of both async and blocking APIs where it makes
27The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy. 27The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
28 28
29* link:https://docs.embassy.dev/embassy-stm32/[embassy-stm32], for all STM32 microcontroller families. 29* link:https://docs.embassy.dev/embassy-stm32/[embassy-stm32], for all STM32 microcontroller families.
30* link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series. 30* link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF54, nRF91 series.
31* link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 as well as RP235x microcontroller. 31* link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 as well as RP235x microcontroller.
32* link:https://docs.embassy.dev/embassy-mspm0/[embassy-mspm0], for the Texas Instruments MSPM0 microcontrollers. 32* link:https://docs.embassy.dev/embassy-mspm0/[embassy-mspm0], for the Texas Instruments MSPM0 microcontrollers.
33* link:https://github.com/esp-rs/esp-hal[esp-hal], for the Espressif Systems ESP32 series of chips. 33* link:https://github.com/esp-rs/esp-hal[esp-hal], for the Espressif Systems ESP32 series of chips.
@@ -42,7 +42,9 @@ as they implement both the link:https://github.com/rust-embedded/embedded-hal[Em
42The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. Several drivers for WiFi and Ethernet chips can be found. 42The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. Several drivers for WiFi and Ethernet chips can be found.
43 43
44=== Bluetooth 44=== Bluetooth
45The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. 45
46* The link:https://github.com/embassy-rs/trouble[trouble] crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the link:https://github.com/embassy-rs/bt-hci[bt-hci] traits (currently `nRF52`, `nrf54`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported).
47* The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
46 48
47=== LoRa 49=== LoRa
48link:https://github.com/lora-rs/lora-rs[lora-rs] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust LoRaWAN implementation. It provides four crates — lora-phy, lora-modulation, lorawan-encoding, and lorawan-device — and basic examples for various development boards. It has support for STM32WL wireless microcontrollers or Semtech SX127x transceivers, among others. 50link:https://github.com/lora-rs/lora-rs[lora-rs] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust LoRaWAN implementation. It provides four crates — lora-phy, lora-modulation, lorawan-encoding, and lorawan-device — and basic examples for various development boards. It has support for STM32WL wireless microcontrollers or Semtech SX127x transceivers, among others.
diff --git a/embassy-mcxa/.gitignore b/embassy-mcxa/.gitignore
new file mode 100644
index 000000000..d128a49cb
--- /dev/null
+++ b/embassy-mcxa/.gitignore
@@ -0,0 +1,19 @@
1# Rust
2/target/
3
4# IDE
5.vscode/
6.idea/
7
8# OS
9.DS_Store
10Thumbs.db
11
12# Embedded
13*.bin
14*.hex
15*.elf
16*.map
17
18# Debug
19*.log
diff --git a/embassy-mcxa/Cargo.toml b/embassy-mcxa/Cargo.toml
new file mode 100644
index 000000000..cb985a2e9
--- /dev/null
+++ b/embassy-mcxa/Cargo.toml
@@ -0,0 +1,69 @@
1[package]
2name = "embassy-mcxa"
3version = "0.1.0"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6description = "Embassy Hardware Abstraction Layer (HAL) for NXP MCXA series of MCUs"
7repository = "https://github.com/embassy-rs/embassy"
8keywords = ["embedded", "hal", "nxp", "mcxa", "embassy"]
9categories = ["embedded", "hardware-support", "no-std"]
10documentation = "https://docs.embassy.dev/embassy-mcxa"
11
12[package.metadata.embassy]
13build = [
14 {target = "thumbv8m.main-none-eabihf", features = ["defmt", "time", "unstable-pac"]},
15]
16
17[package.metadata.embassy_docs]
18src_base = "https://github.com/embassy-rs/embassy/blob/embassy-mcxa-v$VERSION/embassy-mcxa/src/"
19src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-mcxa/src/"
20
21features = ["defmt", "time", "unstable-pac"]
22flavors = [
23 { name = "mcx-a256", target = "thumbv8m.main-none-eabihf" },
24]
25
26[dependencies]
27cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
28# If you would like "device" to be an optional feature, please open an issue.
29cortex-m-rt = { version = "0.7", features = ["device"] }
30critical-section = "1.2.0"
31defmt = { version = "1.0", optional = true }
32embassy-embedded-hal = "0.5.0"
33embassy-hal-internal = { version = "0.3.0", features = ["cortex-m", "prio-bits-3"] }
34embassy-sync = "0.7.2"
35embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
36embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
37embedded-hal-async = { version = "1.0" }
38embedded-hal-nb = { version = "1.0" }
39embedded-io = "0.6"
40embedded-io-async = { version = "0.6.1" }
41heapless = "0.8"
42mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt", "critical-section"], version = "0.1.0", rev = "02bd04a21ef8f8f67f88239ff5df765bb7e60fd8" }
43nb = "1.1.0"
44paste = "1.0.15"
45maitake-sync = { version = "0.2.2", default-features = false, features = ["critical-section", "no-cache-pad"] }
46
47# `time` dependencies
48embassy-time = { version = "0.5.0", optional = true }
49embassy-time-driver = { version = "0.2.1", optional = true }
50
51[features]
52default = ["rt"]
53
54# Base defmt feature enables core + panic handler
55# Use with one logger feature: defmt-rtt (preferred) or defmt-uart (fallback)
56defmt = ["dep:defmt", "mcxa-pac/defmt"]
57
58unstable-pac = []
59
60# dummy feature to silence embassy-hal-internal lint
61#
62# This feature makes no change to embassy-mcxa's operation.
63rt = []
64
65# Embassy time
66time = [
67 "dep:embassy-time",
68 "dep:embassy-time-driver",
69]
diff --git a/embassy-mcxa/README.md b/embassy-mcxa/README.md
new file mode 100644
index 000000000..079eeb487
--- /dev/null
+++ b/embassy-mcxa/README.md
@@ -0,0 +1,5 @@
1# Embassy MCXA256 HAL
2
3A Hardware Abstraction Layer (HAL) for the NXP MCXA256 microcontroller
4using the Embassy async framework. This HAL provides safe, idiomatic
5Rust interfaces for GPIO, UART, and OSTIMER peripherals.
diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs
new file mode 100644
index 000000000..d7d17cf5f
--- /dev/null
+++ b/embassy-mcxa/src/adc.rs
@@ -0,0 +1,752 @@
1//! ADC driver
2use core::marker::PhantomData;
3
4use embassy_hal_internal::{Peri, PeripheralType};
5use maitake_sync::WaitCell;
6use paste::paste;
7
8use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4};
9use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset};
10use crate::gpio::{GpioPin, SealedPin};
11use crate::interrupt::typelevel::{Handler, Interrupt};
12use crate::pac;
13use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres};
14use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts};
15use crate::pac::adc1::cmdl1::{Adch, Mode};
16use crate::pac::adc1::ctrl::CalAvgs;
17use crate::pac::adc1::tctrl::{Tcmd, Tpri};
18
19const G_LPADC_RESULT_SHIFT: u32 = 0;
20
21/// Trigger priority policy for ADC conversions.
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23#[repr(u8)]
24pub enum TriggerPriorityPolicy {
25 ConvPreemptImmediatelyNotAutoResumed = 0,
26 ConvPreemptSoftlyNotAutoResumed = 1,
27 ConvPreemptImmediatelyAutoRestarted = 4,
28 ConvPreemptSoftlyAutoRestarted = 5,
29 ConvPreemptImmediatelyAutoResumed = 12,
30 ConvPreemptSoftlyAutoResumed = 13,
31 ConvPreemptSubsequentlyNotAutoResumed = 2,
32 ConvPreemptSubsequentlyAutoRestarted = 6,
33 ConvPreemptSubsequentlyAutoResumed = 14,
34 TriggerPriorityExceptionDisabled = 16,
35}
36
37/// Configuration for the LPADC peripheral.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct LpadcConfig {
40 /// Control system transition to Stop and Wait power modes while ADC is converting.
41 /// When enabled in Doze mode, immediate entries to Wait or Stop are allowed.
42 /// When disabled, the ADC will wait for the current averaging iteration/FIFO storage to complete before acknowledging stop or wait mode entry.
43 pub enable_in_doze_mode: bool,
44 /// Auto-Calibration Averages.
45 pub conversion_average_mode: CalAvgs,
46 /// ADC analog circuits are pre-enabled and ready to execute conversions without startup delays(at the cost of higher DC current consumption).
47 pub enable_analog_preliminary: bool,
48 /// Power-up delay value (in ADC clock cycles)
49 pub power_up_delay: u8,
50 /// Reference voltage source selection
51 pub reference_voltage_source: Refsel,
52 /// Power configuration selection.
53 pub power_level_mode: Pwrsel,
54 /// Trigger priority policy for handling multiple triggers
55 pub trigger_priority_policy: TriggerPriorityPolicy,
56 /// Enables the ADC pausing function. When enabled, a programmable delay is inserted during command execution sequencing between LOOP iterations,
57 /// between commands in a sequence, and between conversions when command is executing in "Compare Until True" configuration.
58 pub enable_conv_pause: bool,
59 /// Controls the duration of pausing during command execution sequencing. The pause delay is a count of (convPauseDelay*4) ADCK cycles.
60 /// Only available when ADC pausing function is enabled. The available value range is in 9-bit.
61 pub conv_pause_delay: u16,
62 /// FIFO watermark level for interrupt generation.
63 /// When the number of datawords stored in the ADC Result FIFO is greater than the value in this field,
64 /// the ready flag would be asserted to indicate stored data has reached the programmable threshold.
65 pub fifo_watermark: u8,
66 /// Power configuration (normal/deep sleep behavior)
67 pub power: PoweredClock,
68 /// ADC clock source selection
69 pub source: AdcClockSel,
70 /// Clock divider for ADC clock
71 pub div: Div4,
72}
73
74impl Default for LpadcConfig {
75 fn default() -> Self {
76 LpadcConfig {
77 enable_in_doze_mode: true,
78 conversion_average_mode: CalAvgs::NoAverage,
79 enable_analog_preliminary: false,
80 power_up_delay: 0x80,
81 reference_voltage_source: Refsel::Option1,
82 power_level_mode: Pwrsel::Lowest,
83 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
84 enable_conv_pause: false,
85 conv_pause_delay: 0,
86 fifo_watermark: 0,
87 power: PoweredClock::NormalEnabledDeepSleepDisabled,
88 source: AdcClockSel::FroLfDiv,
89 div: Div4::no_div(),
90 }
91 }
92}
93
94/// Configuration for a conversion command.
95///
96/// Defines the parameters for a single ADC conversion operation.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub struct ConvCommandConfig {
99 pub channel_number: Adch,
100 pub chained_next_command_number: Next,
101 pub enable_auto_channel_increment: bool,
102 pub loop_count: u8,
103 pub hardware_average_mode: Avgs,
104 pub sample_time_mode: Sts,
105 pub hardware_compare_mode: Cmpen,
106 pub hardware_compare_value_high: u32,
107 pub hardware_compare_value_low: u32,
108 pub conversion_resolution_mode: Mode,
109 pub enable_wait_trigger: bool,
110}
111
112/// Configuration for a conversion trigger.
113///
114/// Defines how a trigger initiates ADC conversions.
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub struct ConvTriggerConfig {
117 pub target_command_id: Tcmd,
118 pub delay_power: u8,
119 pub priority: Tpri,
120 pub enable_hardware_trigger: bool,
121}
122
123/// Shorthand for `Result<T>`.
124pub type Result<T> = core::result::Result<T, Error>;
125
126/// ADC Error types
127#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128#[cfg_attr(feature = "defmt", derive(defmt::Format))]
129pub enum Error {
130 /// FIFO is empty, no conversion result available
131 FifoEmpty,
132 /// Invalid configuration
133 InvalidConfig,
134 /// Clock configuration error.
135 ClockSetup(ClockError),
136}
137
138/// Result of an ADC conversion.
139///
140/// Contains the conversion value and metadata about the conversion.
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142pub struct ConvResult {
143 pub command_id_source: u8,
144 pub loop_count_index: u8,
145 pub trigger_id_source: u8,
146 pub conv_value: u16,
147}
148
149/// ADC interrupt handler.
150pub struct InterruptHandler<I: Instance> {
151 _phantom: PhantomData<I>,
152}
153
154/// ADC driver instance.
155pub struct Adc<'a, I: Instance, M: ModeAdc> {
156 _inst: PhantomData<&'a mut I>,
157 _phantom: PhantomData<M>,
158}
159
160impl<'a, I: Instance> Adc<'a, I, Blocking> {
161 /// Create a new blocking instance of the ADC driver.
162 /// # Arguments
163 /// * `_inst` - ADC peripheral instance
164 /// * `pin` - GPIO pin to use for ADC
165 /// * `config` - ADC configuration
166 pub fn new_blocking(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin<I>>, config: LpadcConfig) -> Result<Self> {
167 Self::new_inner(_inst, pin, config)
168 }
169}
170
171impl<'a, I: Instance> Adc<'a, I, Async> {
172 /// Initialize ADC with interrupt support.
173 ///
174 /// # Arguments
175 /// * `_inst` - ADC peripheral instance
176 /// * `pin` - GPIO pin to use for ADC
177 /// * `_irq` - Interrupt binding for this ADC instance
178 /// * `config` - ADC configuration
179 pub fn new_async(
180 _inst: Peri<'a, I>,
181 pin: Peri<'a, impl AdcPin<I>>,
182 _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a,
183 config: LpadcConfig,
184 ) -> Result<Self> {
185 let adc = Self::new_inner(_inst, pin, config);
186
187 I::Interrupt::unpend();
188 unsafe { I::Interrupt::enable() };
189
190 adc
191 }
192
193 /// Read ADC value asynchronously.
194 ///
195 /// Performs a single ADC conversion and returns the result when the ADC interrupt is triggered.
196 ///
197 /// The function:
198 /// 1. Enables the FIFO watermark interrupt
199 /// 2. Triggers a software conversion on trigger 0
200 /// 3. Waits for the conversion to complete
201 /// 4. Returns the conversion result
202 ///
203 /// # Returns
204 /// 16-bit ADC conversion value
205 pub async fn read(&mut self) -> Result<u16> {
206 let wait = I::wait_cell().subscribe().await;
207
208 Adc::<'a, I, Async>::enable_interrupt(self, 0x1);
209 Adc::<'a, I, Async>::do_software_trigger(self, 1);
210
211 let _ = wait.await;
212
213 let result = Adc::<'a, I, Async>::get_conv_result(self).unwrap().conv_value >> G_LPADC_RESULT_SHIFT;
214 Ok(result)
215 }
216}
217
218impl<'a, I: Instance, M: ModeAdc> Adc<'a, I, M> {
219 /// Internal initialization function shared by `new_async` and `new_blocking`.
220 fn new_inner(_inst: Peri<'a, I>, pin: Peri<'a, impl AdcPin<I>>, config: LpadcConfig) -> Result<Self> {
221 let adc = I::ptr();
222
223 _ = unsafe {
224 enable_and_reset::<I>(&AdcConfig {
225 power: config.power,
226 source: config.source,
227 div: config.div,
228 })
229 .map_err(Error::ClockSetup)?
230 };
231
232 pin.mux();
233
234 /* Reset the module. */
235 adc.ctrl().modify(|_, w| w.rst().held_in_reset());
236 adc.ctrl().modify(|_, w| w.rst().released_from_reset());
237
238 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
239
240 /* Disable the module before setting configuration. */
241 adc.ctrl().modify(|_, w| w.adcen().disabled());
242
243 /* Configure the module generally. */
244 if config.enable_in_doze_mode {
245 adc.ctrl().modify(|_, w| w.dozen().enabled());
246 } else {
247 adc.ctrl().modify(|_, w| w.dozen().disabled());
248 }
249
250 /* Set calibration average mode. */
251 adc.ctrl()
252 .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode));
253
254 adc.cfg().write(|w| unsafe {
255 let w = if config.enable_analog_preliminary {
256 w.pwren().pre_enabled()
257 } else {
258 w
259 };
260
261 w.pudly()
262 .bits(config.power_up_delay)
263 .refsel()
264 .variant(config.reference_voltage_source)
265 .pwrsel()
266 .variant(config.power_level_mode)
267 .tprictrl()
268 .variant(match config.trigger_priority_policy {
269 TriggerPriorityPolicy::ConvPreemptSoftlyNotAutoResumed
270 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
271 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed => Tprictrl::FinishCurrentOnPriority,
272 TriggerPriorityPolicy::ConvPreemptSubsequentlyNotAutoResumed
273 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
274 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tprictrl::FinishSequenceOnPriority,
275 _ => Tprictrl::AbortCurrentOnPriority,
276 })
277 .tres()
278 .variant(match config.trigger_priority_policy {
279 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoRestarted
280 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
281 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
282 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
283 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
284 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tres::Enabled,
285 _ => Tres::Disabled,
286 })
287 .tcmdres()
288 .variant(match config.trigger_priority_policy {
289 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
290 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
291 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed
292 | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => Tcmdres::Enabled,
293 _ => Tcmdres::Disabled,
294 })
295 .hpt_exdi()
296 .variant(match config.trigger_priority_policy {
297 TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => HptExdi::Disabled,
298 _ => HptExdi::Enabled,
299 })
300 });
301
302 if config.enable_conv_pause {
303 adc.pause()
304 .modify(|_, w| unsafe { w.pauseen().enabled().pausedly().bits(config.conv_pause_delay) });
305 } else {
306 adc.pause().write(|w| unsafe { w.bits(0) });
307 }
308
309 adc.fctrl0()
310 .write(|w| unsafe { w.fwmark().bits(config.fifo_watermark) });
311
312 // Enable ADC
313 adc.ctrl().modify(|_, w| w.adcen().enabled());
314
315 Ok(Self {
316 _inst: PhantomData,
317 _phantom: PhantomData,
318 })
319 }
320
321 /// Deinitialize the ADC peripheral.
322 pub fn deinit(&self) {
323 let adc = I::ptr();
324 adc.ctrl().modify(|_, w| w.adcen().disabled());
325 }
326
327 /// Perform offset calibration.
328 /// Waits for calibration to complete before returning.
329 pub fn do_offset_calibration(&self) {
330 let adc = I::ptr();
331 // Enable calibration mode
332 adc.ctrl()
333 .modify(|_, w| w.calofs().offset_calibration_request_pending());
334
335 // Wait for calibration to complete (polling status register)
336 while adc.stat().read().cal_rdy().is_not_set() {}
337 }
338
339 /// Calculate gain conversion result from gain adjustment factor.
340 ///
341 /// # Arguments
342 /// * `gain_adjustment` - Gain adjustment factor
343 ///
344 /// # Returns
345 /// Gain calibration register value
346 pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 {
347 let mut gcra_array = [0u32; 17];
348 let mut gcalr: u32 = 0;
349
350 for i in (1..=17).rev() {
351 let shift = 16 - (i - 1);
352 let step = 1.0 / (1u32 << shift) as f32;
353 let tmp = (gain_adjustment / step) as u32;
354 gcra_array[i - 1] = tmp;
355 gain_adjustment -= tmp as f32 * step;
356 }
357
358 for i in (1..=17).rev() {
359 gcalr += gcra_array[i - 1] << (i - 1);
360 }
361 gcalr
362 }
363
364 /// Perform automatic gain calibration.
365 pub fn do_auto_calibration(&self) {
366 let adc = I::ptr();
367 adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending());
368
369 while adc.gcc0().read().rdy().is_gain_cal_not_valid() {}
370
371 let mut gcca = adc.gcc0().read().gain_cal().bits() as u32;
372 if gcca & ((0xFFFF + 1) >> 1) != 0 {
373 gcca |= !0xFFFF;
374 }
375
376 let gcra = 131072.0 / (131072.0 - gcca as f32);
377
378 // Write to GCR0
379 adc.gcr0().write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) });
380
381 adc.gcr0().modify(|_, w| w.rdy().set_bit());
382
383 // Wait for calibration to complete (polling status register)
384 while adc.stat().read().cal_rdy().is_not_set() {}
385 }
386
387 /// Trigger ADC conversion(s) via software.
388 ///
389 /// Initiates conversion(s) for the trigger(s) specified in the bitmask.
390 /// Each bit in the mask corresponds to a trigger ID (bit 0 = trigger 0, etc.).
391 ///
392 /// # Arguments
393 /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N)
394 pub fn do_software_trigger(&self, trigger_id_mask: u32) {
395 let adc = I::ptr();
396 adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) });
397 }
398
399 /// Get default conversion command configuration.
400 /// # Returns
401 /// Default conversion command configuration
402 pub fn get_default_conv_command_config(&self) -> ConvCommandConfig {
403 ConvCommandConfig {
404 channel_number: Adch::SelectCh0,
405 chained_next_command_number: Next::NoNextCmdTerminateOnFinish,
406 enable_auto_channel_increment: false,
407 loop_count: 0,
408 hardware_average_mode: Avgs::NoAverage,
409 sample_time_mode: Sts::Sample3p5,
410 hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult,
411 hardware_compare_value_high: 0,
412 hardware_compare_value_low: 0,
413 conversion_resolution_mode: Mode::Data12Bits,
414 enable_wait_trigger: false,
415 }
416 }
417
418 /// Set conversion command configuration.
419 ///
420 /// Configures a conversion command slot with the specified parameters.
421 /// Commands define how conversions are performed (channel, resolution, etc.).
422 ///
423 /// # Arguments
424 /// * `index` - Command index
425 /// * `config` - Command configuration
426 ///
427 /// # Returns
428 /// * `Ok(())` if the command was configured successfully
429 /// * `Err(Error::InvalidConfig)` if the index is out of range
430 pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) -> Result<()> {
431 let adc = I::ptr();
432
433 if index < 1 || index > 7 {
434 return Err(Error::InvalidConfig);
435 }
436
437 macro_rules! write_cmd {
438 ($idx:expr) => {{
439 paste! {
440 adc.[<cmdl $idx>]().write(|w| {
441 w.adch()
442 .variant(config.channel_number)
443 .mode()
444 .variant(config.conversion_resolution_mode)
445 });
446 adc.[<cmdh $idx>]().write(|w| unsafe {
447 w.next()
448 .variant(config.chained_next_command_number)
449 .loop_()
450 .bits(config.loop_count)
451 .avgs()
452 .variant(config.hardware_average_mode)
453 .sts()
454 .variant(config.sample_time_mode)
455 .cmpen()
456 .variant(config.hardware_compare_mode)
457 .wait_trig()
458 .bit(config.enable_wait_trigger)
459 .lwi()
460 .bit(config.enable_auto_channel_increment)
461 });
462 }
463 }};
464 }
465
466 match index {
467 1 => write_cmd!(1),
468 2 => write_cmd!(2),
469 3 => write_cmd!(3),
470 4 => write_cmd!(4),
471 5 => write_cmd!(5),
472 6 => write_cmd!(6),
473 7 => write_cmd!(7),
474 _ => unreachable!(),
475 }
476
477 Ok(())
478 }
479
480 /// Get default conversion trigger configuration.
481 ///
482 /// # Returns
483 /// Default conversion trigger configuration
484 pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig {
485 ConvTriggerConfig {
486 target_command_id: Tcmd::NotValid,
487 delay_power: 0,
488 priority: Tpri::HighestPriority,
489 enable_hardware_trigger: false,
490 }
491 }
492
493 /// Set conversion trigger configuration.
494 ///
495 /// Configures a trigger to initiate conversions. Triggers can be
496 /// activated by software or hardware signals.
497 ///
498 /// # Arguments
499 /// * `trigger_id` - Trigger index (0-15)
500 /// * `config` - Trigger configuration
501 pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) {
502 let adc = I::ptr();
503 let tctrl = &adc.tctrl(trigger_id);
504
505 tctrl.write(|w| unsafe {
506 let w = w.tcmd().variant(config.target_command_id);
507 let w = w.tdly().bits(config.delay_power);
508 w.tpri().variant(config.priority);
509 if config.enable_hardware_trigger {
510 w.hten().enabled()
511 } else {
512 w
513 }
514 });
515 }
516
517 /// Reset the FIFO buffer.
518 ///
519 /// Clears all pending conversion results from the FIFO.
520 pub fn do_reset_fifo(&self) {
521 let adc = I::ptr();
522 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
523 }
524
525 /// Enable ADC interrupts.
526 ///
527 /// Enables the interrupt sources specified in the bitmask.
528 ///
529 /// # Arguments
530 /// * `mask` - Bitmask of interrupt sources to enable
531 pub fn enable_interrupt(&self, mask: u32) {
532 let adc = I::ptr();
533 adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
534 }
535
536 /// Disable ADC interrupts.
537 ///
538 /// Disables the interrupt sources specified in the bitmask.
539 ///
540 /// # Arguments
541 /// * `mask` - Bitmask of interrupt sources to disable
542 pub fn disable_interrupt(&self, mask: u32) {
543 let adc = I::ptr();
544 adc.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) });
545 }
546
547 /// Get conversion result from FIFO.
548 ///
549 /// Reads and returns the next conversion result from the FIFO.
550 /// Returns `None` if the FIFO is empty.
551 ///
552 /// # Returns
553 /// - `Some(ConvResult)` if a result is available
554 /// - `Err(Error::FifoEmpty)` if the FIFO is empty
555 pub fn get_conv_result(&self) -> Result<ConvResult> {
556 let adc = I::ptr();
557 let fifo = adc.resfifo0().read();
558 if !fifo.valid().is_valid() {
559 return Err(Error::FifoEmpty);
560 }
561
562 Ok(ConvResult {
563 command_id_source: fifo.cmdsrc().bits(),
564 loop_count_index: fifo.loopcnt().bits(),
565 trigger_id_source: fifo.tsrc().bits(),
566 conv_value: fifo.d().bits(),
567 })
568 }
569}
570
571impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {
572 unsafe fn on_interrupt() {
573 T::ptr().ie().modify(|r, w| w.bits(r.bits() & !0x1));
574 T::wait_cell().wake();
575 }
576}
577
578mod sealed {
579 /// Seal a trait
580 pub trait Sealed {}
581}
582
583impl<I: GpioPin> sealed::Sealed for I {}
584
585trait SealedInstance {
586 fn ptr() -> &'static pac::adc0::RegisterBlock;
587 fn wait_cell() -> &'static WaitCell;
588}
589
590/// ADC Instance
591#[allow(private_bounds)]
592pub trait Instance: SealedInstance + PeripheralType + Gate<MrccPeriphConfig = AdcConfig> {
593 /// Interrupt for this ADC instance.
594 type Interrupt: Interrupt;
595}
596
597macro_rules! impl_instance {
598 ($($n:expr),*) => {
599 $(
600 paste!{
601 impl SealedInstance for crate::peripherals::[<ADC $n>] {
602 fn ptr() -> &'static pac::adc0::RegisterBlock {
603 unsafe { &*pac::[<Adc $n>]::ptr() }
604 }
605
606 fn wait_cell() -> &'static WaitCell {
607 static WAIT_CELL: WaitCell = WaitCell::new();
608 &WAIT_CELL
609 }
610
611 }
612
613 impl Instance for crate::peripherals::[<ADC $n>] {
614 type Interrupt = crate::interrupt::typelevel::[<ADC $n>];
615 }
616 }
617 )*
618 };
619}
620
621impl_instance!(0, 1, 2, 3);
622
623pub trait AdcPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {
624 /// Set the given pin to the correct muxing state
625 fn mux(&self);
626}
627
628/// Driver mode.
629#[allow(private_bounds)]
630pub trait ModeAdc: sealed::Sealed {}
631
632/// Blocking mode.
633pub struct Blocking;
634impl sealed::Sealed for Blocking {}
635impl ModeAdc for Blocking {}
636
637/// Async mode.
638pub struct Async;
639impl sealed::Sealed for Async {}
640impl ModeAdc for Async {}
641
642macro_rules! impl_pin {
643 ($pin:ident, $peri:ident, $func:ident, $trait:ident) => {
644 impl $trait<crate::peripherals::$peri> for crate::peripherals::$pin {
645 fn mux(&self) {
646 self.set_pull(crate::gpio::Pull::Disabled);
647 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
648 self.set_drive_strength(crate::gpio::DriveStrength::Normal.into());
649 self.set_function(crate::pac::port0::pcr0::Mux::$func);
650 }
651 }
652 };
653}
654
655impl_pin!(P2_0, ADC0, Mux0, AdcPin);
656impl_pin!(P2_4, ADC0, Mux0, AdcPin);
657impl_pin!(P2_15, ADC0, Mux0, AdcPin);
658impl_pin!(P2_3, ADC0, Mux0, AdcPin);
659impl_pin!(P2_2, ADC0, Mux0, AdcPin);
660impl_pin!(P2_12, ADC0, Mux0, AdcPin);
661impl_pin!(P2_16, ADC0, Mux0, AdcPin);
662impl_pin!(P2_7, ADC0, Mux0, AdcPin);
663impl_pin!(P0_18, ADC0, Mux0, AdcPin);
664impl_pin!(P0_19, ADC0, Mux0, AdcPin);
665impl_pin!(P0_20, ADC0, Mux0, AdcPin);
666impl_pin!(P0_21, ADC0, Mux0, AdcPin);
667impl_pin!(P0_22, ADC0, Mux0, AdcPin);
668impl_pin!(P0_23, ADC0, Mux0, AdcPin);
669impl_pin!(P0_3, ADC0, Mux0, AdcPin);
670impl_pin!(P0_6, ADC0, Mux0, AdcPin);
671impl_pin!(P1_0, ADC0, Mux0, AdcPin);
672impl_pin!(P1_1, ADC0, Mux0, AdcPin);
673impl_pin!(P1_2, ADC0, Mux0, AdcPin);
674impl_pin!(P1_3, ADC0, Mux0, AdcPin);
675impl_pin!(P1_4, ADC0, Mux0, AdcPin);
676impl_pin!(P1_5, ADC0, Mux0, AdcPin);
677impl_pin!(P1_6, ADC0, Mux0, AdcPin);
678impl_pin!(P1_7, ADC0, Mux0, AdcPin);
679impl_pin!(P1_10, ADC0, Mux0, AdcPin);
680
681impl_pin!(P2_1, ADC1, Mux0, AdcPin);
682impl_pin!(P2_5, ADC1, Mux0, AdcPin);
683impl_pin!(P2_19, ADC1, Mux0, AdcPin);
684impl_pin!(P2_6, ADC1, Mux0, AdcPin);
685impl_pin!(P2_3, ADC1, Mux0, AdcPin);
686impl_pin!(P2_13, ADC1, Mux0, AdcPin);
687impl_pin!(P2_17, ADC1, Mux0, AdcPin);
688impl_pin!(P2_7, ADC1, Mux0, AdcPin);
689impl_pin!(P1_10, ADC1, Mux0, AdcPin);
690impl_pin!(P1_11, ADC1, Mux0, AdcPin);
691impl_pin!(P1_12, ADC1, Mux0, AdcPin);
692impl_pin!(P1_13, ADC1, Mux0, AdcPin);
693impl_pin!(P1_14, ADC1, Mux0, AdcPin);
694impl_pin!(P1_15, ADC1, Mux0, AdcPin);
695impl_pin!(P1_16, ADC1, Mux0, AdcPin);
696impl_pin!(P1_17, ADC1, Mux0, AdcPin);
697impl_pin!(P1_18, ADC1, Mux0, AdcPin);
698impl_pin!(P1_19, ADC1, Mux0, AdcPin);
699impl_pin!(P3_31, ADC1, Mux0, AdcPin);
700impl_pin!(P3_30, ADC1, Mux0, AdcPin);
701impl_pin!(P3_29, ADC1, Mux0, AdcPin);
702
703impl_pin!(P2_4, ADC2, Mux0, AdcPin);
704impl_pin!(P2_10, ADC2, Mux0, AdcPin);
705impl_pin!(P4_4, ADC2, Mux0, AdcPin);
706impl_pin!(P2_24, ADC2, Mux0, AdcPin);
707impl_pin!(P2_16, ADC2, Mux0, AdcPin);
708impl_pin!(P2_12, ADC2, Mux0, AdcPin);
709impl_pin!(P2_20, ADC2, Mux0, AdcPin);
710impl_pin!(P2_7, ADC2, Mux0, AdcPin);
711impl_pin!(P0_2, ADC2, Mux0, AdcPin);
712impl_pin!(P0_4, ADC2, Mux0, AdcPin);
713impl_pin!(P0_5, ADC2, Mux0, AdcPin);
714impl_pin!(P0_6, ADC2, Mux0, AdcPin);
715impl_pin!(P0_7, ADC2, Mux0, AdcPin);
716impl_pin!(P0_12, ADC2, Mux0, AdcPin);
717impl_pin!(P0_13, ADC2, Mux0, AdcPin);
718impl_pin!(P0_14, ADC2, Mux0, AdcPin);
719impl_pin!(P0_15, ADC2, Mux0, AdcPin);
720impl_pin!(P4_0, ADC2, Mux0, AdcPin);
721impl_pin!(P4_1, ADC2, Mux0, AdcPin);
722impl_pin!(P4_2, ADC2, Mux0, AdcPin);
723impl_pin!(P4_3, ADC2, Mux0, AdcPin);
724//impl_pin!(P4_4, ADC2, Mux0, AdcPin); // Conflit with ADC2_A3 and ADC2_A20 using the same pin
725impl_pin!(P4_5, ADC2, Mux0, AdcPin);
726impl_pin!(P4_6, ADC2, Mux0, AdcPin);
727impl_pin!(P4_7, ADC2, Mux0, AdcPin);
728
729impl_pin!(P2_5, ADC3, Mux0, AdcPin);
730impl_pin!(P2_11, ADC3, Mux0, AdcPin);
731impl_pin!(P2_23, ADC3, Mux0, AdcPin);
732impl_pin!(P2_25, ADC3, Mux0, AdcPin);
733impl_pin!(P2_17, ADC3, Mux0, AdcPin);
734impl_pin!(P2_13, ADC3, Mux0, AdcPin);
735impl_pin!(P2_21, ADC3, Mux0, AdcPin);
736impl_pin!(P2_7, ADC3, Mux0, AdcPin);
737impl_pin!(P3_2, ADC3, Mux0, AdcPin);
738impl_pin!(P3_3, ADC3, Mux0, AdcPin);
739impl_pin!(P3_4, ADC3, Mux0, AdcPin);
740impl_pin!(P3_5, ADC3, Mux0, AdcPin);
741impl_pin!(P3_6, ADC3, Mux0, AdcPin);
742impl_pin!(P3_7, ADC3, Mux0, AdcPin);
743impl_pin!(P3_12, ADC3, Mux0, AdcPin);
744impl_pin!(P3_13, ADC3, Mux0, AdcPin);
745impl_pin!(P3_14, ADC3, Mux0, AdcPin);
746impl_pin!(P3_15, ADC3, Mux0, AdcPin);
747impl_pin!(P3_20, ADC3, Mux0, AdcPin);
748impl_pin!(P3_21, ADC3, Mux0, AdcPin);
749impl_pin!(P3_22, ADC3, Mux0, AdcPin);
750impl_pin!(P3_23, ADC3, Mux0, AdcPin);
751impl_pin!(P3_24, ADC3, Mux0, AdcPin);
752impl_pin!(P3_25, ADC3, Mux0, AdcPin);
diff --git a/embassy-mcxa/src/clkout.rs b/embassy-mcxa/src/clkout.rs
new file mode 100644
index 000000000..5b21f24b0
--- /dev/null
+++ b/embassy-mcxa/src/clkout.rs
@@ -0,0 +1,169 @@
1//! CLKOUT pseudo-peripheral
2//!
3//! CLKOUT is a part of the clock generation subsystem, and can be used
4//! either to generate arbitrary waveforms, or to debug the state of
5//! internal oscillators.
6
7use core::marker::PhantomData;
8
9use embassy_hal_internal::Peri;
10
11pub use crate::clocks::periph_helpers::Div4;
12use crate::clocks::{ClockError, PoweredClock, with_clocks};
13use crate::pac::mrcc0::mrcc_clkout_clksel::Mux;
14use crate::peripherals::CLKOUT;
15
16/// A peripheral representing the CLKOUT pseudo-peripheral
17pub struct ClockOut<'a> {
18 _p: PhantomData<&'a mut CLKOUT>,
19 freq: u32,
20}
21
22/// Selected clock source to output
23pub enum ClockOutSel {
24 /// 12MHz Internal Oscillator
25 Fro12M,
26 /// FRO180M Internal Oscillator, via divisor
27 FroHfDiv,
28 /// External Oscillator
29 ClkIn,
30 /// 16KHz oscillator
31 Clk16K,
32 /// Output of PLL1
33 Pll1Clk,
34 /// Main System CPU clock, divided by 6
35 SlowClk,
36}
37
38/// Configuration for the ClockOut
39pub struct Config {
40 /// Selected Source Clock
41 pub sel: ClockOutSel,
42 /// Selected division level
43 pub div: Div4,
44 /// Selected power level
45 pub level: PoweredClock,
46}
47
48impl<'a> ClockOut<'a> {
49 /// Create a new ClockOut pin. On success, the clock signal will begin immediately
50 /// on the given pin.
51 pub fn new(
52 _peri: Peri<'a, CLKOUT>,
53 pin: Peri<'a, impl sealed::ClockOutPin>,
54 cfg: Config,
55 ) -> Result<Self, ClockError> {
56 // There's no MRCC enable bit, so we check the validity of the clocks here
57 //
58 // TODO: Should we check that the frequency is suitably low?
59 let (freq, mux) = check_sel(cfg.sel, cfg.level)?;
60
61 // All good! Apply requested config, starting with the pin.
62 pin.mux();
63
64 setup_clkout(mux, cfg.div);
65
66 Ok(Self {
67 _p: PhantomData,
68 freq: freq / cfg.div.into_divisor(),
69 })
70 }
71
72 /// Frequency of the clkout pin
73 #[inline]
74 pub fn frequency(&self) -> u32 {
75 self.freq
76 }
77}
78
79impl Drop for ClockOut<'_> {
80 fn drop(&mut self) {
81 disable_clkout();
82 }
83}
84
85/// Check whether the given clock selection is valid
86fn check_sel(sel: ClockOutSel, level: PoweredClock) -> Result<(u32, Mux), ClockError> {
87 let res = with_clocks(|c| {
88 Ok(match sel {
89 ClockOutSel::Fro12M => (c.ensure_fro_hf_active(&level)?, Mux::Clkroot12m),
90 ClockOutSel::FroHfDiv => (c.ensure_fro_hf_div_active(&level)?, Mux::ClkrootFircDiv),
91 ClockOutSel::ClkIn => (c.ensure_clk_in_active(&level)?, Mux::ClkrootSosc),
92 ClockOutSel::Clk16K => (c.ensure_clk_16k_vdd_core_active(&level)?, Mux::Clkroot16k),
93 ClockOutSel::Pll1Clk => (c.ensure_pll1_clk_active(&level)?, Mux::ClkrootSpll),
94 ClockOutSel::SlowClk => (c.ensure_slow_clk_active(&level)?, Mux::ClkrootSlow),
95 })
96 });
97 let Some(res) = res else {
98 return Err(ClockError::NeverInitialized);
99 };
100 res
101}
102
103/// Set up the clkout pin using the given mux and div settings
104fn setup_clkout(mux: Mux, div: Div4) {
105 let mrcc = unsafe { crate::pac::Mrcc0::steal() };
106
107 mrcc.mrcc_clkout_clksel().write(|w| w.mux().variant(mux));
108
109 // Set up clkdiv
110 mrcc.mrcc_clkout_clkdiv().write(|w| {
111 w.halt().set_bit();
112 w.reset().set_bit();
113 unsafe { w.div().bits(div.into_bits()) };
114 w
115 });
116 mrcc.mrcc_clkout_clkdiv().write(|w| {
117 w.halt().clear_bit();
118 w.reset().clear_bit();
119 unsafe { w.div().bits(div.into_bits()) };
120 w
121 });
122
123 while mrcc.mrcc_clkout_clkdiv().read().unstab().bit_is_set() {}
124}
125
126/// Stop the clkout
127fn disable_clkout() {
128 // Stop the output by selecting the "none" clock
129 //
130 // TODO: restore the pin to hi-z or something?
131 let mrcc = unsafe { crate::pac::Mrcc0::steal() };
132 mrcc.mrcc_clkout_clkdiv().write(|w| {
133 w.reset().set_bit();
134 w.halt().set_bit();
135 unsafe {
136 w.div().bits(0);
137 }
138 w
139 });
140 mrcc.mrcc_clkout_clksel().write(|w| unsafe { w.bits(0b111) });
141}
142
143mod sealed {
144 use embassy_hal_internal::PeripheralType;
145
146 use crate::gpio::{Pull, SealedPin};
147
148 /// Sealed marker trait for clockout pins
149 pub trait ClockOutPin: PeripheralType {
150 /// Set the given pin to the correct muxing state
151 fn mux(&self);
152 }
153
154 macro_rules! impl_pin {
155 ($pin:ident, $func:ident) => {
156 impl ClockOutPin for crate::peripherals::$pin {
157 fn mux(&self) {
158 self.set_function(crate::pac::port0::pcr0::Mux::$func);
159 self.set_pull(Pull::Disabled);
160 }
161 }
162 };
163 }
164
165 impl_pin!(P0_6, Mux12);
166 impl_pin!(P3_6, Mux1);
167 impl_pin!(P3_8, Mux12);
168 impl_pin!(P4_2, Mux1);
169}
diff --git a/embassy-mcxa/src/clocks/config.rs b/embassy-mcxa/src/clocks/config.rs
new file mode 100644
index 000000000..0563b8917
--- /dev/null
+++ b/embassy-mcxa/src/clocks/config.rs
@@ -0,0 +1,204 @@
1//! Clock Configuration
2//!
3//! This module holds configuration types used for the system clocks. For
4//! configuration of individual peripherals, see [`super::periph_helpers`].
5
6use super::PoweredClock;
7
8/// This type represents a divider in the range 1..=256.
9///
10/// At a hardware level, this is an 8-bit register from 0..=255,
11/// which adds one.
12#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13pub struct Div8(pub(super) u8);
14
15impl Div8 {
16 /// Store a "raw" divisor value that will divide the source by
17 /// `(n + 1)`, e.g. `Div8::from_raw(0)` will divide the source
18 /// by 1, and `Div8::from_raw(255)` will divide the source by
19 /// 256.
20 pub const fn from_raw(n: u8) -> Self {
21 Self(n)
22 }
23
24 /// Divide by one, or no division
25 pub const fn no_div() -> Self {
26 Self(0)
27 }
28
29 /// Store a specific divisor value that will divide the source
30 /// by `n`. e.g. `Div8::from_divisor(1)` will divide the source
31 /// by 1, and `Div8::from_divisor(256)` will divide the source
32 /// by 256.
33 ///
34 /// Will return `None` if `n` is not in the range `1..=256`.
35 /// Consider [`Self::from_raw`] for an infallible version.
36 pub const fn from_divisor(n: u16) -> Option<Self> {
37 let Some(n) = n.checked_sub(1) else {
38 return None;
39 };
40 if n > (u8::MAX as u16) {
41 return None;
42 }
43 Some(Self(n as u8))
44 }
45
46 /// Convert into "raw" bits form
47 #[inline(always)]
48 pub const fn into_bits(self) -> u8 {
49 self.0
50 }
51
52 /// Convert into "divisor" form, as a u32 for convenient frequency math
53 #[inline(always)]
54 pub const fn into_divisor(self) -> u32 {
55 self.0 as u32 + 1
56 }
57}
58
59/// ```text
60/// ┌─────────────────────────────────────────────────────────┐
61/// │ │
62/// │ ┌───────────┐ clk_out ┌─────────┐ │
63/// XTAL ──────┼──▷│ System │───────────▷│ │ clk_in │
64/// │ │ OSC │ clkout_byp │ MUX │──────────────────┼──────▷
65/// EXTAL ──────┼──▷│ │───────────▷│ │ │
66/// │ └───────────┘ └─────────┘ │
67/// │ │
68/// │ ┌───────────┐ fro_hf_root ┌────┐ fro_hf │
69/// │ │ FRO180 ├───────┬─────▷│ CG │─────────────────────┼──────▷
70/// │ │ │ │ ├────┤ clk_45m │
71/// │ │ │ └─────▷│ CG │─────────────────────┼──────▷
72/// │ └───────────┘ └────┘ │
73/// │ ┌───────────┐ fro_12m_root ┌────┐ fro_12m │
74/// │ │ FRO12M │────────┬─────▷│ CG │────────────────────┼──────▷
75/// │ │ │ │ ├────┤ clk_1m │
76/// │ │ │ └─────▷│1/12│────────────────────┼──────▷
77/// │ └───────────┘ └────┘ │
78/// │ │
79/// │ ┌──────────┐ │
80/// │ │000 │ │
81/// │ clk_in │ │ │
82/// │ ───────────────▷│001 │ │
83/// │ fro_12m │ │ │
84/// │ ───────────────▷│010 │ │
85/// │ fro_hf_root │ │ │
86/// │ ───────────────▷│011 │ main_clk │
87/// │ │ │───────────────────────────┼──────▷
88/// clk_16k ──────┼─────────────────▷│100 │ │
89/// │ none │ │ │
90/// │ ───────────────▷│101 │ │
91/// │ pll1_clk │ │ │
92/// │ ───────────────▷│110 │ │
93/// │ none │ │ │
94/// │ ───────────────▷│111 │ │
95/// │ └──────────┘ │
96/// │ ▲ │
97/// │ │ │
98/// │ SCG SCS │
99/// │ SCG-Lite │
100/// └─────────────────────────────────────────────────────────┘
101///
102///
103/// clk_in ┌─────┐
104/// ───────────────▷│00 │
105/// clk_45m │ │
106/// ───────────────▷│01 │ ┌───────────┐ pll1_clk
107/// none │ │─────▷│ SPLL │───────────────▷
108/// ───────────────▷│10 │ └───────────┘
109/// fro_12m │ │
110/// ───────────────▷│11 │
111/// └─────┘
112/// ```
113#[non_exhaustive]
114pub struct ClocksConfig {
115 /// FIRC, FRO180, 45/60/90/180M clock source
116 pub firc: Option<FircConfig>,
117 /// SIRC, FRO12M, clk_12m clock source
118 // NOTE: I don't think we *can* disable the SIRC?
119 pub sirc: SircConfig,
120 /// FRO16K clock source
121 pub fro16k: Option<Fro16KConfig>,
122}
123
124// FIRC/FRO180M
125
126/// ```text
127/// ┌───────────┐ fro_hf_root ┌────┐ fro_hf
128/// │ FRO180M ├───────┬─────▷│GATE│──────────▷
129/// │ │ │ ├────┤ clk_45m
130/// │ │ └─────▷│GATE│──────────▷
131/// └───────────┘ └────┘
132/// ```
133#[non_exhaustive]
134pub struct FircConfig {
135 /// Selected clock frequency
136 pub frequency: FircFreqSel,
137 /// Selected power state of the clock
138 pub power: PoweredClock,
139 /// Is the "fro_hf" gated clock enabled?
140 pub fro_hf_enabled: bool,
141 /// Is the "clk_45m" gated clock enabled?
142 pub clk_45m_enabled: bool,
143 /// Is the "fro_hf_div" clock enabled? Requires `fro_hf`!
144 pub fro_hf_div: Option<Div8>,
145}
146
147/// Selected FIRC frequency
148pub enum FircFreqSel {
149 /// 45MHz Output
150 Mhz45,
151 /// 60MHz Output
152 Mhz60,
153 /// 90MHz Output
154 Mhz90,
155 /// 180MHz Output
156 Mhz180,
157}
158
159// SIRC/FRO12M
160
161/// ```text
162/// ┌───────────┐ fro_12m_root ┌────┐ fro_12m
163/// │ FRO12M │────────┬─────▷│ CG │──────────▷
164/// │ │ │ ├────┤ clk_1m
165/// │ │ └─────▷│1/12│──────────▷
166/// └───────────┘ └────┘
167/// ```
168#[non_exhaustive]
169pub struct SircConfig {
170 pub power: PoweredClock,
171 // peripheral output, aka sirc_12mhz
172 pub fro_12m_enabled: bool,
173 /// Is the "fro_lf_div" clock enabled? Requires `fro_12m`!
174 pub fro_lf_div: Option<Div8>,
175}
176
177#[non_exhaustive]
178pub struct Fro16KConfig {
179 pub vsys_domain_active: bool,
180 pub vdd_core_domain_active: bool,
181}
182
183impl Default for ClocksConfig {
184 fn default() -> Self {
185 Self {
186 firc: Some(FircConfig {
187 frequency: FircFreqSel::Mhz45,
188 power: PoweredClock::NormalEnabledDeepSleepDisabled,
189 fro_hf_enabled: true,
190 clk_45m_enabled: true,
191 fro_hf_div: None,
192 }),
193 sirc: SircConfig {
194 power: PoweredClock::AlwaysEnabled,
195 fro_12m_enabled: true,
196 fro_lf_div: None,
197 },
198 fro16k: Some(Fro16KConfig {
199 vsys_domain_active: true,
200 vdd_core_domain_active: true,
201 }),
202 }
203 }
204}
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs
new file mode 100644
index 000000000..667d79536
--- /dev/null
+++ b/embassy-mcxa/src/clocks/mod.rs
@@ -0,0 +1,955 @@
1//! # Clock Module
2//!
3//! For the MCX-A, we separate clock and peripheral control into two main stages:
4//!
5//! 1. At startup, e.g. when `embassy_mcxa::init()` is called, we configure the
6//! core system clocks, including external and internal oscillators. This
7//! configuration is then largely static for the duration of the program.
8//! 2. When HAL drivers are created, e.g. `Lpuart::new()` is called, the driver
9//! is responsible for two main things:
10//! * Ensuring that any required "upstream" core system clocks necessary for
11//! clocking the peripheral is active and configured to a reasonable value
12//! * Enabling the clock gates for that peripheral, and resetting the peripheral
13//!
14//! From a user perspective, only step 1 is visible. Step 2 is automatically handled
15//! by HAL drivers, using interfaces defined in this module.
16//!
17//! It is also possible to *view* the state of the clock configuration after [`init()`]
18//! has been called, using the [`with_clocks()`] function, which provides a view of the
19//! [`Clocks`] structure.
20//!
21//! ## For HAL driver implementors
22//!
23//! The majority of peripherals in the MCXA chip are fed from either a "hard-coded" or
24//! configurable clock source, e.g. selecting the FROM12M or `clk_1m` as a source. This
25//! selection, as well as often any pre-scaler division from that source clock, is made
26//! through MRCC registers.
27//!
28//! Any peripheral that is controlled through the MRCC register can automatically implement
29//! the necessary APIs using the `impl_cc_gate!` macro in this module. You will also need
30//! to define the configuration surface and steps necessary to fully configure that peripheral
31//! from a clocks perspective by:
32//!
33//! 1. Defining a configuration type in the [`periph_helpers`] module that contains any selects
34//! or divisions available to the HAL driver
35//! 2. Implementing the [`periph_helpers::SPConfHelper`] trait, which should check that the
36//! necessary input clocks are reasonable
37
38use core::cell::RefCell;
39
40use config::{ClocksConfig, FircConfig, FircFreqSel, Fro16KConfig, SircConfig};
41use mcxa_pac::scg0::firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten};
42use mcxa_pac::scg0::sirccsr::{SircClkPeriphEn, Sircsten};
43use periph_helpers::SPConfHelper;
44
45use crate::pac;
46pub mod config;
47pub mod periph_helpers;
48
49//
50// Statics/Consts
51//
52
53/// The state of system core clocks.
54///
55/// Initialized by [`init()`], and then unchanged for the remainder of the program.
56static CLOCKS: critical_section::Mutex<RefCell<Option<Clocks>>> = critical_section::Mutex::new(RefCell::new(None));
57
58//
59// Free functions
60//
61
62/// Initialize the core system clocks with the given [`ClocksConfig`].
63///
64/// This function should be called EXACTLY once at start-up, usually via a
65/// call to [`embassy_mcxa::init()`](crate::init()). Subsequent calls will
66/// return an error.
67pub fn init(settings: ClocksConfig) -> Result<(), ClockError> {
68 critical_section::with(|cs| {
69 if CLOCKS.borrow_ref(cs).is_some() {
70 Err(ClockError::AlreadyInitialized)
71 } else {
72 Ok(())
73 }
74 })?;
75
76 let mut clocks = Clocks::default();
77 let mut operator = ClockOperator {
78 clocks: &mut clocks,
79 config: &settings,
80
81 _mrcc0: unsafe { pac::Mrcc0::steal() },
82 scg0: unsafe { pac::Scg0::steal() },
83 syscon: unsafe { pac::Syscon::steal() },
84 vbat0: unsafe { pac::Vbat0::steal() },
85 };
86
87 operator.configure_firc_clocks()?;
88 operator.configure_sirc_clocks()?;
89 operator.configure_fro16k_clocks()?;
90
91 // For now, just use FIRC as the main/cpu clock, which should already be
92 // the case on reset
93 assert!(operator.scg0.rccr().read().scs().is_firc());
94 let input = operator.clocks.fro_hf_root.clone().unwrap();
95 operator.clocks.main_clk = Some(input.clone());
96 // We can also assume cpu/system clk == fro_hf because div is /1.
97 assert_eq!(operator.syscon.ahbclkdiv().read().div().bits(), 0);
98 operator.clocks.cpu_system_clk = Some(input);
99
100 critical_section::with(|cs| {
101 let mut clks = CLOCKS.borrow_ref_mut(cs);
102 assert!(clks.is_none(), "Clock setup race!");
103 *clks = Some(clocks);
104 });
105
106 Ok(())
107}
108
109/// Obtain the full clocks structure, calling the given closure in a critical section.
110///
111/// The given closure will be called with read-only access to the state of the system
112/// clocks. This can be used to query and return the state of a given clock.
113///
114/// As the caller's closure will be called in a critical section, care must be taken
115/// not to block or cause any other undue delays while accessing.
116///
117/// Calls to this function will not succeed until after a successful call to `init()`,
118/// and will always return None.
119pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {
120 critical_section::with(|cs| {
121 let c = CLOCKS.borrow_ref(cs);
122 let c = c.as_ref()?;
123 Some(f(c))
124 })
125}
126
127//
128// Structs/Enums
129//
130
131/// The `Clocks` structure contains the initialized state of the core system clocks
132///
133/// These values are configured by providing [`config::ClocksConfig`] to the [`init()`] function
134/// at boot time.
135#[derive(Default, Debug, Clone)]
136#[non_exhaustive]
137pub struct Clocks {
138 /// The `clk_in` is a clock provided by an external oscillator
139 pub clk_in: Option<Clock>,
140
141 // FRO180M stuff
142 //
143 /// `fro_hf_root` is the direct output of the `FRO180M` internal oscillator
144 ///
145 /// It is used to feed downstream clocks, such as `fro_hf`, `clk_45m`,
146 /// and `fro_hf_div`.
147 pub fro_hf_root: Option<Clock>,
148
149 /// `fro_hf` is the same frequency as `fro_hf_root`, but behind a gate.
150 pub fro_hf: Option<Clock>,
151
152 /// `clk_45` is a 45MHz clock, sourced from `fro_hf`.
153 pub clk_45m: Option<Clock>,
154
155 /// `fro_hf_div` is a configurable frequency clock, sourced from `fro_hf`.
156 pub fro_hf_div: Option<Clock>,
157
158 //
159 // End FRO180M
160
161 // FRO12M stuff
162 //
163 /// `fro_12m_root` is the direct output of the `FRO12M` internal oscillator
164 ///
165 /// It is used to feed downstream clocks, such as `fro_12m`, `clk_1m`,
166 /// `and `fro_lf_div`.
167 pub fro_12m_root: Option<Clock>,
168
169 /// `fro_12m` is the same frequency as `fro_12m_root`, but behind a gate.
170 pub fro_12m: Option<Clock>,
171
172 /// `clk_1m` is a 1MHz clock, sourced from `fro_12m`
173 pub clk_1m: Option<Clock>,
174
175 /// `fro_lf_div` is a configurable frequency clock, sourced from `fro_12m`
176 pub fro_lf_div: Option<Clock>,
177 //
178 // End FRO12M stuff
179 /// `clk_16k_vsys` is one of two outputs of the `FRO16K` internal oscillator.
180 ///
181 /// Also referred to as `clk_16k[0]` in the datasheet, it feeds peripherals in
182 /// the system domain, such as the CMP and RTC.
183 pub clk_16k_vsys: Option<Clock>,
184
185 /// `clk_16k_vdd_core` is one of two outputs of the `FRO16K` internal oscillator.
186 ///
187 /// Also referred to as `clk_16k[1]` in the datasheet, it feeds peripherals in
188 /// the VDD Core domain, such as the OSTimer or LPUarts.
189 pub clk_16k_vdd_core: Option<Clock>,
190
191 /// `main_clk` is the main clock used by the CPU, AHB, APB, IPS bus, and some
192 /// peripherals.
193 pub main_clk: Option<Clock>,
194
195 /// `CPU_CLK` or `SYSTEM_CLK` is the output of `main_clk`, run through the `AHBCLKDIV`
196 pub cpu_system_clk: Option<Clock>,
197
198 /// `pll1_clk` is the output of the main system PLL, `pll1`.
199 pub pll1_clk: Option<Clock>,
200}
201
202/// `ClockError` is the main error returned when configuring or checking clock state
203#[derive(Debug, Copy, Clone, Eq, PartialEq)]
204#[cfg_attr(feature = "defmt", derive(defmt::Format))]
205#[non_exhaustive]
206pub enum ClockError {
207 /// The system clocks were never initialized by calling [`init()`]
208 NeverInitialized,
209 /// The [`init()`] function was called more than once
210 AlreadyInitialized,
211 /// The requested configuration was not possible to fulfill, as the system clocks
212 /// were not configured in a compatible way
213 BadConfig { clock: &'static str, reason: &'static str },
214 /// The requested configuration was not possible to fulfill, as the required system
215 /// clocks have not yet been implemented.
216 NotImplemented { clock: &'static str },
217 /// The requested peripheral could not be configured, as the steps necessary to
218 /// enable it have not yet been implemented.
219 UnimplementedConfig,
220}
221
222/// Information regarding a system clock
223#[derive(Debug, Clone)]
224pub struct Clock {
225 /// The frequency, in Hz, of the given clock
226 pub frequency: u32,
227 /// The power state of the clock, e.g. whether it is active in deep sleep mode
228 /// or not.
229 pub power: PoweredClock,
230}
231
232/// The power state of a given clock.
233///
234/// On the MCX-A, when Deep-Sleep is entered, any clock not configured for Deep Sleep
235/// mode will be stopped. This means that any downstream usage, e.g. by peripherals,
236/// will also stop.
237///
238/// In the future, we will provide an API for entering Deep Sleep, and if there are
239/// any peripherals that are NOT using an `AlwaysEnabled` clock active, entry into
240/// Deep Sleep will be prevented, in order to avoid misbehaving peripherals.
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub enum PoweredClock {
243 /// The given clock will NOT continue running in Deep Sleep mode
244 NormalEnabledDeepSleepDisabled,
245 /// The given clock WILL continue running in Deep Sleep mode
246 AlwaysEnabled,
247}
248
249/// The ClockOperator is a private helper type that contains the methods used
250/// during system clock initialization.
251///
252/// # SAFETY
253///
254/// Concurrent access to clock-relevant peripheral registers, such as `MRCC`, `SCG`,
255/// `SYSCON`, and `VBAT` should not be allowed for the duration of the [`init()`] function.
256struct ClockOperator<'a> {
257 /// A mutable reference to the current state of system clocks
258 clocks: &'a mut Clocks,
259 /// A reference to the requested configuration provided by the caller of [`init()`]
260 config: &'a ClocksConfig,
261
262 // We hold on to stolen peripherals
263 _mrcc0: pac::Mrcc0,
264 scg0: pac::Scg0,
265 syscon: pac::Syscon,
266 vbat0: pac::Vbat0,
267}
268
269/// Trait describing an AHB clock gate that can be toggled through MRCC.
270pub trait Gate {
271 type MrccPeriphConfig: SPConfHelper;
272
273 /// Enable the clock gate.
274 ///
275 /// # SAFETY
276 ///
277 /// The current peripheral must be disabled prior to calling this method
278 unsafe fn enable_clock();
279
280 /// Disable the clock gate.
281 ///
282 /// # SAFETY
283 ///
284 /// There must be no active user of this peripheral when calling this method
285 unsafe fn disable_clock();
286
287 /// Drive the peripheral into reset.
288 ///
289 /// # SAFETY
290 ///
291 /// There must be no active user of this peripheral when calling this method
292 unsafe fn assert_reset();
293
294 /// Drive the peripheral out of reset.
295 ///
296 /// # SAFETY
297 ///
298 /// There must be no active user of this peripheral when calling this method
299 unsafe fn release_reset();
300
301 /// Return whether the clock gate for this peripheral is currently enabled.
302 fn is_clock_enabled() -> bool;
303
304 /// Return whether the peripheral is currently held in reset.
305 fn is_reset_released() -> bool;
306}
307
308/// This is the primary helper method HAL drivers are expected to call when creating
309/// an instance of the peripheral.
310///
311/// This method:
312///
313/// 1. Enables the MRCC clock gate for this peripheral
314/// 2. Calls the `G::MrccPeriphConfig::post_enable_config()` method, returning an error
315/// and re-disabling the peripheral if this fails.
316/// 3. Pulses the MRCC reset line, to reset the peripheral to the default state
317/// 4. Returns the frequency, in Hz that is fed into the peripheral, taking into account
318/// the selected upstream clock, as well as any division specified by `cfg`.
319///
320/// NOTE: if a clock is disabled, sourced from an "ambient" clock source, this method
321/// may return `Ok(0)`. In the future, this might be updated to return the correct
322/// "ambient" clock, e.g. the AHB/APB frequency.
323///
324/// # SAFETY
325///
326/// This peripheral must not yet be in use prior to calling `enable_and_reset`.
327#[inline]
328pub unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
329 let freq = enable::<G>(cfg).inspect_err(|_| disable::<G>())?;
330 pulse_reset::<G>();
331 Ok(freq)
332}
333
334/// Enable the clock gate for the given peripheral.
335///
336/// Prefer [`enable_and_reset`] unless you are specifically avoiding a pulse of the reset, or need
337/// to control the duration of the pulse more directly.
338///
339/// # SAFETY
340///
341/// This peripheral must not yet be in use prior to calling `enable`.
342#[inline]
343pub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
344 G::enable_clock();
345 while !G::is_clock_enabled() {}
346 core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags));
347
348 let freq = critical_section::with(|cs| {
349 let clocks = CLOCKS.borrow_ref(cs);
350 let clocks = clocks.as_ref().ok_or(ClockError::NeverInitialized)?;
351 cfg.post_enable_config(clocks)
352 });
353
354 freq.inspect_err(|_e| {
355 G::disable_clock();
356 })
357}
358
359/// Disable the clock gate for the given peripheral.
360///
361/// # SAFETY
362///
363/// This peripheral must no longer be in use prior to calling `enable`.
364#[allow(dead_code)]
365#[inline]
366pub unsafe fn disable<G: Gate>() {
367 G::disable_clock();
368}
369
370/// Check whether a gate is currently enabled.
371#[allow(dead_code)]
372#[inline]
373pub fn is_clock_enabled<G: Gate>() -> bool {
374 G::is_clock_enabled()
375}
376
377/// Release a reset line for the given peripheral set.
378///
379/// Prefer [`enable_and_reset`].
380///
381/// # SAFETY
382///
383/// This peripheral must not yet be in use prior to calling `release_reset`.
384#[inline]
385pub unsafe fn release_reset<G: Gate>() {
386 G::release_reset();
387}
388
389/// Assert a reset line for the given peripheral set.
390///
391/// Prefer [`enable_and_reset`].
392///
393/// # SAFETY
394///
395/// This peripheral must not yet be in use prior to calling `assert_reset`.
396#[inline]
397pub unsafe fn assert_reset<G: Gate>() {
398 G::assert_reset();
399}
400
401/// Check whether the peripheral is held in reset.
402///
403/// # Safety
404///
405/// Must be called with a valid peripheral gate type.
406#[inline]
407pub unsafe fn is_reset_released<G: Gate>() -> bool {
408 G::is_reset_released()
409}
410
411/// Pulse a reset line (assert then release) with a short delay.
412///
413/// Prefer [`enable_and_reset`].
414///
415/// # SAFETY
416///
417/// This peripheral must not yet be in use prior to calling `release_reset`.
418#[inline]
419pub unsafe fn pulse_reset<G: Gate>() {
420 G::assert_reset();
421 cortex_m::asm::nop();
422 cortex_m::asm::nop();
423 G::release_reset();
424}
425
426//
427// `impl`s for structs/enums
428//
429
430/// The [`Clocks`] type's methods generally take the form of "ensure X clock is active".
431///
432/// These methods are intended to be used by HAL peripheral implementors to ensure that their
433/// selected clocks are active at a suitable level at time of construction. These methods
434/// return the frequency of the requested clock, in Hertz, or a [`ClockError`].
435impl Clocks {
436 /// Ensure the `fro_lf_div` clock is active and valid at the given power state.
437 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
438 let Some(clk) = self.fro_lf_div.as_ref() else {
439 return Err(ClockError::BadConfig {
440 clock: "fro_lf_div",
441 reason: "required but not active",
442 });
443 };
444 if !clk.power.meets_requirement_of(at_level) {
445 return Err(ClockError::BadConfig {
446 clock: "fro_lf_div",
447 reason: "not low power active",
448 });
449 }
450 Ok(clk.frequency)
451 }
452
453 /// Ensure the `fro_hf` clock is active and valid at the given power state.
454 pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
455 let Some(clk) = self.fro_hf.as_ref() else {
456 return Err(ClockError::BadConfig {
457 clock: "fro_hf",
458 reason: "required but not active",
459 });
460 };
461 if !clk.power.meets_requirement_of(at_level) {
462 return Err(ClockError::BadConfig {
463 clock: "fro_hf",
464 reason: "not low power active",
465 });
466 }
467 Ok(clk.frequency)
468 }
469
470 /// Ensure the `fro_hf_div` clock is active and valid at the given power state.
471 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
472 let Some(clk) = self.fro_hf_div.as_ref() else {
473 return Err(ClockError::BadConfig {
474 clock: "fro_hf_div",
475 reason: "required but not active",
476 });
477 };
478 if !clk.power.meets_requirement_of(at_level) {
479 return Err(ClockError::BadConfig {
480 clock: "fro_hf_div",
481 reason: "not low power active",
482 });
483 }
484 Ok(clk.frequency)
485 }
486
487 /// Ensure the `clk_in` clock is active and valid at the given power state.
488 pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
489 Err(ClockError::NotImplemented { clock: "clk_in" })
490 }
491
492 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state.
493 pub fn ensure_clk_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
494 // NOTE: clk_16k is always active in low power mode
495 Ok(self
496 .clk_16k_vsys
497 .as_ref()
498 .ok_or(ClockError::BadConfig {
499 clock: "clk_16k_vsys",
500 reason: "required but not active",
501 })?
502 .frequency)
503 }
504
505 /// Ensure the `clk_16k_vdd_core` clock is active and valid at the given power state.
506 pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
507 // NOTE: clk_16k is always active in low power mode
508 Ok(self
509 .clk_16k_vdd_core
510 .as_ref()
511 .ok_or(ClockError::BadConfig {
512 clock: "clk_16k_vdd_core",
513 reason: "required but not active",
514 })?
515 .frequency)
516 }
517
518 /// Ensure the `clk_1m` clock is active and valid at the given power state.
519 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
520 let Some(clk) = self.clk_1m.as_ref() else {
521 return Err(ClockError::BadConfig {
522 clock: "clk_1m",
523 reason: "required but not active",
524 });
525 };
526 if !clk.power.meets_requirement_of(at_level) {
527 return Err(ClockError::BadConfig {
528 clock: "clk_1m",
529 reason: "not low power active",
530 });
531 }
532 Ok(clk.frequency)
533 }
534
535 /// Ensure the `pll1_clk` clock is active and valid at the given power state.
536 pub fn ensure_pll1_clk_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
537 Err(ClockError::NotImplemented { clock: "pll1_clk" })
538 }
539
540 /// Ensure the `pll1_clk_div` clock is active and valid at the given power state.
541 pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
542 Err(ClockError::NotImplemented { clock: "pll1_clk_div" })
543 }
544
545 /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active
546 pub fn ensure_cpu_system_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
547 let Some(clk) = self.cpu_system_clk.as_ref() else {
548 return Err(ClockError::BadConfig {
549 clock: "cpu_system_clk",
550 reason: "required but not active",
551 });
552 };
553 // Can the main_clk ever be active in deep sleep? I think it is gated?
554 match at_level {
555 PoweredClock::NormalEnabledDeepSleepDisabled => {}
556 PoweredClock::AlwaysEnabled => {
557 return Err(ClockError::BadConfig {
558 clock: "main_clk",
559 reason: "not low power active",
560 });
561 }
562 }
563
564 Ok(clk.frequency)
565 }
566
567 pub fn ensure_slow_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
568 let freq = self.ensure_cpu_system_clk_active(at_level)?;
569
570 Ok(freq / 6)
571 }
572}
573
574impl PoweredClock {
575 /// Does THIS clock meet the power requirements of the OTHER clock?
576 pub fn meets_requirement_of(&self, other: &Self) -> bool {
577 match (self, other) {
578 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::AlwaysEnabled) => false,
579 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
580 (PoweredClock::AlwaysEnabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
581 (PoweredClock::AlwaysEnabled, PoweredClock::AlwaysEnabled) => true,
582 }
583 }
584}
585
586impl ClockOperator<'_> {
587 /// Configure the FIRC/FRO180M clock family
588 ///
589 /// NOTE: Currently we require this to be a fairly hardcoded value, as this clock is used
590 /// as the main clock used for the CPU, AHB, APB, etc.
591 fn configure_firc_clocks(&mut self) -> Result<(), ClockError> {
592 const HARDCODED_ERR: Result<(), ClockError> = Err(ClockError::BadConfig {
593 clock: "firc",
594 reason: "For now, FIRC must be enabled and in default state!",
595 });
596
597 // Did the user give us a FIRC config?
598 let Some(firc) = self.config.firc.as_ref() else {
599 return HARDCODED_ERR;
600 };
601 // Is the FIRC set to 45MHz (should be reset default)
602 if !matches!(firc.frequency, FircFreqSel::Mhz45) {
603 return HARDCODED_ERR;
604 }
605 let base_freq = 45_000_000;
606
607 // Now, check if the FIRC as expected for our hardcoded value
608 let mut firc_ok = true;
609
610 // Is the hardware currently set to the default 45MHz?
611 //
612 // NOTE: the SVD currently has the wrong(?) values for these:
613 // 45 -> 48
614 // 60 -> 64
615 // 90 -> 96
616 // 180 -> 192
617 // Probably correct-ish, but for a different trim value?
618 firc_ok &= self.scg0.firccfg().read().freq_sel().is_firc_48mhz_192s();
619
620 // Check some values in the CSR
621 let csr = self.scg0.firccsr().read();
622 // Is it enabled?
623 firc_ok &= csr.fircen().is_enabled();
624 // Is it accurate?
625 firc_ok &= csr.fircacc().is_enabled_and_valid();
626 // Is there no error?
627 firc_ok &= csr.fircerr().is_error_not_detected();
628 // Is the FIRC the system clock?
629 firc_ok &= csr.fircsel().is_firc();
630 // Is it valid?
631 firc_ok &= csr.fircvld().is_enabled_and_valid();
632
633 // Are we happy with the current (hardcoded) state?
634 if !firc_ok {
635 return HARDCODED_ERR;
636 }
637
638 // Note that the fro_hf_root is active
639 self.clocks.fro_hf_root = Some(Clock {
640 frequency: base_freq,
641 power: firc.power,
642 });
643
644 // Okay! Now we're past that, let's enable all the downstream clocks.
645 let FircConfig {
646 frequency: _,
647 power,
648 fro_hf_enabled,
649 clk_45m_enabled,
650 fro_hf_div,
651 } = firc;
652
653 // When is the FRO enabled?
654 let pow_set = match power {
655 PoweredClock::NormalEnabledDeepSleepDisabled => Fircsten::DisabledInStopModes,
656 PoweredClock::AlwaysEnabled => Fircsten::EnabledInStopModes,
657 };
658
659 // Do we enable the `fro_hf` output?
660 let fro_hf_set = if *fro_hf_enabled {
661 self.clocks.fro_hf = Some(Clock {
662 frequency: base_freq,
663 power: *power,
664 });
665 FircFclkPeriphEn::Enabled
666 } else {
667 FircFclkPeriphEn::Disabled
668 };
669
670 // Do we enable the `clk_45m` output?
671 let clk_45m_set = if *clk_45m_enabled {
672 self.clocks.clk_45m = Some(Clock {
673 frequency: 45_000_000,
674 power: *power,
675 });
676 FircSclkPeriphEn::Enabled
677 } else {
678 FircSclkPeriphEn::Disabled
679 };
680
681 self.scg0.firccsr().modify(|_r, w| {
682 w.fircsten().variant(pow_set);
683 w.firc_fclk_periph_en().variant(fro_hf_set);
684 w.firc_sclk_periph_en().variant(clk_45m_set);
685 w
686 });
687
688 // Do we enable the `fro_hf_div` output?
689 if let Some(d) = fro_hf_div.as_ref() {
690 // We need `fro_hf` to be enabled
691 if !*fro_hf_enabled {
692 return Err(ClockError::BadConfig {
693 clock: "fro_hf_div",
694 reason: "fro_hf not enabled",
695 });
696 }
697
698 // Halt and reset the div; then set our desired div.
699 self.syscon.frohfdiv().write(|w| {
700 w.halt().halt();
701 w.reset().asserted();
702 unsafe { w.div().bits(d.into_bits()) };
703 w
704 });
705 // Then unhalt it, and reset it
706 self.syscon.frohfdiv().write(|w| {
707 w.halt().run();
708 w.reset().released();
709 w
710 });
711
712 // Wait for clock to stabilize
713 while self.syscon.frohfdiv().read().unstab().is_ongoing() {}
714
715 // Store off the clock info
716 self.clocks.fro_hf_div = Some(Clock {
717 frequency: base_freq / d.into_divisor(),
718 power: *power,
719 });
720 }
721
722 Ok(())
723 }
724
725 /// Configure the SIRC/FRO12M clock family
726 fn configure_sirc_clocks(&mut self) -> Result<(), ClockError> {
727 let SircConfig {
728 power,
729 fro_12m_enabled,
730 fro_lf_div,
731 } = &self.config.sirc;
732 let base_freq = 12_000_000;
733
734 // Allow writes
735 self.scg0.sirccsr().modify(|_r, w| w.lk().write_enabled());
736 self.clocks.fro_12m_root = Some(Clock {
737 frequency: base_freq,
738 power: *power,
739 });
740
741 let deep = match power {
742 PoweredClock::NormalEnabledDeepSleepDisabled => Sircsten::Disabled,
743 PoweredClock::AlwaysEnabled => Sircsten::Enabled,
744 };
745 let pclk = if *fro_12m_enabled {
746 self.clocks.fro_12m = Some(Clock {
747 frequency: base_freq,
748 power: *power,
749 });
750 self.clocks.clk_1m = Some(Clock {
751 frequency: base_freq / 12,
752 power: *power,
753 });
754 SircClkPeriphEn::Enabled
755 } else {
756 SircClkPeriphEn::Disabled
757 };
758
759 // Set sleep/peripheral usage
760 self.scg0.sirccsr().modify(|_r, w| {
761 w.sircsten().variant(deep);
762 w.sirc_clk_periph_en().variant(pclk);
763 w
764 });
765
766 while self.scg0.sirccsr().read().sircvld().is_disabled_or_not_valid() {}
767 if self.scg0.sirccsr().read().sircerr().is_error_detected() {
768 return Err(ClockError::BadConfig {
769 clock: "sirc",
770 reason: "error set",
771 });
772 }
773
774 // reset lock
775 self.scg0.sirccsr().modify(|_r, w| w.lk().write_disabled());
776
777 // Do we enable the `fro_lf_div` output?
778 if let Some(d) = fro_lf_div.as_ref() {
779 // We need `fro_lf` to be enabled
780 if !*fro_12m_enabled {
781 return Err(ClockError::BadConfig {
782 clock: "fro_lf_div",
783 reason: "fro_12m not enabled",
784 });
785 }
786
787 // Halt and reset the div; then set our desired div.
788 self.syscon.frolfdiv().write(|w| {
789 w.halt().halt();
790 w.reset().asserted();
791 unsafe { w.div().bits(d.into_bits()) };
792 w
793 });
794 // Then unhalt it, and reset it
795 self.syscon.frolfdiv().modify(|_r, w| {
796 w.halt().run();
797 w.reset().released();
798 w
799 });
800
801 // Wait for clock to stabilize
802 while self.syscon.frolfdiv().read().unstab().is_ongoing() {}
803
804 // Store off the clock info
805 self.clocks.fro_lf_div = Some(Clock {
806 frequency: base_freq / d.into_divisor(),
807 power: *power,
808 });
809 }
810
811 Ok(())
812 }
813
814 /// Configure the FRO16K/clk_16k clock family
815 fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> {
816 let Some(fro16k) = self.config.fro16k.as_ref() else {
817 return Ok(());
818 };
819 // Enable FRO16K oscillator
820 self.vbat0.froctla().modify(|_, w| w.fro_en().set_bit());
821
822 // Lock the control register
823 self.vbat0.frolcka().modify(|_, w| w.lock().set_bit());
824
825 let Fro16KConfig {
826 vsys_domain_active,
827 vdd_core_domain_active,
828 } = fro16k;
829
830 // Enable clock outputs to both VSYS and VDD_CORE domains
831 // Bit 0: clk_16k0 to VSYS domain
832 // Bit 1: clk_16k1 to VDD_CORE domain
833 //
834 // TODO: Define sub-fields for this register with a PAC patch?
835 let mut bits = 0;
836 if *vsys_domain_active {
837 bits |= 0b01;
838 self.clocks.clk_16k_vsys = Some(Clock {
839 frequency: 16_384,
840 power: PoweredClock::AlwaysEnabled,
841 });
842 }
843 if *vdd_core_domain_active {
844 bits |= 0b10;
845 self.clocks.clk_16k_vdd_core = Some(Clock {
846 frequency: 16_384,
847 power: PoweredClock::AlwaysEnabled,
848 });
849 }
850 self.vbat0.froclke().modify(|_r, w| unsafe { w.clke().bits(bits) });
851
852 Ok(())
853 }
854}
855
856//
857// Macros/macro impls
858//
859
860/// This macro is used to implement the [`Gate`] trait for a given peripheral
861/// that is controlled by the MRCC peripheral.
862macro_rules! impl_cc_gate {
863 ($name:ident, $clk_reg:ident, $rst_reg:ident, $field:ident, $config:ty) => {
864 impl Gate for crate::peripherals::$name {
865 type MrccPeriphConfig = $config;
866
867 #[inline]
868 unsafe fn enable_clock() {
869 let mrcc = unsafe { pac::Mrcc0::steal() };
870 mrcc.$clk_reg().modify(|_, w| w.$field().enabled());
871 }
872
873 #[inline]
874 unsafe fn disable_clock() {
875 let mrcc = unsafe { pac::Mrcc0::steal() };
876 mrcc.$clk_reg().modify(|_r, w| w.$field().disabled());
877 }
878
879 #[inline]
880 fn is_clock_enabled() -> bool {
881 let mrcc = unsafe { pac::Mrcc0::steal() };
882 mrcc.$clk_reg().read().$field().is_enabled()
883 }
884
885 #[inline]
886 unsafe fn release_reset() {
887 let mrcc = unsafe { pac::Mrcc0::steal() };
888 mrcc.$rst_reg().modify(|_, w| w.$field().enabled());
889 }
890
891 #[inline]
892 unsafe fn assert_reset() {
893 let mrcc = unsafe { pac::Mrcc0::steal() };
894 mrcc.$rst_reg().modify(|_, w| w.$field().disabled());
895 }
896
897 #[inline]
898 fn is_reset_released() -> bool {
899 let mrcc = unsafe { pac::Mrcc0::steal() };
900 mrcc.$rst_reg().read().$field().is_enabled()
901 }
902 }
903 };
904}
905
906/// This module contains implementations of MRCC APIs, specifically of the [`Gate`] trait,
907/// for various low level peripherals.
908pub(crate) mod gate {
909 #[cfg(not(feature = "time"))]
910 use super::periph_helpers::OsTimerConfig;
911 use super::periph_helpers::{AdcConfig, Lpi2cConfig, LpuartConfig, NoConfig};
912 use super::*;
913
914 // These peripherals have no additional upstream clocks or configuration required
915 // other than enabling through the MRCC gate. Currently, these peripherals will
916 // ALWAYS return `Ok(0)` when calling [`enable_and_reset()`] and/or
917 // [`SPConfHelper::post_enable_config()`].
918 impl_cc_gate!(PORT0, mrcc_glb_cc1, mrcc_glb_rst1, port0, NoConfig);
919 impl_cc_gate!(PORT1, mrcc_glb_cc1, mrcc_glb_rst1, port1, NoConfig);
920 impl_cc_gate!(PORT2, mrcc_glb_cc1, mrcc_glb_rst1, port2, NoConfig);
921 impl_cc_gate!(PORT3, mrcc_glb_cc1, mrcc_glb_rst1, port3, NoConfig);
922 impl_cc_gate!(PORT4, mrcc_glb_cc1, mrcc_glb_rst1, port4, NoConfig);
923
924 impl_cc_gate!(GPIO0, mrcc_glb_cc2, mrcc_glb_rst2, gpio0, NoConfig);
925 impl_cc_gate!(GPIO1, mrcc_glb_cc2, mrcc_glb_rst2, gpio1, NoConfig);
926 impl_cc_gate!(GPIO2, mrcc_glb_cc2, mrcc_glb_rst2, gpio2, NoConfig);
927 impl_cc_gate!(GPIO3, mrcc_glb_cc2, mrcc_glb_rst2, gpio3, NoConfig);
928 impl_cc_gate!(GPIO4, mrcc_glb_cc2, mrcc_glb_rst2, gpio4, NoConfig);
929
930 impl_cc_gate!(CRC0, mrcc_glb_cc0, mrcc_glb_rst0, crc0, NoConfig);
931
932 // These peripherals DO have meaningful configuration, and could fail if the system
933 // clocks do not match their needs.
934 #[cfg(not(feature = "time"))]
935 impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig);
936
937 impl_cc_gate!(LPI2C0, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c0, Lpi2cConfig);
938 impl_cc_gate!(LPI2C1, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c1, Lpi2cConfig);
939 impl_cc_gate!(LPI2C2, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c2, Lpi2cConfig);
940 impl_cc_gate!(LPI2C3, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c3, Lpi2cConfig);
941
942 impl_cc_gate!(LPUART0, mrcc_glb_cc0, mrcc_glb_rst0, lpuart0, LpuartConfig);
943 impl_cc_gate!(LPUART1, mrcc_glb_cc0, mrcc_glb_rst0, lpuart1, LpuartConfig);
944 impl_cc_gate!(LPUART2, mrcc_glb_cc0, mrcc_glb_rst0, lpuart2, LpuartConfig);
945 impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig);
946 impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig);
947 impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig);
948 impl_cc_gate!(ADC0, mrcc_glb_cc1, mrcc_glb_rst1, adc0, AdcConfig);
949 impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig);
950 impl_cc_gate!(ADC2, mrcc_glb_cc1, mrcc_glb_rst1, adc2, AdcConfig);
951 impl_cc_gate!(ADC3, mrcc_glb_cc1, mrcc_glb_rst1, adc3, AdcConfig);
952
953 // DMA0 peripheral - uses NoConfig since it has no selectable clock source
954 impl_cc_gate!(DMA0, mrcc_glb_cc0, mrcc_glb_rst0, dma0, NoConfig);
955}
diff --git a/embassy-mcxa/src/clocks/periph_helpers.rs b/embassy-mcxa/src/clocks/periph_helpers.rs
new file mode 100644
index 000000000..f2f51c60c
--- /dev/null
+++ b/embassy-mcxa/src/clocks/periph_helpers.rs
@@ -0,0 +1,499 @@
1//! Peripheral Helpers
2//!
3//! The purpose of this module is to define the per-peripheral special handling
4//! required from a clocking perspective. Different peripherals have different
5//! selectable source clocks, and some peripherals have additional pre-dividers
6//! that can be used.
7//!
8//! See the docs of [`SPConfHelper`] for more details.
9
10use super::{ClockError, Clocks, PoweredClock};
11use crate::pac;
12
13/// Sealed Peripheral Configuration Helper
14///
15/// NOTE: the name "sealed" doesn't *totally* make sense because its not sealed yet in the
16/// embassy-mcxa project, but it derives from embassy-imxrt where it is. We should
17/// fix the name, or actually do the sealing of peripherals.
18///
19/// This trait serves to act as a per-peripheral customization for clocking behavior.
20///
21/// This trait should be implemented on a configuration type for a given peripheral, and
22/// provide the methods that will be called by the higher level operations like
23/// `embassy_mcxa::clocks::enable_and_reset()`.
24pub trait SPConfHelper {
25 /// This method is called AFTER a given MRCC peripheral has been enabled (e.g. un-gated),
26 /// but BEFORE the peripheral reset line is reset.
27 ///
28 /// This function should check that any relevant upstream clocks are enabled, are in a
29 /// reasonable power state, and that the requested configuration can be made. If any of
30 /// these checks fail, an `Err(ClockError)` should be returned, likely `ClockError::BadConfig`.
31 ///
32 /// This function SHOULD NOT make any changes to the system clock configuration, even
33 /// unsafely, as this should remain static for the duration of the program.
34 ///
35 /// This function WILL be called in a critical section, care should be taken not to delay
36 /// for an unreasonable amount of time.
37 ///
38 /// On success, this function MUST return an `Ok(freq)`, where `freq` is the frequency
39 /// fed into the peripheral, taking into account the selected source clock, as well as
40 /// any pre-divisors.
41 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>;
42}
43
44/// Copy and paste macro that:
45///
46/// * Sets the clocksel mux to `$selvar`
47/// * Resets and halts the div, and applies the calculated div4 bits
48/// * Releases reset + halt
49/// * Waits for the div to stabilize
50/// * Returns `Ok($freq / $conf.div.into_divisor())`
51///
52/// Assumes:
53///
54/// * self is a configuration struct that has a field called `div`, which
55/// is a `Div4`
56///
57/// usage:
58///
59/// ```rust
60/// apply_div4!(self, clksel, clkdiv, variant, freq)
61/// ```
62///
63/// In the future if we make all the clksel+clkdiv pairs into commonly derivedFrom
64/// registers, or if we put some kind of simple trait around those regs, we could
65/// do this with something other than a macro, but for now, this is harm-reduction
66/// to avoid incorrect copy + paste
67macro_rules! apply_div4 {
68 ($conf:ident, $selreg:ident, $divreg:ident, $selvar:ident, $freq:ident) => {{
69 // set clksel
70 $selreg.modify(|_r, w| w.mux().variant($selvar));
71
72 // Set up clkdiv
73 $divreg.modify(|_r, w| {
74 unsafe { w.div().bits($conf.div.into_bits()) }
75 .halt()
76 .asserted()
77 .reset()
78 .asserted()
79 });
80 $divreg.modify(|_r, w| w.halt().deasserted().reset().deasserted());
81
82 while $divreg.read().unstab().is_unstable() {}
83
84 Ok($freq / $conf.div.into_divisor())
85 }};
86}
87
88// config types
89
90/// This type represents a divider in the range 1..=16.
91///
92/// At a hardware level, this is an 8-bit register from 0..=15,
93/// which adds one.
94///
95/// While the *clock* domain seems to use 8-bit dividers, the *peripheral* domain
96/// seems to use 4 bit dividers!
97#[derive(Copy, Clone, Debug, PartialEq, Eq)]
98pub struct Div4(pub(super) u8);
99
100impl Div4 {
101 /// Divide by one, or no division
102 pub const fn no_div() -> Self {
103 Self(0)
104 }
105
106 /// Store a "raw" divisor value that will divide the source by
107 /// `(n + 1)`, e.g. `Div4::from_raw(0)` will divide the source
108 /// by 1, and `Div4::from_raw(15)` will divide the source by
109 /// 16.
110 pub const fn from_raw(n: u8) -> Option<Self> {
111 if n > 0b1111 { None } else { Some(Self(n)) }
112 }
113
114 /// Store a specific divisor value that will divide the source
115 /// by `n`. e.g. `Div4::from_divisor(1)` will divide the source
116 /// by 1, and `Div4::from_divisor(16)` will divide the source
117 /// by 16.
118 ///
119 /// Will return `None` if `n` is not in the range `1..=16`.
120 /// Consider [`Self::from_raw`] for an infallible version.
121 pub const fn from_divisor(n: u8) -> Option<Self> {
122 let Some(n) = n.checked_sub(1) else {
123 return None;
124 };
125 if n > 0b1111 {
126 return None;
127 }
128 Some(Self(n))
129 }
130
131 /// Convert into "raw" bits form
132 #[inline(always)]
133 pub const fn into_bits(self) -> u8 {
134 self.0
135 }
136
137 /// Convert into "divisor" form, as a u32 for convenient frequency math
138 #[inline(always)]
139 pub const fn into_divisor(self) -> u32 {
140 self.0 as u32 + 1
141 }
142}
143
144/// A basic type that always returns an error when `post_enable_config` is called.
145///
146/// Should only be used as a placeholder.
147pub struct UnimplementedConfig;
148
149impl SPConfHelper for UnimplementedConfig {
150 fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> {
151 Err(ClockError::UnimplementedConfig)
152 }
153}
154
155/// A basic type that always returns `Ok(0)` when `post_enable_config` is called.
156///
157/// This should only be used for peripherals that are "ambiently" clocked, like `PORTn`
158/// peripherals, which have no selectable/configurable source clock.
159pub struct NoConfig;
160impl SPConfHelper for NoConfig {
161 fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> {
162 Ok(0)
163 }
164}
165
166//
167// LPI2c
168//
169
170/// Selectable clocks for `Lpi2c` peripherals
171#[derive(Debug, Clone, Copy)]
172pub enum Lpi2cClockSel {
173 /// FRO12M/FRO_LF/SIRC clock source, passed through divider
174 /// "fro_lf_div"
175 FroLfDiv,
176 /// FRO180M/FRO_HF/FIRC clock source, passed through divider
177 /// "fro_hf_div"
178 FroHfDiv,
179 /// SOSC/XTAL/EXTAL clock source
180 ClkIn,
181 /// clk_1m/FRO_LF divided by 12
182 Clk1M,
183 /// Output of PLL1, passed through clock divider,
184 /// "pll1_clk_div", maybe "pll1_lf_div"?
185 Pll1ClkDiv,
186 /// Disabled
187 None,
188}
189
190/// Which instance of the `Lpi2c` is this?
191///
192/// Should not be directly selectable by end-users.
193#[derive(Copy, Clone, Debug, PartialEq, Eq)]
194pub enum Lpi2cInstance {
195 /// Instance 0
196 Lpi2c0,
197 /// Instance 1
198 Lpi2c1,
199 /// Instance 2
200 Lpi2c2,
201 /// Instance 3
202 Lpi2c3,
203}
204
205/// Top level configuration for `Lpi2c` instances.
206pub struct Lpi2cConfig {
207 /// Power state required for this peripheral
208 pub power: PoweredClock,
209 /// Clock source
210 pub source: Lpi2cClockSel,
211 /// Clock divisor
212 pub div: Div4,
213 /// Which instance is this?
214 // NOTE: should not be user settable
215 pub(crate) instance: Lpi2cInstance,
216}
217
218impl SPConfHelper for Lpi2cConfig {
219 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
220 // check that source is suitable
221 let mrcc0 = unsafe { pac::Mrcc0::steal() };
222 use mcxa_pac::mrcc0::mrcc_lpi2c0_clksel::Mux;
223
224 let (clkdiv, clksel) = match self.instance {
225 Lpi2cInstance::Lpi2c0 => (mrcc0.mrcc_lpi2c0_clkdiv(), mrcc0.mrcc_lpi2c0_clksel()),
226 Lpi2cInstance::Lpi2c1 => (mrcc0.mrcc_lpi2c1_clkdiv(), mrcc0.mrcc_lpi2c1_clksel()),
227 Lpi2cInstance::Lpi2c2 => (mrcc0.mrcc_lpi2c2_clkdiv(), mrcc0.mrcc_lpi2c2_clksel()),
228 Lpi2cInstance::Lpi2c3 => (mrcc0.mrcc_lpi2c3_clkdiv(), mrcc0.mrcc_lpi2c3_clksel()),
229 };
230
231 let (freq, variant) = match self.source {
232 Lpi2cClockSel::FroLfDiv => {
233 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
234 (freq, Mux::ClkrootFunc0)
235 }
236 Lpi2cClockSel::FroHfDiv => {
237 let freq = clocks.ensure_fro_hf_div_active(&self.power)?;
238 (freq, Mux::ClkrootFunc2)
239 }
240 Lpi2cClockSel::ClkIn => {
241 let freq = clocks.ensure_clk_in_active(&self.power)?;
242 (freq, Mux::ClkrootFunc3)
243 }
244 Lpi2cClockSel::Clk1M => {
245 let freq = clocks.ensure_clk_1m_active(&self.power)?;
246 (freq, Mux::ClkrootFunc5)
247 }
248 Lpi2cClockSel::Pll1ClkDiv => {
249 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
250 (freq, Mux::ClkrootFunc6)
251 }
252 Lpi2cClockSel::None => unsafe {
253 // no ClkrootFunc7, just write manually for now
254 clksel.write(|w| w.bits(0b111));
255 clkdiv.modify(|_r, w| w.reset().asserted().halt().asserted());
256 return Ok(0);
257 },
258 };
259
260 apply_div4!(self, clksel, clkdiv, variant, freq)
261 }
262}
263
264//
265// LPUart
266//
267
268/// Selectable clocks for Lpuart peripherals
269#[derive(Debug, Clone, Copy)]
270pub enum LpuartClockSel {
271 /// FRO12M/FRO_LF/SIRC clock source, passed through divider
272 /// "fro_lf_div"
273 FroLfDiv,
274 /// FRO180M/FRO_HF/FIRC clock source, passed through divider
275 /// "fro_hf_div"
276 FroHfDiv,
277 /// SOSC/XTAL/EXTAL clock source
278 ClkIn,
279 /// FRO16K/clk_16k source
280 Clk16K,
281 /// clk_1m/FRO_LF divided by 12
282 Clk1M,
283 /// Output of PLL1, passed through clock divider,
284 /// "pll1_clk_div", maybe "pll1_lf_div"?
285 Pll1ClkDiv,
286 /// Disabled
287 None,
288}
289
290/// Which instance of the Lpuart is this?
291///
292/// Should not be directly selectable by end-users.
293#[derive(Copy, Clone, Debug, PartialEq, Eq)]
294pub enum LpuartInstance {
295 /// Instance 0
296 Lpuart0,
297 /// Instance 1
298 Lpuart1,
299 /// Instance 2
300 Lpuart2,
301 /// Instance 3
302 Lpuart3,
303 /// Instance 4
304 Lpuart4,
305 /// Instance 5
306 Lpuart5,
307}
308
309/// Top level configuration for `Lpuart` instances.
310pub struct LpuartConfig {
311 /// Power state required for this peripheral
312 pub power: PoweredClock,
313 /// Clock source
314 pub source: LpuartClockSel,
315 /// Clock divisor
316 pub div: Div4,
317 /// Which instance is this?
318 // NOTE: should not be user settable
319 pub(crate) instance: LpuartInstance,
320}
321
322impl SPConfHelper for LpuartConfig {
323 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
324 // check that source is suitable
325 let mrcc0 = unsafe { pac::Mrcc0::steal() };
326 use mcxa_pac::mrcc0::mrcc_lpuart0_clksel::Mux;
327
328 let (clkdiv, clksel) = match self.instance {
329 LpuartInstance::Lpuart0 => (mrcc0.mrcc_lpuart0_clkdiv(), mrcc0.mrcc_lpuart0_clksel()),
330 LpuartInstance::Lpuart1 => (mrcc0.mrcc_lpuart1_clkdiv(), mrcc0.mrcc_lpuart1_clksel()),
331 LpuartInstance::Lpuart2 => (mrcc0.mrcc_lpuart2_clkdiv(), mrcc0.mrcc_lpuart2_clksel()),
332 LpuartInstance::Lpuart3 => (mrcc0.mrcc_lpuart3_clkdiv(), mrcc0.mrcc_lpuart3_clksel()),
333 LpuartInstance::Lpuart4 => (mrcc0.mrcc_lpuart4_clkdiv(), mrcc0.mrcc_lpuart4_clksel()),
334 LpuartInstance::Lpuart5 => (mrcc0.mrcc_lpuart5_clkdiv(), mrcc0.mrcc_lpuart5_clksel()),
335 };
336
337 let (freq, variant) = match self.source {
338 LpuartClockSel::FroLfDiv => {
339 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
340 (freq, Mux::ClkrootFunc0)
341 }
342 LpuartClockSel::FroHfDiv => {
343 let freq = clocks.ensure_fro_hf_div_active(&self.power)?;
344 (freq, Mux::ClkrootFunc2)
345 }
346 LpuartClockSel::ClkIn => {
347 let freq = clocks.ensure_clk_in_active(&self.power)?;
348 (freq, Mux::ClkrootFunc3)
349 }
350 LpuartClockSel::Clk16K => {
351 let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?;
352 (freq, Mux::ClkrootFunc4)
353 }
354 LpuartClockSel::Clk1M => {
355 let freq = clocks.ensure_clk_1m_active(&self.power)?;
356 (freq, Mux::ClkrootFunc5)
357 }
358 LpuartClockSel::Pll1ClkDiv => {
359 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
360 (freq, Mux::ClkrootFunc6)
361 }
362 LpuartClockSel::None => unsafe {
363 // no ClkrootFunc7, just write manually for now
364 clksel.write(|w| w.bits(0b111));
365 clkdiv.modify(|_r, w| {
366 w.reset().asserted();
367 w.halt().asserted();
368 w
369 });
370 return Ok(0);
371 },
372 };
373
374 // set clksel
375 apply_div4!(self, clksel, clkdiv, variant, freq)
376 }
377}
378
379//
380// OSTimer
381//
382
383/// Selectable clocks for the OSTimer peripheral
384#[derive(Copy, Clone, Debug, PartialEq, Eq)]
385pub enum OstimerClockSel {
386 /// 16k clock, sourced from FRO16K (Vdd Core)
387 Clk16kVddCore,
388 /// 1 MHz Clock sourced from FRO12M
389 Clk1M,
390 /// Disabled
391 None,
392}
393
394/// Top level configuration for the `OSTimer` peripheral
395pub struct OsTimerConfig {
396 /// Power state required for this peripheral
397 pub power: PoweredClock,
398 /// Selected clock source for this peripheral
399 pub source: OstimerClockSel,
400}
401
402impl SPConfHelper for OsTimerConfig {
403 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
404 let mrcc0 = unsafe { pac::Mrcc0::steal() };
405 Ok(match self.source {
406 OstimerClockSel::Clk16kVddCore => {
407 let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?;
408 mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_16k());
409 freq
410 }
411 OstimerClockSel::Clk1M => {
412 let freq = clocks.ensure_clk_1m_active(&self.power)?;
413 mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m());
414 freq
415 }
416 OstimerClockSel::None => {
417 mrcc0.mrcc_ostimer0_clksel().write(|w| unsafe { w.mux().bits(0b11) });
418 0
419 }
420 })
421 }
422}
423
424//
425// Adc
426//
427
428/// Selectable clocks for the ADC peripheral
429#[derive(Copy, Clone, Debug, PartialEq, Eq)]
430#[cfg_attr(feature = "defmt", derive(defmt::Format))]
431pub enum AdcClockSel {
432 /// Divided `fro_lf`/`clk_12m`/FRO12M source
433 FroLfDiv,
434 /// Gated `fro_hf`/`FRO180M` source
435 FroHf,
436 /// External Clock Source
437 ClkIn,
438 /// 1MHz clock sourced by a divided `fro_lf`/`clk_12m`
439 Clk1M,
440 /// Internal PLL output, with configurable divisor
441 Pll1ClkDiv,
442 /// No clock/disabled
443 None,
444}
445
446/// Top level configuration for the ADC peripheral
447pub struct AdcConfig {
448 /// Power state required for this peripheral
449 pub power: PoweredClock,
450 /// Selected clock-source for this peripheral
451 pub source: AdcClockSel,
452 /// Pre-divisor, applied to the upstream clock output
453 pub div: Div4,
454}
455
456impl SPConfHelper for AdcConfig {
457 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
458 use mcxa_pac::mrcc0::mrcc_adc_clksel::Mux;
459 let mrcc0 = unsafe { pac::Mrcc0::steal() };
460 let (freq, variant) = match self.source {
461 AdcClockSel::FroLfDiv => {
462 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
463 (freq, Mux::ClkrootFunc0)
464 }
465 AdcClockSel::FroHf => {
466 let freq = clocks.ensure_fro_hf_active(&self.power)?;
467 (freq, Mux::ClkrootFunc1)
468 }
469 AdcClockSel::ClkIn => {
470 let freq = clocks.ensure_clk_in_active(&self.power)?;
471 (freq, Mux::ClkrootFunc3)
472 }
473 AdcClockSel::Clk1M => {
474 let freq = clocks.ensure_clk_1m_active(&self.power)?;
475 (freq, Mux::ClkrootFunc5)
476 }
477 AdcClockSel::Pll1ClkDiv => {
478 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
479 (freq, Mux::ClkrootFunc6)
480 }
481 AdcClockSel::None => {
482 mrcc0.mrcc_adc_clksel().write(|w| unsafe {
483 // no ClkrootFunc7, just write manually for now
484 w.mux().bits(0b111)
485 });
486 mrcc0.mrcc_adc_clkdiv().modify(|_r, w| {
487 w.reset().asserted();
488 w.halt().asserted();
489 w
490 });
491 return Ok(0);
492 }
493 };
494 let clksel = mrcc0.mrcc_adc_clksel();
495 let clkdiv = mrcc0.mrcc_adc_clkdiv();
496
497 apply_div4!(self, clksel, clkdiv, variant, freq)
498 }
499}
diff --git a/embassy-mcxa/src/config.rs b/embassy-mcxa/src/config.rs
new file mode 100644
index 000000000..8d52a1d44
--- /dev/null
+++ b/embassy-mcxa/src/config.rs
@@ -0,0 +1,27 @@
1// HAL configuration (minimal), mirroring embassy-imxrt style
2
3use crate::clocks::config::ClocksConfig;
4use crate::interrupt::Priority;
5
6#[non_exhaustive]
7pub struct Config {
8 #[cfg(feature = "time")]
9 pub time_interrupt_priority: Priority,
10 pub rtc_interrupt_priority: Priority,
11 pub adc_interrupt_priority: Priority,
12 pub gpio_interrupt_priority: Priority,
13 pub clock_cfg: ClocksConfig,
14}
15
16impl Default for Config {
17 fn default() -> Self {
18 Self {
19 #[cfg(feature = "time")]
20 time_interrupt_priority: Priority::from(0),
21 rtc_interrupt_priority: Priority::from(0),
22 adc_interrupt_priority: Priority::from(0),
23 gpio_interrupt_priority: Priority::from(0),
24 clock_cfg: ClocksConfig::default(),
25 }
26 }
27}
diff --git a/embassy-mcxa/src/crc.rs b/embassy-mcxa/src/crc.rs
new file mode 100644
index 000000000..753a59122
--- /dev/null
+++ b/embassy-mcxa/src/crc.rs
@@ -0,0 +1,758 @@
1//! Cyclic Redundancy Check (CRC)
2
3use core::marker::PhantomData;
4
5use embassy_hal_internal::Peri;
6use mcxa_pac::crc0::ctrl::{Fxor, Tcrc, Tot, Totr};
7
8use crate::clocks::enable_and_reset;
9use crate::clocks::periph_helpers::NoConfig;
10use crate::peripherals::CRC0;
11
12/// CRC driver.
13pub struct Crc<'d, M> {
14 _peri: Peri<'d, CRC0>,
15 _phantom: PhantomData<M>,
16}
17
18impl<'d, M: Mode> Crc<'d, M> {
19 fn new_inner(_peri: Peri<'d, CRC0>) -> Self {
20 _ = unsafe { enable_and_reset::<CRC0>(&NoConfig) };
21
22 Crc {
23 _peri,
24 _phantom: PhantomData,
25 }
26 }
27
28 // Configure the underlying peripheral according to the reference manual.
29 fn configure(config: Config, width: Tcrc) {
30 Self::regs().ctrl().modify(|_, w| {
31 w.fxor()
32 .variant(config.complement_out.into())
33 .totr()
34 .variant(config.reflect_out.into())
35 .tot()
36 .variant(config.reflect_in.into())
37 .was()
38 .data()
39 .tcrc()
40 .variant(width)
41 });
42
43 Self::regs().gpoly32().write(|w| unsafe { w.bits(config.polynomial) });
44
45 Self::regs().ctrl().modify(|_, w| w.was().seed());
46 Self::regs().data32().write(|w| unsafe { w.bits(config.seed) });
47 Self::regs().ctrl().modify(|_, w| w.was().data());
48 }
49
50 fn regs() -> &'static crate::pac::crc0::RegisterBlock {
51 unsafe { &*crate::pac::Crc0::ptr() }
52 }
53
54 /// Read the computed CRC value
55 fn finalize_inner<W: Word>(self) -> W {
56 // Reference manual states:
57 //
58 // "After writing all the data, you must wait for at least two
59 // clock cycles to read the data from CRC Data (DATA)
60 // register."
61 cortex_m::asm::delay(2);
62 W::read(Self::regs())
63 }
64
65 fn feed_word<W: Word>(&mut self, word: W) {
66 W::write(Self::regs(), word);
67 }
68
69 /// Feeds a slice of `Word`s into the CRC peripheral. Returns the computed
70 /// checksum.
71 ///
72 /// The input is strided efficiently into as many `u32`s as possible,
73 /// falling back to smaller writes for the remainder.
74 fn feed_inner<W: Word>(&mut self, data: &[W]) {
75 let (prefix, aligned, suffix) = unsafe { data.align_to::<u32>() };
76
77 for w in prefix {
78 self.feed_word(*w);
79 }
80
81 for w in aligned {
82 self.feed_word(*w);
83 }
84
85 for w in suffix {
86 self.feed_word(*w);
87 }
88 }
89}
90
91impl<'d> Crc<'d, Crc16> {
92 /// Instantiates a new CRC peripheral driver in 16-bit mode
93 pub fn new_crc16(peri: Peri<'d, CRC0>, config: Config) -> Self {
94 let inst = Self::new_inner(peri);
95 Self::configure(config, Tcrc::B16);
96 inst
97 }
98
99 /// Instantiates a new CRC peripheral driver for the given `Algorithm16`.
100 pub fn new_algorithm16(peri: Peri<'d, CRC0>, algorithm: Algorithm16) -> Self {
101 Self::new_crc16(peri, algorithm.into_config())
102 }
103
104 /// Instantiates a new CRC peripheral for the `A` algorithm.
105 pub fn new_a(peri: Peri<'d, CRC0>) -> Self {
106 Self::new_algorithm16(peri, Algorithm16::A)
107 }
108
109 /// Instantiates a new CRC peripheral for the `AugCcitt` algorithm.
110 pub fn new_aug_ccitt(peri: Peri<'d, CRC0>) -> Self {
111 Self::new_algorithm16(peri, Algorithm16::AugCcitt)
112 }
113
114 /// Instantiates a new CRC peripheral for the `Arc` algorithm.
115 pub fn new_arc(peri: Peri<'d, CRC0>) -> Self {
116 Self::new_algorithm16(peri, Algorithm16::Arc)
117 }
118
119 /// Instantiates a new CRC peripheral for the `Buypass` algorithm.
120 pub fn new_buypass(peri: Peri<'d, CRC0>) -> Self {
121 Self::new_algorithm16(peri, Algorithm16::Buypass)
122 }
123
124 /// Instantiates a new CRC peripheral for the `CcittFalse` algorithm.
125 pub fn new_ccitt_false(peri: Peri<'d, CRC0>) -> Self {
126 Self::new_algorithm16(peri, Algorithm16::CcittFalse)
127 }
128
129 /// Instantiates a new CRC peripheral for the `CcittZero` algorithm.
130 pub fn new_ccitt_zero(peri: Peri<'d, CRC0>) -> Self {
131 Self::new_algorithm16(peri, Algorithm16::CcittZero)
132 }
133
134 /// Instantiates a new CRC peripheral for the `Cdma2000` algorithm.
135 pub fn new_cdma_2000(peri: Peri<'d, CRC0>) -> Self {
136 Self::new_algorithm16(peri, Algorithm16::Cdma2000)
137 }
138
139 /// Instantiates a new CRC peripheral for the `Dds110` algorithm.
140 pub fn new_dds_110(peri: Peri<'d, CRC0>) -> Self {
141 Self::new_algorithm16(peri, Algorithm16::Dds110)
142 }
143
144 /// Instantiates a new CRC peripheral for the `DectX` algorithm.
145 pub fn new_dect_x(peri: Peri<'d, CRC0>) -> Self {
146 Self::new_algorithm16(peri, Algorithm16::DectX)
147 }
148
149 /// Instantiates a new CRC peripheral for the `Dnp` algorithm.
150 pub fn new_dnp(peri: Peri<'d, CRC0>) -> Self {
151 Self::new_algorithm16(peri, Algorithm16::Dnp)
152 }
153
154 /// Instantiates a new CRC peripheral for the `En13757` algorithm.
155 pub fn new_en13757(peri: Peri<'d, CRC0>) -> Self {
156 Self::new_algorithm16(peri, Algorithm16::En13757)
157 }
158
159 /// Instantiates a new CRC peripheral for the `Genibus` algorithm.
160 pub fn new_genibus(peri: Peri<'d, CRC0>) -> Self {
161 Self::new_algorithm16(peri, Algorithm16::Genibus)
162 }
163
164 /// Instantiates a new CRC peripheral for the `Kermit` algorithm.
165 pub fn new_kermit(peri: Peri<'d, CRC0>) -> Self {
166 Self::new_algorithm16(peri, Algorithm16::Kermit)
167 }
168
169 /// Instantiates a new CRC peripheral for the `Maxim` algorithm.
170 pub fn new_maxim(peri: Peri<'d, CRC0>) -> Self {
171 Self::new_algorithm16(peri, Algorithm16::Maxim)
172 }
173
174 /// Instantiates a new CRC peripheral for the `Mcrf4xx` algorithm.
175 pub fn new_mcrf4xx(peri: Peri<'d, CRC0>) -> Self {
176 Self::new_algorithm16(peri, Algorithm16::Mcrf4xx)
177 }
178
179 /// Instantiates a new CRC peripheral for the `Modbus` algorithm.
180 pub fn new_modbus(peri: Peri<'d, CRC0>) -> Self {
181 Self::new_algorithm16(peri, Algorithm16::Modbus)
182 }
183
184 /// Instantiates a new CRC peripheral for the `Riello` algorithm.
185 pub fn new_riello(peri: Peri<'d, CRC0>) -> Self {
186 Self::new_algorithm16(peri, Algorithm16::Riello)
187 }
188
189 /// Instantiates a new CRC peripheral for the `T10Dif` algorithm.
190 pub fn new_t10_dif(peri: Peri<'d, CRC0>) -> Self {
191 Self::new_algorithm16(peri, Algorithm16::T10Dif)
192 }
193
194 /// Instantiates a new CRC peripheral for the `Teledisk` algorithm.
195 pub fn new_teledisk(peri: Peri<'d, CRC0>) -> Self {
196 Self::new_algorithm16(peri, Algorithm16::Teledisk)
197 }
198
199 /// Instantiates a new CRC peripheral for the `Tms37157` algorithm.
200 pub fn new_tms_37157(peri: Peri<'d, CRC0>) -> Self {
201 Self::new_algorithm16(peri, Algorithm16::Tms37157)
202 }
203
204 /// Instantiates a new CRC peripheral for the `Usb` algorithm.
205 pub fn new_usb(peri: Peri<'d, CRC0>) -> Self {
206 Self::new_algorithm16(peri, Algorithm16::Usb)
207 }
208
209 /// Instantiates a new CRC peripheral for the `X25` algorithm.
210 pub fn new_x25(peri: Peri<'d, CRC0>) -> Self {
211 Self::new_algorithm16(peri, Algorithm16::X25)
212 }
213
214 /// Instantiates a new CRC peripheral for the `Xmodem` algorithm.
215 pub fn new_xmodem(peri: Peri<'d, CRC0>) -> Self {
216 Self::new_algorithm16(peri, Algorithm16::Xmodem)
217 }
218
219 /// Feeds a slice of `Word`s into the CRC peripheral.
220 ///
221 /// The input is strided efficiently into as many `u32`s as possible,
222 /// falling back to smaller writes for the remainder.
223 pub fn feed<W: Word>(&mut self, data: &[W]) {
224 self.feed_inner(data);
225 }
226
227 /// Finalizes the CRC calculation and reads the resulting CRC from the
228 /// hardware consuming `self`.
229 pub fn finalize(self) -> u16 {
230 self.finalize_inner()
231 }
232}
233
234impl<'d> Crc<'d, Crc32> {
235 /// Instantiates a new CRC peripheral driver in 32-bit mode
236 pub fn new_crc32(peri: Peri<'d, CRC0>, config: Config) -> Self {
237 let inst = Self::new_inner(peri);
238 Self::configure(config, Tcrc::B32);
239 inst
240 }
241
242 /// Instantiates a new CRC peripheral driver for the given `Algorithm32`.
243 pub fn new_algorithm32(peri: Peri<'d, CRC0>, algorithm: Algorithm32) -> Self {
244 Self::new_crc32(peri, algorithm.into_config())
245 }
246
247 /// Instantiates a new CRC peripheral for the `Bzip2` algorithm.
248 pub fn new_bzip2(peri: Peri<'d, CRC0>) -> Self {
249 Self::new_algorithm32(peri, Algorithm32::Bzip2)
250 }
251
252 /// Instantiates a new CRC peripheral for the `C` algorithm.
253 pub fn new_c(peri: Peri<'d, CRC0>) -> Self {
254 Self::new_algorithm32(peri, Algorithm32::C)
255 }
256
257 /// Instantiates a new CRC peripheral for the `D` algorithm.
258 pub fn new_d(peri: Peri<'d, CRC0>) -> Self {
259 Self::new_algorithm32(peri, Algorithm32::D)
260 }
261
262 /// Instantiates a new CRC peripheral for the `IsoHdlc` algorithm.
263 pub fn new_iso_hdlc(peri: Peri<'d, CRC0>) -> Self {
264 Self::new_algorithm32(peri, Algorithm32::IsoHdlc)
265 }
266
267 /// Instantiates a new CRC peripheral for the `JamCrc` algorithm.
268 pub fn new_jam_crc(peri: Peri<'d, CRC0>) -> Self {
269 Self::new_algorithm32(peri, Algorithm32::JamCrc)
270 }
271
272 /// Instantiates a new CRC peripheral for the `Mpeg2` algorithm.
273 pub fn new_mpeg2(peri: Peri<'d, CRC0>) -> Self {
274 Self::new_algorithm32(peri, Algorithm32::Mpeg2)
275 }
276
277 /// Instantiates a new CRC peripheral for the `Posix` algorithm.
278 pub fn new_posix(peri: Peri<'d, CRC0>) -> Self {
279 Self::new_algorithm32(peri, Algorithm32::Posix)
280 }
281
282 /// Instantiates a new CRC peripheral for the `Q` algorithm.
283 pub fn new_q(peri: Peri<'d, CRC0>) -> Self {
284 Self::new_algorithm32(peri, Algorithm32::Q)
285 }
286
287 /// Instantiates a new CRC peripheral for the `Xfer` algorithm.
288 pub fn new_xfer(peri: Peri<'d, CRC0>) -> Self {
289 Self::new_algorithm32(peri, Algorithm32::Xfer)
290 }
291
292 /// Feeds a slice of `Word`s into the CRC peripheral.
293 ///
294 /// The input is strided efficiently into as many `u32`s as possible,
295 /// falling back to smaller writes for the remainder.
296 pub fn feed<W: Word>(&mut self, data: &[W]) {
297 self.feed_inner(data);
298 }
299
300 /// Finalizes the CRC calculation and reads the resulting CRC from the
301 /// hardware consuming `self`.
302 pub fn finalize(self) -> u32 {
303 self.finalize_inner()
304 }
305}
306
307mod sealed {
308 pub trait SealedMode {}
309
310 pub trait SealedWord: Copy {
311 fn write(regs: &'static crate::pac::crc0::RegisterBlock, word: Self);
312 fn read(regs: &'static crate::pac::crc0::RegisterBlock) -> Self;
313 }
314}
315
316/// Mode of operation: 32 or 16-bit CRC.
317#[allow(private_bounds)]
318pub trait Mode: sealed::SealedMode {}
319
320/// 16-bit CRC.
321pub struct Crc16;
322impl sealed::SealedMode for Crc16 {}
323impl Mode for Crc16 {}
324
325/// 32-bit CRC.
326pub struct Crc32;
327impl sealed::SealedMode for Crc32 {}
328impl Mode for Crc32 {}
329
330/// Word size for the CRC.
331#[allow(private_bounds)]
332pub trait Word: sealed::SealedWord {}
333
334macro_rules! impl_word {
335 ($t:ty, $width:literal, $write:expr, $read:expr) => {
336 impl sealed::SealedWord for $t {
337 #[inline]
338 fn write(regs: &'static crate::pac::crc0::RegisterBlock, word: Self) {
339 $write(regs, word)
340 }
341
342 #[inline]
343 fn read(regs: &'static crate::pac::crc0::RegisterBlock) -> Self {
344 $read(regs)
345 }
346 }
347
348 impl Word for $t {}
349 };
350}
351
352impl_word!(u8, 8, write_u8, read_u8);
353impl_word!(u16, 16, write_u16, read_u16);
354impl_word!(u32, 32, write_u32, read_u32);
355
356fn write_u8(regs: &'static crate::pac::crc0::RegisterBlock, word: u8) {
357 regs.data8().write(|w| unsafe { w.bits(word) });
358}
359
360fn read_u8(regs: &'static crate::pac::crc0::RegisterBlock) -> u8 {
361 regs.data8().read().bits()
362}
363
364fn write_u16(regs: &'static crate::pac::crc0::RegisterBlock, word: u16) {
365 regs.data16().write(|w| unsafe { w.bits(word) });
366}
367
368fn read_u16(regs: &'static crate::pac::crc0::RegisterBlock) -> u16 {
369 let ctrl = regs.ctrl().read();
370
371 // if transposition is enabled, result sits in the upper 16 bits
372 if ctrl.totr().is_byts_trnps() || ctrl.totr().is_byts_bts_trnps() {
373 (regs.data32().read().bits() >> 16) as u16
374 } else {
375 regs.data16().read().bits()
376 }
377}
378
379fn write_u32(regs: &'static crate::pac::crc0::RegisterBlock, word: u32) {
380 regs.data32().write(|w| unsafe { w.bits(word) });
381}
382
383fn read_u32(regs: &'static crate::pac::crc0::RegisterBlock) -> u32 {
384 regs.data32().read().bits()
385}
386
387/// CRC configuration.
388#[derive(Copy, Clone, Debug)]
389#[non_exhaustive]
390pub struct Config {
391 /// The CRC polynomial to be used.
392 pub polynomial: u32,
393
394 /// Reflect bit order of input?
395 pub reflect_in: Reflect,
396
397 /// Reflect CRC bit order?
398 pub reflect_out: Reflect,
399
400 /// 1's complement CRC?
401 pub complement_out: Complement,
402
403 /// CRC Seed
404 pub seed: u32,
405}
406
407impl Config {
408 /// Create a new CRC config.
409 #[must_use]
410 pub fn new(
411 polynomial: u32,
412 reflect_in: Reflect,
413 reflect_out: Reflect,
414 complement_out: Complement,
415 seed: u32,
416 ) -> Self {
417 Config {
418 polynomial,
419 reflect_in,
420 reflect_out,
421 complement_out,
422 seed,
423 }
424 }
425}
426
427impl Default for Config {
428 fn default() -> Self {
429 Self {
430 polynomial: 0,
431 reflect_in: Reflect::No,
432 reflect_out: Reflect::No,
433 complement_out: Complement::No,
434 seed: 0xffff_ffff,
435 }
436 }
437}
438
439/// Supported standard CRC16 algorithms.
440#[derive(Copy, Clone, Debug)]
441pub enum Algorithm16 {
442 A,
443 Arc,
444 AugCcitt,
445 Buypass,
446 CcittFalse,
447 CcittZero,
448 Cdma2000,
449 Dds110,
450 DectX,
451 Dnp,
452 En13757,
453 Genibus,
454 Kermit,
455 Maxim,
456 Mcrf4xx,
457 Modbus,
458 Riello,
459 T10Dif,
460 Teledisk,
461 Tms37157,
462 Usb,
463 X25,
464 Xmodem,
465}
466
467impl Algorithm16 {
468 fn into_config(self) -> Config {
469 match self {
470 Algorithm16::A => Config {
471 polynomial: 0x1021,
472 reflect_in: Reflect::Yes,
473 reflect_out: Reflect::Yes,
474 complement_out: Complement::No,
475 seed: 0xc6c6,
476 },
477 Algorithm16::Arc => Config {
478 polynomial: 0x8005,
479 reflect_in: Reflect::Yes,
480 reflect_out: Reflect::Yes,
481 complement_out: Complement::No,
482 seed: 0,
483 },
484 Algorithm16::AugCcitt => Config {
485 polynomial: 0x1021,
486 reflect_in: Reflect::No,
487 reflect_out: Reflect::No,
488 complement_out: Complement::No,
489 seed: 0x1d0f,
490 },
491 Algorithm16::Buypass => Config {
492 polynomial: 0x8005,
493 reflect_in: Reflect::No,
494 reflect_out: Reflect::No,
495 complement_out: Complement::No,
496 seed: 0,
497 },
498 Algorithm16::CcittFalse => Config {
499 polynomial: 0x1021,
500 reflect_in: Reflect::No,
501 reflect_out: Reflect::No,
502 complement_out: Complement::No,
503 seed: 0xffff,
504 },
505 Algorithm16::CcittZero => Config {
506 polynomial: 0x1021,
507 reflect_in: Reflect::No,
508 reflect_out: Reflect::No,
509 complement_out: Complement::No,
510 seed: 0,
511 },
512 Algorithm16::Cdma2000 => Config {
513 polynomial: 0xc867,
514 reflect_in: Reflect::No,
515 reflect_out: Reflect::No,
516 complement_out: Complement::No,
517 seed: 0xffff,
518 },
519 Algorithm16::Dds110 => Config {
520 polynomial: 0x8005,
521 reflect_in: Reflect::No,
522 reflect_out: Reflect::No,
523 complement_out: Complement::No,
524 seed: 0x800d,
525 },
526 Algorithm16::DectX => Config {
527 polynomial: 0x0589,
528 reflect_in: Reflect::No,
529 reflect_out: Reflect::No,
530 complement_out: Complement::No,
531 seed: 0,
532 },
533 Algorithm16::Dnp => Config {
534 polynomial: 0x3d65,
535 reflect_in: Reflect::Yes,
536 reflect_out: Reflect::Yes,
537 complement_out: Complement::Yes,
538 seed: 0,
539 },
540 Algorithm16::En13757 => Config {
541 polynomial: 0x3d65,
542 reflect_in: Reflect::No,
543 reflect_out: Reflect::No,
544 complement_out: Complement::Yes,
545 seed: 0,
546 },
547 Algorithm16::Genibus => Config {
548 polynomial: 0x1021,
549 reflect_in: Reflect::No,
550 reflect_out: Reflect::No,
551 complement_out: Complement::Yes,
552 seed: 0xffff,
553 },
554 Algorithm16::Kermit => Config {
555 polynomial: 0x1021,
556 reflect_in: Reflect::Yes,
557 reflect_out: Reflect::Yes,
558 complement_out: Complement::No,
559 seed: 0,
560 },
561 Algorithm16::Maxim => Config {
562 polynomial: 0x8005,
563 reflect_in: Reflect::Yes,
564 reflect_out: Reflect::Yes,
565 complement_out: Complement::Yes,
566 seed: 0,
567 },
568 Algorithm16::Mcrf4xx => Config {
569 polynomial: 0x1021,
570 reflect_in: Reflect::Yes,
571 reflect_out: Reflect::Yes,
572 complement_out: Complement::No,
573 seed: 0xffff,
574 },
575 Algorithm16::Modbus => Config {
576 polynomial: 0x8005,
577 reflect_in: Reflect::Yes,
578 reflect_out: Reflect::Yes,
579 complement_out: Complement::No,
580 seed: 0xffff,
581 },
582 Algorithm16::Riello => Config {
583 polynomial: 0x1021,
584 reflect_in: Reflect::Yes,
585 reflect_out: Reflect::Yes,
586 complement_out: Complement::No,
587 seed: 0xb2aa,
588 },
589 Algorithm16::T10Dif => Config {
590 polynomial: 0x8bb7,
591 reflect_in: Reflect::No,
592 reflect_out: Reflect::No,
593 complement_out: Complement::No,
594 seed: 0,
595 },
596 Algorithm16::Teledisk => Config {
597 polynomial: 0xa097,
598 reflect_in: Reflect::No,
599 reflect_out: Reflect::No,
600 complement_out: Complement::No,
601 seed: 0,
602 },
603 Algorithm16::Tms37157 => Config {
604 polynomial: 0x1021,
605 reflect_in: Reflect::Yes,
606 reflect_out: Reflect::Yes,
607 complement_out: Complement::No,
608 seed: 0x89ec,
609 },
610 Algorithm16::Usb => Config {
611 polynomial: 0x8005,
612 reflect_in: Reflect::Yes,
613 reflect_out: Reflect::Yes,
614 complement_out: Complement::No,
615 seed: 0xffff,
616 },
617 Algorithm16::X25 => Config {
618 polynomial: 0x1021,
619 reflect_in: Reflect::Yes,
620 reflect_out: Reflect::Yes,
621 complement_out: Complement::Yes,
622 seed: 0xffff,
623 },
624 Algorithm16::Xmodem => Config {
625 polynomial: 0x1021,
626 reflect_in: Reflect::No,
627 reflect_out: Reflect::No,
628 complement_out: Complement::No,
629 seed: 0,
630 },
631 }
632 }
633}
634
635/// Supported standard CRC32 algorithms.
636#[derive(Copy, Clone, Debug)]
637pub enum Algorithm32 {
638 Bzip2,
639 C,
640 D,
641 IsoHdlc,
642 JamCrc,
643 Mpeg2,
644 Posix,
645 Q,
646 Xfer,
647}
648
649impl Algorithm32 {
650 fn into_config(self) -> Config {
651 match self {
652 Algorithm32::Bzip2 => Config {
653 polynomial: 0x04c1_1db7,
654 reflect_in: Reflect::No,
655 reflect_out: Reflect::No,
656 complement_out: Complement::Yes,
657 seed: 0xffff_ffff,
658 },
659 Algorithm32::C => Config {
660 polynomial: 0x1edc_6f41,
661 reflect_in: Reflect::Yes,
662 reflect_out: Reflect::Yes,
663 complement_out: Complement::Yes,
664 seed: 0xffff_ffff,
665 },
666 Algorithm32::D => Config {
667 polynomial: 0xa833_982b,
668 reflect_in: Reflect::Yes,
669 reflect_out: Reflect::Yes,
670 complement_out: Complement::Yes,
671 seed: 0xffff_ffff,
672 },
673 Algorithm32::IsoHdlc => Config {
674 polynomial: 0x04c1_1db7,
675 reflect_in: Reflect::Yes,
676 reflect_out: Reflect::Yes,
677 complement_out: Complement::Yes,
678 seed: 0xffff_ffff,
679 },
680 Algorithm32::JamCrc => Config {
681 polynomial: 0x04c1_1db7,
682 reflect_in: Reflect::Yes,
683 reflect_out: Reflect::Yes,
684 complement_out: Complement::No,
685 seed: 0xffff_ffff,
686 },
687 Algorithm32::Mpeg2 => Config {
688 polynomial: 0x04c1_1db7,
689 reflect_in: Reflect::No,
690 reflect_out: Reflect::No,
691 complement_out: Complement::No,
692 seed: 0xffff_ffff,
693 },
694 Algorithm32::Posix => Config {
695 polynomial: 0x04c1_1db7,
696 reflect_in: Reflect::No,
697 reflect_out: Reflect::No,
698 complement_out: Complement::Yes,
699 seed: 0,
700 },
701 Algorithm32::Q => Config {
702 polynomial: 0x8141_41ab,
703 reflect_in: Reflect::No,
704 reflect_out: Reflect::No,
705 complement_out: Complement::No,
706 seed: 0,
707 },
708 Algorithm32::Xfer => Config {
709 polynomial: 0x0000_00af,
710 reflect_in: Reflect::No,
711 reflect_out: Reflect::No,
712 complement_out: Complement::No,
713 seed: 0,
714 },
715 }
716 }
717}
718
719/// Reflect bit order.
720#[derive(Copy, Clone, Debug)]
721pub enum Reflect {
722 No,
723 Yes,
724}
725
726impl From<Reflect> for Tot {
727 fn from(value: Reflect) -> Tot {
728 match value {
729 Reflect::No => Tot::BytsTrnps,
730 Reflect::Yes => Tot::BytsBtsTrnps,
731 }
732 }
733}
734
735impl From<Reflect> for Totr {
736 fn from(value: Reflect) -> Totr {
737 match value {
738 Reflect::No => Totr::Notrnps,
739 Reflect::Yes => Totr::BytsBtsTrnps,
740 }
741 }
742}
743
744/// 1's complement output.
745#[derive(Copy, Clone, Debug)]
746pub enum Complement {
747 No,
748 Yes,
749}
750
751impl From<Complement> for Fxor {
752 fn from(value: Complement) -> Fxor {
753 match value {
754 Complement::No => Fxor::Noxor,
755 Complement::Yes => Fxor::Invert,
756 }
757 }
758}
diff --git a/embassy-mcxa/src/dma.rs b/embassy-mcxa/src/dma.rs
new file mode 100644
index 000000000..8d519d99b
--- /dev/null
+++ b/embassy-mcxa/src/dma.rs
@@ -0,0 +1,2602 @@
1//! DMA driver for MCXA276.
2//!
3//! This module provides a typed channel abstraction over the EDMA_0_TCD0 array
4//! and helpers for configuring the channel MUX. The driver supports both
5//! low-level TCD configuration and higher-level async transfer APIs.
6//!
7//! # Architecture
8//!
9//! The MCXA276 has 8 DMA channels (0-7), each with its own interrupt vector.
10//! Each channel has a Transfer Control Descriptor (TCD) that defines the
11//! transfer parameters.
12//!
13//! # Choosing the Right API
14//!
15//! This module provides several API levels to match different use cases:
16//!
17//! ## High-Level Async API (Recommended for Most Users)
18//!
19//! Use the async methods when you want simple, safe DMA transfers:
20//!
21//! | Method | Description |
22//! |--------|-------------|
23//! | [`DmaChannel::mem_to_mem()`] | Memory-to-memory copy |
24//! | [`DmaChannel::memset()`] | Fill memory with a pattern |
25//! | [`DmaChannel::write()`] | Memory-to-peripheral (TX) |
26//! | [`DmaChannel::read()`] | Peripheral-to-memory (RX) |
27//!
28//! These return a [`Transfer`] future that can be `.await`ed:
29//!
30//! ```no_run
31//! # use embassy_mcxa::dma::{DmaChannel, TransferOptions};
32//! # let dma_ch = DmaChannel::new(p.DMA_CH0);
33//! # let src = [0u32; 4];
34//! # let mut dst = [0u32; 4];
35//! // Simple memory-to-memory transfer
36//! unsafe {
37//! dma_ch.mem_to_mem(&src, &mut dst, TransferOptions::default()).await;
38//! }
39//! ```
40//!
41//! ## Setup Methods (For Peripheral Drivers)
42//!
43//! Use setup methods when you need manual lifecycle control:
44//!
45//! | Method | Description |
46//! |--------|-------------|
47//! | [`DmaChannel::setup_write()`] | Configure TX without starting |
48//! | [`DmaChannel::setup_read()`] | Configure RX without starting |
49//!
50//! These configure the TCD but don't start the transfer. You control:
51//! 1. When to call [`DmaChannel::enable_request()`]
52//! 2. How to detect completion (polling or interrupts)
53//! 3. When to clean up with [`DmaChannel::clear_done()`]
54//!
55//! ## Circular/Ring Buffer API (For Continuous Reception)
56//!
57//! Use [`DmaChannel::setup_circular_read()`] for continuous data reception:
58//!
59//! ```no_run
60//! # use embassy_mcxa::dma::DmaChannel;
61//! # let dma_ch = DmaChannel::new(p.DMA_CH0);
62//! # let uart_rx_addr = 0x4000_0000 as *const u8;
63//! static mut RX_BUF: [u8; 64] = [0; 64];
64//!
65//! let ring_buf = unsafe {
66//! dma_ch.setup_circular_read(uart_rx_addr, &mut RX_BUF)
67//! };
68//!
69//! // Read data as it arrives
70//! let mut buf = [0u8; 16];
71//! let n = ring_buf.read(&mut buf).await.unwrap();
72//! ```
73//!
74//! ## Scatter-Gather Builder (For Chained Transfers)
75//!
76//! Use [`ScatterGatherBuilder`] for complex multi-segment transfers:
77//!
78//! ```no_run
79//! # use embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder};
80//! # let dma_ch = DmaChannel::new(p.DMA_CH0);
81//! let mut builder = ScatterGatherBuilder::<u32>::new();
82//! builder.add_transfer(&src1, &mut dst1);
83//! builder.add_transfer(&src2, &mut dst2);
84//!
85//! let transfer = unsafe { builder.build(&dma_ch).unwrap() };
86//! transfer.await;
87//! ```
88//!
89//! ## Direct TCD Access (For Advanced Use Cases)
90//!
91//! For full control, use the channel's `tcd()` method to access TCD registers directly.
92//! See the `dma_*` examples for patterns.
93//!
94//! # Example
95//!
96//! ```no_run
97//! use embassy_mcxa::dma::{DmaChannel, TransferOptions, Direction};
98//!
99//! let dma_ch = DmaChannel::new(p.DMA_CH0);
100//! // Configure and trigger a transfer...
101//! ```
102
103use core::future::Future;
104use core::marker::PhantomData;
105use core::pin::Pin;
106use core::ptr::NonNull;
107use core::sync::atomic::{AtomicUsize, Ordering, fence};
108use core::task::{Context, Poll};
109
110use embassy_hal_internal::PeripheralType;
111use embassy_sync::waitqueue::AtomicWaker;
112
113use crate::clocks::Gate;
114use crate::pac;
115use crate::pac::Interrupt;
116use crate::peripherals::DMA0;
117
118/// Initialize DMA controller (clock enabled, reset released, controller configured).
119///
120/// This function is intended to be called ONCE during HAL initialization (`hal::init()`).
121///
122/// The function enables the DMA0 clock, releases reset, and configures the controller
123/// for normal operation with round-robin arbitration.
124pub(crate) fn init() {
125 unsafe {
126 // Enable DMA0 clock and release reset
127 DMA0::enable_clock();
128 DMA0::release_reset();
129
130 // Configure DMA controller
131 let dma = &(*pac::Dma0::ptr());
132 dma.mp_csr().modify(|_, w| {
133 w.edbg()
134 .enable()
135 .erca()
136 .enable()
137 .halt()
138 .normal_operation()
139 .gclc()
140 .available()
141 .gmrc()
142 .available()
143 });
144 }
145}
146
147// ============================================================================
148// Phase 1: Foundation Types (Embassy-aligned)
149// ============================================================================
150
151/// DMA transfer direction.
152#[derive(Debug, Copy, Clone, PartialEq, Eq)]
153#[cfg_attr(feature = "defmt", derive(defmt::Format))]
154pub enum Direction {
155 /// Transfer from memory to memory.
156 MemoryToMemory,
157 /// Transfer from memory to a peripheral register.
158 MemoryToPeripheral,
159 /// Transfer from a peripheral register to memory.
160 PeripheralToMemory,
161}
162
163/// DMA transfer priority.
164#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
165#[cfg_attr(feature = "defmt", derive(defmt::Format))]
166pub enum Priority {
167 /// Low priority (channel priority 7).
168 Low,
169 /// Medium priority (channel priority 4).
170 Medium,
171 /// High priority (channel priority 1).
172 #[default]
173 High,
174 /// Highest priority (channel priority 0).
175 Highest,
176}
177
178impl Priority {
179 /// Convert to hardware priority value (0 = highest, 7 = lowest).
180 pub fn to_hw_priority(self) -> u8 {
181 match self {
182 Priority::Low => 7,
183 Priority::Medium => 4,
184 Priority::High => 1,
185 Priority::Highest => 0,
186 }
187 }
188}
189
190/// DMA transfer data width.
191#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
192#[cfg_attr(feature = "defmt", derive(defmt::Format))]
193pub enum WordSize {
194 /// 8-bit (1 byte) transfers.
195 OneByte,
196 /// 16-bit (2 byte) transfers.
197 TwoBytes,
198 /// 32-bit (4 byte) transfers.
199 #[default]
200 FourBytes,
201}
202
203impl WordSize {
204 /// Size in bytes.
205 pub const fn bytes(self) -> usize {
206 match self {
207 WordSize::OneByte => 1,
208 WordSize::TwoBytes => 2,
209 WordSize::FourBytes => 4,
210 }
211 }
212
213 /// Convert to hardware SSIZE/DSIZE field value.
214 pub const fn to_hw_size(self) -> u8 {
215 match self {
216 WordSize::OneByte => 0,
217 WordSize::TwoBytes => 1,
218 WordSize::FourBytes => 2,
219 }
220 }
221
222 /// Create from byte width (1, 2, or 4).
223 pub const fn from_bytes(bytes: u8) -> Option<Self> {
224 match bytes {
225 1 => Some(WordSize::OneByte),
226 2 => Some(WordSize::TwoBytes),
227 4 => Some(WordSize::FourBytes),
228 _ => None,
229 }
230 }
231}
232
233/// Trait for types that can be transferred via DMA.
234///
235/// This provides compile-time type safety for DMA transfers.
236pub trait Word: Copy + 'static {
237 /// The word size for this type.
238 fn size() -> WordSize;
239}
240
241impl Word for u8 {
242 fn size() -> WordSize {
243 WordSize::OneByte
244 }
245}
246
247impl Word for u16 {
248 fn size() -> WordSize {
249 WordSize::TwoBytes
250 }
251}
252
253impl Word for u32 {
254 fn size() -> WordSize {
255 WordSize::FourBytes
256 }
257}
258
259/// DMA transfer options.
260///
261/// This struct configures various aspects of a DMA transfer.
262#[derive(Debug, Copy, Clone, PartialEq, Eq)]
263#[cfg_attr(feature = "defmt", derive(defmt::Format))]
264#[non_exhaustive]
265pub struct TransferOptions {
266 /// Transfer priority.
267 pub priority: Priority,
268 /// Enable circular (continuous) mode.
269 ///
270 /// When enabled, the transfer repeats automatically after completing.
271 pub circular: bool,
272 /// Enable interrupt on half transfer complete.
273 pub half_transfer_interrupt: bool,
274 /// Enable interrupt on transfer complete.
275 pub complete_transfer_interrupt: bool,
276}
277
278impl Default for TransferOptions {
279 fn default() -> Self {
280 Self {
281 priority: Priority::High,
282 circular: false,
283 half_transfer_interrupt: false,
284 complete_transfer_interrupt: true,
285 }
286 }
287}
288
289/// DMA error types.
290#[derive(Debug, Copy, Clone, PartialEq, Eq)]
291#[cfg_attr(feature = "defmt", derive(defmt::Format))]
292pub enum Error {
293 /// The DMA controller reported a bus error.
294 BusError,
295 /// The transfer was aborted.
296 Aborted,
297 /// Configuration error (e.g., invalid parameters).
298 Configuration,
299 /// Buffer overrun (for ring buffers).
300 Overrun,
301}
302
303/// Whether to enable the major loop completion interrupt.
304///
305/// This enum provides better readability than a boolean parameter
306/// for functions that configure DMA interrupt behavior.
307#[derive(Debug, Copy, Clone, PartialEq, Eq)]
308#[cfg_attr(feature = "defmt", derive(defmt::Format))]
309pub enum EnableInterrupt {
310 /// Enable the interrupt on major loop completion.
311 Yes,
312 /// Do not enable the interrupt.
313 No,
314}
315
316// ============================================================================
317// DMA Constants
318// ============================================================================
319
320/// Maximum bytes per DMA transfer (eDMA4 CITER/BITER are 15-bit fields).
321///
322/// This is a hardware limitation of the eDMA4 controller. Transfers larger
323/// than this must be split into multiple DMA operations.
324pub const DMA_MAX_TRANSFER_SIZE: usize = 0x7FFF;
325
326// ============================================================================
327// DMA Request Source Types (Type-Safe API)
328// ============================================================================
329
330/// Trait for type-safe DMA request sources.
331///
332/// Each peripheral that can trigger DMA requests implements this trait
333/// with marker types that encode the correct request source number at
334/// compile time. This prevents using the wrong request source for a
335/// peripheral.
336///
337/// # Example
338///
339/// ```ignore
340/// // The LPUART2 RX request source is automatically derived from the type:
341/// channel.set_request_source::<Lpuart2RxRequest>();
342/// ```
343///
344/// This trait is sealed and cannot be implemented outside this crate.
345#[allow(private_bounds)]
346pub trait DmaRequest: sealed::SealedDmaRequest {
347 /// The hardware request source number for the DMA mux.
348 const REQUEST_NUMBER: u8;
349}
350
351/// Macro to define a DMA request type.
352///
353/// Creates a zero-sized marker type that implements `DmaRequest` with
354/// the specified request number.
355macro_rules! define_dma_request {
356 ($(#[$meta:meta])* $name:ident = $num:expr) => {
357 $(#[$meta])*
358 #[derive(Debug, Copy, Clone)]
359 pub struct $name;
360
361 impl sealed::SealedDmaRequest for $name {}
362
363 impl DmaRequest for $name {
364 const REQUEST_NUMBER: u8 = $num;
365 }
366 };
367}
368
369// LPUART DMA request sources (from MCXA276 reference manual Table 4-8)
370define_dma_request!(
371 /// DMA request source for LPUART0 RX.
372 Lpuart0RxRequest = 21
373);
374define_dma_request!(
375 /// DMA request source for LPUART0 TX.
376 Lpuart0TxRequest = 22
377);
378define_dma_request!(
379 /// DMA request source for LPUART1 RX.
380 Lpuart1RxRequest = 23
381);
382define_dma_request!(
383 /// DMA request source for LPUART1 TX.
384 Lpuart1TxRequest = 24
385);
386define_dma_request!(
387 /// DMA request source for LPUART2 RX.
388 Lpuart2RxRequest = 25
389);
390define_dma_request!(
391 /// DMA request source for LPUART2 TX.
392 Lpuart2TxRequest = 26
393);
394define_dma_request!(
395 /// DMA request source for LPUART3 RX.
396 Lpuart3RxRequest = 27
397);
398define_dma_request!(
399 /// DMA request source for LPUART3 TX.
400 Lpuart3TxRequest = 28
401);
402define_dma_request!(
403 /// DMA request source for LPUART4 RX.
404 Lpuart4RxRequest = 29
405);
406define_dma_request!(
407 /// DMA request source for LPUART4 TX.
408 Lpuart4TxRequest = 30
409);
410define_dma_request!(
411 /// DMA request source for LPUART5 RX.
412 Lpuart5RxRequest = 31
413);
414define_dma_request!(
415 /// DMA request source for LPUART5 TX.
416 Lpuart5TxRequest = 32
417);
418
419// ============================================================================
420// Channel Trait (Sealed Pattern)
421// ============================================================================
422
423mod sealed {
424 use crate::pac::Interrupt;
425
426 /// Sealed trait for DMA channels.
427 pub trait SealedChannel {
428 /// Zero-based channel index into the TCD array.
429 fn index(&self) -> usize;
430 /// Interrupt vector for this channel.
431 fn interrupt(&self) -> Interrupt;
432 }
433
434 /// Sealed trait for DMA request sources.
435 pub trait SealedDmaRequest {}
436}
437
438/// Marker trait implemented by HAL peripheral tokens that map to a DMA0
439/// channel backed by one EDMA_0_TCD0 TCD slot.
440///
441/// This trait is sealed and cannot be implemented outside this crate.
442#[allow(private_bounds)]
443pub trait Channel: sealed::SealedChannel + PeripheralType + Into<AnyChannel> + 'static {
444 /// Zero-based channel index into the TCD array.
445 const INDEX: usize;
446 /// Interrupt vector for this channel.
447 const INTERRUPT: Interrupt;
448}
449
450/// Type-erased DMA channel.
451///
452/// This allows storing DMA channels in a uniform way regardless of their
453/// concrete type, useful for async transfer futures and runtime channel selection.
454#[derive(Debug, Clone, Copy)]
455pub struct AnyChannel {
456 index: usize,
457 interrupt: Interrupt,
458}
459
460impl AnyChannel {
461 /// Get the channel index.
462 #[inline]
463 pub const fn index(&self) -> usize {
464 self.index
465 }
466
467 /// Get the channel interrupt.
468 #[inline]
469 pub const fn interrupt(&self) -> Interrupt {
470 self.interrupt
471 }
472
473 /// Get a reference to the TCD register block for this channel.
474 ///
475 /// This steals the eDMA pointer internally since MCXA276 has only one eDMA instance.
476 #[inline]
477 fn tcd(&self) -> &'static pac::edma_0_tcd0::Tcd {
478 // Safety: MCXA276 has a single eDMA instance, and we're only accessing
479 // the TCD for this specific channel
480 let edma = unsafe { &*pac::Edma0Tcd0::ptr() };
481 edma.tcd(self.index)
482 }
483
484 /// Check if the channel's DONE flag is set.
485 pub fn is_done(&self) -> bool {
486 self.tcd().ch_csr().read().done().bit_is_set()
487 }
488
489 /// Get the waker for this channel.
490 pub fn waker(&self) -> &'static AtomicWaker {
491 &STATES[self.index].waker
492 }
493}
494
495impl sealed::SealedChannel for AnyChannel {
496 fn index(&self) -> usize {
497 self.index
498 }
499
500 fn interrupt(&self) -> Interrupt {
501 self.interrupt
502 }
503}
504
505/// Macro to implement Channel trait for a peripheral.
506macro_rules! impl_channel {
507 ($peri:ident, $index:expr, $irq:ident) => {
508 impl sealed::SealedChannel for crate::peripherals::$peri {
509 fn index(&self) -> usize {
510 $index
511 }
512
513 fn interrupt(&self) -> Interrupt {
514 Interrupt::$irq
515 }
516 }
517
518 impl Channel for crate::peripherals::$peri {
519 const INDEX: usize = $index;
520 const INTERRUPT: Interrupt = Interrupt::$irq;
521 }
522
523 impl From<crate::peripherals::$peri> for AnyChannel {
524 fn from(_: crate::peripherals::$peri) -> Self {
525 AnyChannel {
526 index: $index,
527 interrupt: Interrupt::$irq,
528 }
529 }
530 }
531 };
532}
533
534impl_channel!(DMA_CH0, 0, DMA_CH0);
535impl_channel!(DMA_CH1, 1, DMA_CH1);
536impl_channel!(DMA_CH2, 2, DMA_CH2);
537impl_channel!(DMA_CH3, 3, DMA_CH3);
538impl_channel!(DMA_CH4, 4, DMA_CH4);
539impl_channel!(DMA_CH5, 5, DMA_CH5);
540impl_channel!(DMA_CH6, 6, DMA_CH6);
541impl_channel!(DMA_CH7, 7, DMA_CH7);
542
543/// Strongly-typed handle to a DMA0 channel.
544///
545/// The lifetime of this value is tied to the unique peripheral token
546/// supplied by `embassy_hal_internal::peripherals!`, so safe code cannot
547/// create two `DmaChannel` instances for the same hardware channel.
548pub struct DmaChannel<C: Channel> {
549 _ch: core::marker::PhantomData<C>,
550}
551
552// ============================================================================
553// DMA Transfer Methods - API Overview
554// ============================================================================
555//
556// The DMA API provides two categories of methods for configuring transfers:
557//
558// ## 1. Async Methods (Return `Transfer` Future)
559//
560// These methods return a [`Transfer`] Future that must be `.await`ed:
561//
562// - [`write()`](DmaChannel::write) - Memory-to-peripheral using default eDMA TCD block
563// - [`read()`](DmaChannel::read) - Peripheral-to-memory using default eDMA TCD block
564// - [`write_to_peripheral()`](DmaChannel::write_to_peripheral) - Memory-to-peripheral with custom eDMA TCD block
565// - [`read_from_peripheral()`](DmaChannel::read_from_peripheral) - Peripheral-to-memory with custom eDMA TCD block
566// - [`mem_to_mem()`](DmaChannel::mem_to_mem) - Memory-to-memory using default eDMA TCD block
567//
568// The `Transfer` manages the DMA lifecycle automatically:
569// - Enables channel request
570// - Waits for completion via async/await
571// - Cleans up on completion
572//
573// **Important:** `Transfer::Drop` aborts the transfer if dropped before completion.
574// This means you MUST `.await` the Transfer or it will be aborted when it goes out of scope.
575//
576// **Use case:** When you want to use async/await and let the Transfer handle lifecycle management.
577//
578// ## 2. Setup Methods (Configure TCD Only)
579//
580// These methods configure the TCD but do NOT return a `Transfer`:
581//
582// - [`setup_write()`](DmaChannel::setup_write) - Memory-to-peripheral using default eDMA TCD block
583// - [`setup_read()`](DmaChannel::setup_read) - Peripheral-to-memory using default eDMA TCD block
584// - [`setup_write_to_peripheral()`](DmaChannel::setup_write_to_peripheral) - Memory-to-peripheral with custom eDMA TCD block
585// - [`setup_read_from_peripheral()`](DmaChannel::setup_read_from_peripheral) - Peripheral-to-memory with custom eDMA TCD block
586//
587// The caller is responsible for the complete DMA lifecycle:
588// 1. Call [`enable_request()`](DmaChannel::enable_request) to start the transfer
589// 2. Poll [`is_done()`](DmaChannel::is_done) or use interrupts to detect completion
590// 3. Call [`disable_request()`](DmaChannel::disable_request), [`clear_done()`](DmaChannel::clear_done),
591// [`clear_interrupt()`](DmaChannel::clear_interrupt) for cleanup
592//
593// **Use case:** Peripheral drivers (like LPUART) that need fine-grained control over
594// DMA setup before starting a `Transfer`.
595//
596// ============================================================================
597
598impl<C: Channel> DmaChannel<C> {
599 /// Wrap a DMA channel token (takes ownership of the Peri wrapper).
600 ///
601 /// Note: DMA is initialized during `hal::init()` via `dma::init()`.
602 #[inline]
603 pub fn new(_ch: embassy_hal_internal::Peri<'_, C>) -> Self {
604 unsafe {
605 cortex_m::peripheral::NVIC::unmask(C::INTERRUPT);
606 }
607 Self {
608 _ch: core::marker::PhantomData,
609 }
610 }
611
612 /// Channel index in the EDMA_0_TCD0 array.
613 #[inline]
614 pub const fn index(&self) -> usize {
615 C::INDEX
616 }
617
618 /// Convert this typed channel into a type-erased `AnyChannel`.
619 #[inline]
620 pub fn into_any(self) -> AnyChannel {
621 AnyChannel {
622 index: C::INDEX,
623 interrupt: C::INTERRUPT,
624 }
625 }
626
627 /// Get a reference to the type-erased channel info.
628 #[inline]
629 pub fn as_any(&self) -> AnyChannel {
630 AnyChannel {
631 index: C::INDEX,
632 interrupt: C::INTERRUPT,
633 }
634 }
635
636 /// Return a reference to the underlying TCD register block.
637 ///
638 /// This steals the eDMA pointer internally since MCXA276 has only one eDMA instance.
639 ///
640 /// # Note
641 ///
642 /// This is exposed for advanced use cases that need direct TCD access.
643 /// For most use cases, prefer the higher-level transfer methods.
644 #[inline]
645 pub fn tcd(&self) -> &'static pac::edma_0_tcd0::Tcd {
646 // Safety: MCXA276 has a single eDMA instance
647 let edma = unsafe { &*pac::Edma0Tcd0::ptr() };
648 edma.tcd(C::INDEX)
649 }
650
651 fn clear_tcd(t: &'static pac::edma_0_tcd0::Tcd) {
652 // Full TCD reset following NXP SDK pattern (EDMA_TcdResetExt).
653 // Reset ALL TCD registers to 0 to clear any stale configuration from
654 // previous transfers. This is critical when reusing a channel.
655 t.tcd_saddr().write(|w| unsafe { w.saddr().bits(0) });
656 t.tcd_soff().write(|w| unsafe { w.soff().bits(0) });
657 t.tcd_attr().write(|w| unsafe { w.bits(0) });
658 t.tcd_nbytes_mloffno().write(|w| unsafe { w.nbytes().bits(0) });
659 t.tcd_slast_sda().write(|w| unsafe { w.slast_sda().bits(0) });
660 t.tcd_daddr().write(|w| unsafe { w.daddr().bits(0) });
661 t.tcd_doff().write(|w| unsafe { w.doff().bits(0) });
662 t.tcd_citer_elinkno().write(|w| unsafe { w.bits(0) });
663 t.tcd_dlast_sga().write(|w| unsafe { w.dlast_sga().bits(0) });
664 t.tcd_csr().write(|w| unsafe { w.bits(0) }); // Clear CSR completely
665 t.tcd_biter_elinkno().write(|w| unsafe { w.bits(0) });
666 }
667
668 #[inline]
669 fn set_major_loop_ct_elinkno(t: &'static pac::edma_0_tcd0::Tcd, count: u16) {
670 t.tcd_biter_elinkno().write(|w| unsafe { w.biter().bits(count) });
671 t.tcd_citer_elinkno().write(|w| unsafe { w.citer().bits(count) });
672 }
673
674 #[inline]
675 fn set_minor_loop_ct_no_offsets(t: &'static pac::edma_0_tcd0::Tcd, count: u32) {
676 t.tcd_nbytes_mloffno().write(|w| unsafe { w.nbytes().bits(count) });
677 }
678
679 #[inline]
680 fn set_no_final_adjustments(t: &'static pac::edma_0_tcd0::Tcd) {
681 // No source/dest adjustment after major loop
682 t.tcd_slast_sda().write(|w| unsafe { w.slast_sda().bits(0) });
683 t.tcd_dlast_sga().write(|w| unsafe { w.dlast_sga().bits(0) });
684 }
685
686 #[inline]
687 fn set_source_ptr<T>(t: &'static pac::edma_0_tcd0::Tcd, p: *const T) {
688 t.tcd_saddr().write(|w| unsafe { w.saddr().bits(p as u32) });
689 }
690
691 #[inline]
692 fn set_source_increment(t: &'static pac::edma_0_tcd0::Tcd, sz: WordSize) {
693 t.tcd_soff().write(|w| unsafe { w.soff().bits(sz.bytes() as u16) });
694 }
695
696 #[inline]
697 fn set_source_fixed(t: &'static pac::edma_0_tcd0::Tcd) {
698 t.tcd_soff().write(|w| unsafe { w.soff().bits(0) });
699 }
700
701 #[inline]
702 fn set_dest_ptr<T>(t: &'static pac::edma_0_tcd0::Tcd, p: *mut T) {
703 t.tcd_daddr().write(|w| unsafe { w.daddr().bits(p as u32) });
704 }
705
706 #[inline]
707 fn set_dest_increment(t: &'static pac::edma_0_tcd0::Tcd, sz: WordSize) {
708 t.tcd_doff().write(|w| unsafe { w.doff().bits(sz.bytes() as u16) });
709 }
710
711 #[inline]
712 fn set_dest_fixed(t: &'static pac::edma_0_tcd0::Tcd) {
713 t.tcd_doff().write(|w| unsafe { w.doff().bits(0) });
714 }
715
716 #[inline]
717 fn set_even_transfer_size(t: &'static pac::edma_0_tcd0::Tcd, sz: WordSize) {
718 let hw_size = sz.to_hw_size();
719 t.tcd_attr()
720 .write(|w| unsafe { w.ssize().bits(hw_size).dsize().bits(hw_size) });
721 }
722
723 #[inline]
724 fn reset_channel_state(t: &'static pac::edma_0_tcd0::Tcd) {
725 // CSR: Resets to all zeroes (disabled), "done" is cleared by writing 1
726 t.ch_csr().write(|w| w.done().clear_bit_by_one());
727 // ES: Resets to all zeroes (disabled), "err" is cleared by writing 1
728 t.ch_es().write(|w| w.err().clear_bit_by_one());
729 // INT: Resets to all zeroes (disabled), "int" is cleared by writing 1
730 t.ch_int().write(|w| w.int().clear_bit_by_one());
731 }
732
733 /// Start an async transfer.
734 ///
735 /// The channel must already be configured. This enables the channel
736 /// request and returns a `Transfer` future that resolves when the
737 /// DMA transfer completes.
738 ///
739 /// # Safety
740 ///
741 /// The caller must ensure the DMA channel has been properly configured
742 /// and that source/destination buffers remain valid for the duration
743 /// of the transfer.
744 pub unsafe fn start_transfer(&self) -> Transfer<'_> {
745 // Clear any previous DONE/INT flags
746 let t = self.tcd();
747 t.ch_csr().modify(|_, w| w.done().clear_bit_by_one());
748 t.ch_int().write(|w| w.int().clear_bit_by_one());
749
750 // Enable the channel request
751 t.ch_csr().modify(|_, w| w.erq().enable());
752
753 Transfer::new(self.as_any())
754 }
755
756 // ========================================================================
757 // Type-Safe Transfer Methods (Embassy-style API)
758 // ========================================================================
759
760 /// Perform a memory-to-memory DMA transfer (simplified API).
761 ///
762 /// This is a type-safe wrapper that uses the `Word` trait to determine
763 /// the correct transfer width automatically. Uses the global eDMA TCD
764 /// register accessor internally.
765 ///
766 /// # Arguments
767 ///
768 /// * `src` - Source buffer
769 /// * `dst` - Destination buffer (must be at least as large as src)
770 /// * `options` - Transfer configuration options
771 ///
772 /// # Safety
773 ///
774 /// The source and destination buffers must remain valid for the
775 /// duration of the transfer.
776 pub fn mem_to_mem<W: Word>(
777 &self,
778 src: &[W],
779 dst: &mut [W],
780 options: TransferOptions,
781 ) -> Result<Transfer<'_>, Error> {
782 let mut invalid = false;
783 invalid |= src.is_empty();
784 invalid |= src.len() > dst.len();
785 invalid |= src.len() > 0x7fff;
786 if invalid {
787 return Err(Error::Configuration);
788 }
789
790 let size = W::size();
791 let byte_count = (src.len() * size.bytes()) as u32;
792
793 let t = self.tcd();
794
795 // Reset channel state - clear DONE, disable requests, clear errors
796 Self::reset_channel_state(t);
797
798 // Memory barrier to ensure channel state is fully reset before touching TCD
799 cortex_m::asm::dsb();
800
801 Self::clear_tcd(t);
802
803 // Memory barrier after TCD reset
804 cortex_m::asm::dsb();
805
806 // Note: Priority is managed by round-robin arbitration (set in init())
807 // Per-channel priority can be configured via ch_pri() if needed
808
809 // Now configure the new transfer
810
811 // Source address and increment
812 Self::set_source_ptr(t, src.as_ptr());
813 Self::set_source_increment(t, size);
814
815 // Destination address and increment
816 Self::set_dest_ptr(t, dst.as_mut_ptr());
817 Self::set_dest_increment(t, size);
818
819 // Transfer attributes (size)
820 Self::set_even_transfer_size(t, size);
821
822 // Minor loop: transfer all bytes in one minor loop
823 Self::set_minor_loop_ct_no_offsets(t, byte_count);
824
825 // No source/dest adjustment after major loop
826 Self::set_no_final_adjustments(t);
827
828 // Major loop count = 1 (single major loop)
829 // Write BITER first, then CITER (CITER must match BITER at start)
830 Self::set_major_loop_ct_elinkno(t, 1);
831
832 // Memory barrier before setting START
833 cortex_m::asm::dsb();
834
835 // Control/status: interrupt on major complete, start
836 // Write this last after all other TCD registers are configured
837 let int_major = options.complete_transfer_interrupt;
838 t.tcd_csr().write(|w| {
839 w.intmajor()
840 .bit(int_major)
841 .inthalf()
842 .bit(options.half_transfer_interrupt)
843 .dreq()
844 .set_bit() // Auto-disable request after major loop
845 .start()
846 .set_bit() // Start the channel
847 });
848
849 Ok(Transfer::new(self.as_any()))
850 }
851
852 /// Fill a memory buffer with a pattern value (memset).
853 ///
854 /// This performs a DMA transfer where the source address remains fixed
855 /// (pattern value) while the destination address increments through the buffer.
856 /// It's useful for quickly filling large memory regions with a constant value.
857 ///
858 /// # Arguments
859 ///
860 /// * `pattern` - Reference to the pattern value (will be read repeatedly)
861 /// * `dst` - Destination buffer to fill
862 /// * `options` - Transfer configuration options
863 ///
864 /// # Example
865 ///
866 /// ```no_run
867 /// use embassy_mcxa::dma::{DmaChannel, TransferOptions};
868 ///
869 /// let dma_ch = DmaChannel::new(p.DMA_CH0);
870 /// let pattern: u32 = 0xDEADBEEF;
871 /// let mut buffer = [0u32; 256];
872 ///
873 /// unsafe {
874 /// dma_ch.memset(&pattern, &mut buffer, TransferOptions::default()).await;
875 /// }
876 /// // buffer is now filled with 0xDEADBEEF
877 /// ```
878 ///
879 pub fn memset<W: Word>(&self, pattern: &W, dst: &mut [W], options: TransferOptions) -> Transfer<'_> {
880 assert!(!dst.is_empty());
881 assert!(dst.len() <= 0x7fff);
882
883 let size = W::size();
884 let byte_size = size.bytes();
885 // Total bytes to transfer - all in one minor loop for software-triggered transfers
886 let total_bytes = (dst.len() * byte_size) as u32;
887
888 let t = self.tcd();
889
890 // Reset channel state - clear DONE, disable requests, clear errors
891 Self::reset_channel_state(t);
892
893 // Memory barrier to ensure channel state is fully reset before touching TCD
894 cortex_m::asm::dsb();
895
896 Self::clear_tcd(t);
897
898 // Memory barrier after TCD reset
899 cortex_m::asm::dsb();
900
901 // Now configure the new transfer
902 //
903 // For software-triggered memset, we use a SINGLE minor loop that transfers
904 // all bytes at once. The source address stays fixed (SOFF=0) while the
905 // destination increments (DOFF=byte_size). The eDMA will read from the
906 // same source address for each destination word.
907 //
908 // This is necessary because the START bit only triggers ONE minor loop
909 // iteration. Using CITER>1 with software trigger would require multiple
910 // START triggers.
911
912 // Source: pattern address, fixed (soff=0)
913 Self::set_source_ptr(t, pattern);
914 Self::set_source_fixed(t);
915
916 // Destination: memory buffer, incrementing by word size
917 Self::set_dest_ptr(t, dst.as_mut_ptr());
918 Self::set_dest_increment(t, size);
919
920 // Transfer attributes - source and dest are same word size
921 Self::set_even_transfer_size(t, size);
922
923 // Minor loop: transfer ALL bytes in one minor loop (like mem_to_mem)
924 // This allows the entire transfer to complete with a single START trigger
925 Self::set_minor_loop_ct_no_offsets(t, total_bytes);
926
927 // No address adjustment after major loop
928 Self::set_no_final_adjustments(t);
929
930 // Major loop count = 1 (single major loop, all data in minor loop)
931 // Write BITER first, then CITER (CITER must match BITER at start)
932 Self::set_major_loop_ct_elinkno(t, 1);
933
934 // Memory barrier before setting START
935 cortex_m::asm::dsb();
936
937 // Control/status: interrupt on major complete, start immediately
938 // Write this last after all other TCD registers are configured
939 let int_major = options.complete_transfer_interrupt;
940 t.tcd_csr().write(|w| {
941 w.intmajor()
942 .bit(int_major)
943 .inthalf()
944 .bit(options.half_transfer_interrupt)
945 .dreq()
946 .set_bit() // Auto-disable request after major loop
947 .start()
948 .set_bit() // Start the channel
949 });
950
951 Transfer::new(self.as_any())
952 }
953
954 /// Write data from memory to a peripheral register.
955 ///
956 /// The destination address remains fixed (peripheral register) while
957 /// the source address increments through the buffer.
958 ///
959 /// # Arguments
960 ///
961 /// * `buf` - Source buffer to write from
962 /// * `peri_addr` - Peripheral register address
963 /// * `options` - Transfer configuration options
964 ///
965 /// # Safety
966 ///
967 /// - The buffer must remain valid for the duration of the transfer.
968 /// - The peripheral address must be valid for writes.
969 pub unsafe fn write<W: Word>(&self, buf: &[W], peri_addr: *mut W, options: TransferOptions) -> Transfer<'_> {
970 self.write_to_peripheral(buf, peri_addr, options)
971 }
972
973 /// Configure a memory-to-peripheral DMA transfer without starting it.
974 ///
975 /// This is a convenience wrapper around [`setup_write_to_peripheral()`](Self::setup_write_to_peripheral)
976 /// that uses the default eDMA TCD register block.
977 ///
978 /// This method configures the TCD but does NOT return a `Transfer`. The caller
979 /// is responsible for the complete DMA lifecycle:
980 /// 1. Call [`enable_request()`](Self::enable_request) to start the transfer
981 /// 2. Poll [`is_done()`](Self::is_done) or use interrupts to detect completion
982 /// 3. Call [`disable_request()`](Self::disable_request), [`clear_done()`](Self::clear_done),
983 /// [`clear_interrupt()`](Self::clear_interrupt) for cleanup
984 ///
985 /// # Example
986 ///
987 /// ```no_run
988 /// # use embassy_mcxa::dma::DmaChannel;
989 /// # let dma_ch = DmaChannel::new(p.DMA_CH0);
990 /// # let uart_tx_addr = 0x4000_0000 as *mut u8;
991 /// let data = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; // "Hello"
992 ///
993 /// unsafe {
994 /// // Configure the transfer
995 /// dma_ch.setup_write(&data, uart_tx_addr, EnableInterrupt::Yes);
996 ///
997 /// // Start when peripheral is ready
998 /// dma_ch.enable_request();
999 ///
1000 /// // Wait for completion (or use interrupt)
1001 /// while !dma_ch.is_done() {}
1002 ///
1003 /// // Clean up
1004 /// dma_ch.clear_done();
1005 /// dma_ch.clear_interrupt();
1006 /// }
1007 /// ```
1008 ///
1009 /// # Arguments
1010 ///
1011 /// * `buf` - Source buffer to write from
1012 /// * `peri_addr` - Peripheral register address
1013 /// * `enable_interrupt` - Whether to enable interrupt on completion
1014 ///
1015 /// # Safety
1016 ///
1017 /// - The buffer must remain valid for the duration of the transfer.
1018 /// - The peripheral address must be valid for writes.
1019 pub unsafe fn setup_write<W: Word>(&self, buf: &[W], peri_addr: *mut W, enable_interrupt: EnableInterrupt) {
1020 self.setup_write_to_peripheral(buf, peri_addr, enable_interrupt)
1021 }
1022
1023 /// Write data from memory to a peripheral register.
1024 ///
1025 /// The destination address remains fixed (peripheral register) while
1026 /// the source address increments through the buffer.
1027 ///
1028 /// # Arguments
1029 ///
1030 /// * `buf` - Source buffer to write from
1031 /// * `peri_addr` - Peripheral register address
1032 /// * `options` - Transfer configuration options
1033 ///
1034 /// # Safety
1035 ///
1036 /// - The buffer must remain valid for the duration of the transfer.
1037 /// - The peripheral address must be valid for writes.
1038 pub unsafe fn write_to_peripheral<W: Word>(
1039 &self,
1040 buf: &[W],
1041 peri_addr: *mut W,
1042 options: TransferOptions,
1043 ) -> Transfer<'_> {
1044 assert!(!buf.is_empty());
1045 assert!(buf.len() <= 0x7fff);
1046
1047 let size = W::size();
1048 let byte_size = size.bytes();
1049
1050 let t = self.tcd();
1051
1052 // Reset channel state
1053 Self::reset_channel_state(t);
1054
1055 // Addresses
1056 Self::set_source_ptr(t, buf.as_ptr());
1057 Self::set_dest_ptr(t, peri_addr);
1058
1059 // Offsets: Source increments, Dest fixed
1060 Self::set_source_increment(t, size);
1061 Self::set_dest_fixed(t);
1062
1063 // Attributes: set size and explicitly disable modulo
1064 Self::set_even_transfer_size(t, size);
1065
1066 // Minor loop: transfer one word per request (match old: only set nbytes)
1067 Self::set_minor_loop_ct_no_offsets(t, byte_size as u32);
1068
1069 // No final adjustments
1070 Self::set_no_final_adjustments(t);
1071
1072 // Major loop count = number of words
1073 let count = buf.len() as u16;
1074 Self::set_major_loop_ct_elinkno(t, count);
1075
1076 // CSR: interrupt on major loop complete and auto-clear ERQ
1077 t.tcd_csr().write(|w| {
1078 let w = if options.complete_transfer_interrupt {
1079 w.intmajor().enable()
1080 } else {
1081 w.intmajor().disable()
1082 };
1083 w.inthalf()
1084 .disable()
1085 .dreq()
1086 .erq_field_clear() // Disable request when done
1087 .esg()
1088 .normal_format()
1089 .majorelink()
1090 .disable()
1091 .eeop()
1092 .disable()
1093 .esda()
1094 .disable()
1095 .bwc()
1096 .no_stall()
1097 });
1098
1099 // Ensure all TCD writes have completed before DMA engine reads them
1100 cortex_m::asm::dsb();
1101
1102 Transfer::new(self.as_any())
1103 }
1104
1105 /// Read data from a peripheral register to memory.
1106 ///
1107 /// The source address remains fixed (peripheral register) while
1108 /// the destination address increments through the buffer.
1109 ///
1110 /// # Arguments
1111 ///
1112 /// * `peri_addr` - Peripheral register address
1113 /// * `buf` - Destination buffer to read into
1114 /// * `options` - Transfer configuration options
1115 ///
1116 /// # Safety
1117 ///
1118 /// - The buffer must remain valid for the duration of the transfer.
1119 /// - The peripheral address must be valid for reads.
1120 pub unsafe fn read<W: Word>(&self, peri_addr: *const W, buf: &mut [W], options: TransferOptions) -> Transfer<'_> {
1121 self.read_from_peripheral(peri_addr, buf, options)
1122 }
1123
1124 /// Configure a peripheral-to-memory DMA transfer without starting it.
1125 ///
1126 /// This is a convenience wrapper around [`setup_read_from_peripheral()`](Self::setup_read_from_peripheral)
1127 /// that uses the default eDMA TCD register block.
1128 ///
1129 /// This method configures the TCD but does NOT return a `Transfer`. The caller
1130 /// is responsible for the complete DMA lifecycle:
1131 /// 1. Call [`enable_request()`](Self::enable_request) to start the transfer
1132 /// 2. Poll [`is_done()`](Self::is_done) or use interrupts to detect completion
1133 /// 3. Call [`disable_request()`](Self::disable_request), [`clear_done()`](Self::clear_done),
1134 /// [`clear_interrupt()`](Self::clear_interrupt) for cleanup
1135 ///
1136 /// # Example
1137 ///
1138 /// ```no_run
1139 /// # use embassy_mcxa::dma::DmaChannel;
1140 /// # let dma_ch = DmaChannel::new(p.DMA_CH0);
1141 /// # let uart_rx_addr = 0x4000_0000 as *const u8;
1142 /// let mut buf = [0u8; 32];
1143 ///
1144 /// unsafe {
1145 /// // Configure the transfer
1146 /// dma_ch.setup_read(uart_rx_addr, &mut buf, EnableInterrupt::Yes);
1147 ///
1148 /// // Start when peripheral is ready
1149 /// dma_ch.enable_request();
1150 ///
1151 /// // Wait for completion (or use interrupt)
1152 /// while !dma_ch.is_done() {}
1153 ///
1154 /// // Clean up
1155 /// dma_ch.clear_done();
1156 /// dma_ch.clear_interrupt();
1157 /// }
1158 /// // buf now contains received data
1159 /// ```
1160 ///
1161 /// # Arguments
1162 ///
1163 /// * `peri_addr` - Peripheral register address
1164 /// * `buf` - Destination buffer to read into
1165 /// * `enable_interrupt` - Whether to enable interrupt on completion
1166 ///
1167 /// # Safety
1168 ///
1169 /// - The buffer must remain valid for the duration of the transfer.
1170 /// - The peripheral address must be valid for reads.
1171 pub unsafe fn setup_read<W: Word>(&self, peri_addr: *const W, buf: &mut [W], enable_interrupt: EnableInterrupt) {
1172 self.setup_read_from_peripheral(peri_addr, buf, enable_interrupt)
1173 }
1174
1175 /// Read data from a peripheral register to memory.
1176 ///
1177 /// The source address remains fixed (peripheral register) while
1178 /// the destination address increments through the buffer.
1179 ///
1180 /// # Arguments
1181 ///
1182 /// * `peri_addr` - Peripheral register address
1183 /// * `buf` - Destination buffer to read into
1184 /// * `options` - Transfer configuration options
1185 ///
1186 /// # Safety
1187 ///
1188 /// - The buffer must remain valid for the duration of the transfer.
1189 /// - The peripheral address must be valid for reads.
1190 pub unsafe fn read_from_peripheral<W: Word>(
1191 &self,
1192 peri_addr: *const W,
1193 buf: &mut [W],
1194 options: TransferOptions,
1195 ) -> Transfer<'_> {
1196 assert!(!buf.is_empty());
1197 assert!(buf.len() <= 0x7fff);
1198
1199 let size = W::size();
1200 let byte_size = size.bytes();
1201
1202 let t = self.tcd();
1203
1204 // Reset channel control/error/interrupt state
1205 Self::reset_channel_state(t);
1206
1207 // Source: peripheral register, fixed
1208 Self::set_source_ptr(t, peri_addr);
1209 Self::set_source_fixed(t);
1210
1211 // Destination: memory buffer, incrementing
1212 Self::set_dest_ptr(t, buf.as_mut_ptr());
1213 Self::set_dest_increment(t, size);
1214
1215 // Transfer attributes: set size and explicitly disable modulo
1216 Self::set_even_transfer_size(t, size);
1217
1218 // Minor loop: transfer one word per request, no offsets
1219 Self::set_minor_loop_ct_no_offsets(t, byte_size as u32);
1220
1221 // Major loop count = number of words
1222 let count = buf.len() as u16;
1223 Self::set_major_loop_ct_elinkno(t, count);
1224
1225 // No address adjustment after major loop
1226 Self::set_no_final_adjustments(t);
1227
1228 // Control/status: interrupt on major complete, auto-clear ERQ when done
1229 t.tcd_csr().write(|w| {
1230 let w = if options.complete_transfer_interrupt {
1231 w.intmajor().enable()
1232 } else {
1233 w.intmajor().disable()
1234 };
1235 let w = if options.half_transfer_interrupt {
1236 w.inthalf().enable()
1237 } else {
1238 w.inthalf().disable()
1239 };
1240 w.dreq()
1241 .erq_field_clear() // Disable request when done (important for peripheral DMA)
1242 .esg()
1243 .normal_format()
1244 .majorelink()
1245 .disable()
1246 .eeop()
1247 .disable()
1248 .esda()
1249 .disable()
1250 .bwc()
1251 .no_stall()
1252 });
1253
1254 // Ensure all TCD writes have completed before DMA engine reads them
1255 cortex_m::asm::dsb();
1256
1257 Transfer::new(self.as_any())
1258 }
1259
1260 /// Configure a memory-to-peripheral DMA transfer without starting it.
1261 ///
1262 /// This configures the TCD for a memory-to-peripheral transfer but does NOT
1263 /// return a Transfer object. The caller is responsible for:
1264 /// 1. Enabling the peripheral's DMA request
1265 /// 2. Calling `enable_request()` to start the transfer
1266 /// 3. Polling `is_done()` or using interrupts to detect completion
1267 /// 4. Calling `disable_request()`, `clear_done()`, `clear_interrupt()` for cleanup
1268 ///
1269 /// Use this when you need manual control over the DMA lifecycle (e.g., in
1270 /// peripheral drivers that have their own completion polling).
1271 ///
1272 /// # Arguments
1273 ///
1274 /// * `buf` - Source buffer to write from
1275 /// * `peri_addr` - Peripheral register address
1276 /// * `enable_interrupt` - Whether to enable interrupt on completion
1277 ///
1278 /// # Safety
1279 ///
1280 /// - The buffer must remain valid for the duration of the transfer.
1281 /// - The peripheral address must be valid for writes.
1282 pub unsafe fn setup_write_to_peripheral<W: Word>(
1283 &self,
1284 buf: &[W],
1285 peri_addr: *mut W,
1286 enable_interrupt: EnableInterrupt,
1287 ) {
1288 assert!(!buf.is_empty());
1289 assert!(buf.len() <= 0x7fff);
1290
1291 let size = W::size();
1292 let byte_size = size.bytes();
1293
1294 let t = self.tcd();
1295
1296 // Reset channel state
1297 Self::reset_channel_state(t);
1298
1299 // Addresses
1300 Self::set_source_ptr(t, buf.as_ptr());
1301 Self::set_dest_ptr(t, peri_addr);
1302
1303 // Offsets: Source increments, Dest fixed
1304 Self::set_source_increment(t, size);
1305 Self::set_dest_fixed(t);
1306
1307 // Attributes: set size and explicitly disable modulo
1308 Self::set_even_transfer_size(t, size);
1309
1310 // Minor loop: transfer one word per request
1311 Self::set_minor_loop_ct_no_offsets(t, byte_size as u32);
1312
1313 // No final adjustments
1314 Self::set_no_final_adjustments(t);
1315
1316 // Major loop count = number of words
1317 let count = buf.len() as u16;
1318 Self::set_major_loop_ct_elinkno(t, count);
1319
1320 // CSR: optional interrupt on major loop complete and auto-clear ERQ
1321 t.tcd_csr().write(|w| {
1322 let w = match enable_interrupt {
1323 EnableInterrupt::Yes => w.intmajor().enable(),
1324 EnableInterrupt::No => w.intmajor().disable(),
1325 };
1326 w.inthalf()
1327 .disable()
1328 .dreq()
1329 .erq_field_clear()
1330 .esg()
1331 .normal_format()
1332 .majorelink()
1333 .disable()
1334 .eeop()
1335 .disable()
1336 .esda()
1337 .disable()
1338 .bwc()
1339 .no_stall()
1340 });
1341
1342 // Ensure all TCD writes have completed before DMA engine reads them
1343 cortex_m::asm::dsb();
1344 }
1345
1346 /// Configure a peripheral-to-memory DMA transfer without starting it.
1347 ///
1348 /// This configures the TCD for a peripheral-to-memory transfer but does NOT
1349 /// return a Transfer object. The caller is responsible for:
1350 /// 1. Enabling the peripheral's DMA request
1351 /// 2. Calling `enable_request()` to start the transfer
1352 /// 3. Polling `is_done()` or using interrupts to detect completion
1353 /// 4. Calling `disable_request()`, `clear_done()`, `clear_interrupt()` for cleanup
1354 ///
1355 /// Use this when you need manual control over the DMA lifecycle (e.g., in
1356 /// peripheral drivers that have their own completion polling).
1357 ///
1358 /// # Arguments
1359 ///
1360 /// * `peri_addr` - Peripheral register address
1361 /// * `buf` - Destination buffer to read into
1362 /// * `enable_interrupt` - Whether to enable interrupt on completion
1363 ///
1364 /// # Safety
1365 ///
1366 /// - The buffer must remain valid for the duration of the transfer.
1367 /// - The peripheral address must be valid for reads.
1368 pub unsafe fn setup_read_from_peripheral<W: Word>(
1369 &self,
1370 peri_addr: *const W,
1371 buf: &mut [W],
1372 enable_interrupt: EnableInterrupt,
1373 ) {
1374 assert!(!buf.is_empty());
1375 assert!(buf.len() <= 0x7fff);
1376
1377 let size = W::size();
1378 let byte_size = size.bytes();
1379
1380 let t = self.tcd();
1381
1382 // Reset channel control/error/interrupt state
1383 Self::reset_channel_state(t);
1384
1385 // Source: peripheral register, fixed
1386 Self::set_source_ptr(t, peri_addr);
1387 Self::set_source_fixed(t);
1388
1389 // Destination: memory buffer, incrementing
1390 Self::set_dest_ptr(t, buf.as_mut_ptr());
1391 Self::set_dest_increment(t, size);
1392
1393 // Attributes: set size and explicitly disable modulo
1394 Self::set_even_transfer_size(t, size);
1395
1396 // Minor loop: transfer one word per request
1397 Self::set_minor_loop_ct_no_offsets(t, byte_size as u32);
1398
1399 // No final adjustments
1400 Self::set_no_final_adjustments(t);
1401
1402 // Major loop count = number of words
1403 let count = buf.len() as u16;
1404 Self::set_major_loop_ct_elinkno(t, count);
1405
1406 // CSR: optional interrupt on major loop complete and auto-clear ERQ
1407 t.tcd_csr().write(|w| {
1408 let w = match enable_interrupt {
1409 EnableInterrupt::Yes => w.intmajor().enable(),
1410 EnableInterrupt::No => w.intmajor().disable(),
1411 };
1412 w.inthalf()
1413 .disable()
1414 .dreq()
1415 .erq_field_clear()
1416 .esg()
1417 .normal_format()
1418 .majorelink()
1419 .disable()
1420 .eeop()
1421 .disable()
1422 .esda()
1423 .disable()
1424 .bwc()
1425 .no_stall()
1426 });
1427
1428 // Ensure all TCD writes have completed before DMA engine reads them
1429 cortex_m::asm::dsb();
1430 }
1431
1432 /// Configure the integrated channel MUX to use the given typed
1433 /// DMA request source (e.g., [`Lpuart2TxRequest`] or [`Lpuart2RxRequest`]).
1434 ///
1435 /// This is the type-safe version that uses marker types to ensure
1436 /// compile-time verification of request source validity.
1437 ///
1438 /// # Safety
1439 ///
1440 /// The channel must be properly configured before enabling requests.
1441 /// The caller must ensure the DMA request source matches the peripheral
1442 /// that will drive this channel.
1443 ///
1444 /// # Note
1445 ///
1446 /// The NXP SDK requires a two-step write sequence: first clear
1447 /// the mux to 0, then set the actual source. This is a hardware
1448 /// requirement on eDMA4 for the mux to properly latch.
1449 ///
1450 /// # Example
1451 ///
1452 /// ```ignore
1453 /// use embassy_mcxa::dma::{DmaChannel, Lpuart2RxRequest};
1454 ///
1455 /// // Type-safe: compiler verifies this is a valid DMA request type
1456 /// unsafe {
1457 /// channel.set_request_source::<Lpuart2RxRequest>();
1458 /// }
1459 /// ```
1460 #[inline]
1461 pub unsafe fn set_request_source<R: DmaRequest>(&self) {
1462 // Two-step write per NXP SDK: clear to 0, then set actual source.
1463 self.tcd().ch_mux().write(|w| w.src().bits(0));
1464 cortex_m::asm::dsb(); // Ensure the clear completes before setting new source
1465 self.tcd().ch_mux().write(|w| w.src().bits(R::REQUEST_NUMBER));
1466 }
1467
1468 /// Enable hardware requests for this channel (ERQ=1).
1469 ///
1470 /// # Safety
1471 ///
1472 /// The channel must be properly configured before enabling requests.
1473 pub unsafe fn enable_request(&self) {
1474 let t = self.tcd();
1475 t.ch_csr().modify(|_, w| w.erq().enable());
1476 }
1477
1478 /// Disable hardware requests for this channel (ERQ=0).
1479 ///
1480 /// # Safety
1481 ///
1482 /// Disabling requests on an active transfer may leave the transfer incomplete.
1483 pub unsafe fn disable_request(&self) {
1484 let t = self.tcd();
1485 t.ch_csr().modify(|_, w| w.erq().disable());
1486 }
1487
1488 /// Return true if the channel's DONE flag is set.
1489 pub fn is_done(&self) -> bool {
1490 let t = self.tcd();
1491 t.ch_csr().read().done().bit_is_set()
1492 }
1493
1494 /// Clear the DONE flag for this channel.
1495 ///
1496 /// Uses modify to preserve other bits (especially ERQ) unlike write
1497 /// which would clear ERQ and halt an active transfer.
1498 ///
1499 /// # Safety
1500 ///
1501 /// Clearing DONE while a transfer is in progress may cause undefined behavior.
1502 pub unsafe fn clear_done(&self) {
1503 let t = self.tcd();
1504 t.ch_csr().modify(|_, w| w.done().clear_bit_by_one());
1505 }
1506
1507 /// Clear the channel interrupt flag (CH_INT.INT).
1508 ///
1509 /// # Safety
1510 ///
1511 /// Must be called from the correct interrupt context or with interrupts disabled.
1512 pub unsafe fn clear_interrupt(&self) {
1513 let t = self.tcd();
1514 t.ch_int().write(|w| w.int().clear_bit_by_one());
1515 }
1516
1517 /// Trigger a software start for this channel.
1518 ///
1519 /// # Safety
1520 ///
1521 /// The channel must be properly configured with a valid TCD before triggering.
1522 pub unsafe fn trigger_start(&self) {
1523 let t = self.tcd();
1524 t.tcd_csr().modify(|_, w| w.start().channel_started());
1525 }
1526
1527 /// Get the waker for this channel
1528 pub fn waker(&self) -> &'static AtomicWaker {
1529 &STATES[C::INDEX].waker
1530 }
1531
1532 /// Enable the interrupt for this channel in the NVIC.
1533 pub fn enable_interrupt(&self) {
1534 unsafe {
1535 cortex_m::peripheral::NVIC::unmask(C::INTERRUPT);
1536 }
1537 }
1538
1539 /// Enable Major Loop Linking.
1540 ///
1541 /// When the major loop completes, the hardware will trigger a service request
1542 /// on `link_ch`.
1543 ///
1544 /// # Arguments
1545 ///
1546 /// * `link_ch` - Target channel index (0-7) to link to
1547 ///
1548 /// # Safety
1549 ///
1550 /// The channel must be properly configured before setting up linking.
1551 pub unsafe fn set_major_link(&self, link_ch: usize) {
1552 let t = self.tcd();
1553 t.tcd_csr()
1554 .modify(|_, w| w.majorelink().enable().majorlinkch().bits(link_ch as u8));
1555 }
1556
1557 /// Disable Major Loop Linking.
1558 ///
1559 /// Removes any major loop channel linking previously configured.
1560 ///
1561 /// # Safety
1562 ///
1563 /// The caller must ensure this doesn't disrupt an active transfer that
1564 /// depends on the linking.
1565 pub unsafe fn clear_major_link(&self) {
1566 let t = self.tcd();
1567 t.tcd_csr().modify(|_, w| w.majorelink().disable());
1568 }
1569
1570 /// Enable Minor Loop Linking.
1571 ///
1572 /// After each minor loop, the hardware will trigger a service request
1573 /// on `link_ch`.
1574 ///
1575 /// # Arguments
1576 ///
1577 /// * `link_ch` - Target channel index (0-7) to link to
1578 ///
1579 /// # Note
1580 ///
1581 /// This rewrites CITER and BITER registers to the ELINKYES format.
1582 /// It preserves the current loop count.
1583 ///
1584 /// # Safety
1585 ///
1586 /// The channel must be properly configured before setting up linking.
1587 pub unsafe fn set_minor_link(&self, link_ch: usize) {
1588 let t = self.tcd();
1589
1590 // Read current CITER (assuming ELINKNO format initially)
1591 let current_citer = t.tcd_citer_elinkno().read().citer().bits();
1592 let current_biter = t.tcd_biter_elinkno().read().biter().bits();
1593
1594 // Write back using ELINKYES format
1595 t.tcd_citer_elinkyes().write(|w| {
1596 w.citer()
1597 .bits(current_citer)
1598 .elink()
1599 .enable()
1600 .linkch()
1601 .bits(link_ch as u8)
1602 });
1603
1604 t.tcd_biter_elinkyes().write(|w| {
1605 w.biter()
1606 .bits(current_biter)
1607 .elink()
1608 .enable()
1609 .linkch()
1610 .bits(link_ch as u8)
1611 });
1612 }
1613
1614 /// Disable Minor Loop Linking.
1615 ///
1616 /// Removes any minor loop channel linking previously configured.
1617 /// This rewrites CITER and BITER registers to the ELINKNO format,
1618 /// preserving the current loop count.
1619 ///
1620 /// # Safety
1621 ///
1622 /// The caller must ensure this doesn't disrupt an active transfer that
1623 /// depends on the linking.
1624 pub unsafe fn clear_minor_link(&self) {
1625 let t = self.tcd();
1626
1627 // Read current CITER (could be in either format, but we only need the count)
1628 // Note: In ELINKYES format, citer is 9 bits; in ELINKNO, it's 15 bits.
1629 // We read from ELINKNO which will give us the combined value.
1630 let current_citer = t.tcd_citer_elinkno().read().citer().bits();
1631 let current_biter = t.tcd_biter_elinkno().read().biter().bits();
1632
1633 // Write back using ELINKNO format (disabling link)
1634 t.tcd_citer_elinkno()
1635 .write(|w| w.citer().bits(current_citer).elink().disable());
1636
1637 t.tcd_biter_elinkno()
1638 .write(|w| w.biter().bits(current_biter).elink().disable());
1639 }
1640
1641 /// Load a TCD from memory into the hardware channel registers.
1642 ///
1643 /// This is useful for scatter/gather and ping-pong transfers where
1644 /// TCDs are prepared in RAM and then loaded into the hardware.
1645 ///
1646 /// # Safety
1647 ///
1648 /// - The TCD must be properly initialized.
1649 /// - The caller must ensure no concurrent access to the same channel.
1650 pub unsafe fn load_tcd(&self, tcd: &Tcd) {
1651 let t = self.tcd();
1652 t.tcd_saddr().write(|w| w.saddr().bits(tcd.saddr));
1653 t.tcd_soff().write(|w| w.soff().bits(tcd.soff as u16));
1654 t.tcd_attr().write(|w| w.bits(tcd.attr));
1655 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(tcd.nbytes));
1656 t.tcd_slast_sda().write(|w| w.slast_sda().bits(tcd.slast as u32));
1657 t.tcd_daddr().write(|w| w.daddr().bits(tcd.daddr));
1658 t.tcd_doff().write(|w| w.doff().bits(tcd.doff as u16));
1659 t.tcd_citer_elinkno().write(|w| w.citer().bits(tcd.citer));
1660 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(tcd.dlast_sga as u32));
1661 t.tcd_csr().write(|w| w.bits(tcd.csr));
1662 t.tcd_biter_elinkno().write(|w| w.biter().bits(tcd.biter));
1663 }
1664}
1665
1666/// In-memory representation of a Transfer Control Descriptor (TCD).
1667///
1668/// This matches the hardware layout (32 bytes).
1669#[repr(C, align(32))]
1670#[derive(Clone, Copy, Debug, Default)]
1671#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1672pub struct Tcd {
1673 pub saddr: u32,
1674 pub soff: i16,
1675 pub attr: u16,
1676 pub nbytes: u32,
1677 pub slast: i32,
1678 pub daddr: u32,
1679 pub doff: i16,
1680 pub citer: u16,
1681 pub dlast_sga: i32,
1682 pub csr: u16,
1683 pub biter: u16,
1684}
1685
1686struct State {
1687 /// Waker for transfer complete interrupt
1688 waker: AtomicWaker,
1689 /// Waker for half-transfer interrupt
1690 half_waker: AtomicWaker,
1691}
1692
1693impl State {
1694 const fn new() -> Self {
1695 Self {
1696 waker: AtomicWaker::new(),
1697 half_waker: AtomicWaker::new(),
1698 }
1699 }
1700}
1701
1702static STATES: [State; 8] = [
1703 State::new(),
1704 State::new(),
1705 State::new(),
1706 State::new(),
1707 State::new(),
1708 State::new(),
1709 State::new(),
1710 State::new(),
1711];
1712
1713pub(crate) fn waker(idx: usize) -> &'static AtomicWaker {
1714 &STATES[idx].waker
1715}
1716
1717pub(crate) fn half_waker(idx: usize) -> &'static AtomicWaker {
1718 &STATES[idx].half_waker
1719}
1720
1721// ============================================================================
1722// Async Transfer Future
1723// ============================================================================
1724
1725/// An in-progress DMA transfer.
1726///
1727/// This type implements `Future` and can be `.await`ed to wait for the
1728/// transfer to complete. Dropping the transfer will abort it.
1729#[must_use = "futures do nothing unless you `.await` or poll them"]
1730pub struct Transfer<'a> {
1731 channel: AnyChannel,
1732 _phantom: core::marker::PhantomData<&'a ()>,
1733}
1734
1735impl<'a> Transfer<'a> {
1736 /// Create a new transfer for the given channel.
1737 ///
1738 /// The caller must have already configured and started the DMA channel.
1739 pub(crate) fn new(channel: AnyChannel) -> Self {
1740 Self {
1741 channel,
1742 _phantom: core::marker::PhantomData,
1743 }
1744 }
1745
1746 /// Check if the transfer is still running.
1747 pub fn is_running(&self) -> bool {
1748 !self.channel.is_done()
1749 }
1750
1751 /// Get the remaining transfer count.
1752 pub fn remaining(&self) -> u16 {
1753 let t = self.channel.tcd();
1754 t.tcd_citer_elinkno().read().citer().bits()
1755 }
1756
1757 /// Block until the transfer completes.
1758 pub fn blocking_wait(self) {
1759 while self.is_running() {
1760 core::hint::spin_loop();
1761 }
1762
1763 // Ensure all DMA writes are visible
1764 fence(Ordering::SeqCst);
1765
1766 // Don't run drop (which would abort)
1767 core::mem::forget(self);
1768 }
1769
1770 /// Wait for the half-transfer interrupt asynchronously.
1771 ///
1772 /// This is useful for double-buffering scenarios where you want to process
1773 /// the first half of the buffer while the second half is being filled.
1774 ///
1775 /// Returns `true` if the half-transfer occurred, `false` if the transfer
1776 /// completed before the half-transfer interrupt.
1777 ///
1778 /// # Note
1779 ///
1780 /// The transfer must be configured with `TransferOptions::half_transfer_interrupt = true`
1781 /// for this method to work correctly.
1782 pub async fn wait_half(&mut self) -> Result<bool, TransferErrorRaw> {
1783 use core::future::poll_fn;
1784
1785 poll_fn(|cx| {
1786 let state = &STATES[self.channel.index];
1787
1788 // Register the half-transfer waker
1789 state.half_waker.register(cx.waker());
1790
1791 // Check if there's an error
1792 let t = self.channel.tcd();
1793 let es = t.ch_es().read();
1794 if es.err().is_error() {
1795 // Currently, all error fields are in the lowest 8 bits, as-casting truncates
1796 let errs = es.bits() as u8;
1797 return Poll::Ready(Err(TransferErrorRaw(errs)));
1798 }
1799
1800 // Check if we're past the half-way point
1801 let biter = t.tcd_biter_elinkno().read().biter().bits();
1802 let citer = t.tcd_citer_elinkno().read().citer().bits();
1803 let half_point = biter / 2;
1804
1805 if self.channel.is_done() {
1806 // Transfer completed before half-transfer
1807 Poll::Ready(Ok(false))
1808 } else if citer <= half_point {
1809 // We're past the half-way point
1810 fence(Ordering::SeqCst);
1811 Poll::Ready(Ok(true))
1812 } else {
1813 Poll::Pending
1814 }
1815 })
1816 .await
1817 }
1818
1819 /// Abort the transfer.
1820 fn abort(&mut self) {
1821 let t = self.channel.tcd();
1822
1823 // Disable channel requests
1824 t.ch_csr().modify(|_, w| w.erq().disable());
1825
1826 // Clear any pending interrupt
1827 t.ch_int().write(|w| w.int().clear_bit_by_one());
1828
1829 // Clear DONE flag
1830 t.ch_csr().modify(|_, w| w.done().clear_bit_by_one());
1831
1832 fence(Ordering::SeqCst);
1833 }
1834}
1835
1836/// Raw transfer error bits. Can be queried or all errors can be iterated over
1837#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1838#[derive(Copy, Clone, Debug)]
1839pub struct TransferErrorRaw(u8);
1840
1841#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1842#[derive(Copy, Clone, Debug)]
1843pub struct TransferErrorRawIter(u8);
1844
1845impl TransferErrorRaw {
1846 const MAP: &[(u8, TransferError)] = &[
1847 (1 << 0, TransferError::DestinationBus),
1848 (1 << 1, TransferError::SourceBus),
1849 (1 << 2, TransferError::ScatterGatherConfiguration),
1850 (1 << 3, TransferError::NbytesCiterConfiguration),
1851 (1 << 4, TransferError::DestinationOffset),
1852 (1 << 5, TransferError::DestinationAddress),
1853 (1 << 6, TransferError::SourceOffset),
1854 (1 << 7, TransferError::SourceAddress),
1855 ];
1856
1857 /// Convert to an iterator of contained errors
1858 pub fn err_iter(self) -> TransferErrorRawIter {
1859 TransferErrorRawIter(self.0)
1860 }
1861
1862 /// Destination Bus Error
1863 #[inline]
1864 pub fn has_destination_bus_err(&self) -> bool {
1865 (self.0 & (1 << 0)) != 0
1866 }
1867
1868 /// Source Bus Error
1869 #[inline]
1870 pub fn has_source_bus_err(&self) -> bool {
1871 (self.0 & (1 << 1)) != 0
1872 }
1873
1874 /// Indicates that `TCDn_DLAST_SGA` is not on a 32-byte boundary. This field is
1875 /// checked at the beginning of a scatter/gather operation after major loop completion
1876 /// if `TCDn_CSR[ESG]` is enabled.
1877 #[inline]
1878 pub fn has_scatter_gather_configuration_err(&self) -> bool {
1879 (self.0 & (1 << 2)) != 0
1880 }
1881
1882 /// This error indicates that one of the following has occurred:
1883 ///
1884 /// * `TCDn_NBYTES` is not a multiple of `TCDn_ATTR[SSIZE]` and `TCDn_ATTR[DSIZE]`
1885 /// * `TCDn_CITER[CITER]` is equal to zero
1886 /// * `TCDn_CITER[ELINK]` is not equal to `TCDn_BITER[ELINK]`
1887 #[inline]
1888 pub fn has_nbytes_citer_configuration_err(&self) -> bool {
1889 (self.0 & (1 << 3)) != 0
1890 }
1891
1892 /// `TCDn_DOFF` is inconsistent with `TCDn_ATTR[DSIZE]`.
1893 #[inline]
1894 pub fn has_destination_offset_err(&self) -> bool {
1895 (self.0 & (1 << 4)) != 0
1896 }
1897
1898 /// `TCDn_DADDR` is inconsistent with `TCDn_ATTR[DSIZE]`.
1899 #[inline]
1900 pub fn has_destination_address_err(&self) -> bool {
1901 (self.0 & (1 << 5)) != 0
1902 }
1903
1904 /// `TCDn_SOFF` is inconsistent with `TCDn_ATTR[SSIZE]`.
1905 #[inline]
1906 pub fn has_source_offset_err(&self) -> bool {
1907 (self.0 & (1 << 6)) != 0
1908 }
1909
1910 /// `TCDn_SADDR` is inconsistent with `TCDn_ATTR[SSIZE]`
1911 #[inline]
1912 pub fn has_source_address_err(&self) -> bool {
1913 (self.0 & (1 << 7)) != 0
1914 }
1915}
1916
1917impl Iterator for TransferErrorRawIter {
1918 type Item = TransferError;
1919
1920 fn next(&mut self) -> Option<Self::Item> {
1921 if self.0 == 0 {
1922 return None;
1923 }
1924
1925 for (mask, var) in TransferErrorRaw::MAP {
1926 // If the bit is set...
1927 if self.0 | mask != 0 {
1928 // clear the bit
1929 self.0 &= !mask;
1930 // and return the answer
1931 return Some(*var);
1932 }
1933 }
1934
1935 // Shouldn't happen, but oh well.
1936 None
1937 }
1938}
1939
1940#[derive(Copy, Clone, Debug)]
1941#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1942pub enum TransferError {
1943 /// `TCDn_SADDR` is inconsistent with `TCDn_ATTR[SSIZE]`
1944 SourceAddress,
1945 /// `TCDn_SOFF` is inconsistent with `TCDn_ATTR[SSIZE]`.
1946 SourceOffset,
1947 /// `TCDn_DADDR` is inconsistent with `TCDn_ATTR[DSIZE]`.
1948 DestinationAddress,
1949 /// `TCDn_DOFF` is inconsistent with `TCDn_ATTR[DSIZE]`.
1950 DestinationOffset,
1951 /// This error indicates that one of the following has occurred:
1952 ///
1953 /// * `TCDn_NBYTES` is not a multiple of `TCDn_ATTR[SSIZE]` and `TCDn_ATTR[DSIZE]`
1954 /// * `TCDn_CITER[CITER]` is equal to zero
1955 /// * `TCDn_CITER[ELINK]` is not equal to `TCDn_BITER[ELINK]`
1956 NbytesCiterConfiguration,
1957 /// Indicates that `TCDn_DLAST_SGA` is not on a 32-byte boundary. This field is
1958 /// checked at the beginning of a scatter/gather operation after major loop completion
1959 /// if `TCDn_CSR[ESG]` is enabled.
1960 ScatterGatherConfiguration,
1961 /// Source Bus Error
1962 SourceBus,
1963 /// Destination Bus Error
1964 DestinationBus,
1965}
1966
1967impl<'a> Unpin for Transfer<'a> {}
1968
1969impl<'a> Future for Transfer<'a> {
1970 type Output = Result<(), TransferErrorRaw>;
1971
1972 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1973 let state = &STATES[self.channel.index];
1974
1975 // Register waker first
1976 state.waker.register(cx.waker());
1977
1978 let done = self.channel.is_done();
1979
1980 if done {
1981 // Ensure all DMA writes are visible before returning
1982 fence(Ordering::SeqCst);
1983
1984 let es = self.channel.tcd().ch_es().read();
1985 if es.err().is_error() {
1986 // Currently, all error fields are in the lowest 8 bits, as-casting truncates
1987 let errs = es.bits() as u8;
1988 Poll::Ready(Err(TransferErrorRaw(errs)))
1989 } else {
1990 Poll::Ready(Ok(()))
1991 }
1992 } else {
1993 Poll::Pending
1994 }
1995 }
1996}
1997
1998impl<'a> Drop for Transfer<'a> {
1999 fn drop(&mut self) {
2000 // Only abort if the transfer is still running
2001 // If already complete, no need to abort
2002 if self.is_running() {
2003 self.abort();
2004
2005 // Wait for abort to complete
2006 while self.is_running() {
2007 core::hint::spin_loop();
2008 }
2009 }
2010
2011 fence(Ordering::SeqCst);
2012 }
2013}
2014
2015// ============================================================================
2016// Ring Buffer for Circular DMA
2017// ============================================================================
2018
2019/// A ring buffer for continuous DMA reception.
2020///
2021/// This structure manages a circular DMA transfer, allowing continuous
2022/// reception of data without losing bytes between reads. It uses both
2023/// half-transfer and complete-transfer interrupts to track available data.
2024///
2025/// # Example
2026///
2027/// ```no_run
2028/// use embassy_mcxa::dma::{DmaChannel, RingBuffer, TransferOptions};
2029///
2030/// static mut RX_BUF: [u8; 64] = [0; 64];
2031///
2032/// let dma_ch = DmaChannel::new(p.DMA_CH0);
2033/// let ring_buf = unsafe {
2034/// dma_ch.setup_circular_read(
2035/// uart_rx_addr,
2036/// &mut RX_BUF,
2037/// )
2038/// };
2039///
2040/// // Read data as it arrives
2041/// let mut buf = [0u8; 16];
2042/// let n = ring_buf.read(&mut buf).await?;
2043/// ```
2044pub struct RingBuffer<'a, W: Word> {
2045 channel: AnyChannel,
2046 /// Buffer pointer. We use NonNull instead of &mut because DMA acts like
2047 /// a separate thread writing to this buffer, and &mut claims exclusive
2048 /// access which the compiler could optimize incorrectly.
2049 buf: NonNull<[W]>,
2050 /// Buffer length cached for convenience
2051 buf_len: usize,
2052 /// Read position in the buffer (consumer side)
2053 read_pos: AtomicUsize,
2054 /// Phantom data to tie the lifetime to the original buffer
2055 _lt: PhantomData<&'a mut [W]>,
2056}
2057
2058impl<'a, W: Word> RingBuffer<'a, W> {
2059 /// Create a new ring buffer for the given channel and buffer.
2060 ///
2061 /// # Safety
2062 ///
2063 /// The caller must ensure:
2064 /// - The DMA channel has been configured for circular transfer
2065 /// - The buffer remains valid for the lifetime of the ring buffer
2066 /// - Only one RingBuffer exists per DMA channel at a time
2067 pub(crate) unsafe fn new(channel: AnyChannel, buf: &'a mut [W]) -> Self {
2068 let buf_len = buf.len();
2069 Self {
2070 channel,
2071 buf: NonNull::from(buf),
2072 buf_len,
2073 read_pos: AtomicUsize::new(0),
2074 _lt: PhantomData,
2075 }
2076 }
2077
2078 /// Get a slice reference to the buffer.
2079 ///
2080 /// # Safety
2081 ///
2082 /// The caller must ensure that DMA is not actively writing to the
2083 /// portion of the buffer being accessed, or that the access is
2084 /// appropriately synchronized.
2085 #[inline]
2086 unsafe fn buf_slice(&self) -> &[W] {
2087 self.buf.as_ref()
2088 }
2089
2090 /// Get the current DMA write position in the buffer.
2091 ///
2092 /// This reads the current destination address from the DMA controller
2093 /// and calculates the buffer offset.
2094 fn dma_write_pos(&self) -> usize {
2095 let t = self.channel.tcd();
2096 let daddr = t.tcd_daddr().read().daddr().bits() as usize;
2097 let buf_start = self.buf.as_ptr() as *const W as usize;
2098
2099 // Calculate offset from buffer start
2100 let offset = daddr.wrapping_sub(buf_start) / core::mem::size_of::<W>();
2101
2102 // Ensure we're within bounds (DMA wraps around)
2103 offset % self.buf_len
2104 }
2105
2106 /// Returns the number of bytes available to read.
2107 pub fn available(&self) -> usize {
2108 let write_pos = self.dma_write_pos();
2109 let read_pos = self.read_pos.load(Ordering::Acquire);
2110
2111 if write_pos >= read_pos {
2112 write_pos - read_pos
2113 } else {
2114 self.buf_len - read_pos + write_pos
2115 }
2116 }
2117
2118 /// Check if the buffer has overrun (data was lost).
2119 ///
2120 /// This happens when DMA writes faster than the application reads.
2121 pub fn is_overrun(&self) -> bool {
2122 // In a true overrun, the DMA would have wrapped around and caught up
2123 // to our read position. We can detect this by checking if available()
2124 // equals the full buffer size (minus 1 to distinguish from empty).
2125 self.available() >= self.buf_len - 1
2126 }
2127
2128 /// Read data from the ring buffer into the provided slice.
2129 ///
2130 /// Returns the number of elements read, which may be less than
2131 /// `dst.len()` if not enough data is available.
2132 ///
2133 /// This method does not block; use `read_async()` for async waiting.
2134 pub fn read_immediate(&self, dst: &mut [W]) -> usize {
2135 let write_pos = self.dma_write_pos();
2136 let read_pos = self.read_pos.load(Ordering::Acquire);
2137
2138 // Calculate available bytes
2139 let available = if write_pos >= read_pos {
2140 write_pos - read_pos
2141 } else {
2142 self.buf_len - read_pos + write_pos
2143 };
2144
2145 let to_read = dst.len().min(available);
2146 if to_read == 0 {
2147 return 0;
2148 }
2149
2150 // Safety: We only read from portions of the buffer that DMA has
2151 // already written to (between read_pos and write_pos).
2152 let buf = unsafe { self.buf_slice() };
2153
2154 // Read data, handling wrap-around
2155 let first_chunk = (self.buf_len - read_pos).min(to_read);
2156 dst[..first_chunk].copy_from_slice(&buf[read_pos..read_pos + first_chunk]);
2157
2158 if to_read > first_chunk {
2159 let second_chunk = to_read - first_chunk;
2160 dst[first_chunk..to_read].copy_from_slice(&buf[..second_chunk]);
2161 }
2162
2163 // Update read position
2164 let new_read_pos = (read_pos + to_read) % self.buf_len;
2165 self.read_pos.store(new_read_pos, Ordering::Release);
2166
2167 to_read
2168 }
2169
2170 /// Read data from the ring buffer asynchronously.
2171 ///
2172 /// This waits until at least one byte is available, then reads as much
2173 /// as possible into the destination buffer.
2174 ///
2175 /// Returns the number of elements read.
2176 pub async fn read(&self, dst: &mut [W]) -> Result<usize, Error> {
2177 use core::future::poll_fn;
2178
2179 if dst.is_empty() {
2180 return Ok(0);
2181 }
2182
2183 poll_fn(|cx| {
2184 // Check for overrun
2185 if self.is_overrun() {
2186 return Poll::Ready(Err(Error::Overrun));
2187 }
2188
2189 // Try to read immediately
2190 let n = self.read_immediate(dst);
2191 if n > 0 {
2192 return Poll::Ready(Ok(n));
2193 }
2194
2195 // Register wakers for both half and complete interrupts
2196 let state = &STATES[self.channel.index()];
2197 state.waker.register(cx.waker());
2198 state.half_waker.register(cx.waker());
2199
2200 // Check again after registering waker (avoid race)
2201 let n = self.read_immediate(dst);
2202 if n > 0 {
2203 return Poll::Ready(Ok(n));
2204 }
2205
2206 Poll::Pending
2207 })
2208 .await
2209 }
2210
2211 /// Clear the ring buffer, discarding all unread data.
2212 pub fn clear(&self) {
2213 let write_pos = self.dma_write_pos();
2214 self.read_pos.store(write_pos, Ordering::Release);
2215 }
2216
2217 /// Stop the DMA transfer and consume the ring buffer.
2218 ///
2219 /// Returns any remaining unread data count.
2220 pub fn stop(mut self) -> usize {
2221 let res = self.teardown();
2222 drop(self);
2223 res
2224 }
2225
2226 /// Stop the DMA transfer. Intended to be called by `stop()` or `Drop`.
2227 fn teardown(&mut self) -> usize {
2228 let available = self.available();
2229
2230 // Disable the channel
2231 let t = self.channel.tcd();
2232 t.ch_csr().modify(|_, w| w.erq().disable());
2233
2234 // Clear flags
2235 t.ch_int().write(|w| w.int().clear_bit_by_one());
2236 t.ch_csr().modify(|_, w| w.done().clear_bit_by_one());
2237
2238 fence(Ordering::SeqCst);
2239
2240 available
2241 }
2242}
2243
2244impl<'a, W: Word> Drop for RingBuffer<'a, W> {
2245 fn drop(&mut self) {
2246 self.teardown();
2247 }
2248}
2249
2250impl<C: Channel> DmaChannel<C> {
2251 /// Set up a circular DMA transfer for continuous peripheral-to-memory reception.
2252 ///
2253 /// This configures the DMA channel for circular operation with both half-transfer
2254 /// and complete-transfer interrupts enabled. The transfer runs continuously until
2255 /// stopped via [`RingBuffer::stop()`].
2256 ///
2257 /// # Arguments
2258 ///
2259 /// * `peri_addr` - Peripheral register address to read from
2260 /// * `buf` - Destination buffer (should be power-of-2 size for best efficiency)
2261 ///
2262 /// # Returns
2263 ///
2264 /// A [`RingBuffer`] that can be used to read received data.
2265 ///
2266 /// # Safety
2267 ///
2268 /// - The buffer must remain valid for the lifetime of the returned RingBuffer.
2269 /// - The peripheral address must be valid for reads.
2270 /// - The peripheral's DMA request must be configured to trigger this channel.
2271 pub unsafe fn setup_circular_read<'a, W: Word>(&self, peri_addr: *const W, buf: &'a mut [W]) -> RingBuffer<'a, W> {
2272 assert!(!buf.is_empty());
2273 assert!(buf.len() <= 0x7fff);
2274 // For circular mode, buffer size should ideally be power of 2
2275 // but we don't enforce it
2276
2277 let size = W::size();
2278 let byte_size = size.bytes();
2279
2280 let t = self.tcd();
2281
2282 // Reset channel state
2283 Self::reset_channel_state(t);
2284
2285 // Source: peripheral register, fixed
2286 Self::set_source_ptr(t, peri_addr);
2287 Self::set_source_fixed(t);
2288
2289 // Destination: memory buffer, incrementing
2290 Self::set_dest_ptr(t, buf.as_mut_ptr());
2291 Self::set_dest_increment(t, size);
2292
2293 // Transfer attributes
2294 Self::set_even_transfer_size(t, size);
2295
2296 // Minor loop: transfer one word per request
2297 Self::set_minor_loop_ct_no_offsets(t, byte_size as u32);
2298
2299 // Major loop count = buffer size
2300 let count = buf.len() as u16;
2301 Self::set_major_loop_ct_elinkno(t, count);
2302
2303 // After major loop: reset destination to buffer start (circular)
2304 let buf_bytes = (buf.len() * byte_size) as i32;
2305 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); // Source doesn't change
2306 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits((-buf_bytes) as u32));
2307
2308 // Control/status: enable both half and complete interrupts, NO DREQ (continuous)
2309 t.tcd_csr().write(|w| {
2310 w.intmajor()
2311 .enable()
2312 .inthalf()
2313 .enable()
2314 .dreq()
2315 .channel_not_affected() // Don't clear ERQ on complete (circular)
2316 .esg()
2317 .normal_format()
2318 .majorelink()
2319 .disable()
2320 .eeop()
2321 .disable()
2322 .esda()
2323 .disable()
2324 .bwc()
2325 .no_stall()
2326 });
2327
2328 cortex_m::asm::dsb();
2329
2330 // Enable the channel request
2331 t.ch_csr().modify(|_, w| w.erq().enable());
2332
2333 // Enable NVIC interrupt for this channel so async wakeups work
2334 self.enable_interrupt();
2335
2336 RingBuffer::new(self.as_any(), buf)
2337 }
2338}
2339
2340// ============================================================================
2341// Scatter-Gather Builder
2342// ============================================================================
2343
2344/// Maximum number of TCDs in a scatter-gather chain.
2345pub const MAX_SCATTER_GATHER_TCDS: usize = 16;
2346
2347/// A builder for constructing scatter-gather DMA transfer chains.
2348///
2349/// This provides a type-safe way to build TCD chains for scatter-gather
2350/// transfers without manual TCD manipulation.
2351///
2352/// # Example
2353///
2354/// ```no_run
2355/// use embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder};
2356///
2357/// let mut builder = ScatterGatherBuilder::<u32>::new();
2358///
2359/// // Add transfer segments
2360/// builder.add_transfer(&src1, &mut dst1);
2361/// builder.add_transfer(&src2, &mut dst2);
2362/// builder.add_transfer(&src3, &mut dst3);
2363///
2364/// // Build and execute
2365/// let transfer = unsafe { builder.build(&dma_ch).unwrap() };
2366/// transfer.await;
2367/// ```
2368pub struct ScatterGatherBuilder<'a, W: Word> {
2369 /// TCD pool (must be 32-byte aligned)
2370 tcds: [Tcd; MAX_SCATTER_GATHER_TCDS],
2371 /// Number of TCDs configured
2372 count: usize,
2373 /// Phantom marker for word type
2374 _phantom: core::marker::PhantomData<W>,
2375
2376 _plt: core::marker::PhantomData<&'a mut W>,
2377}
2378
2379impl<'a, W: Word> ScatterGatherBuilder<'a, W> {
2380 /// Create a new scatter-gather builder.
2381 pub fn new() -> Self {
2382 ScatterGatherBuilder {
2383 tcds: [Tcd::default(); MAX_SCATTER_GATHER_TCDS],
2384 count: 0,
2385 _phantom: core::marker::PhantomData,
2386 _plt: core::marker::PhantomData,
2387 }
2388 }
2389
2390 /// Add a memory-to-memory transfer segment to the chain.
2391 ///
2392 /// # Arguments
2393 ///
2394 /// * `src` - Source buffer for this segment
2395 /// * `dst` - Destination buffer for this segment
2396 ///
2397 /// # Panics
2398 ///
2399 /// Panics if the maximum number of segments (16) is exceeded.
2400 pub fn add_transfer<'b: 'a>(&mut self, src: &'b [W], dst: &'b mut [W]) -> &mut Self {
2401 assert!(self.count < MAX_SCATTER_GATHER_TCDS, "Too many scatter-gather segments");
2402 assert!(!src.is_empty());
2403 assert!(dst.len() >= src.len());
2404
2405 let size = W::size();
2406 let byte_size = size.bytes();
2407 let hw_size = size.to_hw_size();
2408 let nbytes = (src.len() * byte_size) as u32;
2409
2410 // Build the TCD for this segment
2411 self.tcds[self.count] = Tcd {
2412 saddr: src.as_ptr() as u32,
2413 soff: byte_size as i16,
2414 attr: ((hw_size as u16) << 8) | (hw_size as u16), // SSIZE | DSIZE
2415 nbytes,
2416 slast: 0,
2417 daddr: dst.as_mut_ptr() as u32,
2418 doff: byte_size as i16,
2419 citer: 1,
2420 dlast_sga: 0, // Will be filled in by build()
2421 csr: 0x0002, // INTMAJOR only (ESG will be set for non-last TCDs)
2422 biter: 1,
2423 };
2424
2425 self.count += 1;
2426 self
2427 }
2428
2429 /// Get the number of transfer segments added.
2430 pub fn segment_count(&self) -> usize {
2431 self.count
2432 }
2433
2434 /// Build the scatter-gather chain and start the transfer.
2435 ///
2436 /// # Arguments
2437 ///
2438 /// * `channel` - The DMA channel to use for the transfer
2439 ///
2440 /// # Returns
2441 ///
2442 /// A `Transfer` future that completes when the entire chain has executed.
2443 pub fn build<C: Channel>(&mut self, channel: &DmaChannel<C>) -> Result<Transfer<'a>, Error> {
2444 if self.count == 0 {
2445 return Err(Error::Configuration);
2446 }
2447
2448 // Link TCDs together
2449 //
2450 // CSR bit definitions:
2451 // - START = bit 0 = 0x0001 (triggers transfer when set)
2452 // - INTMAJOR = bit 1 = 0x0002 (interrupt on major loop complete)
2453 // - ESG = bit 4 = 0x0010 (enable scatter-gather, loads next TCD on complete)
2454 //
2455 // When hardware loads a TCD via scatter-gather (ESG), it copies the TCD's
2456 // CSR directly into the hardware register. If START is not set in that CSR,
2457 // the hardware will NOT auto-execute the loaded TCD.
2458 //
2459 // Strategy:
2460 // - First TCD: ESG | INTMAJOR (no START - we add it manually after loading)
2461 // - Middle TCDs: ESG | INTMAJOR | START (auto-execute when loaded via S/G)
2462 // - Last TCD: INTMAJOR | START (auto-execute, no further linking)
2463 for i in 0..self.count {
2464 let is_first = i == 0;
2465 let is_last = i == self.count - 1;
2466
2467 if is_first {
2468 if is_last {
2469 // Only one TCD - no ESG, no START (we add START manually)
2470 self.tcds[i].dlast_sga = 0;
2471 self.tcds[i].csr = 0x0002; // INTMAJOR only
2472 } else {
2473 // First of multiple - ESG to link, no START (we add START manually)
2474 self.tcds[i].dlast_sga = &self.tcds[i + 1] as *const Tcd as i32;
2475 self.tcds[i].csr = 0x0012; // ESG | INTMAJOR
2476 }
2477 } else if is_last {
2478 // Last TCD (not first) - no ESG, but START so it auto-executes
2479 self.tcds[i].dlast_sga = 0;
2480 self.tcds[i].csr = 0x0003; // INTMAJOR | START
2481 } else {
2482 // Middle TCD - ESG to link, and START so it auto-executes
2483 self.tcds[i].dlast_sga = &self.tcds[i + 1] as *const Tcd as i32;
2484 self.tcds[i].csr = 0x0013; // ESG | INTMAJOR | START
2485 }
2486 }
2487
2488 let t = channel.tcd();
2489
2490 // Reset channel state - clear DONE, disable requests, clear errors
2491 // This ensures the channel is in a clean state before loading the TCD
2492 DmaChannel::<C>::reset_channel_state(t);
2493
2494 // Memory barrier to ensure channel state is reset before loading TCD
2495 cortex_m::asm::dsb();
2496
2497 // Load first TCD into hardware
2498 unsafe {
2499 channel.load_tcd(&self.tcds[0]);
2500 }
2501
2502 // Memory barrier before setting START
2503 cortex_m::asm::dsb();
2504
2505 // Start the transfer
2506 t.tcd_csr().modify(|_, w| w.start().channel_started());
2507
2508 Ok(Transfer::new(channel.as_any()))
2509 }
2510
2511 /// Reset the builder for reuse.
2512 pub fn clear(&mut self) {
2513 self.count = 0;
2514 }
2515}
2516
2517impl<W: Word> Default for ScatterGatherBuilder<'_, W> {
2518 fn default() -> Self {
2519 Self::new()
2520 }
2521}
2522
2523/// A completed scatter-gather transfer result.
2524///
2525/// This type is returned after a scatter-gather transfer completes,
2526/// providing access to any error information.
2527#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2528pub struct ScatterGatherResult {
2529 /// Number of segments successfully transferred
2530 pub segments_completed: usize,
2531 /// Error if any occurred
2532 pub error: Option<Error>,
2533}
2534
2535// ============================================================================
2536// Interrupt Handler
2537// ============================================================================
2538
2539/// Interrupt handler helper.
2540///
2541/// Call this from your interrupt handler to clear the interrupt flag and wake the waker.
2542/// This handles both half-transfer and complete-transfer interrupts.
2543///
2544/// # Safety
2545/// Must be called from the correct DMA channel interrupt context.
2546pub unsafe fn on_interrupt(ch_index: usize) {
2547 let p = pac::Peripherals::steal();
2548 let edma = &p.edma_0_tcd0;
2549 let t = edma.tcd(ch_index);
2550
2551 // Read TCD CSR to determine interrupt source
2552 let csr = t.tcd_csr().read();
2553
2554 // Check if this is a half-transfer interrupt
2555 // INTHALF is set and we're at or past the half-way point
2556 if csr.inthalf().bit_is_set() {
2557 let biter = t.tcd_biter_elinkno().read().biter().bits();
2558 let citer = t.tcd_citer_elinkno().read().citer().bits();
2559 let half_point = biter / 2;
2560
2561 if citer <= half_point && citer > 0 {
2562 // Half-transfer interrupt - wake half_waker
2563 half_waker(ch_index).wake();
2564 }
2565 }
2566
2567 // Clear INT flag
2568 t.ch_int().write(|w| w.int().clear_bit_by_one());
2569
2570 // If DONE is set, this is a complete-transfer interrupt
2571 // Only wake the full-transfer waker when the transfer is actually complete
2572 if t.ch_csr().read().done().bit_is_set() {
2573 waker(ch_index).wake();
2574 }
2575}
2576
2577// ============================================================================
2578// Type-level Interrupt Handlers
2579// ============================================================================
2580
2581/// Macro to generate DMA channel interrupt handlers.
2582macro_rules! impl_dma_interrupt_handler {
2583 ($irq:ident, $ch:expr) => {
2584 #[interrupt]
2585 fn $irq() {
2586 unsafe {
2587 on_interrupt($ch);
2588 }
2589 }
2590 };
2591}
2592
2593use crate::pac::interrupt;
2594
2595impl_dma_interrupt_handler!(DMA_CH0, 0);
2596impl_dma_interrupt_handler!(DMA_CH1, 1);
2597impl_dma_interrupt_handler!(DMA_CH2, 2);
2598impl_dma_interrupt_handler!(DMA_CH3, 3);
2599impl_dma_interrupt_handler!(DMA_CH4, 4);
2600impl_dma_interrupt_handler!(DMA_CH5, 5);
2601impl_dma_interrupt_handler!(DMA_CH6, 6);
2602impl_dma_interrupt_handler!(DMA_CH7, 7);
diff --git a/embassy-mcxa/src/gpio.rs b/embassy-mcxa/src/gpio.rs
new file mode 100644
index 000000000..65f8df985
--- /dev/null
+++ b/embassy-mcxa/src/gpio.rs
@@ -0,0 +1,1062 @@
1//! GPIO driver built around a type-erased `Flex` pin, similar to other Embassy HALs.
2//! The exported `Output`/`Input` drivers own a `Flex` so they no longer depend on the
3//! concrete pin type.
4
5use core::convert::Infallible;
6use core::future::Future;
7use core::marker::PhantomData;
8use core::pin::pin;
9
10use embassy_hal_internal::{Peri, PeripheralType};
11use maitake_sync::WaitMap;
12use paste::paste;
13
14use crate::pac::interrupt;
15use crate::pac::port0::pcr0::{Dse, Inv, Mux, Pe, Ps, Sre};
16
17struct BitIter(u32);
18
19impl Iterator for BitIter {
20 type Item = usize;
21
22 fn next(&mut self) -> Option<Self::Item> {
23 match self.0.trailing_zeros() {
24 32 => None,
25 b => {
26 self.0 &= !(1 << b);
27 Some(b as usize)
28 }
29 }
30 }
31}
32
33const PORT_COUNT: usize = 5;
34
35static PORT_WAIT_MAPS: [WaitMap<usize, ()>; PORT_COUNT] = [
36 WaitMap::new(),
37 WaitMap::new(),
38 WaitMap::new(),
39 WaitMap::new(),
40 WaitMap::new(),
41];
42
43fn irq_handler(port_index: usize, gpio_base: *const crate::pac::gpio0::RegisterBlock) {
44 let gpio = unsafe { &*gpio_base };
45 let isfr = gpio.isfr0().read().bits();
46
47 for pin in BitIter(isfr) {
48 // Clear all pending interrupts
49 gpio.isfr0().write(|w| unsafe { w.bits(1 << pin) });
50 gpio.icr(pin).modify(|_, w| w.irqc().irqc0()); // Disable interrupt
51
52 // Wake the corresponding port waker
53 if let Some(w) = PORT_WAIT_MAPS.get(port_index) {
54 w.wake(&pin, ());
55 }
56 }
57}
58
59#[interrupt]
60fn GPIO0() {
61 irq_handler(0, crate::pac::Gpio0::ptr());
62}
63
64#[interrupt]
65fn GPIO1() {
66 irq_handler(1, crate::pac::Gpio1::ptr());
67}
68
69#[interrupt]
70fn GPIO2() {
71 irq_handler(2, crate::pac::Gpio2::ptr());
72}
73
74#[interrupt]
75fn GPIO3() {
76 irq_handler(3, crate::pac::Gpio3::ptr());
77}
78
79#[interrupt]
80fn GPIO4() {
81 irq_handler(4, crate::pac::Gpio4::ptr());
82}
83
84pub(crate) unsafe fn init() {
85 use embassy_hal_internal::interrupt::InterruptExt;
86
87 crate::pac::interrupt::GPIO0.enable();
88 crate::pac::interrupt::GPIO1.enable();
89 crate::pac::interrupt::GPIO2.enable();
90 crate::pac::interrupt::GPIO3.enable();
91 crate::pac::interrupt::GPIO4.enable();
92
93 cortex_m::interrupt::enable();
94}
95
96/// Logical level for GPIO pins.
97#[derive(Copy, Clone, Eq, PartialEq, Debug)]
98#[cfg_attr(feature = "defmt", derive(defmt::Format))]
99pub enum Level {
100 Low,
101 High,
102}
103
104impl From<bool> for Level {
105 fn from(val: bool) -> Self {
106 match val {
107 true => Self::High,
108 false => Self::Low,
109 }
110 }
111}
112
113#[derive(Copy, Clone, Eq, PartialEq, Debug)]
114pub enum Pull {
115 Disabled,
116 Up,
117 Down,
118}
119
120impl From<Pull> for (Pe, Ps) {
121 fn from(pull: Pull) -> Self {
122 match pull {
123 Pull::Disabled => (Pe::Pe0, Ps::Ps0),
124 Pull::Up => (Pe::Pe1, Ps::Ps1),
125 Pull::Down => (Pe::Pe1, Ps::Ps0),
126 }
127 }
128}
129
130#[derive(Copy, Clone, Eq, PartialEq, Debug)]
131pub enum SlewRate {
132 Fast,
133 Slow,
134}
135
136impl From<SlewRate> for Sre {
137 fn from(slew_rate: SlewRate) -> Self {
138 match slew_rate {
139 SlewRate::Fast => Sre::Sre0,
140 SlewRate::Slow => Sre::Sre1,
141 }
142 }
143}
144
145#[derive(Copy, Clone, Eq, PartialEq, Debug)]
146pub enum DriveStrength {
147 Normal,
148 Double,
149}
150
151impl From<DriveStrength> for Dse {
152 fn from(strength: DriveStrength) -> Self {
153 match strength {
154 DriveStrength::Normal => Dse::Dse0,
155 DriveStrength::Double => Dse::Dse1,
156 }
157 }
158}
159
160#[derive(Copy, Clone, Eq, PartialEq, Debug)]
161pub enum Inverter {
162 Disabled,
163 Enabled,
164}
165
166impl From<Inverter> for Inv {
167 fn from(strength: Inverter) -> Self {
168 match strength {
169 Inverter::Disabled => Inv::Inv0,
170 Inverter::Enabled => Inv::Inv1,
171 }
172 }
173}
174
175pub type Gpio = crate::peripherals::GPIO0;
176
177/// Type-erased representation of a GPIO pin.
178pub struct AnyPin {
179 port: usize,
180 pin: usize,
181 gpio: &'static crate::pac::gpio0::RegisterBlock,
182 port_reg: &'static crate::pac::port0::RegisterBlock,
183 pcr_reg: &'static crate::pac::port0::Pcr0,
184}
185
186impl AnyPin {
187 /// Create an `AnyPin` from raw components.
188 fn new(
189 port: usize,
190 pin: usize,
191 gpio: &'static crate::pac::gpio0::RegisterBlock,
192 port_reg: &'static crate::pac::port0::RegisterBlock,
193 pcr_reg: &'static crate::pac::port0::Pcr0,
194 ) -> Self {
195 Self {
196 port,
197 pin,
198 gpio,
199 port_reg,
200 pcr_reg,
201 }
202 }
203
204 #[inline(always)]
205 fn mask(&self) -> u32 {
206 1 << self.pin
207 }
208
209 #[inline(always)]
210 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
211 self.gpio
212 }
213
214 #[inline(always)]
215 pub fn port_index(&self) -> usize {
216 self.port
217 }
218
219 #[inline(always)]
220 pub fn pin_index(&self) -> usize {
221 self.pin
222 }
223
224 #[inline(always)]
225 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock {
226 self.port_reg
227 }
228
229 #[inline(always)]
230 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 {
231 self.pcr_reg
232 }
233}
234
235embassy_hal_internal::impl_peripheral!(AnyPin);
236
237pub(crate) trait SealedPin {
238 fn pin_port(&self) -> usize;
239
240 fn port(&self) -> usize {
241 self.pin_port() / 32
242 }
243
244 fn pin(&self) -> usize {
245 self.pin_port() % 32
246 }
247
248 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock;
249
250 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock;
251
252 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0;
253
254 fn set_function(&self, function: Mux);
255
256 fn set_pull(&self, pull: Pull);
257
258 fn set_drive_strength(&self, strength: Dse);
259
260 fn set_slew_rate(&self, slew_rate: Sre);
261
262 fn set_enable_input_buffer(&self);
263}
264
265/// GPIO pin trait.
266#[allow(private_bounds)]
267pub trait GpioPin: SealedPin + Sized + PeripheralType + Into<AnyPin> + 'static {
268 /// Type-erase the pin.
269 fn degrade(self) -> AnyPin {
270 // SAFETY: This is only called within the GpioPin trait, which is only
271 // implemented within this module on valid pin peripherals and thus
272 // has been verified to be correct.
273 AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg())
274 }
275}
276
277impl SealedPin for AnyPin {
278 fn pin_port(&self) -> usize {
279 self.port * 32 + self.pin
280 }
281
282 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
283 self.gpio()
284 }
285
286 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock {
287 self.port_reg()
288 }
289
290 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 {
291 self.pcr_reg()
292 }
293
294 fn set_function(&self, function: Mux) {
295 self.pcr_reg().modify(|_, w| w.mux().variant(function));
296 }
297
298 fn set_pull(&self, pull: Pull) {
299 let (pull_enable, pull_select) = pull.into();
300 self.pcr_reg().modify(|_, w| {
301 w.pe().variant(pull_enable);
302 w.ps().variant(pull_select)
303 });
304 }
305
306 fn set_drive_strength(&self, strength: Dse) {
307 self.pcr_reg().modify(|_, w| w.dse().variant(strength));
308 }
309
310 fn set_slew_rate(&self, slew_rate: Sre) {
311 self.pcr_reg().modify(|_, w| w.sre().variant(slew_rate));
312 }
313
314 fn set_enable_input_buffer(&self) {
315 self.pcr_reg().modify(|_, w| w.ibe().ibe1());
316 }
317}
318
319impl GpioPin for AnyPin {}
320
321macro_rules! impl_pin {
322 ($peri:ident, $port:expr, $pin:expr, $block:ident) => {
323 paste! {
324 impl SealedPin for crate::peripherals::$peri {
325 fn pin_port(&self) -> usize {
326 $port * 32 + $pin
327 }
328
329 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
330 unsafe { &*crate::pac::$block::ptr() }
331 }
332
333 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock {
334 unsafe { &*crate::pac::[<Port $port>]::ptr() }
335 }
336
337 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 {
338 self.port_reg().[<pcr $pin>]()
339 }
340
341 fn set_function(&self, function: Mux) {
342 unsafe {
343 let port_reg = &*crate::pac::[<Port $port>]::ptr();
344 port_reg.[<pcr $pin>]().modify(|_, w| {
345 w.mux().variant(function)
346 });
347 }
348 }
349
350 fn set_pull(&self, pull: Pull) {
351 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
352 let (pull_enable, pull_select) = pull.into();
353 port_reg.[<pcr $pin>]().modify(|_, w| {
354 w.pe().variant(pull_enable);
355 w.ps().variant(pull_select)
356 });
357 }
358
359 fn set_drive_strength(&self, strength: Dse) {
360 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
361 port_reg.[<pcr $pin>]().modify(|_, w| w.dse().variant(strength));
362 }
363
364 fn set_slew_rate(&self, slew_rate: Sre) {
365 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
366 port_reg.[<pcr $pin>]().modify(|_, w| w.sre().variant(slew_rate));
367 }
368
369 fn set_enable_input_buffer(&self) {
370 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
371 port_reg.[<pcr $pin>]().modify(|_, w| w.ibe().ibe1());
372 }
373 }
374
375 impl GpioPin for crate::peripherals::$peri {}
376
377 impl From<crate::peripherals::$peri> for AnyPin {
378 fn from(value: crate::peripherals::$peri) -> Self {
379 value.degrade()
380 }
381 }
382
383 impl crate::peripherals::$peri {
384 /// Convenience helper to obtain a type-erased handle to this pin.
385 pub fn degrade(&self) -> AnyPin {
386 AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg())
387 }
388 }
389 }
390 };
391}
392
393impl_pin!(P0_0, 0, 0, Gpio0);
394impl_pin!(P0_1, 0, 1, Gpio0);
395impl_pin!(P0_2, 0, 2, Gpio0);
396impl_pin!(P0_3, 0, 3, Gpio0);
397impl_pin!(P0_4, 0, 4, Gpio0);
398impl_pin!(P0_5, 0, 5, Gpio0);
399impl_pin!(P0_6, 0, 6, Gpio0);
400impl_pin!(P0_7, 0, 7, Gpio0);
401impl_pin!(P0_8, 0, 8, Gpio0);
402impl_pin!(P0_9, 0, 9, Gpio0);
403impl_pin!(P0_10, 0, 10, Gpio0);
404impl_pin!(P0_11, 0, 11, Gpio0);
405impl_pin!(P0_12, 0, 12, Gpio0);
406impl_pin!(P0_13, 0, 13, Gpio0);
407impl_pin!(P0_14, 0, 14, Gpio0);
408impl_pin!(P0_15, 0, 15, Gpio0);
409impl_pin!(P0_16, 0, 16, Gpio0);
410impl_pin!(P0_17, 0, 17, Gpio0);
411impl_pin!(P0_18, 0, 18, Gpio0);
412impl_pin!(P0_19, 0, 19, Gpio0);
413impl_pin!(P0_20, 0, 20, Gpio0);
414impl_pin!(P0_21, 0, 21, Gpio0);
415impl_pin!(P0_22, 0, 22, Gpio0);
416impl_pin!(P0_23, 0, 23, Gpio0);
417impl_pin!(P0_24, 0, 24, Gpio0);
418impl_pin!(P0_25, 0, 25, Gpio0);
419impl_pin!(P0_26, 0, 26, Gpio0);
420impl_pin!(P0_27, 0, 27, Gpio0);
421impl_pin!(P0_28, 0, 28, Gpio0);
422impl_pin!(P0_29, 0, 29, Gpio0);
423impl_pin!(P0_30, 0, 30, Gpio0);
424impl_pin!(P0_31, 0, 31, Gpio0);
425
426impl_pin!(P1_0, 1, 0, Gpio1);
427impl_pin!(P1_1, 1, 1, Gpio1);
428impl_pin!(P1_2, 1, 2, Gpio1);
429impl_pin!(P1_3, 1, 3, Gpio1);
430impl_pin!(P1_4, 1, 4, Gpio1);
431impl_pin!(P1_5, 1, 5, Gpio1);
432impl_pin!(P1_6, 1, 6, Gpio1);
433impl_pin!(P1_7, 1, 7, Gpio1);
434impl_pin!(P1_8, 1, 8, Gpio1);
435impl_pin!(P1_9, 1, 9, Gpio1);
436impl_pin!(P1_10, 1, 10, Gpio1);
437impl_pin!(P1_11, 1, 11, Gpio1);
438impl_pin!(P1_12, 1, 12, Gpio1);
439impl_pin!(P1_13, 1, 13, Gpio1);
440impl_pin!(P1_14, 1, 14, Gpio1);
441impl_pin!(P1_15, 1, 15, Gpio1);
442impl_pin!(P1_16, 1, 16, Gpio1);
443impl_pin!(P1_17, 1, 17, Gpio1);
444impl_pin!(P1_18, 1, 18, Gpio1);
445impl_pin!(P1_19, 1, 19, Gpio1);
446impl_pin!(P1_20, 1, 20, Gpio1);
447impl_pin!(P1_21, 1, 21, Gpio1);
448impl_pin!(P1_22, 1, 22, Gpio1);
449impl_pin!(P1_23, 1, 23, Gpio1);
450impl_pin!(P1_24, 1, 24, Gpio1);
451impl_pin!(P1_25, 1, 25, Gpio1);
452impl_pin!(P1_26, 1, 26, Gpio1);
453impl_pin!(P1_27, 1, 27, Gpio1);
454impl_pin!(P1_28, 1, 28, Gpio1);
455impl_pin!(P1_29, 1, 29, Gpio1);
456impl_pin!(P1_30, 1, 30, Gpio1);
457impl_pin!(P1_31, 1, 31, Gpio1);
458
459impl_pin!(P2_0, 2, 0, Gpio2);
460impl_pin!(P2_1, 2, 1, Gpio2);
461impl_pin!(P2_2, 2, 2, Gpio2);
462impl_pin!(P2_3, 2, 3, Gpio2);
463impl_pin!(P2_4, 2, 4, Gpio2);
464impl_pin!(P2_5, 2, 5, Gpio2);
465impl_pin!(P2_6, 2, 6, Gpio2);
466impl_pin!(P2_7, 2, 7, Gpio2);
467impl_pin!(P2_8, 2, 8, Gpio2);
468impl_pin!(P2_9, 2, 9, Gpio2);
469impl_pin!(P2_10, 2, 10, Gpio2);
470impl_pin!(P2_11, 2, 11, Gpio2);
471impl_pin!(P2_12, 2, 12, Gpio2);
472impl_pin!(P2_13, 2, 13, Gpio2);
473impl_pin!(P2_14, 2, 14, Gpio2);
474impl_pin!(P2_15, 2, 15, Gpio2);
475impl_pin!(P2_16, 2, 16, Gpio2);
476impl_pin!(P2_17, 2, 17, Gpio2);
477impl_pin!(P2_18, 2, 18, Gpio2);
478impl_pin!(P2_19, 2, 19, Gpio2);
479impl_pin!(P2_20, 2, 20, Gpio2);
480impl_pin!(P2_21, 2, 21, Gpio2);
481impl_pin!(P2_22, 2, 22, Gpio2);
482impl_pin!(P2_23, 2, 23, Gpio2);
483impl_pin!(P2_24, 2, 24, Gpio2);
484impl_pin!(P2_25, 2, 25, Gpio2);
485impl_pin!(P2_26, 2, 26, Gpio2);
486impl_pin!(P2_27, 2, 27, Gpio2);
487impl_pin!(P2_28, 2, 28, Gpio2);
488impl_pin!(P2_29, 2, 29, Gpio2);
489impl_pin!(P2_30, 2, 30, Gpio2);
490impl_pin!(P2_31, 2, 31, Gpio2);
491
492impl_pin!(P3_0, 3, 0, Gpio3);
493impl_pin!(P3_1, 3, 1, Gpio3);
494impl_pin!(P3_2, 3, 2, Gpio3);
495impl_pin!(P3_3, 3, 3, Gpio3);
496impl_pin!(P3_4, 3, 4, Gpio3);
497impl_pin!(P3_5, 3, 5, Gpio3);
498impl_pin!(P3_6, 3, 6, Gpio3);
499impl_pin!(P3_7, 3, 7, Gpio3);
500impl_pin!(P3_8, 3, 8, Gpio3);
501impl_pin!(P3_9, 3, 9, Gpio3);
502impl_pin!(P3_10, 3, 10, Gpio3);
503impl_pin!(P3_11, 3, 11, Gpio3);
504impl_pin!(P3_12, 3, 12, Gpio3);
505impl_pin!(P3_13, 3, 13, Gpio3);
506impl_pin!(P3_14, 3, 14, Gpio3);
507impl_pin!(P3_15, 3, 15, Gpio3);
508impl_pin!(P3_16, 3, 16, Gpio3);
509impl_pin!(P3_17, 3, 17, Gpio3);
510impl_pin!(P3_18, 3, 18, Gpio3);
511impl_pin!(P3_19, 3, 19, Gpio3);
512impl_pin!(P3_20, 3, 20, Gpio3);
513impl_pin!(P3_21, 3, 21, Gpio3);
514impl_pin!(P3_22, 3, 22, Gpio3);
515impl_pin!(P3_23, 3, 23, Gpio3);
516impl_pin!(P3_24, 3, 24, Gpio3);
517impl_pin!(P3_25, 3, 25, Gpio3);
518impl_pin!(P3_26, 3, 26, Gpio3);
519impl_pin!(P3_27, 3, 27, Gpio3);
520impl_pin!(P3_28, 3, 28, Gpio3);
521impl_pin!(P3_29, 3, 29, Gpio3);
522impl_pin!(P3_30, 3, 30, Gpio3);
523impl_pin!(P3_31, 3, 31, Gpio3);
524
525impl_pin!(P4_0, 4, 0, Gpio4);
526impl_pin!(P4_1, 4, 1, Gpio4);
527impl_pin!(P4_2, 4, 2, Gpio4);
528impl_pin!(P4_3, 4, 3, Gpio4);
529impl_pin!(P4_4, 4, 4, Gpio4);
530impl_pin!(P4_5, 4, 5, Gpio4);
531impl_pin!(P4_6, 4, 6, Gpio4);
532impl_pin!(P4_7, 4, 7, Gpio4);
533impl_pin!(P4_8, 4, 8, Gpio4);
534impl_pin!(P4_9, 4, 9, Gpio4);
535impl_pin!(P4_10, 4, 10, Gpio4);
536impl_pin!(P4_11, 4, 11, Gpio4);
537impl_pin!(P4_12, 4, 12, Gpio4);
538impl_pin!(P4_13, 4, 13, Gpio4);
539impl_pin!(P4_14, 4, 14, Gpio4);
540impl_pin!(P4_15, 4, 15, Gpio4);
541impl_pin!(P4_16, 4, 16, Gpio4);
542impl_pin!(P4_17, 4, 17, Gpio4);
543impl_pin!(P4_18, 4, 18, Gpio4);
544impl_pin!(P4_19, 4, 19, Gpio4);
545impl_pin!(P4_20, 4, 20, Gpio4);
546impl_pin!(P4_21, 4, 21, Gpio4);
547impl_pin!(P4_22, 4, 22, Gpio4);
548impl_pin!(P4_23, 4, 23, Gpio4);
549impl_pin!(P4_24, 4, 24, Gpio4);
550impl_pin!(P4_25, 4, 25, Gpio4);
551impl_pin!(P4_26, 4, 26, Gpio4);
552impl_pin!(P4_27, 4, 27, Gpio4);
553impl_pin!(P4_28, 4, 28, Gpio4);
554impl_pin!(P4_29, 4, 29, Gpio4);
555impl_pin!(P4_30, 4, 30, Gpio4);
556impl_pin!(P4_31, 4, 31, Gpio4);
557
558/// A flexible pin that can be configured as input or output.
559pub struct Flex<'d> {
560 pin: Peri<'d, AnyPin>,
561 _marker: PhantomData<&'d mut ()>,
562}
563
564impl<'d> Flex<'d> {
565 /// Wrap the pin in a `Flex`.
566 ///
567 /// The pin remains unmodified. The initial output level is unspecified, but
568 /// can be changed before the pin is put into output mode.
569 pub fn new(pin: Peri<'d, impl GpioPin>) -> Self {
570 pin.set_function(Mux::Mux0);
571 Self {
572 pin: pin.into(),
573 _marker: PhantomData,
574 }
575 }
576
577 #[inline]
578 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
579 self.pin.gpio()
580 }
581
582 #[inline]
583 fn mask(&self) -> u32 {
584 self.pin.mask()
585 }
586
587 /// Put the pin into input mode.
588 pub fn set_as_input(&mut self) {
589 let mask = self.mask();
590 let gpio = self.gpio();
591
592 self.set_enable_input_buffer();
593
594 gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() & !mask) });
595 }
596
597 /// Put the pin into output mode.
598 pub fn set_as_output(&mut self) {
599 let mask = self.mask();
600 let gpio = self.gpio();
601
602 self.set_pull(Pull::Disabled);
603
604 gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
605 }
606
607 /// Set output level to High.
608 #[inline]
609 pub fn set_high(&mut self) {
610 self.gpio().psor().write(|w| unsafe { w.bits(self.mask()) });
611 }
612
613 /// Set output level to Low.
614 #[inline]
615 pub fn set_low(&mut self) {
616 self.gpio().pcor().write(|w| unsafe { w.bits(self.mask()) });
617 }
618
619 /// Set output level to the given `Level`.
620 #[inline]
621 pub fn set_level(&mut self, level: Level) {
622 match level {
623 Level::High => self.set_high(),
624 Level::Low => self.set_low(),
625 }
626 }
627
628 /// Toggle output level.
629 #[inline]
630 pub fn toggle(&mut self) {
631 self.gpio().ptor().write(|w| unsafe { w.bits(self.mask()) });
632 }
633
634 /// Get whether the pin input level is high.
635 #[inline]
636 pub fn is_high(&self) -> bool {
637 (self.gpio().pdir().read().bits() & self.mask()) != 0
638 }
639
640 /// Get whether the pin input level is low.
641 #[inline]
642 pub fn is_low(&self) -> bool {
643 !self.is_high()
644 }
645
646 /// Is the output pin set as high?
647 #[inline]
648 pub fn is_set_high(&self) -> bool {
649 self.is_high()
650 }
651
652 /// Is the output pin set as low?
653 #[inline]
654 pub fn is_set_low(&self) -> bool {
655 !self.is_set_high()
656 }
657
658 /// Configure the pin pull up/down level.
659 pub fn set_pull(&mut self, pull_select: Pull) {
660 self.pin.set_pull(pull_select);
661 }
662
663 /// Configure the pin drive strength.
664 pub fn set_drive_strength(&mut self, strength: DriveStrength) {
665 self.pin.set_drive_strength(strength.into());
666 }
667
668 /// Configure the pin slew rate.
669 pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {
670 self.pin.set_slew_rate(slew_rate.into());
671 }
672
673 /// Enable input buffer for the pin.
674 pub fn set_enable_input_buffer(&mut self) {
675 self.pin.set_enable_input_buffer();
676 }
677
678 /// Get pin level.
679 pub fn get_level(&self) -> Level {
680 self.is_high().into()
681 }
682}
683
684/// Async methods
685impl<'d> Flex<'d> {
686 /// Helper function that waits for a given interrupt trigger
687 async fn wait_for_inner(&mut self, level: crate::pac::gpio0::icr::Irqc) {
688 // First, ensure that we have a waker that is ready for this port+pin
689 let w = PORT_WAIT_MAPS[self.pin.port].wait(self.pin.pin);
690 let mut w = pin!(w);
691 // Wait for the subscription to occur, which requires polling at least once
692 //
693 // This function returns a result, but can only be an Err if:
694 //
695 // * We call `.close()` on a WaitMap, which we never do
696 // * We have a duplicate key, which can't happen because `wait_for_*` methods
697 // take an &mut ref of their unique port+pin combo
698 //
699 // So we wait for it to complete, but ignore the result.
700 _ = w.as_mut().subscribe().await;
701
702 // Now that our waker is in the map, we can enable the appropriate interrupt
703 //
704 // Clear any existing pending interrupt on this pin
705 self.pin
706 .gpio()
707 .isfr0()
708 .write(|w| unsafe { w.bits(1 << self.pin.pin()) });
709 self.pin.gpio().icr(self.pin.pin()).write(|w| w.isf().isf1());
710
711 // Pin interrupt configuration
712 self.pin
713 .gpio()
714 .icr(self.pin.pin())
715 .modify(|_, w| w.irqc().variant(level));
716
717 // Finally, we can await the matching call to `.wake()` from the interrupt.
718 //
719 // Again, technically, this could return a result, but for the same reasons
720 // as above, this can't be an error in our case, so just wait for it to complete
721 _ = w.await;
722 }
723
724 /// Wait until the pin is high. If it is already high, return immediately.
725 #[inline]
726 pub fn wait_for_high(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
727 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc12)
728 }
729
730 /// Wait until the pin is low. If it is already low, return immediately.
731 #[inline]
732 pub fn wait_for_low(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
733 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc8)
734 }
735
736 /// Wait for the pin to undergo a transition from low to high.
737 #[inline]
738 pub fn wait_for_rising_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
739 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc9)
740 }
741
742 /// Wait for the pin to undergo a transition from high to low.
743 #[inline]
744 pub fn wait_for_falling_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
745 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc10)
746 }
747
748 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
749 #[inline]
750 pub fn wait_for_any_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
751 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc11)
752 }
753}
754
755/// GPIO output driver that owns a `Flex` pin.
756pub struct Output<'d> {
757 flex: Flex<'d>,
758}
759
760impl<'d> Output<'d> {
761 /// Create a GPIO output driver for a [GpioPin] with the provided [Level].
762 pub fn new(pin: Peri<'d, impl GpioPin>, initial: Level, strength: DriveStrength, slew_rate: SlewRate) -> Self {
763 let mut flex = Flex::new(pin);
764 flex.set_level(initial);
765 flex.set_as_output();
766 flex.set_drive_strength(strength);
767 flex.set_slew_rate(slew_rate);
768 Self { flex }
769 }
770
771 /// Set the output as high.
772 #[inline]
773 pub fn set_high(&mut self) {
774 self.flex.set_high();
775 }
776
777 /// Set the output as low.
778 #[inline]
779 pub fn set_low(&mut self) {
780 self.flex.set_low();
781 }
782
783 /// Set the output level.
784 #[inline]
785 pub fn set_level(&mut self, level: Level) {
786 self.flex.set_level(level);
787 }
788
789 /// Toggle the output level.
790 #[inline]
791 pub fn toggle(&mut self) {
792 self.flex.toggle();
793 }
794
795 /// Is the output pin set as high?
796 #[inline]
797 pub fn is_set_high(&self) -> bool {
798 self.flex.is_high()
799 }
800
801 /// Is the output pin set as low?
802 #[inline]
803 pub fn is_set_low(&self) -> bool {
804 !self.is_set_high()
805 }
806
807 /// Expose the inner `Flex` if callers need to reconfigure the pin.
808 #[inline]
809 pub fn into_flex(self) -> Flex<'d> {
810 self.flex
811 }
812}
813
814/// GPIO input driver that owns a `Flex` pin.
815pub struct Input<'d> {
816 flex: Flex<'d>,
817}
818
819impl<'d> Input<'d> {
820 /// Create a GPIO input driver for a [GpioPin].
821 ///
822 pub fn new(pin: Peri<'d, impl GpioPin>, pull_select: Pull) -> Self {
823 let mut flex = Flex::new(pin);
824 flex.set_as_input();
825 flex.set_pull(pull_select);
826 Self { flex }
827 }
828
829 /// Get whether the pin input level is high.
830 #[inline]
831 pub fn is_high(&self) -> bool {
832 self.flex.is_high()
833 }
834
835 /// Get whether the pin input level is low.
836 #[inline]
837 pub fn is_low(&self) -> bool {
838 self.flex.is_low()
839 }
840
841 /// Expose the inner `Flex` if callers need to reconfigure the pin.
842 ///
843 /// Since Drive Strength and Slew Rate are not set when creating the Input
844 /// pin, they need to be set when converting
845 #[inline]
846 pub fn into_flex(mut self, strength: DriveStrength, slew_rate: SlewRate) -> Flex<'d> {
847 self.flex.set_drive_strength(strength);
848 self.flex.set_slew_rate(slew_rate);
849 self.flex
850 }
851
852 /// Get the pin level.
853 pub fn get_level(&self) -> Level {
854 self.flex.get_level()
855 }
856}
857
858/// Async methods
859impl<'d> Input<'d> {
860 /// Wait until the pin is high. If it is already high, return immediately.
861 #[inline]
862 pub fn wait_for_high(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
863 self.flex.wait_for_high()
864 }
865
866 /// Wait until the pin is low. If it is already low, return immediately.
867 #[inline]
868 pub fn wait_for_low(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
869 self.flex.wait_for_low()
870 }
871
872 /// Wait for the pin to undergo a transition from low to high.
873 #[inline]
874 pub fn wait_for_rising_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
875 self.flex.wait_for_rising_edge()
876 }
877
878 /// Wait for the pin to undergo a transition from high to low.
879 #[inline]
880 pub fn wait_for_falling_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
881 self.flex.wait_for_falling_edge()
882 }
883
884 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
885 #[inline]
886 pub fn wait_for_any_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
887 self.flex.wait_for_any_edge()
888 }
889}
890
891impl embedded_hal_async::digital::Wait for Input<'_> {
892 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
893 self.wait_for_high().await;
894 Ok(())
895 }
896
897 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
898 self.wait_for_low().await;
899 Ok(())
900 }
901
902 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
903 self.wait_for_rising_edge().await;
904 Ok(())
905 }
906
907 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
908 self.wait_for_falling_edge().await;
909 Ok(())
910 }
911
912 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
913 self.wait_for_any_edge().await;
914 Ok(())
915 }
916}
917
918impl embedded_hal_async::digital::Wait for Flex<'_> {
919 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
920 self.wait_for_high().await;
921 Ok(())
922 }
923
924 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
925 self.wait_for_low().await;
926 Ok(())
927 }
928
929 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
930 self.wait_for_rising_edge().await;
931 Ok(())
932 }
933
934 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
935 self.wait_for_falling_edge().await;
936 Ok(())
937 }
938
939 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
940 self.wait_for_any_edge().await;
941 Ok(())
942 }
943}
944
945// Both embedded_hal 0.2 and 1.0 must be supported by embassy HALs.
946impl embedded_hal_02::digital::v2::InputPin for Flex<'_> {
947 // GPIO operations on this block cannot fail, therefor we set the error type
948 // to Infallible to guarantee that we can only produce Ok variants.
949 type Error = Infallible;
950
951 #[inline]
952 fn is_high(&self) -> Result<bool, Self::Error> {
953 Ok(self.is_high())
954 }
955
956 #[inline]
957 fn is_low(&self) -> Result<bool, Self::Error> {
958 Ok(self.is_low())
959 }
960}
961
962impl embedded_hal_02::digital::v2::InputPin for Input<'_> {
963 type Error = Infallible;
964
965 #[inline]
966 fn is_high(&self) -> Result<bool, Self::Error> {
967 Ok(self.is_high())
968 }
969
970 #[inline]
971 fn is_low(&self) -> Result<bool, Self::Error> {
972 Ok(self.is_low())
973 }
974}
975
976impl embedded_hal_02::digital::v2::OutputPin for Flex<'_> {
977 type Error = Infallible;
978
979 #[inline]
980 fn set_high(&mut self) -> Result<(), Self::Error> {
981 self.set_high();
982 Ok(())
983 }
984
985 #[inline]
986 fn set_low(&mut self) -> Result<(), Self::Error> {
987 self.set_low();
988 Ok(())
989 }
990}
991
992impl embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'_> {
993 #[inline]
994 fn is_set_high(&self) -> Result<bool, Self::Error> {
995 Ok(self.is_set_high())
996 }
997
998 #[inline]
999 fn is_set_low(&self) -> Result<bool, Self::Error> {
1000 Ok(self.is_set_low())
1001 }
1002}
1003
1004impl embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'_> {
1005 type Error = Infallible;
1006
1007 #[inline]
1008 fn toggle(&mut self) -> Result<(), Self::Error> {
1009 self.toggle();
1010 Ok(())
1011 }
1012}
1013
1014impl embedded_hal_1::digital::ErrorType for Flex<'_> {
1015 type Error = Infallible;
1016}
1017
1018impl embedded_hal_1::digital::ErrorType for Input<'_> {
1019 type Error = Infallible;
1020}
1021
1022impl embedded_hal_1::digital::ErrorType for Output<'_> {
1023 type Error = Infallible;
1024}
1025
1026impl embedded_hal_1::digital::InputPin for Input<'_> {
1027 #[inline]
1028 fn is_high(&mut self) -> Result<bool, Self::Error> {
1029 Ok((*self).is_high())
1030 }
1031
1032 #[inline]
1033 fn is_low(&mut self) -> Result<bool, Self::Error> {
1034 Ok((*self).is_low())
1035 }
1036}
1037
1038impl embedded_hal_1::digital::OutputPin for Flex<'_> {
1039 #[inline]
1040 fn set_high(&mut self) -> Result<(), Self::Error> {
1041 self.set_high();
1042 Ok(())
1043 }
1044
1045 #[inline]
1046 fn set_low(&mut self) -> Result<(), Self::Error> {
1047 self.set_low();
1048 Ok(())
1049 }
1050}
1051
1052impl embedded_hal_1::digital::StatefulOutputPin for Flex<'_> {
1053 #[inline]
1054 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
1055 Ok((*self).is_set_high())
1056 }
1057
1058 #[inline]
1059 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
1060 Ok((*self).is_set_low())
1061 }
1062}
diff --git a/embassy-mcxa/src/i2c/controller.rs b/embassy-mcxa/src/i2c/controller.rs
new file mode 100644
index 000000000..c27d508b0
--- /dev/null
+++ b/embassy-mcxa/src/i2c/controller.rs
@@ -0,0 +1,742 @@
1//! LPI2C controller driver
2
3use core::future::Future;
4use core::marker::PhantomData;
5
6use embassy_hal_internal::Peri;
7use embassy_hal_internal::drop::OnDrop;
8use mcxa_pac::lpi2c0::mtdr::Cmd;
9
10use super::{Async, Blocking, Error, Instance, InterruptHandler, Mode, Result, SclPin, SdaPin};
11use crate::AnyPin;
12use crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig};
13use crate::clocks::{PoweredClock, enable_and_reset};
14use crate::interrupt::typelevel::Interrupt;
15
16/// Bus speed (nominal SCL, no clock stretching)
17#[derive(Clone, Copy, Default, PartialEq)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub enum Speed {
20 #[default]
21 /// 100 kbit/sec
22 Standard,
23 /// 400 kbit/sec
24 Fast,
25 /// 1 Mbit/sec
26 FastPlus,
27 /// 3.4 Mbit/sec
28 UltraFast,
29}
30
31impl From<Speed> for (u8, u8, u8, u8) {
32 fn from(value: Speed) -> (u8, u8, u8, u8) {
33 match value {
34 Speed::Standard => (0x3d, 0x37, 0x3b, 0x1d),
35 Speed::Fast => (0x0e, 0x0c, 0x0d, 0x06),
36 Speed::FastPlus => (0x04, 0x03, 0x03, 0x02),
37
38 // UltraFast is "special". Leaving it unimplemented until
39 // the driver and the clock API is further stabilized.
40 Speed::UltraFast => todo!(),
41 }
42 }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
47enum SendStop {
48 No,
49 Yes,
50}
51
52/// I2C controller configuration
53#[derive(Clone, Copy, Default)]
54#[non_exhaustive]
55pub struct Config {
56 /// Bus speed
57 pub speed: Speed,
58}
59
60/// I2C Controller Driver.
61pub struct I2c<'d, T: Instance, M: Mode> {
62 _peri: Peri<'d, T>,
63 _scl: Peri<'d, AnyPin>,
64 _sda: Peri<'d, AnyPin>,
65 _phantom: PhantomData<M>,
66 is_hs: bool,
67}
68
69impl<'d, T: Instance> I2c<'d, T, Blocking> {
70 /// Create a new blocking instance of the I2C Controller bus driver.
71 pub fn new_blocking(
72 peri: Peri<'d, T>,
73 scl: Peri<'d, impl SclPin<T>>,
74 sda: Peri<'d, impl SdaPin<T>>,
75 config: Config,
76 ) -> Result<Self> {
77 Self::new_inner(peri, scl, sda, config)
78 }
79}
80
81impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
82 fn new_inner(
83 _peri: Peri<'d, T>,
84 scl: Peri<'d, impl SclPin<T>>,
85 sda: Peri<'d, impl SdaPin<T>>,
86 config: Config,
87 ) -> Result<Self> {
88 let (power, source, div) = Self::clock_config(config.speed);
89
90 // Enable clocks
91 let conf = Lpi2cConfig {
92 power,
93 source,
94 div,
95 instance: T::CLOCK_INSTANCE,
96 };
97
98 _ = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
99
100 scl.mux();
101 sda.mux();
102
103 let _scl = scl.into();
104 let _sda = sda.into();
105
106 Self::set_config(&config)?;
107
108 Ok(Self {
109 _peri,
110 _scl,
111 _sda,
112 _phantom: PhantomData,
113 is_hs: config.speed == Speed::UltraFast,
114 })
115 }
116
117 fn set_config(config: &Config) -> Result<()> {
118 // Disable the controller.
119 critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().disabled()));
120
121 // Soft-reset the controller, read and write FIFOs.
122 Self::reset_fifos();
123 critical_section::with(|_| {
124 T::regs().mcr().modify(|_, w| w.rst().reset());
125 // According to Reference Manual section 40.7.1.4, "There
126 // is no minimum delay required before clearing the
127 // software reset", therefore we clear it immediately.
128 T::regs().mcr().modify(|_, w| w.rst().not_reset());
129
130 T::regs().mcr().modify(|_, w| w.dozen().clear_bit().dbgen().clear_bit());
131 });
132
133 let (clklo, clkhi, sethold, datavd) = config.speed.into();
134
135 critical_section::with(|_| {
136 T::regs().mccr0().modify(|_, w| unsafe {
137 w.clklo()
138 .bits(clklo)
139 .clkhi()
140 .bits(clkhi)
141 .sethold()
142 .bits(sethold)
143 .datavd()
144 .bits(datavd)
145 })
146 });
147
148 // Enable the controller.
149 critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().enabled()));
150
151 // Clear all flags
152 T::regs().msr().write(|w| {
153 w.epf()
154 .clear_bit_by_one()
155 .sdf()
156 .clear_bit_by_one()
157 .ndf()
158 .clear_bit_by_one()
159 .alf()
160 .clear_bit_by_one()
161 .fef()
162 .clear_bit_by_one()
163 .pltf()
164 .clear_bit_by_one()
165 .dmf()
166 .clear_bit_by_one()
167 .stf()
168 .clear_bit_by_one()
169 });
170
171 Ok(())
172 }
173
174 // REVISIT: turn this into a function of the speed parameter
175 fn clock_config(speed: Speed) -> (PoweredClock, Lpi2cClockSel, Div4) {
176 match speed {
177 Speed::Standard | Speed::Fast | Speed::FastPlus => (
178 PoweredClock::NormalEnabledDeepSleepDisabled,
179 Lpi2cClockSel::FroLfDiv,
180 const { Div4::no_div() },
181 ),
182 Speed::UltraFast => (
183 PoweredClock::NormalEnabledDeepSleepDisabled,
184 Lpi2cClockSel::FroHfDiv,
185 const { Div4::no_div() },
186 ),
187 }
188 }
189
190 /// Resets both TX and RX FIFOs dropping their contents.
191 fn reset_fifos() {
192 critical_section::with(|_| {
193 T::regs().mcr().modify(|_, w| w.rtf().reset().rrf().reset());
194 });
195 }
196
197 /// Checks whether the TX FIFO is full
198 fn is_tx_fifo_full() -> bool {
199 let txfifo_size = 1 << T::regs().param().read().mtxfifo().bits();
200 T::regs().mfsr().read().txcount().bits() == txfifo_size
201 }
202
203 /// Checks whether the TX FIFO is empty
204 fn is_tx_fifo_empty() -> bool {
205 T::regs().mfsr().read().txcount() == 0
206 }
207
208 /// Checks whether the RX FIFO is empty.
209 fn is_rx_fifo_empty() -> bool {
210 T::regs().mfsr().read().rxcount() == 0
211 }
212
213 /// Reads and parses the controller status producing an
214 /// appropriate `Result<(), Error>` variant.
215 fn status() -> Result<()> {
216 let msr = T::regs().msr().read();
217 T::regs().msr().write(|w| {
218 w.epf()
219 .clear_bit_by_one()
220 .sdf()
221 .clear_bit_by_one()
222 .ndf()
223 .clear_bit_by_one()
224 .alf()
225 .clear_bit_by_one()
226 .fef()
227 .clear_bit_by_one()
228 .fef()
229 .clear_bit_by_one()
230 .pltf()
231 .clear_bit_by_one()
232 .dmf()
233 .clear_bit_by_one()
234 .stf()
235 .clear_bit_by_one()
236 });
237
238 if msr.ndf().bit_is_set() {
239 Err(Error::AddressNack)
240 } else if msr.alf().bit_is_set() {
241 Err(Error::ArbitrationLoss)
242 } else if msr.fef().bit_is_set() {
243 Err(Error::FifoError)
244 } else {
245 Ok(())
246 }
247 }
248
249 /// Inserts the given command into the outgoing FIFO.
250 ///
251 /// Caller must ensure there is space in the FIFO for the new
252 /// command.
253 fn send_cmd(cmd: Cmd, data: u8) {
254 #[cfg(feature = "defmt")]
255 defmt::trace!(
256 "Sending cmd '{}' ({}) with data '{:08x}' MSR: {:08x}",
257 cmd,
258 cmd as u8,
259 data,
260 T::regs().msr().read().bits()
261 );
262
263 T::regs()
264 .mtdr()
265 .write(|w| unsafe { w.data().bits(data) }.cmd().variant(cmd));
266 }
267
268 /// Prepares an appropriate Start condition on bus by issuing a
269 /// `Start` command together with the device address and R/w bit.
270 ///
271 /// Blocks waiting for space in the FIFO to become available, then
272 /// sends the command and blocks waiting for the FIFO to become
273 /// empty ensuring the command was sent.
274 fn start(&mut self, address: u8, read: bool) -> Result<()> {
275 if address >= 0x80 {
276 return Err(Error::AddressOutOfRange(address));
277 }
278
279 // Wait until we have space in the TxFIFO
280 while Self::is_tx_fifo_full() {}
281
282 let addr_rw = address << 1 | if read { 1 } else { 0 };
283 Self::send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw);
284
285 // Wait for TxFIFO to be drained
286 while !Self::is_tx_fifo_empty() {}
287
288 // Check controller status
289 Self::status()
290 }
291
292 /// Prepares a Stop condition on the bus.
293 ///
294 /// Analogous to `start`, this blocks waiting for space in the
295 /// FIFO to become available, then sends the command and blocks
296 /// waiting for the FIFO to become empty ensuring the command was
297 /// sent.
298 fn stop() -> Result<()> {
299 // Wait until we have space in the TxFIFO
300 while Self::is_tx_fifo_full() {}
301
302 Self::send_cmd(Cmd::Stop, 0);
303
304 // Wait for TxFIFO to be drained
305 while !Self::is_tx_fifo_empty() {}
306
307 Self::status()
308 }
309
310 fn blocking_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> {
311 if read.is_empty() {
312 return Err(Error::InvalidReadBufferLength);
313 }
314
315 for chunk in read.chunks_mut(256) {
316 self.start(address, true)?;
317
318 // Wait until we have space in the TxFIFO
319 while Self::is_tx_fifo_full() {}
320
321 Self::send_cmd(Cmd::Receive, (chunk.len() - 1) as u8);
322
323 for byte in chunk.iter_mut() {
324 // Wait until there's data in the RxFIFO
325 while Self::is_rx_fifo_empty() {}
326
327 *byte = T::regs().mrdr().read().data().bits();
328 }
329 }
330
331 if send_stop == SendStop::Yes {
332 Self::stop()?;
333 }
334
335 Ok(())
336 }
337
338 fn blocking_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> {
339 self.start(address, false)?;
340
341 // Usually, embassy HALs error out with an empty write,
342 // however empty writes are useful for writing I2C scanning
343 // logic through write probing. That is, we send a start with
344 // R/w bit cleared, but instead of writing any data, just send
345 // the stop onto the bus. This has the effect of checking if
346 // the resulting address got an ACK but causing no
347 // side-effects to the device on the other end.
348 //
349 // Because of this, we are not going to error out in case of
350 // empty writes.
351 #[cfg(feature = "defmt")]
352 if write.is_empty() {
353 defmt::trace!("Empty write, write probing?");
354 }
355
356 for byte in write {
357 // Wait until we have space in the TxFIFO
358 while Self::is_tx_fifo_full() {}
359
360 Self::send_cmd(Cmd::Transmit, *byte);
361 }
362
363 if send_stop == SendStop::Yes {
364 Self::stop()?;
365 }
366
367 Ok(())
368 }
369
370 // Public API: Blocking
371
372 /// Read from address into buffer blocking caller until done.
373 pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<()> {
374 self.blocking_read_internal(address, read, SendStop::Yes)
375 }
376
377 /// Write to address from buffer blocking caller until done.
378 pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<()> {
379 self.blocking_write_internal(address, write, SendStop::Yes)
380 }
381
382 /// Write to address from bytes and read from address into buffer blocking caller until done.
383 pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> {
384 self.blocking_write_internal(address, write, SendStop::No)?;
385 self.blocking_read_internal(address, read, SendStop::Yes)
386 }
387}
388
389impl<'d, T: Instance> I2c<'d, T, Async> {
390 /// Create a new async instance of the I2C Controller bus driver.
391 pub fn new_async(
392 peri: Peri<'d, T>,
393 scl: Peri<'d, impl SclPin<T>>,
394 sda: Peri<'d, impl SdaPin<T>>,
395 _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
396 config: Config,
397 ) -> Result<Self> {
398 T::Interrupt::unpend();
399
400 // Safety: `_irq` ensures an Interrupt Handler exists.
401 unsafe { T::Interrupt::enable() };
402
403 Self::new_inner(peri, scl, sda, config)
404 }
405
406 fn remediation() {
407 #[cfg(feature = "defmt")]
408 defmt::trace!("Future dropped, issuing stop",);
409
410 // if the FIFO is not empty, drop its contents.
411 if !Self::is_tx_fifo_empty() {
412 Self::reset_fifos();
413 }
414
415 // send a stop command
416 let _ = Self::stop();
417 }
418
419 fn enable_rx_ints(&mut self) {
420 T::regs().mier().write(|w| {
421 w.rdie()
422 .enabled()
423 .ndie()
424 .enabled()
425 .alie()
426 .enabled()
427 .feie()
428 .enabled()
429 .pltie()
430 .enabled()
431 });
432 }
433
434 fn enable_tx_ints(&mut self) {
435 T::regs().mier().write(|w| {
436 w.tdie()
437 .enabled()
438 .ndie()
439 .enabled()
440 .alie()
441 .enabled()
442 .feie()
443 .enabled()
444 .pltie()
445 .enabled()
446 });
447 }
448
449 async fn async_start(&mut self, address: u8, read: bool) -> Result<()> {
450 if address >= 0x80 {
451 return Err(Error::AddressOutOfRange(address));
452 }
453
454 // send the start command
455 let addr_rw = address << 1 | if read { 1 } else { 0 };
456 Self::send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw);
457
458 T::wait_cell()
459 .wait_for(|| {
460 // enable interrupts
461 self.enable_tx_ints();
462 // if the command FIFO is empty, we're done sending start
463 Self::is_tx_fifo_empty()
464 })
465 .await
466 .map_err(|_| Error::Other)?;
467
468 Self::status()
469 }
470
471 async fn async_stop(&mut self) -> Result<()> {
472 // send the stop command
473 Self::send_cmd(Cmd::Stop, 0);
474
475 T::wait_cell()
476 .wait_for(|| {
477 // enable interrupts
478 self.enable_tx_ints();
479 // if the command FIFO is empty, we're done sending stop
480 Self::is_tx_fifo_empty()
481 })
482 .await
483 .map_err(|_| Error::Other)?;
484
485 Self::status()
486 }
487
488 async fn async_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> {
489 if read.is_empty() {
490 return Err(Error::InvalidReadBufferLength);
491 }
492
493 // perform corrective action if the future is dropped
494 let on_drop = OnDrop::new(|| Self::remediation());
495
496 for chunk in read.chunks_mut(256) {
497 self.async_start(address, true).await?;
498
499 // send receive command
500 Self::send_cmd(Cmd::Receive, (chunk.len() - 1) as u8);
501
502 T::wait_cell()
503 .wait_for(|| {
504 // enable interrupts
505 self.enable_tx_ints();
506 // if the command FIFO is empty, we're done sending start
507 Self::is_tx_fifo_empty()
508 })
509 .await
510 .map_err(|_| Error::Other)?;
511
512 for byte in chunk.iter_mut() {
513 T::wait_cell()
514 .wait_for(|| {
515 // enable interrupts
516 self.enable_rx_ints();
517 // if the rx FIFO is not empty, we need to read a byte
518 !Self::is_rx_fifo_empty()
519 })
520 .await
521 .map_err(|_| Error::ReadFail)?;
522
523 *byte = T::regs().mrdr().read().data().bits();
524 }
525 }
526
527 if send_stop == SendStop::Yes {
528 self.async_stop().await?;
529 }
530
531 // defuse it if the future is not dropped
532 on_drop.defuse();
533
534 Ok(())
535 }
536
537 async fn async_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> {
538 self.async_start(address, false).await?;
539
540 // perform corrective action if the future is dropped
541 let on_drop = OnDrop::new(|| Self::remediation());
542
543 // Usually, embassy HALs error out with an empty write,
544 // however empty writes are useful for writing I2C scanning
545 // logic through write probing. That is, we send a start with
546 // R/w bit cleared, but instead of writing any data, just send
547 // the stop onto the bus. This has the effect of checking if
548 // the resulting address got an ACK but causing no
549 // side-effects to the device on the other end.
550 //
551 // Because of this, we are not going to error out in case of
552 // empty writes.
553 #[cfg(feature = "defmt")]
554 if write.is_empty() {
555 defmt::trace!("Empty write, write probing?");
556 }
557
558 for byte in write {
559 T::wait_cell()
560 .wait_for(|| {
561 // enable interrupts
562 self.enable_tx_ints();
563 // initiate transmit
564 Self::send_cmd(Cmd::Transmit, *byte);
565 // if the tx FIFO is empty, we're done transmiting
566 Self::is_tx_fifo_empty()
567 })
568 .await
569 .map_err(|_| Error::WriteFail)?;
570 }
571
572 if send_stop == SendStop::Yes {
573 self.async_stop().await?;
574 }
575
576 // defuse it if the future is not dropped
577 on_drop.defuse();
578
579 Ok(())
580 }
581
582 // Public API: Async
583
584 /// Read from address into buffer asynchronously.
585 pub fn async_read<'a>(
586 &mut self,
587 address: u8,
588 read: &'a mut [u8],
589 ) -> impl Future<Output = Result<()>> + use<'_, 'a, 'd, T> {
590 self.async_read_internal(address, read, SendStop::Yes)
591 }
592
593 /// Write to address from buffer asynchronously.
594 pub fn async_write<'a>(
595 &mut self,
596 address: u8,
597 write: &'a [u8],
598 ) -> impl Future<Output = Result<()>> + use<'_, 'a, 'd, T> {
599 self.async_write_internal(address, write, SendStop::Yes)
600 }
601
602 /// Write to address from bytes and read from address into buffer asynchronously.
603 pub async fn async_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> {
604 self.async_write_internal(address, write, SendStop::No).await?;
605 self.async_read_internal(address, read, SendStop::Yes).await
606 }
607}
608
609impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> {
610 type Error = Error;
611
612 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<()> {
613 self.blocking_read(address, buffer)
614 }
615}
616
617impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> {
618 type Error = Error;
619
620 fn write(&mut self, address: u8, bytes: &[u8]) -> Result<()> {
621 self.blocking_write(address, bytes)
622 }
623}
624
625impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> {
626 type Error = Error;
627
628 fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<()> {
629 self.blocking_write_read(address, bytes, buffer)
630 }
631}
632
633impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, T, M> {
634 type Error = Error;
635
636 fn exec(&mut self, address: u8, operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>]) -> Result<()> {
637 if let Some((last, rest)) = operations.split_last_mut() {
638 for op in rest {
639 match op {
640 embedded_hal_02::blocking::i2c::Operation::Read(buf) => {
641 self.blocking_read_internal(address, buf, SendStop::No)?
642 }
643 embedded_hal_02::blocking::i2c::Operation::Write(buf) => {
644 self.blocking_write_internal(address, buf, SendStop::No)?
645 }
646 }
647 }
648
649 match last {
650 embedded_hal_02::blocking::i2c::Operation::Read(buf) => {
651 self.blocking_read_internal(address, buf, SendStop::Yes)
652 }
653 embedded_hal_02::blocking::i2c::Operation::Write(buf) => {
654 self.blocking_write_internal(address, buf, SendStop::Yes)
655 }
656 }
657 } else {
658 Ok(())
659 }
660 }
661}
662
663impl embedded_hal_1::i2c::Error for Error {
664 fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
665 match *self {
666 Self::ArbitrationLoss => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
667 Self::AddressNack => {
668 embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
669 }
670 _ => embedded_hal_1::i2c::ErrorKind::Other,
671 }
672 }
673}
674
675impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> {
676 type Error = Error;
677}
678
679impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {
680 fn transaction(&mut self, address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>]) -> Result<()> {
681 if let Some((last, rest)) = operations.split_last_mut() {
682 for op in rest {
683 match op {
684 embedded_hal_1::i2c::Operation::Read(buf) => {
685 self.blocking_read_internal(address, buf, SendStop::No)?
686 }
687 embedded_hal_1::i2c::Operation::Write(buf) => {
688 self.blocking_write_internal(address, buf, SendStop::No)?
689 }
690 }
691 }
692
693 match last {
694 embedded_hal_1::i2c::Operation::Read(buf) => self.blocking_read_internal(address, buf, SendStop::Yes),
695 embedded_hal_1::i2c::Operation::Write(buf) => self.blocking_write_internal(address, buf, SendStop::Yes),
696 }
697 } else {
698 Ok(())
699 }
700 }
701}
702
703impl<'d, T: Instance> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> {
704 async fn transaction(
705 &mut self,
706 address: u8,
707 operations: &mut [embedded_hal_async::i2c::Operation<'_>],
708 ) -> Result<()> {
709 if let Some((last, rest)) = operations.split_last_mut() {
710 for op in rest {
711 match op {
712 embedded_hal_async::i2c::Operation::Read(buf) => {
713 self.async_read_internal(address, buf, SendStop::No).await?
714 }
715 embedded_hal_async::i2c::Operation::Write(buf) => {
716 self.async_write_internal(address, buf, SendStop::No).await?
717 }
718 }
719 }
720
721 match last {
722 embedded_hal_async::i2c::Operation::Read(buf) => {
723 self.async_read_internal(address, buf, SendStop::Yes).await
724 }
725 embedded_hal_async::i2c::Operation::Write(buf) => {
726 self.async_write_internal(address, buf, SendStop::Yes).await
727 }
728 }
729 } else {
730 Ok(())
731 }
732 }
733}
734
735impl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> {
736 type Config = Config;
737 type ConfigError = Error;
738
739 fn set_config(&mut self, config: &Self::Config) -> Result<()> {
740 Self::set_config(config)
741 }
742}
diff --git a/embassy-mcxa/src/i2c/mod.rs b/embassy-mcxa/src/i2c/mod.rs
new file mode 100644
index 000000000..9a014224a
--- /dev/null
+++ b/embassy-mcxa/src/i2c/mod.rs
@@ -0,0 +1,194 @@
1//! I2C Support
2
3use core::marker::PhantomData;
4
5use embassy_hal_internal::PeripheralType;
6use maitake_sync::WaitCell;
7use paste::paste;
8
9use crate::clocks::periph_helpers::Lpi2cConfig;
10use crate::clocks::{ClockError, Gate};
11use crate::gpio::{GpioPin, SealedPin};
12use crate::{interrupt, pac};
13
14/// Shorthand for `Result<T>`.
15pub type Result<T> = core::result::Result<T, Error>;
16
17pub mod controller;
18
19/// Error information type
20#[derive(Debug, Copy, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub enum Error {
23 /// Clock configuration error.
24 ClockSetup(ClockError),
25 /// FIFO Error
26 FifoError,
27 /// Reading for I2C failed.
28 ReadFail,
29 /// Writing to I2C failed.
30 WriteFail,
31 /// I2C address NAK condition.
32 AddressNack,
33 /// Bus level arbitration loss.
34 ArbitrationLoss,
35 /// Address out of range.
36 AddressOutOfRange(u8),
37 /// Invalid write buffer length.
38 InvalidWriteBufferLength,
39 /// Invalid read buffer length.
40 InvalidReadBufferLength,
41 /// Other internal errors or unexpected state.
42 Other,
43}
44
45/// I2C interrupt handler.
46pub struct InterruptHandler<T: Instance> {
47 _phantom: PhantomData<T>,
48}
49
50impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
51 unsafe fn on_interrupt() {
52 if T::regs().mier().read().bits() != 0 {
53 T::regs().mier().write(|w| {
54 w.tdie()
55 .disabled()
56 .rdie()
57 .disabled()
58 .epie()
59 .disabled()
60 .sdie()
61 .disabled()
62 .ndie()
63 .disabled()
64 .alie()
65 .disabled()
66 .feie()
67 .disabled()
68 .pltie()
69 .disabled()
70 .dmie()
71 .disabled()
72 .stie()
73 .disabled()
74 });
75
76 T::wait_cell().wake();
77 }
78 }
79}
80
81mod sealed {
82 /// Seal a trait
83 pub trait Sealed {}
84}
85
86impl<T: GpioPin> sealed::Sealed for T {}
87
88trait SealedInstance {
89 fn regs() -> &'static pac::lpi2c0::RegisterBlock;
90 fn wait_cell() -> &'static WaitCell;
91}
92
93/// I2C Instance
94#[allow(private_bounds)]
95pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = Lpi2cConfig> {
96 /// Interrupt for this I2C instance.
97 type Interrupt: interrupt::typelevel::Interrupt;
98 /// Clock instance
99 const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance;
100}
101
102macro_rules! impl_instance {
103 ($($n:expr),*) => {
104 $(
105 paste!{
106 impl SealedInstance for crate::peripherals::[<LPI2C $n>] {
107 fn regs() -> &'static pac::lpi2c0::RegisterBlock {
108 unsafe { &*pac::[<Lpi2c $n>]::ptr() }
109 }
110
111 fn wait_cell() -> &'static WaitCell {
112 static WAIT_CELL: WaitCell = WaitCell::new();
113 &WAIT_CELL
114 }
115 }
116
117 impl Instance for crate::peripherals::[<LPI2C $n>] {
118 type Interrupt = crate::interrupt::typelevel::[<LPI2C $n>];
119 const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance
120 = crate::clocks::periph_helpers::Lpi2cInstance::[<Lpi2c $n>];
121 }
122 }
123 )*
124 };
125}
126
127impl_instance!(0, 1, 2, 3);
128
129/// SCL pin trait.
130pub trait SclPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {
131 fn mux(&self);
132}
133
134/// SDA pin trait.
135pub trait SdaPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {
136 fn mux(&self);
137}
138
139/// Driver mode.
140#[allow(private_bounds)]
141pub trait Mode: sealed::Sealed {}
142
143/// Blocking mode.
144pub struct Blocking;
145impl sealed::Sealed for Blocking {}
146impl Mode for Blocking {}
147
148/// Async mode.
149pub struct Async;
150impl sealed::Sealed for Async {}
151impl Mode for Async {}
152
153macro_rules! impl_pin {
154 ($pin:ident, $peri:ident, $fn:ident, $trait:ident) => {
155 impl $trait<crate::peripherals::$peri> for crate::peripherals::$pin {
156 fn mux(&self) {
157 self.set_pull(crate::gpio::Pull::Disabled);
158 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
159 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
160 self.set_function(crate::pac::port0::pcr0::Mux::$fn);
161 self.set_enable_input_buffer();
162 }
163 }
164 };
165}
166
167impl_pin!(P0_16, LPI2C0, Mux2, SdaPin);
168impl_pin!(P0_17, LPI2C0, Mux2, SclPin);
169impl_pin!(P0_18, LPI2C0, Mux2, SclPin);
170impl_pin!(P0_19, LPI2C0, Mux2, SdaPin);
171impl_pin!(P1_0, LPI2C1, Mux3, SdaPin);
172impl_pin!(P1_1, LPI2C1, Mux3, SclPin);
173impl_pin!(P1_2, LPI2C1, Mux3, SdaPin);
174impl_pin!(P1_3, LPI2C1, Mux3, SclPin);
175impl_pin!(P1_8, LPI2C2, Mux3, SdaPin);
176impl_pin!(P1_9, LPI2C2, Mux3, SclPin);
177impl_pin!(P1_10, LPI2C2, Mux3, SdaPin);
178impl_pin!(P1_11, LPI2C2, Mux3, SclPin);
179impl_pin!(P1_12, LPI2C1, Mux2, SdaPin);
180impl_pin!(P1_13, LPI2C1, Mux2, SclPin);
181impl_pin!(P1_14, LPI2C1, Mux2, SclPin);
182impl_pin!(P1_15, LPI2C1, Mux2, SdaPin);
183impl_pin!(P1_30, LPI2C0, Mux3, SdaPin);
184impl_pin!(P1_31, LPI2C0, Mux3, SclPin);
185impl_pin!(P3_27, LPI2C3, Mux2, SclPin);
186impl_pin!(P3_28, LPI2C3, Mux2, SdaPin);
187// impl_pin!(P3_29, LPI2C3, Mux2, HreqPin); What is this HREQ pin?
188impl_pin!(P3_30, LPI2C3, Mux2, SclPin);
189impl_pin!(P3_31, LPI2C3, Mux2, SdaPin);
190impl_pin!(P4_2, LPI2C2, Mux2, SdaPin);
191impl_pin!(P4_3, LPI2C0, Mux2, SclPin);
192impl_pin!(P4_4, LPI2C2, Mux2, SdaPin);
193impl_pin!(P4_5, LPI2C0, Mux2, SclPin);
194// impl_pin!(P4_6, LPI2C0, Mux2, HreqPin); What is this HREQ pin?
diff --git a/embassy-mcxa/src/interrupt.rs b/embassy-mcxa/src/interrupt.rs
new file mode 100644
index 000000000..725f8d499
--- /dev/null
+++ b/embassy-mcxa/src/interrupt.rs
@@ -0,0 +1,536 @@
1//! Minimal interrupt helpers mirroring embassy-imxrt style for OS_EVENT and LPUART2.
2//! Type-level interrupt traits and bindings are provided by the
3//! `embassy_hal_internal::interrupt_mod!` macro via the generated module below.
4
5// TODO(AJM): As of 2025-11-13, we need to do a pass to ensure safety docs
6// are complete prior to release.
7#![allow(clippy::missing_safety_doc)]
8
9mod generated {
10 #[rustfmt::skip]
11 embassy_hal_internal::interrupt_mod!(
12 ADC0,
13 ADC1,
14 ADC2,
15 ADC3,
16 DMA_CH0,
17 DMA_CH1,
18 DMA_CH2,
19 DMA_CH3,
20 DMA_CH4,
21 DMA_CH5,
22 DMA_CH6,
23 DMA_CH7,
24 GPIO0,
25 GPIO1,
26 GPIO2,
27 GPIO3,
28 GPIO4,
29 LPI2C0,
30 LPI2C1,
31 LPI2C2,
32 LPI2C3,
33 LPUART0,
34 LPUART1,
35 LPUART2,
36 LPUART3,
37 LPUART4,
38 LPUART5,
39 OS_EVENT,
40 RTC,
41 );
42}
43
44use core::sync::atomic::{AtomicU16, AtomicU32, Ordering};
45
46pub use generated::interrupt::{Priority, typelevel};
47
48use crate::pac::Interrupt;
49
50/// Trait for configuring and controlling interrupts.
51///
52/// This trait provides a consistent interface for interrupt management across
53/// different interrupt sources, similar to embassy-imxrt's InterruptExt.
54pub trait InterruptExt {
55 /// Clear any pending interrupt in NVIC.
56 fn unpend(&self);
57
58 /// Set NVIC priority for this interrupt.
59 fn set_priority(&self, priority: Priority);
60
61 /// Enable this interrupt in NVIC.
62 ///
63 /// # Safety
64 /// This function is unsafe because it can enable interrupts that may not be
65 /// properly configured, potentially leading to undefined behavior.
66 unsafe fn enable(&self);
67
68 /// Disable this interrupt in NVIC.
69 ///
70 /// # Safety
71 /// This function is unsafe because disabling interrupts may leave the system
72 /// in an inconsistent state if the interrupt was expected to fire.
73 unsafe fn disable(&self);
74
75 /// Check if the interrupt is pending in NVIC.
76 fn is_pending(&self) -> bool;
77}
78
79#[derive(Clone, Copy, Debug, Default)]
80pub struct DefaultHandlerSnapshot {
81 pub vector: u16,
82 pub count: u32,
83 pub cfsr: u32,
84 pub hfsr: u32,
85 pub stacked_pc: u32,
86 pub stacked_lr: u32,
87 pub stacked_sp: u32,
88}
89
90static LAST_DEFAULT_VECTOR: AtomicU16 = AtomicU16::new(0);
91static LAST_DEFAULT_COUNT: AtomicU32 = AtomicU32::new(0);
92static LAST_DEFAULT_CFSR: AtomicU32 = AtomicU32::new(0);
93static LAST_DEFAULT_HFSR: AtomicU32 = AtomicU32::new(0);
94static LAST_DEFAULT_PC: AtomicU32 = AtomicU32::new(0);
95static LAST_DEFAULT_LR: AtomicU32 = AtomicU32::new(0);
96static LAST_DEFAULT_SP: AtomicU32 = AtomicU32::new(0);
97
98#[inline]
99pub fn default_handler_snapshot() -> DefaultHandlerSnapshot {
100 DefaultHandlerSnapshot {
101 vector: LAST_DEFAULT_VECTOR.load(Ordering::Relaxed),
102 count: LAST_DEFAULT_COUNT.load(Ordering::Relaxed),
103 cfsr: LAST_DEFAULT_CFSR.load(Ordering::Relaxed),
104 hfsr: LAST_DEFAULT_HFSR.load(Ordering::Relaxed),
105 stacked_pc: LAST_DEFAULT_PC.load(Ordering::Relaxed),
106 stacked_lr: LAST_DEFAULT_LR.load(Ordering::Relaxed),
107 stacked_sp: LAST_DEFAULT_SP.load(Ordering::Relaxed),
108 }
109}
110
111#[inline]
112pub fn clear_default_handler_snapshot() {
113 LAST_DEFAULT_VECTOR.store(0, Ordering::Relaxed);
114 LAST_DEFAULT_COUNT.store(0, Ordering::Relaxed);
115 LAST_DEFAULT_CFSR.store(0, Ordering::Relaxed);
116 LAST_DEFAULT_HFSR.store(0, Ordering::Relaxed);
117 LAST_DEFAULT_PC.store(0, Ordering::Relaxed);
118 LAST_DEFAULT_LR.store(0, Ordering::Relaxed);
119 LAST_DEFAULT_SP.store(0, Ordering::Relaxed);
120}
121
122/// OS_EVENT interrupt helper with methods similar to embassy-imxrt's InterruptExt.
123pub struct OsEvent;
124pub const OS_EVENT: OsEvent = OsEvent;
125
126impl InterruptExt for OsEvent {
127 /// Clear any pending OS_EVENT in NVIC.
128 #[inline]
129 fn unpend(&self) {
130 cortex_m::peripheral::NVIC::unpend(Interrupt::OS_EVENT);
131 }
132
133 /// Set NVIC priority for OS_EVENT.
134 #[inline]
135 fn set_priority(&self, priority: Priority) {
136 unsafe {
137 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
138 nvic.set_priority(Interrupt::OS_EVENT, u8::from(priority));
139 }
140 }
141
142 /// Enable OS_EVENT in NVIC.
143 #[inline]
144 unsafe fn enable(&self) {
145 cortex_m::peripheral::NVIC::unmask(Interrupt::OS_EVENT);
146 }
147
148 /// Disable OS_EVENT in NVIC.
149 #[inline]
150 unsafe fn disable(&self) {
151 cortex_m::peripheral::NVIC::mask(Interrupt::OS_EVENT);
152 }
153
154 /// Check if OS_EVENT is pending in NVIC.
155 #[inline]
156 fn is_pending(&self) -> bool {
157 cortex_m::peripheral::NVIC::is_pending(Interrupt::OS_EVENT)
158 }
159}
160
161impl OsEvent {
162 /// Configure OS_EVENT interrupt for timer operation.
163 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
164 /// Also performs a software event to wake any pending WFE.
165 pub fn configure_for_timer(&self, priority: Priority) {
166 // Configure NVIC
167 self.unpend();
168 self.set_priority(priority);
169 unsafe {
170 self.enable();
171 }
172
173 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
174 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
175 unsafe {
176 cortex_m::interrupt::enable();
177 }
178
179 // Wake any executor WFE that might be sleeping when we armed the first deadline
180 cortex_m::asm::sev();
181 }
182}
183
184/// LPUART2 interrupt helper with methods similar to embassy-imxrt's InterruptExt.
185pub struct Lpuart2;
186pub const LPUART2: Lpuart2 = Lpuart2;
187
188impl InterruptExt for Lpuart2 {
189 /// Clear any pending LPUART2 in NVIC.
190 #[inline]
191 fn unpend(&self) {
192 cortex_m::peripheral::NVIC::unpend(Interrupt::LPUART2);
193 }
194
195 /// Set NVIC priority for LPUART2.
196 #[inline]
197 fn set_priority(&self, priority: Priority) {
198 unsafe {
199 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
200 nvic.set_priority(Interrupt::LPUART2, u8::from(priority));
201 }
202 }
203
204 /// Enable LPUART2 in NVIC.
205 #[inline]
206 unsafe fn enable(&self) {
207 cortex_m::peripheral::NVIC::unmask(Interrupt::LPUART2);
208 }
209
210 /// Disable LPUART2 in NVIC.
211 #[inline]
212 unsafe fn disable(&self) {
213 cortex_m::peripheral::NVIC::mask(Interrupt::LPUART2);
214 }
215
216 /// Check if LPUART2 is pending in NVIC.
217 #[inline]
218 fn is_pending(&self) -> bool {
219 cortex_m::peripheral::NVIC::is_pending(Interrupt::LPUART2)
220 }
221}
222
223impl Lpuart2 {
224 /// Configure LPUART2 interrupt for UART operation.
225 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
226 pub fn configure_for_uart(&self, priority: Priority) {
227 // Configure NVIC
228 self.unpend();
229 self.set_priority(priority);
230 unsafe {
231 self.enable();
232 }
233
234 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
235 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
236 unsafe {
237 cortex_m::interrupt::enable();
238 }
239 }
240
241 /// Install LPUART2 handler into the RAM vector table.
242 /// Safety: See `install_irq_handler`.
243 pub unsafe fn install_handler(&self, handler: unsafe extern "C" fn()) {
244 install_irq_handler(Interrupt::LPUART2, handler);
245 }
246}
247
248pub struct Rtc;
249pub const RTC: Rtc = Rtc;
250
251impl InterruptExt for Rtc {
252 /// Clear any pending RTC in NVIC.
253 #[inline]
254 fn unpend(&self) {
255 cortex_m::peripheral::NVIC::unpend(Interrupt::RTC);
256 }
257
258 /// Set NVIC priority for RTC.
259 #[inline]
260 fn set_priority(&self, priority: Priority) {
261 unsafe {
262 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
263 nvic.set_priority(Interrupt::RTC, u8::from(priority));
264 }
265 }
266
267 /// Enable RTC in NVIC.
268 #[inline]
269 unsafe fn enable(&self) {
270 cortex_m::peripheral::NVIC::unmask(Interrupt::RTC);
271 }
272
273 /// Disable RTC in NVIC.
274 #[inline]
275 unsafe fn disable(&self) {
276 cortex_m::peripheral::NVIC::mask(Interrupt::RTC);
277 }
278
279 /// Check if RTC is pending in NVIC.
280 #[inline]
281 fn is_pending(&self) -> bool {
282 cortex_m::peripheral::NVIC::is_pending(Interrupt::RTC)
283 }
284}
285
286pub struct Gpio0;
287pub const GPIO0: Gpio0 = Gpio0;
288
289impl InterruptExt for Gpio0 {
290 /// Clear any pending GPIO0 in NVIC.
291 #[inline]
292 fn unpend(&self) {
293 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO0);
294 }
295
296 /// Set NVIC priority for GPIO0.
297 #[inline]
298 fn set_priority(&self, priority: Priority) {
299 unsafe {
300 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
301 nvic.set_priority(Interrupt::GPIO0, u8::from(priority));
302 }
303 }
304
305 /// Enable GPIO0 in NVIC.
306 #[inline]
307 unsafe fn enable(&self) {
308 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO0);
309 }
310
311 /// Disable GPIO0 in NVIC.
312 #[inline]
313 unsafe fn disable(&self) {
314 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO0);
315 }
316
317 /// Check if GPIO0 is pending in NVIC.
318 #[inline]
319 fn is_pending(&self) -> bool {
320 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO0)
321 }
322}
323
324pub struct Gpio1;
325pub const GPIO1: Gpio1 = Gpio1;
326
327impl InterruptExt for Gpio1 {
328 /// Clear any pending GPIO1 in NVIC.
329 #[inline]
330 fn unpend(&self) {
331 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO1);
332 }
333
334 /// Set NVIC priority for GPIO1.
335 #[inline]
336 fn set_priority(&self, priority: Priority) {
337 unsafe {
338 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
339 nvic.set_priority(Interrupt::GPIO1, u8::from(priority));
340 }
341 }
342
343 /// Enable GPIO1 in NVIC.
344 #[inline]
345 unsafe fn enable(&self) {
346 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO1);
347 }
348
349 /// Disable GPIO1 in NVIC.
350 #[inline]
351 unsafe fn disable(&self) {
352 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO1);
353 }
354
355 /// Check if GPIO1 is pending in NVIC.
356 #[inline]
357 fn is_pending(&self) -> bool {
358 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO1)
359 }
360}
361
362pub struct Gpio2;
363pub const GPIO2: Gpio2 = Gpio2;
364
365impl InterruptExt for Gpio2 {
366 /// Clear any pending GPIO2 in NVIC.
367 #[inline]
368 fn unpend(&self) {
369 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO2);
370 }
371
372 /// Set NVIC priority for GPIO2.
373 #[inline]
374 fn set_priority(&self, priority: Priority) {
375 unsafe {
376 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
377 nvic.set_priority(Interrupt::GPIO2, u8::from(priority));
378 }
379 }
380
381 /// Enable GPIO2 in NVIC.
382 #[inline]
383 unsafe fn enable(&self) {
384 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO2);
385 }
386
387 /// Disable GPIO2 in NVIC.
388 #[inline]
389 unsafe fn disable(&self) {
390 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO2);
391 }
392
393 /// Check if GPIO2 is pending in NVIC.
394 #[inline]
395 fn is_pending(&self) -> bool {
396 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO2)
397 }
398}
399
400pub struct Gpio3;
401pub const GPIO3: Gpio3 = Gpio3;
402
403impl InterruptExt for Gpio3 {
404 /// Clear any pending GPIO3 in NVIC.
405 #[inline]
406 fn unpend(&self) {
407 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO3);
408 }
409
410 /// Set NVIC priority for GPIO3.
411 #[inline]
412 fn set_priority(&self, priority: Priority) {
413 unsafe {
414 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
415 nvic.set_priority(Interrupt::GPIO3, u8::from(priority));
416 }
417 }
418
419 /// Enable GPIO3 in NVIC.
420 #[inline]
421 unsafe fn enable(&self) {
422 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO3);
423 }
424
425 /// Disable GPIO3 in NVIC.
426 #[inline]
427 unsafe fn disable(&self) {
428 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO3);
429 }
430
431 /// Check if GPIO3 is pending in NVIC.
432 #[inline]
433 fn is_pending(&self) -> bool {
434 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO3)
435 }
436}
437
438pub struct Gpio4;
439pub const GPIO4: Gpio4 = Gpio4;
440
441impl InterruptExt for Gpio4 {
442 /// Clear any pending GPIO4 in NVIC.
443 #[inline]
444 fn unpend(&self) {
445 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO4);
446 }
447
448 /// Set NVIC priority for GPIO4.
449 #[inline]
450 fn set_priority(&self, priority: Priority) {
451 unsafe {
452 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
453 nvic.set_priority(Interrupt::GPIO4, u8::from(priority));
454 }
455 }
456
457 /// Enable GPIO4 in NVIC.
458 #[inline]
459 unsafe fn enable(&self) {
460 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO4);
461 }
462
463 /// Disable GPIO4 in NVIC.
464 #[inline]
465 unsafe fn disable(&self) {
466 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO4);
467 }
468
469 /// Check if GPIO4 is pending in NVIC.
470 #[inline]
471 fn is_pending(&self) -> bool {
472 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO4)
473 }
474}
475
476/// Set VTOR (Vector Table Offset) to a RAM-based vector table.
477/// Pass a pointer to the first word in the RAM table (stack pointer slot 0).
478/// Safety: Caller must ensure the RAM table is valid and aligned as required by the core.
479pub unsafe fn vtor_set_ram_vector_base(base: *const u32) {
480 core::ptr::write_volatile(0xE000_ED08 as *mut u32, base as u32);
481}
482
483/// Install an interrupt handler into the current VTOR-based vector table.
484/// This writes the function pointer at index 16 + irq number.
485/// Safety: Caller must ensure VTOR points at a writable RAM table and that `handler`
486/// has the correct ABI and lifetime.
487pub unsafe fn install_irq_handler(irq: Interrupt, handler: unsafe extern "C" fn()) {
488 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
489 let idx = 16 + (irq as isize as usize);
490 core::ptr::write_volatile(vtor_base.add(idx), handler as usize as u32);
491}
492
493impl OsEvent {
494 /// Convenience to install the OS_EVENT handler into the RAM vector table.
495 /// Safety: See `install_irq_handler`.
496 pub unsafe fn install_handler(&self, handler: extern "C" fn()) {
497 install_irq_handler(Interrupt::OS_EVENT, handler);
498 }
499}
500
501/// Install OS_EVENT handler by raw address. Useful to avoid fn pointer type mismatches.
502/// Safety: Caller must ensure the address is a valid `extern "C" fn()` handler.
503pub unsafe fn os_event_install_handler_raw(handler_addr: usize) {
504 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
505 let idx = 16 + (Interrupt::OS_EVENT as isize as usize);
506 core::ptr::write_volatile(vtor_base.add(idx), handler_addr as u32);
507}
508
509/// Provide a conservative default IRQ handler that avoids wedging the system.
510/// It clears all NVIC pending bits and returns, so spurious or reserved IRQs
511/// don’t trap the core in an infinite WFI loop during bring-up.
512#[no_mangle]
513pub unsafe extern "C" fn DefaultHandler() {
514 let active = core::ptr::read_volatile(0xE000_ED04 as *const u32) & 0x1FF;
515 let cfsr = core::ptr::read_volatile(0xE000_ED28 as *const u32);
516 let hfsr = core::ptr::read_volatile(0xE000_ED2C as *const u32);
517
518 let sp = cortex_m::register::msp::read();
519 let stacked = sp as *const u32;
520 // Stacked registers follow ARMv8-M procedure call standard order
521 let stacked_pc = unsafe { stacked.add(6).read() };
522 let stacked_lr = unsafe { stacked.add(5).read() };
523
524 LAST_DEFAULT_VECTOR.store(active as u16, Ordering::Relaxed);
525 LAST_DEFAULT_CFSR.store(cfsr, Ordering::Relaxed);
526 LAST_DEFAULT_HFSR.store(hfsr, Ordering::Relaxed);
527 LAST_DEFAULT_COUNT.fetch_add(1, Ordering::Relaxed);
528 LAST_DEFAULT_PC.store(stacked_pc, Ordering::Relaxed);
529 LAST_DEFAULT_LR.store(stacked_lr, Ordering::Relaxed);
530 LAST_DEFAULT_SP.store(sp, Ordering::Relaxed);
531
532 // Do nothing here: on some MCUs/TrustZone setups, writing NVIC from a spurious
533 // handler can fault if targeting the Secure bank. Just return.
534 cortex_m::asm::dsb();
535 cortex_m::asm::isb();
536}
diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs
new file mode 100644
index 000000000..be279e509
--- /dev/null
+++ b/embassy-mcxa/src/lib.rs
@@ -0,0 +1,447 @@
1#![no_std]
2#![allow(async_fn_in_trait)]
3#![doc = include_str!("../README.md")]
4
5// //! ## Feature flags
6// #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
7
8pub mod clocks; // still provide clock helpers
9pub mod dma;
10pub mod gpio;
11
12pub mod adc;
13pub mod clkout;
14pub mod config;
15pub mod crc;
16pub mod i2c;
17pub mod interrupt;
18pub mod lpuart;
19pub mod ostimer;
20pub mod rtc;
21
22pub use crate::pac::NVIC_PRIO_BITS;
23
24#[rustfmt::skip]
25embassy_hal_internal::peripherals!(
26 ADC0,
27 ADC1,
28 ADC2,
29 ADC3,
30
31 AOI0,
32 AOI1,
33
34 CAN0,
35 CAN1,
36
37 CDOG0,
38 CDOG1,
39
40 // CLKOUT is not specifically a peripheral (it's part of SYSCON),
41 // but we still want it to be a singleton.
42 CLKOUT,
43
44 CMC,
45 CMP0,
46 CMP1,
47 CRC0,
48
49 CTIMER0,
50 CTIMER1,
51 CTIMER2,
52 CTIMER3,
53 CTIMER4,
54
55 DBGMAILBOX,
56 DMA0,
57 DMA_CH0,
58 DMA_CH1,
59 DMA_CH2,
60 DMA_CH3,
61 DMA_CH4,
62 DMA_CH5,
63 DMA_CH6,
64 DMA_CH7,
65 EDMA0_TCD0,
66 EIM0,
67 EQDC0,
68 EQDC1,
69 ERM0,
70 FLEXIO0,
71 FLEXPWM0,
72 FLEXPWM1,
73 FMC0,
74 FMU0,
75 FREQME0,
76 GLIKEY0,
77
78 GPIO0,
79 GPIO1,
80 GPIO2,
81 GPIO3,
82 GPIO4,
83
84 I3C0,
85 INPUTMUX0,
86
87 LPI2C0,
88 LPI2C1,
89 LPI2C2,
90 LPI2C3,
91
92 LPSPI0,
93 LPSPI1,
94
95 LPTMR0,
96
97 LPUART0,
98 LPUART1,
99 LPUART2,
100 LPUART3,
101 LPUART4,
102 LPUART5,
103
104 MAU0,
105 MBC0,
106 MRCC0,
107 OPAMP0,
108
109 #[cfg(not(feature = "time"))]
110 OSTIMER0,
111
112 P0_0,
113 P0_1,
114 P0_2,
115 P0_3,
116 P0_4,
117 P0_5,
118 P0_6,
119 P0_7,
120 P0_8,
121 P0_9,
122 P0_10,
123 P0_11,
124 P0_12,
125 P0_13,
126 P0_14,
127 P0_15,
128 P0_16,
129 P0_17,
130 P0_18,
131 P0_19,
132 P0_20,
133 P0_21,
134 P0_22,
135 P0_23,
136 P0_24,
137 P0_25,
138 P0_26,
139 P0_27,
140 P0_28,
141 P0_29,
142 P0_30,
143 P0_31,
144
145 P1_0,
146 P1_1,
147 P1_2,
148 P1_3,
149 P1_4,
150 P1_5,
151 P1_6,
152 P1_7,
153 P1_8,
154 P1_9,
155 P1_10,
156 P1_11,
157 P1_12,
158 P1_13,
159 P1_14,
160 P1_15,
161 P1_16,
162 P1_17,
163 P1_18,
164 P1_19,
165 P1_20,
166 P1_21,
167 P1_22,
168 P1_23,
169 P1_24,
170 P1_25,
171 P1_26,
172 P1_27,
173 P1_28,
174 P1_29,
175 P1_30,
176 P1_31,
177
178 P2_0,
179 P2_1,
180 P2_2,
181 P2_3,
182 P2_4,
183 P2_5,
184 P2_6,
185 P2_7,
186 P2_8,
187 P2_9,
188 P2_10,
189 P2_11,
190 P2_12,
191 P2_13,
192 P2_14,
193 P2_15,
194 P2_16,
195 P2_17,
196 P2_18,
197 P2_19,
198 P2_20,
199 P2_21,
200 P2_22,
201 P2_23,
202 P2_24,
203 P2_25,
204 P2_26,
205 P2_27,
206 P2_28,
207 P2_29,
208 P2_30,
209 P2_31,
210
211 P3_0,
212 P3_1,
213 P3_2,
214 P3_3,
215 P3_4,
216 P3_5,
217 P3_6,
218 P3_7,
219 P3_8,
220 P3_9,
221 P3_10,
222 P3_11,
223 P3_12,
224 P3_13,
225 P3_14,
226 P3_15,
227 P3_16,
228 P3_17,
229 P3_18,
230 P3_19,
231 P3_20,
232 P3_21,
233 P3_22,
234 P3_23,
235 P3_24,
236 P3_25,
237 P3_26,
238 P3_27,
239 P3_28,
240 P3_29,
241 P3_30,
242 P3_31,
243
244 P4_0,
245 P4_1,
246 P4_2,
247 P4_3,
248 P4_4,
249 P4_5,
250 P4_6,
251 P4_7,
252 P4_8,
253 P4_9,
254 P4_10,
255 P4_11,
256 P4_12,
257 P4_13,
258 P4_14,
259 P4_15,
260 P4_16,
261 P4_17,
262 P4_18,
263 P4_19,
264 P4_20,
265 P4_21,
266 P4_22,
267 P4_23,
268 P4_24,
269 P4_25,
270 P4_26,
271 P4_27,
272 P4_28,
273 P4_29,
274 P4_30,
275 P4_31,
276
277 P5_0,
278 P5_1,
279 P5_2,
280 P5_3,
281 P5_4,
282 P5_5,
283 P5_6,
284 P5_7,
285 P5_8,
286 P5_9,
287 P5_10,
288 P5_11,
289 P5_12,
290 P5_13,
291 P5_14,
292 P5_15,
293 P5_16,
294 P5_17,
295 P5_18,
296 P5_19,
297 P5_20,
298 P5_21,
299 P5_22,
300 P5_23,
301 P5_24,
302 P5_25,
303 P5_26,
304 P5_27,
305 P5_28,
306 P5_29,
307 P5_30,
308 P5_31,
309
310 PKC0,
311
312 PORT0,
313 PORT1,
314 PORT2,
315 PORT3,
316 PORT4,
317
318 RTC0,
319 SAU,
320 SCG0,
321 SCN_SCB,
322 SGI0,
323 SMARTDMA0,
324 SPC0,
325 SYSCON,
326 TDET0,
327 TRNG0,
328 UDF0,
329 USB0,
330 UTICK0,
331 VBAT0,
332 WAKETIMER0,
333 WUU0,
334 WWDT0,
335);
336
337// Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it.
338
339// Re-export interrupt traits and types
340pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output};
341pub use interrupt::InterruptExt;
342#[cfg(feature = "unstable-pac")]
343pub use mcxa_pac as pac;
344#[cfg(not(feature = "unstable-pac"))]
345pub(crate) use mcxa_pac as pac;
346
347/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals.
348/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling).
349pub fn init(cfg: crate::config::Config) -> Peripherals {
350 let peripherals = Peripherals::take();
351 // Apply user-configured priority early; enabling is left to examples/apps
352 #[cfg(feature = "time")]
353 crate::interrupt::OS_EVENT.set_priority(cfg.time_interrupt_priority);
354 // Apply user-configured priority early; enabling is left to examples/apps
355 crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority);
356 // Apply user-configured priority early; enabling is left to examples/apps
357 crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority);
358 // Apply user-configured priority early; enabling is left to examples/apps
359 crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority);
360 // Apply user-configured priority early; enabling is left to examples/apps
361 crate::interrupt::GPIO2.set_priority(cfg.gpio_interrupt_priority);
362 // Apply user-configured priority early; enabling is left to examples/apps
363 crate::interrupt::GPIO3.set_priority(cfg.gpio_interrupt_priority);
364 // Apply user-configured priority early; enabling is left to examples/apps
365 crate::interrupt::GPIO4.set_priority(cfg.gpio_interrupt_priority);
366
367 // Configure clocks
368 crate::clocks::init(cfg.clock_cfg).unwrap();
369
370 unsafe {
371 crate::gpio::init();
372 }
373
374 // Initialize DMA controller (clock, reset, configuration)
375 crate::dma::init();
376
377 // Initialize embassy-time global driver backed by OSTIMER0
378 #[cfg(feature = "time")]
379 crate::ostimer::time_driver::init(crate::config::Config::default().time_interrupt_priority, 1_000_000);
380
381 // Enable GPIO clocks
382 unsafe {
383 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT0>(&crate::clocks::periph_helpers::NoConfig);
384 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO0>(&crate::clocks::periph_helpers::NoConfig);
385
386 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT1>(&crate::clocks::periph_helpers::NoConfig);
387 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO1>(&crate::clocks::periph_helpers::NoConfig);
388
389 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT2>(&crate::clocks::periph_helpers::NoConfig);
390 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO2>(&crate::clocks::periph_helpers::NoConfig);
391
392 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT3>(&crate::clocks::periph_helpers::NoConfig);
393 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO3>(&crate::clocks::periph_helpers::NoConfig);
394
395 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT4>(&crate::clocks::periph_helpers::NoConfig);
396 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO4>(&crate::clocks::periph_helpers::NoConfig);
397 }
398
399 peripherals
400}
401
402/// Macro to bind interrupts to handlers, similar to embassy-imxrt.
403///
404/// Example:
405/// - Bind OS_EVENT to the OSTIMER time-driver handler
406/// bind_interrupts!(struct Irqs { OS_EVENT => crate::ostimer::time_driver::OsEventHandler; });
407#[macro_export]
408macro_rules! bind_interrupts {
409 ($(#[$attr:meta])* $vis:vis struct $name:ident {
410 $(
411 $(#[cfg($cond_irq:meta)])?
412 $irq:ident => $(
413 $(#[cfg($cond_handler:meta)])?
414 $handler:ty
415 ),*;
416 )*
417 }) => {
418 #[derive(Copy, Clone)]
419 $(#[$attr])*
420 $vis struct $name;
421
422 $(
423 #[allow(non_snake_case)]
424 #[no_mangle]
425 $(#[cfg($cond_irq)])?
426 unsafe extern "C" fn $irq() {
427 unsafe {
428 $(
429 $(#[cfg($cond_handler)])?
430 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
431 )*
432 }
433 }
434
435 $(#[cfg($cond_irq)])?
436 $crate::bind_interrupts!(@inner
437 $(
438 $(#[cfg($cond_handler)])?
439 unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
440 )*
441 );
442 )*
443 };
444 (@inner $($t:tt)*) => {
445 $($t)*
446 }
447}
diff --git a/embassy-mcxa/src/lpuart/buffered.rs b/embassy-mcxa/src/lpuart/buffered.rs
new file mode 100644
index 000000000..34fdbb76c
--- /dev/null
+++ b/embassy-mcxa/src/lpuart/buffered.rs
@@ -0,0 +1,780 @@
1use core::future::poll_fn;
2use core::marker::PhantomData;
3use core::sync::atomic::{AtomicBool, Ordering};
4use core::task::Poll;
5
6use embassy_hal_internal::Peri;
7use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
8use embassy_sync::waitqueue::AtomicWaker;
9
10use super::*;
11use crate::interrupt;
12
13// ============================================================================
14// STATIC STATE MANAGEMENT
15// ============================================================================
16
17/// State for buffered LPUART operations
18pub struct State {
19 tx_waker: AtomicWaker,
20 tx_buf: RingBuffer,
21 tx_done: AtomicBool,
22 rx_waker: AtomicWaker,
23 rx_buf: RingBuffer,
24 initialized: AtomicBool,
25}
26
27impl Default for State {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl State {
34 /// Create a new state instance
35 pub const fn new() -> Self {
36 Self {
37 tx_waker: AtomicWaker::new(),
38 tx_buf: RingBuffer::new(),
39 tx_done: AtomicBool::new(true),
40 rx_waker: AtomicWaker::new(),
41 rx_buf: RingBuffer::new(),
42 initialized: AtomicBool::new(false),
43 }
44 }
45}
46// ============================================================================
47// BUFFERED DRIVER STRUCTURES
48// ============================================================================
49
50/// Buffered LPUART driver
51pub struct BufferedLpuart<'a> {
52 tx: BufferedLpuartTx<'a>,
53 rx: BufferedLpuartRx<'a>,
54}
55
56/// Buffered LPUART TX driver
57pub struct BufferedLpuartTx<'a> {
58 info: Info,
59 state: &'static State,
60 _tx_pin: Peri<'a, AnyPin>,
61 _cts_pin: Option<Peri<'a, AnyPin>>,
62}
63
64/// Buffered LPUART RX driver
65pub struct BufferedLpuartRx<'a> {
66 info: Info,
67 state: &'static State,
68 _rx_pin: Peri<'a, AnyPin>,
69 _rts_pin: Option<Peri<'a, AnyPin>>,
70}
71
72// ============================================================================
73// BUFFERED LPUART IMPLEMENTATION
74// ============================================================================
75
76impl<'a> BufferedLpuart<'a> {
77 /// Common initialization logic
78 fn init_common<T: Instance>(
79 _inner: &Peri<'a, T>,
80 tx_buffer: Option<&'a mut [u8]>,
81 rx_buffer: Option<&'a mut [u8]>,
82 config: &Config,
83 enable_tx: bool,
84 enable_rx: bool,
85 enable_rts: bool,
86 enable_cts: bool,
87 ) -> Result<&'static State> {
88 let state = T::buffered_state();
89
90 if state.initialized.load(Ordering::Relaxed) {
91 return Err(Error::InvalidArgument);
92 }
93
94 // Initialize buffers
95 if let Some(tx_buffer) = tx_buffer {
96 if tx_buffer.is_empty() {
97 return Err(Error::InvalidArgument);
98 }
99 unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), tx_buffer.len()) };
100 }
101
102 if let Some(rx_buffer) = rx_buffer {
103 if rx_buffer.is_empty() {
104 return Err(Error::InvalidArgument);
105 }
106 unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_buffer.len()) };
107 }
108
109 state.initialized.store(true, Ordering::Relaxed);
110
111 // Enable clocks and initialize hardware
112 let conf = LpuartConfig {
113 power: config.power,
114 source: config.source,
115 div: config.div,
116 instance: T::CLOCK_INSTANCE,
117 };
118 let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
119
120 Self::init_hardware(
121 T::info().regs,
122 *config,
123 clock_freq,
124 enable_tx,
125 enable_rx,
126 enable_rts,
127 enable_cts,
128 )?;
129
130 Ok(state)
131 }
132
133 /// Helper for full-duplex initialization
134 fn new_inner<T: Instance>(
135 inner: Peri<'a, T>,
136 tx_pin: Peri<'a, AnyPin>,
137 rx_pin: Peri<'a, AnyPin>,
138 rts_pin: Option<Peri<'a, AnyPin>>,
139 cts_pin: Option<Peri<'a, AnyPin>>,
140 tx_buffer: &'a mut [u8],
141 rx_buffer: &'a mut [u8],
142 config: Config,
143 ) -> Result<(BufferedLpuartTx<'a>, BufferedLpuartRx<'a>)> {
144 let state = Self::init_common::<T>(
145 &inner,
146 Some(tx_buffer),
147 Some(rx_buffer),
148 &config,
149 true,
150 true,
151 rts_pin.is_some(),
152 cts_pin.is_some(),
153 )?;
154
155 let tx = BufferedLpuartTx {
156 info: T::info(),
157 state,
158 _tx_pin: tx_pin,
159 _cts_pin: cts_pin,
160 };
161
162 let rx = BufferedLpuartRx {
163 info: T::info(),
164 state,
165 _rx_pin: rx_pin,
166 _rts_pin: rts_pin,
167 };
168
169 Ok((tx, rx))
170 }
171
172 /// Common hardware initialization logic
173 fn init_hardware(
174 regs: &'static mcxa_pac::lpuart0::RegisterBlock,
175 config: Config,
176 clock_freq: u32,
177 enable_tx: bool,
178 enable_rx: bool,
179 enable_rts: bool,
180 enable_cts: bool,
181 ) -> Result<()> {
182 // Perform standard initialization
183 perform_software_reset(regs);
184 disable_transceiver(regs);
185 configure_baudrate(regs, config.baudrate_bps, clock_freq)?;
186 configure_frame_format(regs, &config);
187 configure_control_settings(regs, &config);
188 configure_fifo(regs, &config);
189 clear_all_status_flags(regs);
190 configure_flow_control(regs, enable_rts, enable_cts, &config);
191 configure_bit_order(regs, config.msb_first);
192
193 // Enable interrupts for buffered operation
194 cortex_m::interrupt::free(|_| {
195 regs.ctrl().modify(|_, w| {
196 w.rie()
197 .enabled() // RX interrupt
198 .orie()
199 .enabled() // Overrun interrupt
200 .peie()
201 .enabled() // Parity error interrupt
202 .feie()
203 .enabled() // Framing error interrupt
204 .neie()
205 .enabled() // Noise error interrupt
206 });
207 });
208
209 // Enable the transceiver
210 enable_transceiver(regs, enable_rx, enable_tx);
211
212 Ok(())
213 }
214
215 /// Create a new full duplex buffered LPUART
216 pub fn new<T: Instance>(
217 inner: Peri<'a, T>,
218 tx_pin: Peri<'a, impl TxPin<T>>,
219 rx_pin: Peri<'a, impl RxPin<T>>,
220 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
221 tx_buffer: &'a mut [u8],
222 rx_buffer: &'a mut [u8],
223 config: Config,
224 ) -> Result<Self> {
225 tx_pin.as_tx();
226 rx_pin.as_rx();
227
228 let (tx, rx) = Self::new_inner::<T>(
229 inner,
230 tx_pin.into(),
231 rx_pin.into(),
232 None,
233 None,
234 tx_buffer,
235 rx_buffer,
236 config,
237 )?;
238
239 Ok(Self { tx, rx })
240 }
241
242 /// Create a new buffered LPUART instance with RTS/CTS flow control
243 pub fn new_with_rtscts<T: Instance>(
244 inner: Peri<'a, T>,
245 tx_pin: Peri<'a, impl TxPin<T>>,
246 rx_pin: Peri<'a, impl RxPin<T>>,
247 rts_pin: Peri<'a, impl RtsPin<T>>,
248 cts_pin: Peri<'a, impl CtsPin<T>>,
249 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
250 tx_buffer: &'a mut [u8],
251 rx_buffer: &'a mut [u8],
252 config: Config,
253 ) -> Result<Self> {
254 tx_pin.as_tx();
255 rx_pin.as_rx();
256 rts_pin.as_rts();
257 cts_pin.as_cts();
258
259 let (tx, rx) = Self::new_inner::<T>(
260 inner,
261 tx_pin.into(),
262 rx_pin.into(),
263 Some(rts_pin.into()),
264 Some(cts_pin.into()),
265 tx_buffer,
266 rx_buffer,
267 config,
268 )?;
269
270 Ok(Self { tx, rx })
271 }
272
273 /// Create a new buffered LPUART with only RTS flow control (RX flow control)
274 pub fn new_with_rts<T: Instance>(
275 inner: Peri<'a, T>,
276 tx_pin: Peri<'a, impl TxPin<T>>,
277 rx_pin: Peri<'a, impl RxPin<T>>,
278 rts_pin: Peri<'a, impl RtsPin<T>>,
279 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
280 tx_buffer: &'a mut [u8],
281 rx_buffer: &'a mut [u8],
282 config: Config,
283 ) -> Result<Self> {
284 tx_pin.as_tx();
285 rx_pin.as_rx();
286 rts_pin.as_rts();
287
288 let (tx, rx) = Self::new_inner::<T>(
289 inner,
290 tx_pin.into(),
291 rx_pin.into(),
292 Some(rts_pin.into()),
293 None,
294 tx_buffer,
295 rx_buffer,
296 config,
297 )?;
298
299 Ok(Self { tx, rx })
300 }
301
302 /// Create a new buffered LPUART with only CTS flow control (TX flow control)
303 pub fn new_with_cts<T: Instance>(
304 inner: Peri<'a, T>,
305 tx_pin: Peri<'a, impl TxPin<T>>,
306 rx_pin: Peri<'a, impl RxPin<T>>,
307 cts_pin: Peri<'a, impl CtsPin<T>>,
308 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
309 tx_buffer: &'a mut [u8],
310 rx_buffer: &'a mut [u8],
311 config: Config,
312 ) -> Result<Self> {
313 tx_pin.as_tx();
314 rx_pin.as_rx();
315 cts_pin.as_cts();
316
317 let (tx, rx) = Self::new_inner::<T>(
318 inner,
319 tx_pin.into(),
320 rx_pin.into(),
321 None,
322 Some(cts_pin.into()),
323 tx_buffer,
324 rx_buffer,
325 config,
326 )?;
327
328 Ok(Self { tx, rx })
329 }
330
331 /// Split the buffered LPUART into separate TX and RX parts
332 pub fn split(self) -> (BufferedLpuartTx<'a>, BufferedLpuartRx<'a>) {
333 (self.tx, self.rx)
334 }
335
336 /// Get mutable references to TX and RX parts
337 pub fn split_ref(&mut self) -> (&mut BufferedLpuartTx<'a>, &mut BufferedLpuartRx<'a>) {
338 (&mut self.tx, &mut self.rx)
339 }
340}
341
342// ============================================================================
343// BUFFERED TX IMPLEMENTATION
344// ============================================================================
345
346impl<'a> BufferedLpuartTx<'a> {
347 /// Helper for TX-only initialization
348 fn new_inner<T: Instance>(
349 inner: Peri<'a, T>,
350 tx_pin: Peri<'a, AnyPin>,
351 cts_pin: Option<Peri<'a, AnyPin>>,
352 tx_buffer: &'a mut [u8],
353 config: Config,
354 ) -> Result<BufferedLpuartTx<'a>> {
355 let state = BufferedLpuart::init_common::<T>(
356 &inner,
357 Some(tx_buffer),
358 None,
359 &config,
360 true,
361 false,
362 false,
363 cts_pin.is_some(),
364 )?;
365
366 Ok(BufferedLpuartTx {
367 info: T::info(),
368 state,
369 _tx_pin: tx_pin,
370 _cts_pin: cts_pin,
371 })
372 }
373
374 pub fn new<T: Instance>(
375 inner: Peri<'a, T>,
376 tx_pin: Peri<'a, impl TxPin<T>>,
377 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
378 tx_buffer: &'a mut [u8],
379 config: Config,
380 ) -> Result<Self> {
381 tx_pin.as_tx();
382
383 Self::new_inner::<T>(inner, tx_pin.into(), None, tx_buffer, config)
384 }
385
386 /// Create a new TX-only buffered LPUART with CTS flow control
387 pub fn new_with_cts<T: Instance>(
388 inner: Peri<'a, T>,
389 tx_pin: Peri<'a, impl TxPin<T>>,
390 cts_pin: Peri<'a, impl CtsPin<T>>,
391 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
392 tx_buffer: &'a mut [u8],
393 config: Config,
394 ) -> Result<Self> {
395 tx_pin.as_tx();
396 cts_pin.as_cts();
397
398 Self::new_inner::<T>(inner, tx_pin.into(), Some(cts_pin.into()), tx_buffer, config)
399 }
400}
401
402impl<'a> BufferedLpuartTx<'a> {
403 /// Write data asynchronously
404 pub async fn write(&mut self, buf: &[u8]) -> Result<usize> {
405 let mut written = 0;
406
407 for &byte in buf {
408 // Wait for space in the buffer
409 poll_fn(|cx| {
410 self.state.tx_waker.register(cx.waker());
411
412 let mut writer = unsafe { self.state.tx_buf.writer() };
413 if writer.push_one(byte) {
414 // Enable TX interrupt to start transmission
415 cortex_m::interrupt::free(|_| {
416 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
417 });
418 Poll::Ready(Ok(()))
419 } else {
420 Poll::Pending
421 }
422 })
423 .await?;
424
425 written += 1;
426 }
427
428 Ok(written)
429 }
430
431 /// Flush the TX buffer and wait for transmission to complete
432 pub async fn flush(&mut self) -> Result<()> {
433 // Wait for TX buffer to empty and transmission to complete
434 poll_fn(|cx| {
435 self.state.tx_waker.register(cx.waker());
436
437 let tx_empty = self.state.tx_buf.is_empty();
438 let fifo_empty = self.info.regs.water().read().txcount().bits() == 0;
439 let tc_complete = self.info.regs.stat().read().tc().is_complete();
440
441 if tx_empty && fifo_empty && tc_complete {
442 Poll::Ready(Ok(()))
443 } else {
444 // Enable appropriate interrupt
445 cortex_m::interrupt::free(|_| {
446 if !tx_empty {
447 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
448 } else {
449 self.info.regs.ctrl().modify(|_, w| w.tcie().enabled());
450 }
451 });
452 Poll::Pending
453 }
454 })
455 .await
456 }
457
458 /// Try to write without blocking
459 pub fn try_write(&mut self, buf: &[u8]) -> Result<usize> {
460 let mut writer = unsafe { self.state.tx_buf.writer() };
461 let mut written = 0;
462
463 for &byte in buf {
464 if writer.push_one(byte) {
465 written += 1;
466 } else {
467 break;
468 }
469 }
470
471 if written > 0 {
472 // Enable TX interrupt to start transmission
473 cortex_m::interrupt::free(|_| {
474 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
475 });
476 }
477
478 Ok(written)
479 }
480}
481
482// ============================================================================
483// BUFFERED RX IMPLEMENTATION
484// ============================================================================
485
486impl<'a> BufferedLpuartRx<'a> {
487 /// Helper for RX-only initialization
488 fn new_inner<T: Instance>(
489 inner: Peri<'a, T>,
490 rx_pin: Peri<'a, AnyPin>,
491 rts_pin: Option<Peri<'a, AnyPin>>,
492 rx_buffer: &'a mut [u8],
493 config: Config,
494 ) -> Result<BufferedLpuartRx<'a>> {
495 let state = BufferedLpuart::init_common::<T>(
496 &inner,
497 None,
498 Some(rx_buffer),
499 &config,
500 false,
501 true,
502 rts_pin.is_some(),
503 false,
504 )?;
505
506 Ok(BufferedLpuartRx {
507 info: T::info(),
508 state,
509 _rx_pin: rx_pin,
510 _rts_pin: rts_pin,
511 })
512 }
513
514 /// Create a new RX-only buffered LPUART
515 pub fn new<T: Instance>(
516 inner: Peri<'a, T>,
517 rx_pin: Peri<'a, impl RxPin<T>>,
518 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
519 rx_buffer: &'a mut [u8],
520 config: Config,
521 ) -> Result<Self> {
522 rx_pin.as_rx();
523
524 Self::new_inner::<T>(inner, rx_pin.into(), None, rx_buffer, config)
525 }
526
527 /// Create a new RX-only buffered LPUART with RTS flow control
528 pub fn new_with_rts<T: Instance>(
529 inner: Peri<'a, T>,
530 rx_pin: Peri<'a, impl RxPin<T>>,
531 rts_pin: Peri<'a, impl RtsPin<T>>,
532 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
533 rx_buffer: &'a mut [u8],
534 config: Config,
535 ) -> Result<Self> {
536 rx_pin.as_rx();
537 rts_pin.as_rts();
538
539 Self::new_inner::<T>(inner, rx_pin.into(), Some(rts_pin.into()), rx_buffer, config)
540 }
541}
542
543impl<'a> BufferedLpuartRx<'a> {
544 /// Read data asynchronously
545 pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
546 if buf.is_empty() {
547 return Ok(0);
548 }
549
550 let mut read = 0;
551
552 // Try to read available data
553 poll_fn(|cx| {
554 self.state.rx_waker.register(cx.waker());
555
556 // Disable RX interrupt while reading from buffer
557 cortex_m::interrupt::free(|_| {
558 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
559 });
560
561 let mut reader = unsafe { self.state.rx_buf.reader() };
562 let available = reader.pop(|data| {
563 let to_copy = core::cmp::min(data.len(), buf.len() - read);
564 if to_copy > 0 {
565 buf[read..read + to_copy].copy_from_slice(&data[..to_copy]);
566 read += to_copy;
567 }
568 to_copy
569 });
570
571 // Re-enable RX interrupt
572 cortex_m::interrupt::free(|_| {
573 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
574 });
575
576 if read > 0 {
577 Poll::Ready(Ok(read))
578 } else if available == 0 {
579 Poll::Pending
580 } else {
581 Poll::Ready(Ok(0))
582 }
583 })
584 .await
585 }
586
587 /// Try to read without blocking
588 pub fn try_read(&mut self, buf: &mut [u8]) -> Result<usize> {
589 if buf.is_empty() {
590 return Ok(0);
591 }
592
593 // Disable RX interrupt while reading from buffer
594 cortex_m::interrupt::free(|_| {
595 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
596 });
597
598 let mut reader = unsafe { self.state.rx_buf.reader() };
599 let read = reader.pop(|data| {
600 let to_copy = core::cmp::min(data.len(), buf.len());
601 if to_copy > 0 {
602 buf[..to_copy].copy_from_slice(&data[..to_copy]);
603 }
604 to_copy
605 });
606
607 // Re-enable RX interrupt
608 cortex_m::interrupt::free(|_| {
609 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
610 });
611
612 Ok(read)
613 }
614}
615
616// ============================================================================
617// INTERRUPT HANDLER
618// ============================================================================
619
620/// Buffered UART interrupt handler
621pub struct BufferedInterruptHandler<T: Instance> {
622 _phantom: PhantomData<T>,
623}
624
625impl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> {
626 unsafe fn on_interrupt() {
627 let regs = T::info().regs;
628 let state = T::buffered_state();
629
630 // Check if this instance is initialized
631 if !state.initialized.load(Ordering::Relaxed) {
632 return;
633 }
634
635 let ctrl = regs.ctrl().read();
636 let stat = regs.stat().read();
637 let has_fifo = regs.param().read().rxfifo().bits() > 0;
638
639 // Handle overrun error
640 if stat.or().is_overrun() {
641 regs.stat().write(|w| w.or().clear_bit_by_one());
642 state.rx_waker.wake();
643 return;
644 }
645
646 // Clear other error flags
647 if stat.pf().is_parity() {
648 regs.stat().write(|w| w.pf().clear_bit_by_one());
649 }
650 if stat.fe().is_error() {
651 regs.stat().write(|w| w.fe().clear_bit_by_one());
652 }
653 if stat.nf().is_noise() {
654 regs.stat().write(|w| w.nf().clear_bit_by_one());
655 }
656
657 // Handle RX data
658 if ctrl.rie().is_enabled() && (has_data(regs) || stat.idle().is_idle()) {
659 let mut pushed_any = false;
660 let mut writer = state.rx_buf.writer();
661
662 if has_fifo {
663 // Read from FIFO
664 while regs.water().read().rxcount().bits() > 0 {
665 let byte = (regs.data().read().bits() & 0xFF) as u8;
666 if writer.push_one(byte) {
667 pushed_any = true;
668 } else {
669 // Buffer full, stop reading
670 break;
671 }
672 }
673 } else {
674 // Read single byte
675 if regs.stat().read().rdrf().is_rxdata() {
676 let byte = (regs.data().read().bits() & 0xFF) as u8;
677 if writer.push_one(byte) {
678 pushed_any = true;
679 }
680 }
681 }
682
683 if pushed_any {
684 state.rx_waker.wake();
685 }
686
687 // Clear idle flag if set
688 if stat.idle().is_idle() {
689 regs.stat().write(|w| w.idle().clear_bit_by_one());
690 }
691 }
692
693 // Handle TX data
694 if ctrl.tie().is_enabled() {
695 let mut sent_any = false;
696 let mut reader = state.tx_buf.reader();
697
698 // Send data while TX buffer is ready and we have data
699 while regs.stat().read().tdre().is_no_txdata() {
700 if let Some(byte) = reader.pop_one() {
701 regs.data().write(|w| w.bits(u32::from(byte)));
702 sent_any = true;
703 } else {
704 // No more data to send
705 break;
706 }
707 }
708
709 if sent_any {
710 state.tx_waker.wake();
711 }
712
713 // If buffer is empty, switch to TC interrupt or disable
714 if state.tx_buf.is_empty() {
715 cortex_m::interrupt::free(|_| {
716 regs.ctrl().modify(|_, w| w.tie().disabled().tcie().enabled());
717 });
718 }
719 }
720
721 // Handle transmission complete
722 if ctrl.tcie().is_enabled() && regs.stat().read().tc().is_complete() {
723 state.tx_done.store(true, Ordering::Release);
724 state.tx_waker.wake();
725
726 // Disable TC interrupt
727 cortex_m::interrupt::free(|_| {
728 regs.ctrl().modify(|_, w| w.tcie().disabled());
729 });
730 }
731 }
732}
733
734// ============================================================================
735// EMBEDDED-IO ASYNC TRAIT IMPLEMENTATIONS
736// ============================================================================
737
738impl embedded_io_async::ErrorType for BufferedLpuartTx<'_> {
739 type Error = Error;
740}
741
742impl embedded_io_async::ErrorType for BufferedLpuartRx<'_> {
743 type Error = Error;
744}
745
746impl embedded_io_async::ErrorType for BufferedLpuart<'_> {
747 type Error = Error;
748}
749
750impl embedded_io_async::Write for BufferedLpuartTx<'_> {
751 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
752 self.write(buf).await
753 }
754
755 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
756 self.flush().await
757 }
758}
759
760impl embedded_io_async::Read for BufferedLpuartRx<'_> {
761 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
762 self.read(buf).await
763 }
764}
765
766impl embedded_io_async::Write for BufferedLpuart<'_> {
767 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
768 self.tx.write(buf).await
769 }
770
771 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
772 self.tx.flush().await
773 }
774}
775
776impl embedded_io_async::Read for BufferedLpuart<'_> {
777 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
778 self.rx.read(buf).await
779 }
780}
diff --git a/embassy-mcxa/src/lpuart/mod.rs b/embassy-mcxa/src/lpuart/mod.rs
new file mode 100644
index 000000000..e59ce8140
--- /dev/null
+++ b/embassy-mcxa/src/lpuart/mod.rs
@@ -0,0 +1,1783 @@
1use core::future::Future;
2use core::marker::PhantomData;
3
4use embassy_hal_internal::{Peri, PeripheralType};
5use paste::paste;
6
7use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig};
8use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset};
9use crate::gpio::SealedPin;
10use crate::pac::lpuart0::baud::Sbns as StopBits;
11use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, M as DataBits, Pt as Parity};
12use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource};
13use crate::pac::lpuart0::stat::Msbf as MsbFirst;
14use crate::{AnyPin, interrupt, pac};
15
16pub mod buffered;
17
18// ============================================================================
19// DMA INTEGRATION
20// ============================================================================
21
22use crate::dma::{
23 Channel as DmaChannelTrait, DMA_MAX_TRANSFER_SIZE, DmaChannel, DmaRequest, EnableInterrupt, RingBuffer,
24};
25
26// ============================================================================
27// MISC
28// ============================================================================
29
30mod sealed {
31 /// Simply seal a trait to prevent external implementations
32 pub trait Sealed {}
33}
34
35// ============================================================================
36// INSTANCE TRAIT
37// ============================================================================
38
39pub type Regs = &'static crate::pac::lpuart0::RegisterBlock;
40
41pub trait SealedInstance {
42 fn info() -> Info;
43 fn index() -> usize;
44 fn buffered_state() -> &'static buffered::State;
45}
46
47pub struct Info {
48 pub regs: Regs,
49}
50
51/// Trait for LPUART peripheral instances
52#[allow(private_bounds)]
53pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = LpuartConfig> {
54 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance;
55 type Interrupt: interrupt::typelevel::Interrupt;
56 /// Type-safe DMA request source for TX
57 type TxDmaRequest: DmaRequest;
58 /// Type-safe DMA request source for RX
59 type RxDmaRequest: DmaRequest;
60}
61
62macro_rules! impl_instance {
63 ($($n:expr);* $(;)?) => {
64 $(
65 paste!{
66 impl SealedInstance for crate::peripherals::[<LPUART $n>] {
67 fn info() -> Info {
68 Info {
69 regs: unsafe { &*pac::[<Lpuart $n>]::ptr() },
70 }
71 }
72
73 #[inline]
74 fn index() -> usize {
75 $n
76 }
77
78 fn buffered_state() -> &'static buffered::State {
79 static BUFFERED_STATE: buffered::State = buffered::State::new();
80 &BUFFERED_STATE
81 }
82 }
83
84 impl Instance for crate::peripherals::[<LPUART $n>] {
85 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance
86 = crate::clocks::periph_helpers::LpuartInstance::[<Lpuart $n>];
87 type Interrupt = crate::interrupt::typelevel::[<LPUART $n>];
88 type TxDmaRequest = crate::dma::[<Lpuart $n TxRequest>];
89 type RxDmaRequest = crate::dma::[<Lpuart $n RxRequest>];
90 }
91 }
92 )*
93 };
94}
95
96// DMA request sources are now type-safe via associated types.
97// The request source numbers are defined in src/dma.rs:
98// LPUART0: RX=21, TX=22 -> Lpuart0RxRequest, Lpuart0TxRequest
99// LPUART1: RX=23, TX=24 -> Lpuart1RxRequest, Lpuart1TxRequest
100// LPUART2: RX=25, TX=26 -> Lpuart2RxRequest, Lpuart2TxRequest
101// LPUART3: RX=27, TX=28 -> Lpuart3RxRequest, Lpuart3TxRequest
102// LPUART4: RX=29, TX=30 -> Lpuart4RxRequest, Lpuart4TxRequest
103// LPUART5: RX=31, TX=32 -> Lpuart5RxRequest, Lpuart5TxRequest
104impl_instance!(0; 1; 2; 3; 4; 5);
105
106// ============================================================================
107// INSTANCE HELPER FUNCTIONS
108// ============================================================================
109
110/// Perform software reset on the LPUART peripheral
111pub fn perform_software_reset(regs: Regs) {
112 // Software reset - set and clear RST bit (Global register)
113 regs.global().write(|w| w.rst().reset());
114 regs.global().write(|w| w.rst().no_effect());
115}
116
117/// Disable both transmitter and receiver
118pub fn disable_transceiver(regs: Regs) {
119 regs.ctrl().modify(|_, w| w.te().disabled().re().disabled());
120}
121
122/// Calculate and configure baudrate settings
123pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock_freq: u32) -> Result<()> {
124 let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?;
125
126 // Configure BAUD register
127 regs.baud().modify(|_, w| unsafe {
128 // Clear and set OSR
129 w.osr().bits(osr - 1);
130 // Clear and set SBR
131 w.sbr().bits(sbr);
132 // Set BOTHEDGE if OSR is between 4 and 7
133 if osr > 3 && osr < 8 {
134 w.bothedge().enabled()
135 } else {
136 w.bothedge().disabled()
137 }
138 });
139
140 Ok(())
141}
142
143/// Configure frame format (stop bits, data bits)
144pub fn configure_frame_format(regs: Regs, config: &Config) {
145 // Configure stop bits
146 regs.baud().modify(|_, w| w.sbns().variant(config.stop_bits_count));
147
148 // Clear M10 for now (10-bit mode)
149 regs.baud().modify(|_, w| w.m10().disabled());
150}
151
152/// Configure control settings (parity, data bits, idle config, pin swap)
153pub fn configure_control_settings(regs: Regs, config: &Config) {
154 regs.ctrl().modify(|_, w| {
155 // Parity configuration
156 let mut w = if let Some(parity) = config.parity_mode {
157 w.pe().enabled().pt().variant(parity)
158 } else {
159 w.pe().disabled()
160 };
161
162 // Data bits configuration
163 w = match config.data_bits_count {
164 DataBits::Data8 => {
165 if config.parity_mode.is_some() {
166 w.m().data9() // 8 data + 1 parity = 9 bits
167 } else {
168 w.m().data8() // 8 data bits only
169 }
170 }
171 DataBits::Data9 => w.m().data9(),
172 };
173
174 // Idle configuration
175 w = w.idlecfg().variant(config.rx_idle_config);
176 w = w.ilt().variant(config.rx_idle_type);
177
178 // Swap TXD/RXD if configured
179 if config.swap_txd_rxd {
180 w.swap().swap()
181 } else {
182 w.swap().standard()
183 }
184 });
185}
186
187/// Configure FIFO settings and watermarks
188pub fn configure_fifo(regs: Regs, config: &Config) {
189 // Configure WATER register for FIFO watermarks
190 regs.water().write(|w| unsafe {
191 w.rxwater()
192 .bits(config.rx_fifo_watermark)
193 .txwater()
194 .bits(config.tx_fifo_watermark)
195 });
196
197 // Enable TX/RX FIFOs
198 regs.fifo().modify(|_, w| w.txfe().enabled().rxfe().enabled());
199
200 // Flush FIFOs
201 regs.fifo()
202 .modify(|_, w| w.txflush().txfifo_rst().rxflush().rxfifo_rst());
203}
204
205/// Clear all status flags
206pub fn clear_all_status_flags(regs: Regs) {
207 regs.stat().reset();
208}
209
210/// Configure hardware flow control if enabled
211pub fn configure_flow_control(regs: Regs, enable_tx_cts: bool, enable_rx_rts: bool, config: &Config) {
212 if enable_rx_rts || enable_tx_cts {
213 regs.modir().modify(|_, w| {
214 let mut w = w;
215
216 // Configure TX CTS
217 w = w.txctsc().variant(config.tx_cts_config);
218 w = w.txctssrc().variant(config.tx_cts_source);
219
220 if enable_rx_rts {
221 w = w.rxrtse().enabled();
222 } else {
223 w = w.rxrtse().disabled();
224 }
225
226 if enable_tx_cts {
227 w = w.txctse().enabled();
228 } else {
229 w = w.txctse().disabled();
230 }
231
232 w
233 });
234 }
235}
236
237/// Configure bit order (MSB first or LSB first)
238pub fn configure_bit_order(regs: Regs, msb_first: MsbFirst) {
239 regs.stat().modify(|_, w| w.msbf().variant(msb_first));
240}
241
242/// Enable transmitter and/or receiver based on configuration
243pub fn enable_transceiver(regs: Regs, enable_tx: bool, enable_rx: bool) {
244 regs.ctrl().modify(|_, w| {
245 let mut w = w;
246 if enable_tx {
247 w = w.te().enabled();
248 }
249 if enable_rx {
250 w = w.re().enabled();
251 }
252 w
253 });
254}
255
256pub fn calculate_baudrate(baudrate: u32, src_clock_hz: u32) -> Result<(u8, u16)> {
257 let mut baud_diff = baudrate;
258 let mut osr = 0u8;
259 let mut sbr = 0u16;
260
261 // Try OSR values from 4 to 32
262 for osr_temp in 4u8..=32u8 {
263 // Calculate SBR: (srcClock_Hz * 2 / (baudRate * osr) + 1) / 2
264 let sbr_calc = ((src_clock_hz * 2) / (baudrate * osr_temp as u32)).div_ceil(2);
265
266 let sbr_temp = if sbr_calc == 0 {
267 1
268 } else if sbr_calc > 0x1FFF {
269 0x1FFF
270 } else {
271 sbr_calc as u16
272 };
273
274 // Calculate actual baud rate
275 let calculated_baud = src_clock_hz / (osr_temp as u32 * sbr_temp as u32);
276
277 let temp_diff = calculated_baud.abs_diff(baudrate);
278
279 if temp_diff <= baud_diff {
280 baud_diff = temp_diff;
281 osr = osr_temp;
282 sbr = sbr_temp;
283 }
284 }
285
286 // Check if baud rate difference is within 3%
287 if baud_diff > (baudrate / 100) * 3 {
288 return Err(Error::UnsupportedBaudrate);
289 }
290
291 Ok((osr, sbr))
292}
293
294/// Wait for all transmit operations to complete
295pub fn wait_for_tx_complete(regs: Regs) {
296 // Wait for TX FIFO to empty
297 while regs.water().read().txcount().bits() != 0 {
298 // Wait for TX FIFO to drain
299 }
300
301 // Wait for last character to shift out (TC = Transmission Complete)
302 while regs.stat().read().tc().is_active() {
303 // Wait for transmission to complete
304 }
305}
306
307pub fn check_and_clear_rx_errors(regs: Regs) -> Result<()> {
308 let stat = regs.stat().read();
309 let mut status = Ok(());
310
311 // Check for overrun first - other error flags are prevented when OR is set
312 if stat.or().is_overrun() {
313 regs.stat().write(|w| w.or().clear_bit_by_one());
314
315 return Err(Error::Overrun);
316 }
317
318 if stat.pf().is_parity() {
319 regs.stat().write(|w| w.pf().clear_bit_by_one());
320 status = Err(Error::Parity);
321 }
322
323 if stat.fe().is_error() {
324 regs.stat().write(|w| w.fe().clear_bit_by_one());
325 status = Err(Error::Framing);
326 }
327
328 if stat.nf().is_noise() {
329 regs.stat().write(|w| w.nf().clear_bit_by_one());
330 status = Err(Error::Noise);
331 }
332
333 status
334}
335
336pub fn has_data(regs: Regs) -> bool {
337 if regs.param().read().rxfifo().bits() > 0 {
338 // FIFO is available - check RXCOUNT in WATER register
339 regs.water().read().rxcount().bits() > 0
340 } else {
341 // No FIFO - check RDRF flag in STAT register
342 regs.stat().read().rdrf().is_rxdata()
343 }
344}
345
346// ============================================================================
347// PIN TRAITS FOR LPUART FUNCTIONALITY
348// ============================================================================
349
350impl<T: SealedPin> sealed::Sealed for T {}
351
352/// io configuration trait for Lpuart Tx configuration
353pub trait TxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
354 /// convert the pin to appropriate function for Lpuart Tx usage
355 fn as_tx(&self);
356}
357
358/// io configuration trait for Lpuart Rx configuration
359pub trait RxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
360 /// convert the pin to appropriate function for Lpuart Rx usage
361 fn as_rx(&self);
362}
363
364/// io configuration trait for Lpuart Cts
365pub trait CtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
366 /// convert the pin to appropriate function for Lpuart Cts usage
367 fn as_cts(&self);
368}
369
370/// io configuration trait for Lpuart Rts
371pub trait RtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
372 /// convert the pin to appropriate function for Lpuart Rts usage
373 fn as_rts(&self);
374}
375
376macro_rules! impl_tx_pin {
377 ($inst:ident, $pin:ident, $alt:ident) => {
378 impl TxPin<crate::peripherals::$inst> for crate::peripherals::$pin {
379 fn as_tx(&self) {
380 // TODO: Check these are right
381 self.set_pull(crate::gpio::Pull::Up);
382 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
383 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
384 self.set_function(crate::pac::port0::pcr0::Mux::$alt);
385 self.set_enable_input_buffer();
386 }
387 }
388 };
389}
390
391macro_rules! impl_rx_pin {
392 ($inst:ident, $pin:ident, $alt:ident) => {
393 impl RxPin<crate::peripherals::$inst> for crate::peripherals::$pin {
394 fn as_rx(&self) {
395 // TODO: Check these are right
396 self.set_pull(crate::gpio::Pull::Up);
397 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
398 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
399 self.set_function(crate::pac::port0::pcr0::Mux::$alt);
400 self.set_enable_input_buffer();
401 }
402 }
403 };
404}
405
406// TODO: Macro and impls for CTS/RTS pins
407macro_rules! impl_cts_pin {
408 ($inst:ident, $pin:ident, $alt:ident) => {
409 impl CtsPin<crate::peripherals::$inst> for crate::peripherals::$pin {
410 fn as_cts(&self) {
411 todo!()
412 }
413 }
414 };
415}
416
417macro_rules! impl_rts_pin {
418 ($inst:ident, $pin:ident, $alt:ident) => {
419 impl RtsPin<crate::peripherals::$inst> for crate::peripherals::$pin {
420 fn as_rts(&self) {
421 todo!()
422 }
423 }
424 };
425}
426
427// LPUART 0
428impl_tx_pin!(LPUART0, P0_3, Mux2);
429impl_tx_pin!(LPUART0, P0_21, Mux3);
430impl_tx_pin!(LPUART0, P2_1, Mux2);
431
432impl_rx_pin!(LPUART0, P0_2, Mux2);
433impl_rx_pin!(LPUART0, P0_20, Mux3);
434impl_rx_pin!(LPUART0, P2_0, Mux2);
435
436impl_cts_pin!(LPUART0, P0_1, Mux2);
437impl_cts_pin!(LPUART0, P0_23, Mux3);
438impl_cts_pin!(LPUART0, P2_3, Mux2);
439
440impl_rts_pin!(LPUART0, P0_0, Mux2);
441impl_rts_pin!(LPUART0, P0_22, Mux3);
442impl_rts_pin!(LPUART0, P2_2, Mux2);
443
444// LPUART 1
445impl_tx_pin!(LPUART1, P1_9, Mux2);
446impl_tx_pin!(LPUART1, P2_13, Mux3);
447impl_tx_pin!(LPUART1, P3_9, Mux3);
448impl_tx_pin!(LPUART1, P3_21, Mux3);
449
450impl_rx_pin!(LPUART1, P1_8, Mux2);
451impl_rx_pin!(LPUART1, P2_12, Mux3);
452impl_rx_pin!(LPUART1, P3_8, Mux3);
453impl_rx_pin!(LPUART1, P3_20, Mux3);
454
455impl_cts_pin!(LPUART1, P1_11, Mux2);
456impl_cts_pin!(LPUART1, P2_17, Mux3);
457impl_cts_pin!(LPUART1, P3_11, Mux3);
458impl_cts_pin!(LPUART1, P3_23, Mux3);
459
460impl_rts_pin!(LPUART1, P1_10, Mux2);
461impl_rts_pin!(LPUART1, P2_15, Mux3);
462impl_rts_pin!(LPUART1, P2_16, Mux3);
463impl_rts_pin!(LPUART1, P3_10, Mux3);
464
465// LPUART 2
466impl_tx_pin!(LPUART2, P1_5, Mux3);
467impl_tx_pin!(LPUART2, P1_13, Mux3);
468impl_tx_pin!(LPUART2, P2_2, Mux3);
469impl_tx_pin!(LPUART2, P2_10, Mux3);
470impl_tx_pin!(LPUART2, P3_15, Mux2);
471
472impl_rx_pin!(LPUART2, P1_4, Mux3);
473impl_rx_pin!(LPUART2, P1_12, Mux3);
474impl_rx_pin!(LPUART2, P2_3, Mux3);
475impl_rx_pin!(LPUART2, P2_11, Mux3);
476impl_rx_pin!(LPUART2, P3_14, Mux2);
477
478impl_cts_pin!(LPUART2, P1_7, Mux3);
479impl_cts_pin!(LPUART2, P1_15, Mux3);
480impl_cts_pin!(LPUART2, P2_4, Mux3);
481impl_cts_pin!(LPUART2, P3_13, Mux2);
482
483impl_rts_pin!(LPUART2, P1_6, Mux3);
484impl_rts_pin!(LPUART2, P1_14, Mux3);
485impl_rts_pin!(LPUART2, P2_5, Mux3);
486impl_rts_pin!(LPUART2, P3_12, Mux2);
487
488// LPUART 3
489impl_tx_pin!(LPUART3, P3_1, Mux3);
490impl_tx_pin!(LPUART3, P3_12, Mux3);
491impl_tx_pin!(LPUART3, P4_5, Mux3);
492
493impl_rx_pin!(LPUART3, P3_0, Mux3);
494impl_rx_pin!(LPUART3, P3_13, Mux3);
495impl_rx_pin!(LPUART3, P4_2, Mux3);
496
497impl_cts_pin!(LPUART3, P3_7, Mux3);
498impl_cts_pin!(LPUART3, P3_14, Mux3);
499impl_cts_pin!(LPUART3, P4_6, Mux3);
500
501impl_rts_pin!(LPUART3, P3_6, Mux3);
502impl_rts_pin!(LPUART3, P3_15, Mux3);
503impl_rts_pin!(LPUART3, P4_7, Mux3);
504
505// LPUART 4
506impl_tx_pin!(LPUART4, P2_7, Mux3);
507impl_tx_pin!(LPUART4, P3_19, Mux2);
508impl_tx_pin!(LPUART4, P3_27, Mux3);
509impl_tx_pin!(LPUART4, P4_3, Mux3);
510
511impl_rx_pin!(LPUART4, P2_6, Mux3);
512impl_rx_pin!(LPUART4, P3_18, Mux2);
513impl_rx_pin!(LPUART4, P3_28, Mux3);
514impl_rx_pin!(LPUART4, P4_4, Mux3);
515
516impl_cts_pin!(LPUART4, P2_0, Mux3);
517impl_cts_pin!(LPUART4, P3_17, Mux2);
518impl_cts_pin!(LPUART4, P3_31, Mux3);
519
520impl_rts_pin!(LPUART4, P2_1, Mux3);
521impl_rts_pin!(LPUART4, P3_16, Mux2);
522impl_rts_pin!(LPUART4, P3_30, Mux3);
523
524// LPUART 5
525impl_tx_pin!(LPUART5, P1_10, Mux8);
526impl_tx_pin!(LPUART5, P1_17, Mux8);
527
528impl_rx_pin!(LPUART5, P1_11, Mux8);
529impl_rx_pin!(LPUART5, P1_16, Mux8);
530
531impl_cts_pin!(LPUART5, P1_12, Mux8);
532impl_cts_pin!(LPUART5, P1_19, Mux8);
533
534impl_rts_pin!(LPUART5, P1_13, Mux8);
535impl_rts_pin!(LPUART5, P1_18, Mux8);
536
537// ============================================================================
538// ERROR TYPES AND RESULTS
539// ============================================================================
540
541/// LPUART error types
542#[derive(Debug, Copy, Clone, Eq, PartialEq)]
543#[cfg_attr(feature = "defmt", derive(defmt::Format))]
544pub enum Error {
545 /// Read error
546 Read,
547 /// Buffer overflow
548 Overrun,
549 /// Noise error
550 Noise,
551 /// Framing error
552 Framing,
553 /// Parity error
554 Parity,
555 /// Failure
556 Fail,
557 /// Invalid argument
558 InvalidArgument,
559 /// Lpuart baud rate cannot be supported with the given clock
560 UnsupportedBaudrate,
561 /// RX FIFO Empty
562 RxFifoEmpty,
563 /// TX FIFO Full
564 TxFifoFull,
565 /// TX Busy
566 TxBusy,
567 /// Clock Error
568 ClockSetup(ClockError),
569}
570
571/// A specialized Result type for LPUART operations
572pub type Result<T> = core::result::Result<T, Error>;
573
574// ============================================================================
575// CONFIGURATION STRUCTURES
576// ============================================================================
577
578/// Lpuart config
579#[derive(Debug, Clone, Copy)]
580pub struct Config {
581 /// Power state required for this peripheral
582 pub power: PoweredClock,
583 /// Clock source
584 pub source: LpuartClockSel,
585 /// Clock divisor
586 pub div: Div4,
587 /// Baud rate in bits per second
588 pub baudrate_bps: u32,
589 /// Parity configuration
590 pub parity_mode: Option<Parity>,
591 /// Number of data bits
592 pub data_bits_count: DataBits,
593 /// MSB First or LSB First configuration
594 pub msb_first: MsbFirst,
595 /// Number of stop bits
596 pub stop_bits_count: StopBits,
597 /// TX FIFO watermark
598 pub tx_fifo_watermark: u8,
599 /// RX FIFO watermark
600 pub rx_fifo_watermark: u8,
601 /// TX CTS source
602 pub tx_cts_source: TxCtsSource,
603 /// TX CTS configure
604 pub tx_cts_config: TxCtsConfig,
605 /// RX IDLE type
606 pub rx_idle_type: IdleType,
607 /// RX IDLE configuration
608 pub rx_idle_config: IdleConfig,
609 /// Swap TXD and RXD pins
610 pub swap_txd_rxd: bool,
611}
612
613impl Default for Config {
614 fn default() -> Self {
615 Self {
616 baudrate_bps: 115_200u32,
617 parity_mode: None,
618 data_bits_count: DataBits::Data8,
619 msb_first: MsbFirst::LsbFirst,
620 stop_bits_count: StopBits::One,
621 tx_fifo_watermark: 0,
622 rx_fifo_watermark: 1,
623 tx_cts_source: TxCtsSource::Cts,
624 tx_cts_config: TxCtsConfig::Start,
625 rx_idle_type: IdleType::FromStart,
626 rx_idle_config: IdleConfig::Idle1,
627 swap_txd_rxd: false,
628 power: PoweredClock::NormalEnabledDeepSleepDisabled,
629 source: LpuartClockSel::FroLfDiv,
630 div: Div4::no_div(),
631 }
632 }
633}
634
635/// LPUART status flags
636#[derive(Debug, Clone, Copy)]
637#[cfg_attr(feature = "defmt", derive(defmt::Format))]
638pub struct Status {
639 /// Transmit data register empty
640 pub tx_empty: bool,
641 /// Transmission complete
642 pub tx_complete: bool,
643 /// Receive data register full
644 pub rx_full: bool,
645 /// Idle line detected
646 pub idle: bool,
647 /// Receiver overrun
648 pub overrun: bool,
649 /// Noise error
650 pub noise: bool,
651 /// Framing error
652 pub framing: bool,
653 /// Parity error
654 pub parity: bool,
655}
656
657// ============================================================================
658// MODE TRAITS (BLOCKING/ASYNC)
659// ============================================================================
660
661/// Driver move trait.
662#[allow(private_bounds)]
663pub trait Mode: sealed::Sealed {}
664
665/// Blocking mode.
666pub struct Blocking;
667impl sealed::Sealed for Blocking {}
668impl Mode for Blocking {}
669
670/// Async mode.
671pub struct Async;
672impl sealed::Sealed for Async {}
673impl Mode for Async {}
674
675// ============================================================================
676// CORE DRIVER STRUCTURES
677// ============================================================================
678
679/// Lpuart driver.
680pub struct Lpuart<'a, M: Mode> {
681 info: Info,
682 tx: LpuartTx<'a, M>,
683 rx: LpuartRx<'a, M>,
684}
685
686/// Lpuart TX driver.
687pub struct LpuartTx<'a, M: Mode> {
688 info: Info,
689 _tx_pin: Peri<'a, AnyPin>,
690 _cts_pin: Option<Peri<'a, AnyPin>>,
691 mode: PhantomData<(&'a (), M)>,
692}
693
694/// Lpuart Rx driver.
695pub struct LpuartRx<'a, M: Mode> {
696 info: Info,
697 _rx_pin: Peri<'a, AnyPin>,
698 _rts_pin: Option<Peri<'a, AnyPin>>,
699 mode: PhantomData<(&'a (), M)>,
700}
701
702/// Lpuart TX driver with DMA support.
703pub struct LpuartTxDma<'a, T: Instance, C: DmaChannelTrait> {
704 info: Info,
705 _tx_pin: Peri<'a, AnyPin>,
706 tx_dma: DmaChannel<C>,
707 _instance: core::marker::PhantomData<T>,
708}
709
710/// Lpuart RX driver with DMA support.
711pub struct LpuartRxDma<'a, T: Instance, C: DmaChannelTrait> {
712 info: Info,
713 _rx_pin: Peri<'a, AnyPin>,
714 rx_dma: DmaChannel<C>,
715 _instance: core::marker::PhantomData<T>,
716}
717
718/// Lpuart driver with DMA support for both TX and RX.
719pub struct LpuartDma<'a, T: Instance, TxC: DmaChannelTrait, RxC: DmaChannelTrait> {
720 tx: LpuartTxDma<'a, T, TxC>,
721 rx: LpuartRxDma<'a, T, RxC>,
722}
723
724/// Lpuart RX driver with ring-buffered DMA support.
725pub struct LpuartRxRingDma<'peri, 'ring, T: Instance, C: DmaChannelTrait> {
726 _inner: LpuartRxDma<'peri, T, C>,
727 ring: RingBuffer<'ring, u8>,
728}
729
730// ============================================================================
731// LPUART CORE IMPLEMENTATION
732// ============================================================================
733
734impl<'a, M: Mode> Lpuart<'a, M> {
735 fn init<T: Instance>(
736 enable_tx: bool,
737 enable_rx: bool,
738 enable_tx_cts: bool,
739 enable_rx_rts: bool,
740 config: Config,
741 ) -> Result<()> {
742 let regs = T::info().regs;
743
744 // Enable clocks
745 let conf = LpuartConfig {
746 power: config.power,
747 source: config.source,
748 div: config.div,
749 instance: T::CLOCK_INSTANCE,
750 };
751 let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
752
753 // Perform initialization sequence
754 perform_software_reset(regs);
755 disable_transceiver(regs);
756 configure_baudrate(regs, config.baudrate_bps, clock_freq)?;
757 configure_frame_format(regs, &config);
758 configure_control_settings(regs, &config);
759 configure_fifo(regs, &config);
760 clear_all_status_flags(regs);
761 configure_flow_control(regs, enable_tx_cts, enable_rx_rts, &config);
762 configure_bit_order(regs, config.msb_first);
763 enable_transceiver(regs, enable_rx, enable_tx);
764
765 Ok(())
766 }
767
768 /// Deinitialize the LPUART peripheral
769 pub fn deinit(&self) -> Result<()> {
770 let regs = self.info.regs;
771
772 // Wait for TX operations to complete
773 wait_for_tx_complete(regs);
774
775 // Clear all status flags
776 clear_all_status_flags(regs);
777
778 // Disable the module - clear all CTRL register bits
779 regs.ctrl().reset();
780
781 Ok(())
782 }
783
784 /// Split the Lpuart into a transmitter and receiver
785 pub fn split(self) -> (LpuartTx<'a, M>, LpuartRx<'a, M>) {
786 (self.tx, self.rx)
787 }
788
789 /// Split the Lpuart into a transmitter and receiver by mutable reference
790 pub fn split_ref(&mut self) -> (&mut LpuartTx<'a, M>, &mut LpuartRx<'a, M>) {
791 (&mut self.tx, &mut self.rx)
792 }
793}
794
795// ============================================================================
796// BLOCKING MODE IMPLEMENTATIONS
797// ============================================================================
798
799impl<'a> Lpuart<'a, Blocking> {
800 /// Create a new blocking LPUART instance with RX/TX pins.
801 pub fn new_blocking<T: Instance>(
802 _inner: Peri<'a, T>,
803 tx_pin: Peri<'a, impl TxPin<T>>,
804 rx_pin: Peri<'a, impl RxPin<T>>,
805 config: Config,
806 ) -> Result<Self> {
807 // Configure the pins for LPUART usage
808 tx_pin.as_tx();
809 rx_pin.as_rx();
810
811 // Initialize the peripheral
812 Self::init::<T>(true, true, false, false, config)?;
813
814 Ok(Self {
815 info: T::info(),
816 tx: LpuartTx::new_inner(T::info(), tx_pin.into(), None),
817 rx: LpuartRx::new_inner(T::info(), rx_pin.into(), None),
818 })
819 }
820
821 /// Create a new blocking LPUART instance with RX, TX and RTS/CTS flow control pins
822 pub fn new_blocking_with_rtscts<T: Instance>(
823 _inner: Peri<'a, T>,
824 tx_pin: Peri<'a, impl TxPin<T>>,
825 rx_pin: Peri<'a, impl RxPin<T>>,
826 cts_pin: Peri<'a, impl CtsPin<T>>,
827 rts_pin: Peri<'a, impl RtsPin<T>>,
828 config: Config,
829 ) -> Result<Self> {
830 // Configure the pins for LPUART usage
831 rx_pin.as_rx();
832 tx_pin.as_tx();
833 rts_pin.as_rts();
834 cts_pin.as_cts();
835
836 // Initialize the peripheral with flow control
837 Self::init::<T>(true, true, true, true, config)?;
838
839 Ok(Self {
840 info: T::info(),
841 rx: LpuartRx::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into())),
842 tx: LpuartTx::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into())),
843 })
844 }
845}
846
847// ----------------------------------------------------------------------------
848// Blocking TX Implementation
849// ----------------------------------------------------------------------------
850
851impl<'a, M: Mode> LpuartTx<'a, M> {
852 fn new_inner(info: Info, tx_pin: Peri<'a, AnyPin>, cts_pin: Option<Peri<'a, AnyPin>>) -> Self {
853 Self {
854 info,
855 _tx_pin: tx_pin,
856 _cts_pin: cts_pin,
857 mode: PhantomData,
858 }
859 }
860}
861
862impl<'a> LpuartTx<'a, Blocking> {
863 /// Create a new blocking LPUART transmitter instance
864 pub fn new_blocking<T: Instance>(
865 _inner: Peri<'a, T>,
866 tx_pin: Peri<'a, impl TxPin<T>>,
867 config: Config,
868 ) -> Result<Self> {
869 // Configure the pins for LPUART usage
870 tx_pin.as_tx();
871
872 // Initialize the peripheral
873 Lpuart::<Blocking>::init::<T>(true, false, false, false, config)?;
874
875 Ok(Self::new_inner(T::info(), tx_pin.into(), None))
876 }
877
878 /// Create a new blocking LPUART transmitter instance with CTS flow control
879 pub fn new_blocking_with_cts<T: Instance>(
880 _inner: Peri<'a, T>,
881 tx_pin: Peri<'a, impl TxPin<T>>,
882 cts_pin: Peri<'a, impl CtsPin<T>>,
883 config: Config,
884 ) -> Result<Self> {
885 tx_pin.as_tx();
886 cts_pin.as_cts();
887
888 Lpuart::<Blocking>::init::<T>(true, false, true, false, config)?;
889
890 Ok(Self::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into())))
891 }
892
893 fn write_byte_internal(&mut self, byte: u8) -> Result<()> {
894 self.info.regs.data().modify(|_, w| unsafe { w.bits(u32::from(byte)) });
895
896 Ok(())
897 }
898
899 fn blocking_write_byte(&mut self, byte: u8) -> Result<()> {
900 while self.info.regs.stat().read().tdre().is_txdata() {}
901 self.write_byte_internal(byte)
902 }
903
904 fn write_byte(&mut self, byte: u8) -> Result<()> {
905 if self.info.regs.stat().read().tdre().is_txdata() {
906 Err(Error::TxFifoFull)
907 } else {
908 self.write_byte_internal(byte)
909 }
910 }
911
912 /// Write data to LPUART TX blocking execution until all data is sent.
913 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
914 for x in buf {
915 self.blocking_write_byte(*x)?;
916 }
917
918 Ok(())
919 }
920
921 pub fn write_str_blocking(&mut self, buf: &str) {
922 let _ = self.blocking_write(buf.as_bytes());
923 }
924
925 /// Write data to LPUART TX without blocking.
926 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
927 for x in buf {
928 self.write_byte(*x)?;
929 }
930
931 Ok(())
932 }
933
934 /// Flush LPUART TX blocking execution until all data has been transmitted.
935 pub fn blocking_flush(&mut self) -> Result<()> {
936 while self.info.regs.water().read().txcount().bits() != 0 {
937 // Wait for TX FIFO to drain
938 }
939
940 // Wait for last character to shift out
941 while self.info.regs.stat().read().tc().is_active() {
942 // Wait for transmission to complete
943 }
944
945 Ok(())
946 }
947
948 /// Flush LPUART TX.
949 pub fn flush(&mut self) -> Result<()> {
950 // Check if TX FIFO is empty
951 if self.info.regs.water().read().txcount().bits() != 0 {
952 return Err(Error::TxBusy);
953 }
954
955 // Check if transmission is complete
956 if self.info.regs.stat().read().tc().is_active() {
957 return Err(Error::TxBusy);
958 }
959
960 Ok(())
961 }
962}
963
964// ----------------------------------------------------------------------------
965// Blocking RX Implementation
966// ----------------------------------------------------------------------------
967
968impl<'a, M: Mode> LpuartRx<'a, M> {
969 fn new_inner(info: Info, rx_pin: Peri<'a, AnyPin>, rts_pin: Option<Peri<'a, AnyPin>>) -> Self {
970 Self {
971 info,
972 _rx_pin: rx_pin,
973 _rts_pin: rts_pin,
974 mode: PhantomData,
975 }
976 }
977}
978
979impl<'a> LpuartRx<'a, Blocking> {
980 /// Create a new blocking LPUART Receiver instance
981 pub fn new_blocking<T: Instance>(
982 _inner: Peri<'a, T>,
983 rx_pin: Peri<'a, impl RxPin<T>>,
984 config: Config,
985 ) -> Result<Self> {
986 rx_pin.as_rx();
987
988 Lpuart::<Blocking>::init::<T>(false, true, false, false, config)?;
989
990 Ok(Self::new_inner(T::info(), rx_pin.into(), None))
991 }
992
993 /// Create a new blocking LPUART Receiver instance with RTS flow control
994 pub fn new_blocking_with_rts<T: Instance>(
995 _inner: Peri<'a, T>,
996 rx_pin: Peri<'a, impl RxPin<T>>,
997 rts_pin: Peri<'a, impl RtsPin<T>>,
998 config: Config,
999 ) -> Result<Self> {
1000 rx_pin.as_rx();
1001 rts_pin.as_rts();
1002
1003 Lpuart::<Blocking>::init::<T>(false, true, false, true, config)?;
1004
1005 Ok(Self::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into())))
1006 }
1007
1008 fn read_byte_internal(&mut self) -> Result<u8> {
1009 let data = self.info.regs.data().read();
1010
1011 Ok((data.bits() & 0xFF) as u8)
1012 }
1013
1014 fn read_byte(&mut self) -> Result<u8> {
1015 check_and_clear_rx_errors(self.info.regs)?;
1016
1017 if !has_data(self.info.regs) {
1018 return Err(Error::RxFifoEmpty);
1019 }
1020
1021 self.read_byte_internal()
1022 }
1023
1024 fn blocking_read_byte(&mut self) -> Result<u8> {
1025 loop {
1026 if has_data(self.info.regs) {
1027 return self.read_byte_internal();
1028 }
1029
1030 check_and_clear_rx_errors(self.info.regs)?;
1031 }
1032 }
1033
1034 /// Read data from LPUART RX without blocking.
1035 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
1036 for byte in buf.iter_mut() {
1037 *byte = self.read_byte()?;
1038 }
1039 Ok(())
1040 }
1041
1042 /// Read data from LPUART RX blocking execution until the buffer is filled.
1043 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
1044 for byte in buf.iter_mut() {
1045 *byte = self.blocking_read_byte()?;
1046 }
1047 Ok(())
1048 }
1049}
1050
1051impl<'a> Lpuart<'a, Blocking> {
1052 /// Read data from LPUART RX blocking execution until the buffer is filled
1053 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
1054 self.rx.blocking_read(buf)
1055 }
1056
1057 /// Read data from LPUART RX without blocking
1058 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
1059 self.rx.read(buf)
1060 }
1061
1062 /// Write data to LPUART TX blocking execution until all data is sent
1063 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
1064 self.tx.blocking_write(buf)
1065 }
1066
1067 pub fn write_byte(&mut self, byte: u8) -> Result<()> {
1068 self.tx.write_byte(byte)
1069 }
1070
1071 pub fn read_byte_blocking(&mut self) -> u8 {
1072 loop {
1073 if let Ok(b) = self.rx.read_byte() {
1074 return b;
1075 }
1076 }
1077 }
1078
1079 pub fn write_str_blocking(&mut self, buf: &str) {
1080 self.tx.write_str_blocking(buf);
1081 }
1082
1083 /// Write data to LPUART TX without blocking
1084 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
1085 self.tx.write(buf)
1086 }
1087
1088 /// Flush LPUART TX blocking execution until all data has been transmitted
1089 pub fn blocking_flush(&mut self) -> Result<()> {
1090 self.tx.blocking_flush()
1091 }
1092
1093 /// Flush LPUART TX without blocking
1094 pub fn flush(&mut self) -> Result<()> {
1095 self.tx.flush()
1096 }
1097}
1098
1099// ============================================================================
1100// ASYNC MODE IMPLEMENTATIONS (DMA-based)
1101// ============================================================================
1102
1103/// Guard struct that ensures DMA is stopped if the async future is cancelled.
1104///
1105/// This implements the RAII pattern: if the future is dropped before completion
1106/// (e.g., due to a timeout), the DMA transfer is automatically aborted to prevent
1107/// use-after-free when the buffer goes out of scope.
1108struct TxDmaGuard<'a, C: DmaChannelTrait> {
1109 dma: &'a DmaChannel<C>,
1110 regs: Regs,
1111}
1112
1113impl<'a, C: DmaChannelTrait> TxDmaGuard<'a, C> {
1114 fn new(dma: &'a DmaChannel<C>, regs: Regs) -> Self {
1115 Self { dma, regs }
1116 }
1117
1118 /// Complete the transfer normally (don't abort on drop).
1119 fn complete(self) {
1120 // Cleanup
1121 self.regs.baud().modify(|_, w| w.tdmae().disabled());
1122 unsafe {
1123 self.dma.disable_request();
1124 self.dma.clear_done();
1125 }
1126 // Don't run drop since we've cleaned up
1127 core::mem::forget(self);
1128 }
1129}
1130
1131impl<C: DmaChannelTrait> Drop for TxDmaGuard<'_, C> {
1132 fn drop(&mut self) {
1133 // Abort the DMA transfer if still running
1134 unsafe {
1135 self.dma.disable_request();
1136 self.dma.clear_done();
1137 self.dma.clear_interrupt();
1138 }
1139 // Disable UART TX DMA request
1140 self.regs.baud().modify(|_, w| w.tdmae().disabled());
1141 }
1142}
1143
1144/// Guard struct for RX DMA transfers.
1145struct RxDmaGuard<'a, C: DmaChannelTrait> {
1146 dma: &'a DmaChannel<C>,
1147 regs: Regs,
1148}
1149
1150impl<'a, C: DmaChannelTrait> RxDmaGuard<'a, C> {
1151 fn new(dma: &'a DmaChannel<C>, regs: Regs) -> Self {
1152 Self { dma, regs }
1153 }
1154
1155 /// Complete the transfer normally (don't abort on drop).
1156 fn complete(self) {
1157 // Ensure DMA writes are visible to CPU
1158 cortex_m::asm::dsb();
1159 // Cleanup
1160 self.regs.baud().modify(|_, w| w.rdmae().disabled());
1161 unsafe {
1162 self.dma.disable_request();
1163 self.dma.clear_done();
1164 }
1165 // Don't run drop since we've cleaned up
1166 core::mem::forget(self);
1167 }
1168}
1169
1170impl<C: DmaChannelTrait> Drop for RxDmaGuard<'_, C> {
1171 fn drop(&mut self) {
1172 // Abort the DMA transfer if still running
1173 unsafe {
1174 self.dma.disable_request();
1175 self.dma.clear_done();
1176 self.dma.clear_interrupt();
1177 }
1178 // Disable UART RX DMA request
1179 self.regs.baud().modify(|_, w| w.rdmae().disabled());
1180 }
1181}
1182
1183impl<'a, T: Instance, C: DmaChannelTrait> LpuartTxDma<'a, T, C> {
1184 /// Create a new LPUART TX driver with DMA support.
1185 pub fn new(
1186 _inner: Peri<'a, T>,
1187 tx_pin: Peri<'a, impl TxPin<T>>,
1188 tx_dma_ch: Peri<'a, C>,
1189 config: Config,
1190 ) -> Result<Self> {
1191 tx_pin.as_tx();
1192 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
1193
1194 // Initialize LPUART with TX enabled, RX disabled, no flow control
1195 Lpuart::<Async>::init::<T>(true, false, false, false, config)?;
1196
1197 // Enable interrupt
1198 let tx_dma = DmaChannel::new(tx_dma_ch);
1199 tx_dma.enable_interrupt();
1200
1201 Ok(Self {
1202 info: T::info(),
1203 _tx_pin: tx_pin,
1204 tx_dma,
1205 _instance: core::marker::PhantomData,
1206 })
1207 }
1208
1209 /// Write data using DMA.
1210 ///
1211 /// This configures the DMA channel for a memory-to-peripheral transfer
1212 /// and waits for completion asynchronously. Large buffers are automatically
1213 /// split into chunks that fit within the DMA transfer limit.
1214 ///
1215 /// The DMA request source is automatically derived from the LPUART instance type.
1216 ///
1217 /// # Safety
1218 ///
1219 /// If the returned future is dropped before completion (e.g., due to a timeout),
1220 /// the DMA transfer is automatically aborted to prevent use-after-free.
1221 ///
1222 /// # Arguments
1223 /// * `buf` - Data buffer to transmit
1224 pub async fn write_dma(&mut self, buf: &[u8]) -> Result<usize> {
1225 if buf.is_empty() {
1226 return Ok(0);
1227 }
1228
1229 let mut total = 0;
1230 for chunk in buf.chunks(DMA_MAX_TRANSFER_SIZE) {
1231 total += self.write_dma_inner(chunk).await?;
1232 }
1233
1234 Ok(total)
1235 }
1236
1237 /// Internal helper to write a single chunk (max 0x7FFF bytes) using DMA.
1238 async fn write_dma_inner(&mut self, buf: &[u8]) -> Result<usize> {
1239 let len = buf.len();
1240 let peri_addr = self.info.regs.data().as_ptr() as *mut u8;
1241
1242 unsafe {
1243 // Clean up channel state
1244 self.tx_dma.disable_request();
1245 self.tx_dma.clear_done();
1246 self.tx_dma.clear_interrupt();
1247
1248 // Set DMA request source from instance type (type-safe)
1249 self.tx_dma.set_request_source::<T::TxDmaRequest>();
1250
1251 // Configure TCD for memory-to-peripheral transfer
1252 self.tx_dma
1253 .setup_write_to_peripheral(buf, peri_addr, EnableInterrupt::Yes);
1254
1255 // Enable UART TX DMA request
1256 self.info.regs.baud().modify(|_, w| w.tdmae().enabled());
1257
1258 // Enable DMA channel request
1259 self.tx_dma.enable_request();
1260 }
1261
1262 // Create guard that will abort DMA if this future is dropped
1263 let guard = TxDmaGuard::new(&self.tx_dma, self.info.regs);
1264
1265 // Wait for completion asynchronously
1266 core::future::poll_fn(|cx| {
1267 self.tx_dma.waker().register(cx.waker());
1268 if self.tx_dma.is_done() {
1269 core::task::Poll::Ready(())
1270 } else {
1271 core::task::Poll::Pending
1272 }
1273 })
1274 .await;
1275
1276 // Transfer completed successfully - clean up without aborting
1277 guard.complete();
1278
1279 Ok(len)
1280 }
1281
1282 /// Blocking write (fallback when DMA is not needed)
1283 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
1284 for &byte in buf {
1285 while self.info.regs.stat().read().tdre().is_txdata() {}
1286 self.info.regs.data().modify(|_, w| unsafe { w.bits(u32::from(byte)) });
1287 }
1288 Ok(())
1289 }
1290
1291 /// Flush TX blocking
1292 pub fn blocking_flush(&mut self) -> Result<()> {
1293 while self.info.regs.water().read().txcount().bits() != 0 {}
1294 while self.info.regs.stat().read().tc().is_active() {}
1295 Ok(())
1296 }
1297}
1298
1299impl<'a, T: Instance, C: DmaChannelTrait> LpuartRxDma<'a, T, C> {
1300 /// Create a new LPUART RX driver with DMA support.
1301 pub fn new(
1302 _inner: Peri<'a, T>,
1303 rx_pin: Peri<'a, impl RxPin<T>>,
1304 rx_dma_ch: Peri<'a, C>,
1305 config: Config,
1306 ) -> Result<Self> {
1307 rx_pin.as_rx();
1308 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
1309
1310 // Initialize LPUART with TX disabled, RX enabled, no flow control
1311 Lpuart::<Async>::init::<T>(false, true, false, false, config)?;
1312
1313 // Enable dma interrupt
1314 let rx_dma = DmaChannel::new(rx_dma_ch);
1315 rx_dma.enable_interrupt();
1316
1317 Ok(Self {
1318 info: T::info(),
1319 _rx_pin: rx_pin,
1320 rx_dma,
1321 _instance: core::marker::PhantomData,
1322 })
1323 }
1324
1325 /// Read data using DMA.
1326 ///
1327 /// This configures the DMA channel for a peripheral-to-memory transfer
1328 /// and waits for completion asynchronously. Large buffers are automatically
1329 /// split into chunks that fit within the DMA transfer limit.
1330 ///
1331 /// The DMA request source is automatically derived from the LPUART instance type.
1332 ///
1333 /// # Safety
1334 ///
1335 /// If the returned future is dropped before completion (e.g., due to a timeout),
1336 /// the DMA transfer is automatically aborted to prevent use-after-free.
1337 ///
1338 /// # Arguments
1339 /// * `buf` - Buffer to receive data into
1340 pub async fn read_dma(&mut self, buf: &mut [u8]) -> Result<usize> {
1341 if buf.is_empty() {
1342 return Ok(0);
1343 }
1344
1345 let mut total = 0;
1346 for chunk in buf.chunks_mut(DMA_MAX_TRANSFER_SIZE) {
1347 total += self.read_dma_inner(chunk).await?;
1348 }
1349
1350 Ok(total)
1351 }
1352
1353 /// Internal helper to read a single chunk (max 0x7FFF bytes) using DMA.
1354 async fn read_dma_inner(&mut self, buf: &mut [u8]) -> Result<usize> {
1355 let len = buf.len();
1356 let peri_addr = self.info.regs.data().as_ptr() as *const u8;
1357
1358 unsafe {
1359 // Clean up channel state
1360 self.rx_dma.disable_request();
1361 self.rx_dma.clear_done();
1362 self.rx_dma.clear_interrupt();
1363
1364 // Set DMA request source from instance type (type-safe)
1365 self.rx_dma.set_request_source::<T::RxDmaRequest>();
1366
1367 // Configure TCD for peripheral-to-memory transfer
1368 self.rx_dma
1369 .setup_read_from_peripheral(peri_addr, buf, EnableInterrupt::Yes);
1370
1371 // Enable UART RX DMA request
1372 self.info.regs.baud().modify(|_, w| w.rdmae().enabled());
1373
1374 // Enable DMA channel request
1375 self.rx_dma.enable_request();
1376 }
1377
1378 // Create guard that will abort DMA if this future is dropped
1379 let guard = RxDmaGuard::new(&self.rx_dma, self.info.regs);
1380
1381 // Wait for completion asynchronously
1382 core::future::poll_fn(|cx| {
1383 self.rx_dma.waker().register(cx.waker());
1384 if self.rx_dma.is_done() {
1385 core::task::Poll::Ready(())
1386 } else {
1387 core::task::Poll::Pending
1388 }
1389 })
1390 .await;
1391
1392 // Transfer completed successfully - clean up without aborting
1393 guard.complete();
1394
1395 Ok(len)
1396 }
1397
1398 /// Blocking read (fallback when DMA is not needed)
1399 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
1400 for byte in buf.iter_mut() {
1401 loop {
1402 if has_data(self.info.regs) {
1403 *byte = (self.info.regs.data().read().bits() & 0xFF) as u8;
1404 break;
1405 }
1406 check_and_clear_rx_errors(self.info.regs)?;
1407 }
1408 }
1409 Ok(())
1410 }
1411
1412 pub fn into_ring_dma_rx<'buf>(self, buf: &'buf mut [u8]) -> LpuartRxRingDma<'a, 'buf, T, C> {
1413 unsafe {
1414 let ring = self.setup_ring_buffer(buf);
1415 self.enable_dma_request();
1416 LpuartRxRingDma { _inner: self, ring }
1417 }
1418 }
1419
1420 /// Set up a ring buffer for continuous DMA reception.
1421 ///
1422 /// This configures the DMA channel for circular operation, enabling continuous
1423 /// reception of data without gaps. The DMA will continuously write received
1424 /// bytes into the buffer, wrapping around when it reaches the end.
1425 ///
1426 /// This method encapsulates all the low-level setup:
1427 /// - Configures the DMA request source for this LPUART instance
1428 /// - Enables the RX DMA request in the LPUART peripheral
1429 /// - Sets up the circular DMA transfer
1430 /// - Enables the NVIC interrupt for async wakeups
1431 ///
1432 /// # Arguments
1433 ///
1434 /// * `buf` - Destination buffer for received data (power-of-2 size is ideal for efficiency)
1435 ///
1436 /// # Returns
1437 ///
1438 /// A [`RingBuffer`] that can be used to asynchronously read received data.
1439 ///
1440 /// # Example
1441 ///
1442 /// ```no_run
1443 /// static mut RX_BUF: [u8; 64] = [0; 64];
1444 ///
1445 /// let rx = LpuartRxDma::new(p.LPUART2, p.P2_3, p.DMA_CH0, config).unwrap();
1446 /// let ring_buf = unsafe { rx.setup_ring_buffer(&mut RX_BUF) };
1447 ///
1448 /// // Read data as it arrives
1449 /// let mut buf = [0u8; 16];
1450 /// let n = ring_buf.read(&mut buf).await.unwrap();
1451 /// ```
1452 ///
1453 /// # Safety
1454 ///
1455 /// - The buffer must remain valid for the lifetime of the returned RingBuffer.
1456 /// - Only one RingBuffer should exist per LPUART RX channel at a time.
1457 /// - The caller must ensure the static buffer is not accessed elsewhere while
1458 /// the ring buffer is active.
1459 unsafe fn setup_ring_buffer<'b>(&self, buf: &'b mut [u8]) -> RingBuffer<'b, u8> {
1460 // Get the peripheral data register address
1461 let peri_addr = self.info.regs.data().as_ptr() as *const u8;
1462
1463 // Configure DMA request source for this LPUART instance (type-safe)
1464 self.rx_dma.set_request_source::<T::RxDmaRequest>();
1465
1466 // Enable RX DMA request in the LPUART peripheral
1467 self.info.regs.baud().modify(|_, w| w.rdmae().enabled());
1468
1469 // Set up circular DMA transfer (this also enables NVIC interrupt)
1470 self.rx_dma.setup_circular_read(peri_addr, buf)
1471 }
1472
1473 /// Enable the DMA channel request.
1474 ///
1475 /// Call this after `setup_ring_buffer()` to start continuous reception.
1476 /// This is separated from setup to allow for any additional configuration
1477 /// before starting the transfer.
1478 unsafe fn enable_dma_request(&self) {
1479 self.rx_dma.enable_request();
1480 }
1481}
1482
1483impl<'peri, 'buf, T: Instance, C: DmaChannelTrait> LpuartRxRingDma<'peri, 'buf, T, C> {
1484 /// Read from the ring buffer
1485 pub fn read<'d>(
1486 &mut self,
1487 dst: &'d mut [u8],
1488 ) -> impl Future<Output = core::result::Result<usize, crate::dma::Error>> + use<'_, 'buf, 'd, T, C> {
1489 self.ring.read(dst)
1490 }
1491
1492 /// Clear the current contents of the ring buffer
1493 pub fn clear(&mut self) {
1494 self.ring.clear();
1495 }
1496}
1497
1498impl<'a, T: Instance, TxC: DmaChannelTrait, RxC: DmaChannelTrait> LpuartDma<'a, T, TxC, RxC> {
1499 /// Create a new LPUART driver with DMA support for both TX and RX.
1500 pub fn new(
1501 _inner: Peri<'a, T>,
1502 tx_pin: Peri<'a, impl TxPin<T>>,
1503 rx_pin: Peri<'a, impl RxPin<T>>,
1504 tx_dma_ch: Peri<'a, TxC>,
1505 rx_dma_ch: Peri<'a, RxC>,
1506 config: Config,
1507 ) -> Result<Self> {
1508 tx_pin.as_tx();
1509 rx_pin.as_rx();
1510
1511 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
1512 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
1513
1514 // Initialize LPUART with both TX and RX enabled, no flow control
1515 Lpuart::<Async>::init::<T>(true, true, false, false, config)?;
1516
1517 // Enable DMA interrupts
1518 let tx_dma = DmaChannel::new(tx_dma_ch);
1519 let rx_dma = DmaChannel::new(rx_dma_ch);
1520 tx_dma.enable_interrupt();
1521 rx_dma.enable_interrupt();
1522
1523 Ok(Self {
1524 tx: LpuartTxDma {
1525 info: T::info(),
1526 _tx_pin: tx_pin,
1527 tx_dma,
1528 _instance: core::marker::PhantomData,
1529 },
1530 rx: LpuartRxDma {
1531 info: T::info(),
1532 _rx_pin: rx_pin,
1533 rx_dma,
1534 _instance: core::marker::PhantomData,
1535 },
1536 })
1537 }
1538
1539 /// Split into separate TX and RX drivers
1540 pub fn split(self) -> (LpuartTxDma<'a, T, TxC>, LpuartRxDma<'a, T, RxC>) {
1541 (self.tx, self.rx)
1542 }
1543
1544 /// Write data using DMA
1545 pub async fn write_dma(&mut self, buf: &[u8]) -> Result<usize> {
1546 self.tx.write_dma(buf).await
1547 }
1548
1549 /// Read data using DMA
1550 pub async fn read_dma(&mut self, buf: &mut [u8]) -> Result<usize> {
1551 self.rx.read_dma(buf).await
1552 }
1553}
1554
1555// ============================================================================
1556// EMBEDDED-IO-ASYNC TRAIT IMPLEMENTATIONS
1557// ============================================================================
1558
1559impl<T: Instance, C: DmaChannelTrait> embedded_io::ErrorType for LpuartTxDma<'_, T, C> {
1560 type Error = Error;
1561}
1562
1563impl<T: Instance, C: DmaChannelTrait> embedded_io::ErrorType for LpuartRxDma<'_, T, C> {
1564 type Error = Error;
1565}
1566
1567impl<T: Instance, TxC: DmaChannelTrait, RxC: DmaChannelTrait> embedded_io::ErrorType for LpuartDma<'_, T, TxC, RxC> {
1568 type Error = Error;
1569}
1570
1571// ============================================================================
1572// EMBEDDED-HAL 0.2 TRAIT IMPLEMENTATIONS
1573// ============================================================================
1574
1575impl embedded_hal_02::serial::Read<u8> for LpuartRx<'_, Blocking> {
1576 type Error = Error;
1577
1578 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1579 let mut buf = [0; 1];
1580 match self.read(&mut buf) {
1581 Ok(_) => Ok(buf[0]),
1582 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1583 Err(e) => Err(nb::Error::Other(e)),
1584 }
1585 }
1586}
1587
1588impl embedded_hal_02::serial::Write<u8> for LpuartTx<'_, Blocking> {
1589 type Error = Error;
1590
1591 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1592 match self.write(&[word]) {
1593 Ok(_) => Ok(()),
1594 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1595 Err(e) => Err(nb::Error::Other(e)),
1596 }
1597 }
1598
1599 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1600 match self.flush() {
1601 Ok(_) => Ok(()),
1602 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1603 Err(e) => Err(nb::Error::Other(e)),
1604 }
1605 }
1606}
1607
1608impl embedded_hal_02::blocking::serial::Write<u8> for LpuartTx<'_, Blocking> {
1609 type Error = Error;
1610
1611 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1612 self.blocking_write(buffer)
1613 }
1614
1615 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1616 self.blocking_flush()
1617 }
1618}
1619
1620impl embedded_hal_02::serial::Read<u8> for Lpuart<'_, Blocking> {
1621 type Error = Error;
1622
1623 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1624 embedded_hal_02::serial::Read::read(&mut self.rx)
1625 }
1626}
1627
1628impl embedded_hal_02::serial::Write<u8> for Lpuart<'_, Blocking> {
1629 type Error = Error;
1630
1631 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1632 embedded_hal_02::serial::Write::write(&mut self.tx, word)
1633 }
1634
1635 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1636 embedded_hal_02::serial::Write::flush(&mut self.tx)
1637 }
1638}
1639
1640impl embedded_hal_02::blocking::serial::Write<u8> for Lpuart<'_, Blocking> {
1641 type Error = Error;
1642
1643 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1644 self.blocking_write(buffer)
1645 }
1646
1647 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1648 self.blocking_flush()
1649 }
1650}
1651
1652// ============================================================================
1653// EMBEDDED-HAL-NB TRAIT IMPLEMENTATIONS
1654// ============================================================================
1655
1656impl embedded_hal_nb::serial::Error for Error {
1657 fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
1658 match *self {
1659 Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
1660 Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
1661 Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
1662 Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise,
1663 _ => embedded_hal_nb::serial::ErrorKind::Other,
1664 }
1665 }
1666}
1667
1668impl embedded_hal_nb::serial::ErrorType for LpuartRx<'_, Blocking> {
1669 type Error = Error;
1670}
1671
1672impl embedded_hal_nb::serial::ErrorType for LpuartTx<'_, Blocking> {
1673 type Error = Error;
1674}
1675
1676impl embedded_hal_nb::serial::ErrorType for Lpuart<'_, Blocking> {
1677 type Error = Error;
1678}
1679
1680impl embedded_hal_nb::serial::Read for LpuartRx<'_, Blocking> {
1681 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1682 let mut buf = [0; 1];
1683 match self.read(&mut buf) {
1684 Ok(_) => Ok(buf[0]),
1685 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1686 Err(e) => Err(nb::Error::Other(e)),
1687 }
1688 }
1689}
1690
1691impl embedded_hal_nb::serial::Write for LpuartTx<'_, Blocking> {
1692 fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
1693 match self.write(&[word]) {
1694 Ok(_) => Ok(()),
1695 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1696 Err(e) => Err(nb::Error::Other(e)),
1697 }
1698 }
1699
1700 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1701 match self.flush() {
1702 Ok(_) => Ok(()),
1703 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1704 Err(e) => Err(nb::Error::Other(e)),
1705 }
1706 }
1707}
1708
1709impl core::fmt::Write for LpuartTx<'_, Blocking> {
1710 fn write_str(&mut self, s: &str) -> core::fmt::Result {
1711 self.blocking_write(s.as_bytes()).map_err(|_| core::fmt::Error)
1712 }
1713}
1714
1715impl embedded_hal_nb::serial::Read for Lpuart<'_, Blocking> {
1716 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1717 embedded_hal_nb::serial::Read::read(&mut self.rx)
1718 }
1719}
1720
1721impl embedded_hal_nb::serial::Write for Lpuart<'_, Blocking> {
1722 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
1723 embedded_hal_nb::serial::Write::write(&mut self.tx, char)
1724 }
1725
1726 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1727 embedded_hal_nb::serial::Write::flush(&mut self.tx)
1728 }
1729}
1730
1731// ============================================================================
1732// EMBEDDED-IO TRAIT IMPLEMENTATIONS
1733// ============================================================================
1734
1735impl embedded_io::Error for Error {
1736 fn kind(&self) -> embedded_io::ErrorKind {
1737 embedded_io::ErrorKind::Other
1738 }
1739}
1740
1741impl embedded_io::ErrorType for LpuartRx<'_, Blocking> {
1742 type Error = Error;
1743}
1744
1745impl embedded_io::ErrorType for LpuartTx<'_, Blocking> {
1746 type Error = Error;
1747}
1748
1749impl embedded_io::ErrorType for Lpuart<'_, Blocking> {
1750 type Error = Error;
1751}
1752
1753impl embedded_io::Read for LpuartRx<'_, Blocking> {
1754 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1755 self.blocking_read(buf).map(|_| buf.len())
1756 }
1757}
1758
1759impl embedded_io::Write for LpuartTx<'_, Blocking> {
1760 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1761 self.blocking_write(buf).map(|_| buf.len())
1762 }
1763
1764 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1765 self.blocking_flush()
1766 }
1767}
1768
1769impl embedded_io::Read for Lpuart<'_, Blocking> {
1770 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1771 embedded_io::Read::read(&mut self.rx, buf)
1772 }
1773}
1774
1775impl embedded_io::Write for Lpuart<'_, Blocking> {
1776 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1777 embedded_io::Write::write(&mut self.tx, buf)
1778 }
1779
1780 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1781 embedded_io::Write::flush(&mut self.tx)
1782 }
1783}
diff --git a/embassy-mcxa/src/ostimer.rs b/embassy-mcxa/src/ostimer.rs
new file mode 100644
index 000000000..9e66e82d8
--- /dev/null
+++ b/embassy-mcxa/src/ostimer.rs
@@ -0,0 +1,745 @@
1//! # OSTIMER Driver with Robustness Features
2//!
3//! This module provides an async timer driver for the NXP MCXA276 OSTIMER peripheral
4//! with protection against race conditions and timer rollover issues.
5//!
6//! ## Features
7//!
8//! - Async timing with embassy-time integration
9//! - Gray code counter handling (42-bit counter)
10//! - Interrupt-driven wakeups
11//! - Configurable interrupt priority
12//! - **Race condition protection**: Critical sections and atomic operations
13//! - **Timer rollover handling**: Bounds checking and rollover prevention
14//!
15//! ## Clock Frequency Configuration
16//!
17//! The OSTIMER frequency depends on your system's clock configuration. You must provide
18//! the actual frequency when calling `time_driver::init()`.
19//!
20//! ## Race Condition Protection
21//! - Critical sections in interrupt handlers prevent concurrent access
22//! - Atomic register operations with memory barriers
23//! - Proper interrupt flag clearing and validation
24//!
25//! ## Timer Rollover Handling
26//! - Bounds checking prevents scheduling beyond timer capacity
27//! - Immediate wake for timestamps that would cause rollover issues
28#![allow(dead_code)]
29
30use core::sync::atomic::{AtomicBool, Ordering};
31
32use embassy_hal_internal::{Peri, PeripheralType};
33
34use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel};
35use crate::clocks::{Gate, PoweredClock, assert_reset, enable_and_reset, is_reset_released, release_reset};
36use crate::interrupt::InterruptExt;
37use crate::pac;
38
39// PAC defines the shared RegisterBlock under `ostimer0`.
40type Regs = pac::ostimer0::RegisterBlock;
41
42// OSTIMER EVTIMER register layout constants
43/// Total width of the EVTIMER counter in bits (42 bits total)
44const EVTIMER_TOTAL_BITS: u32 = 42;
45/// Width of the low part of EVTIMER (bits 31:0)
46const EVTIMER_LO_BITS: u32 = 32;
47/// Width of the high part of EVTIMER (bits 41:32)
48const EVTIMER_HI_BITS: u32 = 10;
49/// Bit position where high part starts in the combined 64-bit value
50const EVTIMER_HI_SHIFT: u32 = 32;
51
52/// Bit mask for the high part of EVTIMER
53const EVTIMER_HI_MASK: u16 = (1 << EVTIMER_HI_BITS) - 1;
54
55/// Maximum value for MATCH_L register (32-bit)
56const MATCH_L_MAX: u32 = u32::MAX;
57/// Maximum value for MATCH_H register (10-bit)
58const MATCH_H_MAX: u16 = EVTIMER_HI_MASK;
59
60/// Bit mask for extracting the low 32 bits from a 64-bit value
61const LOW_32_BIT_MASK: u64 = u32::MAX as u64;
62
63/// Gray code conversion bit shifts (most significant to least)
64const GRAY_CONVERSION_SHIFTS: [u32; 6] = [32, 16, 8, 4, 2, 1];
65
66/// Maximum timer value before rollover (2^42 - 1 ticks)
67/// Actual rollover time depends on the configured clock frequency
68const TIMER_MAX_VALUE: u64 = (1u64 << EVTIMER_TOTAL_BITS) - 1;
69
70/// Threshold for detecting timer rollover in comparisons (1 second at 1MHz)
71const TIMER_ROLLOVER_THRESHOLD: u64 = 1_000_000;
72
73/// Common default interrupt priority for OSTIMER
74const DEFAULT_INTERRUPT_PRIORITY: u8 = 3;
75
76// Global alarm state for interrupt handling
77static ALARM_ACTIVE: AtomicBool = AtomicBool::new(false);
78static mut ALARM_CALLBACK: Option<fn()> = None;
79static mut ALARM_FLAG: Option<*const AtomicBool> = None;
80static mut ALARM_TARGET_TIME: u64 = 0;
81
82/// Number of tight spin iterations between elapsed time checks while waiting for MATCH writes to return to the idle (0) state.
83const MATCH_WRITE_READY_SPINS: usize = 512;
84/// Maximum time (in OSTIMER ticks) to wait for MATCH registers to become writable (~5 ms at 1 MHz).
85const MATCH_WRITE_READY_TIMEOUT_TICKS: u64 = 5_000;
86/// Short stabilization delay executed after toggling the MRCC reset line to let the OSTIMER bus interface settle.
87const RESET_STABILIZE_SPINS: usize = 512;
88
89pub(super) fn wait_for_match_write_ready(r: &Regs) -> bool {
90 let start = now_ticks_read();
91 let mut spin_budget = 0usize;
92
93 loop {
94 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
95 return true;
96 }
97
98 cortex_m::asm::nop();
99 spin_budget += 1;
100
101 if spin_budget >= MATCH_WRITE_READY_SPINS {
102 spin_budget = 0;
103
104 let elapsed = now_ticks_read().wrapping_sub(start);
105 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
106 return false;
107 }
108 }
109 }
110}
111
112pub(super) fn wait_for_match_write_complete(r: &Regs) -> bool {
113 let start = now_ticks_read();
114 let mut spin_budget = 0usize;
115
116 loop {
117 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
118 return true;
119 }
120
121 cortex_m::asm::nop();
122 spin_budget += 1;
123
124 if spin_budget >= MATCH_WRITE_READY_SPINS {
125 spin_budget = 0;
126
127 let elapsed = now_ticks_read().wrapping_sub(start);
128 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
129 return false;
130 }
131 }
132 }
133}
134
135fn prime_match_registers(r: &Regs) {
136 // Disable the interrupt, clear any pending flag, then wait until the MATCH registers are writable.
137 r.osevent_ctrl()
138 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
139
140 if wait_for_match_write_ready(r) {
141 r.match_l().write(|w| unsafe { w.match_value().bits(MATCH_L_MAX) });
142 r.match_h().write(|w| unsafe { w.match_value().bits(MATCH_H_MAX) });
143 let _ = wait_for_match_write_complete(r);
144 }
145}
146
147/// Single-shot alarm functionality for OSTIMER
148pub struct Alarm<'d> {
149 /// Whether the alarm is currently active
150 active: AtomicBool,
151 /// Callback to execute when alarm expires (optional)
152 callback: Option<fn()>,
153 /// Flag that gets set when alarm expires (optional)
154 flag: Option<&'d AtomicBool>,
155 _phantom: core::marker::PhantomData<&'d mut ()>,
156}
157
158impl<'d> Default for Alarm<'d> {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl<'d> Alarm<'d> {
165 /// Create a new alarm instance
166 pub fn new() -> Self {
167 Self {
168 active: AtomicBool::new(false),
169 callback: None,
170 flag: None,
171 _phantom: core::marker::PhantomData,
172 }
173 }
174
175 /// Set a callback that will be executed when the alarm expires
176 /// Note: Due to interrupt handler constraints, callbacks must be static function pointers
177 pub fn with_callback(mut self, callback: fn()) -> Self {
178 self.callback = Some(callback);
179 self
180 }
181
182 /// Set a flag that will be set to true when the alarm expires
183 pub fn with_flag(mut self, flag: &'d AtomicBool) -> Self {
184 self.flag = Some(flag);
185 self
186 }
187
188 /// Check if the alarm is currently active
189 pub fn is_active(&self) -> bool {
190 self.active.load(Ordering::Acquire)
191 }
192
193 /// Cancel the alarm if it's active
194 pub fn cancel(&self) {
195 self.active.store(false, Ordering::Release);
196 }
197}
198
199/// Configuration for Ostimer::new()
200#[derive(Copy, Clone)]
201pub struct Config {
202 /// Initialize MATCH registers to their max values and mask/clear the interrupt flag.
203 pub init_match_max: bool,
204 pub power: PoweredClock,
205 pub source: OstimerClockSel,
206}
207
208impl Default for Config {
209 fn default() -> Self {
210 Self {
211 init_match_max: true,
212 power: PoweredClock::NormalEnabledDeepSleepDisabled,
213 source: OstimerClockSel::Clk1M,
214 }
215 }
216}
217
218/// OSTIMER peripheral instance
219pub struct Ostimer<'d, I: Instance> {
220 _inst: core::marker::PhantomData<I>,
221 clock_frequency_hz: u64,
222 _phantom: core::marker::PhantomData<&'d mut ()>,
223}
224
225impl<'d, I: Instance> Ostimer<'d, I> {
226 /// Construct OSTIMER handle.
227 /// Requires clocks for the instance to be enabled by the board before calling.
228 /// Does not enable NVIC or INTENA; use time_driver::init() for async operation.
229 pub fn new(_inst: Peri<'d, I>, cfg: Config) -> Self {
230 let clock_freq = unsafe {
231 enable_and_reset::<I>(&OsTimerConfig {
232 power: cfg.power,
233 source: cfg.source,
234 })
235 .expect("Enabling OsTimer clock should not fail")
236 };
237
238 assert!(clock_freq > 0, "OSTIMER frequency must be greater than 0");
239
240 if cfg.init_match_max {
241 let r: &Regs = unsafe { &*I::ptr() };
242 // Mask INTENA, clear pending flag, and set MATCH to max so no spurious IRQ fires.
243 prime_match_registers(r);
244 }
245
246 Self {
247 _inst: core::marker::PhantomData,
248 clock_frequency_hz: clock_freq as u64,
249 _phantom: core::marker::PhantomData,
250 }
251 }
252
253 /// Get the configured clock frequency in Hz
254 pub fn clock_frequency_hz(&self) -> u64 {
255 self.clock_frequency_hz
256 }
257
258 /// Read the current timer counter value in timer ticks
259 ///
260 /// # Returns
261 /// Current timer counter value as a 64-bit unsigned integer
262 pub fn now(&self) -> u64 {
263 now_ticks_read()
264 }
265
266 /// Reset the timer counter to zero
267 ///
268 /// This performs a hardware reset of the OSTIMER peripheral, which will reset
269 /// the counter to zero and clear any pending interrupts. Note that this will
270 /// affect all timer operations including embassy-time.
271 ///
272 /// # Safety
273 /// This operation will reset the entire OSTIMER peripheral. Any active alarms
274 /// or time_driver operations will be disrupted. Use with caution.
275 pub fn reset(&self, _peripherals: &crate::pac::Peripherals) {
276 critical_section::with(|_| {
277 let r: &Regs = unsafe { &*I::ptr() };
278
279 // Mask the peripheral interrupt flag before we toggle the reset line so that
280 // no new NVIC activity races with the reset sequence.
281 r.osevent_ctrl()
282 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
283
284 unsafe {
285 assert_reset::<I>();
286
287 for _ in 0..RESET_STABILIZE_SPINS {
288 cortex_m::asm::nop();
289 }
290
291 release_reset::<I>();
292
293 while !is_reset_released::<I>() {
294 cortex_m::asm::nop();
295 }
296 }
297
298 for _ in 0..RESET_STABILIZE_SPINS {
299 cortex_m::asm::nop();
300 }
301
302 // Clear alarm bookkeeping before re-arming MATCH registers.
303 ALARM_ACTIVE.store(false, Ordering::Release);
304 unsafe {
305 ALARM_TARGET_TIME = 0;
306 ALARM_CALLBACK = None;
307 ALARM_FLAG = None;
308 }
309
310 prime_match_registers(r);
311 });
312
313 // Ensure no stale OS_EVENT request remains pending after the reset sequence.
314 crate::interrupt::OS_EVENT.unpend();
315 }
316
317 /// Schedule a single-shot alarm to expire after the specified delay in microseconds
318 ///
319 /// # Parameters
320 /// * `alarm` - The alarm instance to schedule
321 /// * `delay_us` - Delay in microseconds from now
322 ///
323 /// # Returns
324 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
325 pub fn schedule_alarm_delay(&self, alarm: &Alarm, delay_us: u64) -> bool {
326 let delay_ticks = (delay_us * self.clock_frequency_hz) / 1_000_000;
327 let target_time = now_ticks_read() + delay_ticks;
328 self.schedule_alarm_at(alarm, target_time)
329 }
330
331 /// Schedule a single-shot alarm to expire at the specified absolute time in timer ticks
332 ///
333 /// # Parameters
334 /// * `alarm` - The alarm instance to schedule
335 /// * `target_ticks` - Absolute time in timer ticks when the alarm should expire
336 ///
337 /// # Returns
338 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
339 pub fn schedule_alarm_at(&self, alarm: &Alarm, target_ticks: u64) -> bool {
340 let now = now_ticks_read();
341
342 // Check if target time is in the past
343 if target_ticks <= now {
344 // Execute callback immediately if alarm was supposed to be active
345 if alarm.active.load(Ordering::Acquire) {
346 alarm.active.store(false, Ordering::Release);
347 if let Some(callback) = alarm.callback {
348 callback();
349 }
350 if let Some(flag) = &alarm.flag {
351 flag.store(true, Ordering::Release);
352 }
353 }
354 return true;
355 }
356
357 // Check for timer rollover
358 let max_future = now + TIMER_MAX_VALUE;
359 if target_ticks > max_future {
360 return false; // Would exceed timer capacity
361 }
362
363 // Program the timer
364 let r: &Regs = unsafe { &*I::ptr() };
365
366 critical_section::with(|_| {
367 // Disable interrupt and clear flag
368 r.osevent_ctrl()
369 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
370
371 if !wait_for_match_write_ready(r) {
372 prime_match_registers(r);
373
374 if !wait_for_match_write_ready(r) {
375 alarm.active.store(false, Ordering::Release);
376 ALARM_ACTIVE.store(false, Ordering::Release);
377 unsafe {
378 ALARM_TARGET_TIME = 0;
379 ALARM_CALLBACK = None;
380 ALARM_FLAG = None;
381 }
382 return false;
383 }
384 }
385
386 // Mark alarm as active now that we know the MATCH registers are writable
387 alarm.active.store(true, Ordering::Release);
388
389 // Set global alarm state for interrupt handler
390 ALARM_ACTIVE.store(true, Ordering::Release);
391 unsafe {
392 ALARM_TARGET_TIME = target_ticks;
393 ALARM_CALLBACK = alarm.callback;
394 ALARM_FLAG = alarm.flag.map(|f| f as *const AtomicBool);
395 }
396
397 // Program MATCH registers (Gray-coded)
398 let gray = bin_to_gray(target_ticks);
399 let l = (gray & LOW_32_BIT_MASK) as u32;
400 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
401
402 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
403 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
404
405 if !wait_for_match_write_complete(r) {
406 alarm.active.store(false, Ordering::Release);
407 ALARM_ACTIVE.store(false, Ordering::Release);
408 unsafe {
409 ALARM_TARGET_TIME = 0;
410 ALARM_CALLBACK = None;
411 ALARM_FLAG = None;
412 }
413 return false;
414 }
415
416 let now_after_program = now_ticks_read();
417 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
418 if now_after_program >= target_ticks && !intrflag_set {
419 alarm.active.store(false, Ordering::Release);
420 ALARM_ACTIVE.store(false, Ordering::Release);
421 unsafe {
422 ALARM_TARGET_TIME = 0;
423 ALARM_CALLBACK = None;
424 ALARM_FLAG = None;
425 }
426 return false;
427 }
428
429 // Enable interrupt
430 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
431
432 true
433 })
434 }
435
436 /// Cancel any active alarm
437 pub fn cancel_alarm(&self, alarm: &Alarm) {
438 critical_section::with(|_| {
439 alarm.cancel();
440
441 // Clear global alarm state
442 ALARM_ACTIVE.store(false, Ordering::Release);
443 unsafe { ALARM_TARGET_TIME = 0 };
444
445 // Reset MATCH registers to maximum values to prevent spurious interrupts
446 let r: &Regs = unsafe { &*I::ptr() };
447 prime_match_registers(r);
448 });
449 }
450
451 /// Check if an alarm has expired (call this from your interrupt handler)
452 /// Returns true if the alarm was active and has now expired
453 pub fn check_alarm_expired(&self, alarm: &Alarm) -> bool {
454 if alarm.active.load(Ordering::Acquire) {
455 alarm.active.store(false, Ordering::Release);
456
457 // Execute callback
458 if let Some(callback) = alarm.callback {
459 callback();
460 }
461
462 // Set flag
463 if let Some(flag) = &alarm.flag {
464 flag.store(true, Ordering::Release);
465 }
466
467 true
468 } else {
469 false
470 }
471 }
472}
473
474/// Read current EVTIMER (Gray-coded) and convert to binary ticks.
475#[inline(always)]
476fn now_ticks_read() -> u64 {
477 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
478
479 // Read high then low to minimize incoherent snapshots
480 let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64);
481 let lo = r.evtimerl().read().evtimer_count_value().bits() as u64;
482 // Combine and convert from Gray code to binary
483 let gray = lo | (hi << EVTIMER_HI_SHIFT);
484 gray_to_bin(gray)
485}
486
487// Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance
488pub trait Instance: Gate<MrccPeriphConfig = OsTimerConfig> + PeripheralType {
489 fn ptr() -> *const Regs;
490}
491
492#[cfg(not(feature = "time"))]
493impl Instance for crate::peripherals::OSTIMER0 {
494 #[inline(always)]
495 fn ptr() -> *const Regs {
496 pac::Ostimer0::ptr()
497 }
498}
499
500#[inline(always)]
501fn bin_to_gray(x: u64) -> u64 {
502 x ^ (x >> 1)
503}
504
505#[inline(always)]
506fn gray_to_bin(gray: u64) -> u64 {
507 // More efficient iterative conversion using predefined shifts
508 let mut bin = gray;
509 for &shift in &GRAY_CONVERSION_SHIFTS {
510 bin ^= bin >> shift;
511 }
512 bin
513}
514
515#[cfg(feature = "time")]
516pub mod time_driver {
517 use core::sync::atomic::Ordering;
518 use core::task::Waker;
519
520 use embassy_sync::waitqueue::AtomicWaker;
521 use embassy_time_driver as etd;
522
523 use super::{
524 ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME, EVTIMER_HI_MASK, EVTIMER_HI_SHIFT,
525 LOW_32_BIT_MASK, Regs, bin_to_gray, now_ticks_read,
526 };
527 use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel};
528 use crate::clocks::{PoweredClock, enable_and_reset};
529 use crate::pac;
530
531 #[allow(non_camel_case_types)]
532 pub(crate) struct _OSTIMER0_TIME_DRIVER {
533 _x: (),
534 }
535
536 // #[cfg(feature = "time")]
537 // impl_cc_gate!(_OSTIMER0_TIME_DRIVER, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig);
538
539 impl crate::clocks::Gate for _OSTIMER0_TIME_DRIVER {
540 type MrccPeriphConfig = crate::clocks::periph_helpers::OsTimerConfig;
541
542 #[inline]
543 unsafe fn enable_clock() {
544 let mrcc = unsafe { pac::Mrcc0::steal() };
545 mrcc.mrcc_glb_cc1().modify(|_, w| w.ostimer0().enabled());
546 }
547
548 #[inline]
549 unsafe fn disable_clock() {
550 let mrcc = unsafe { pac::Mrcc0::steal() };
551 mrcc.mrcc_glb_cc1().modify(|_r, w| w.ostimer0().disabled());
552 }
553
554 #[inline]
555 fn is_clock_enabled() -> bool {
556 let mrcc = unsafe { pac::Mrcc0::steal() };
557 mrcc.mrcc_glb_cc1().read().ostimer0().is_enabled()
558 }
559
560 #[inline]
561 unsafe fn release_reset() {
562 let mrcc = unsafe { pac::Mrcc0::steal() };
563 mrcc.mrcc_glb_rst1().modify(|_, w| w.ostimer0().enabled());
564 }
565
566 #[inline]
567 unsafe fn assert_reset() {
568 let mrcc = unsafe { pac::Mrcc0::steal() };
569 mrcc.mrcc_glb_rst1().modify(|_, w| w.ostimer0().disabled());
570 }
571
572 #[inline]
573 fn is_reset_released() -> bool {
574 let mrcc = unsafe { pac::Mrcc0::steal() };
575 mrcc.mrcc_glb_rst1().read().ostimer0().is_enabled()
576 }
577 }
578
579 pub struct Driver;
580 static TIMER_WAKER: AtomicWaker = AtomicWaker::new();
581
582 impl etd::Driver for Driver {
583 fn now(&self) -> u64 {
584 // Use the hardware counter (frequency configured in init)
585 super::now_ticks_read()
586 }
587
588 fn schedule_wake(&self, timestamp: u64, waker: &Waker) {
589 let now = self.now();
590
591 // If timestamp is in the past or very close to now, wake immediately
592 if timestamp <= now {
593 waker.wake_by_ref();
594 return;
595 }
596
597 // Prevent scheduling too far in the future (beyond timer rollover)
598 // This prevents wraparound issues
599 let max_future = now + super::TIMER_MAX_VALUE;
600 if timestamp > max_future {
601 // For very long timeouts, wake immediately to avoid rollover issues
602 waker.wake_by_ref();
603 return;
604 }
605
606 // Register the waker first so any immediate wake below is observed by the executor.
607 TIMER_WAKER.register(waker);
608
609 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
610
611 critical_section::with(|_| {
612 // Mask INTENA and clear flag
613 r.osevent_ctrl()
614 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
615
616 // Read back to ensure W1C took effect on hardware
617 let _ = r.osevent_ctrl().read().ostimer_intrflag().bit();
618
619 if !super::wait_for_match_write_ready(r) {
620 super::prime_match_registers(r);
621
622 if !super::wait_for_match_write_ready(r) {
623 // If we can't safely program MATCH, wake immediately and leave INTENA masked.
624 waker.wake_by_ref();
625 return;
626 }
627 }
628
629 // Program MATCH (Gray-coded). Write low then high, then fence.
630 let gray = bin_to_gray(timestamp);
631 let l = (gray & LOW_32_BIT_MASK) as u32;
632
633 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
634
635 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
636 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
637
638 if !super::wait_for_match_write_complete(r) {
639 waker.wake_by_ref();
640 return;
641 }
642
643 let now_after_program = super::now_ticks_read();
644 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
645 if now_after_program >= timestamp && !intrflag_set {
646 waker.wake_by_ref();
647 return;
648 }
649
650 // Enable peripheral interrupt
651 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
652 });
653 }
654 }
655
656 /// Install the global embassy-time driver and configure NVIC priority for OS_EVENT.
657 ///
658 /// # Parameters
659 /// * `priority` - Interrupt priority for the OSTIMER interrupt
660 /// * `frequency_hz` - Actual OSTIMER clock frequency in Hz (stored for future use)
661 ///
662 /// Note: The frequency parameter is currently accepted for API compatibility.
663 /// The embassy_time_driver macro handles driver registration automatically.
664 pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) {
665 let _clock_freq = unsafe {
666 enable_and_reset::<_OSTIMER0_TIME_DRIVER>(&OsTimerConfig {
667 power: PoweredClock::AlwaysEnabled,
668 source: OstimerClockSel::Clk1M,
669 })
670 .expect("Enabling OsTimer clock should not fail")
671 };
672
673 // Mask/clear at peripheral and set default MATCH
674 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
675 super::prime_match_registers(r);
676
677 // Configure NVIC for timer operation
678 crate::interrupt::OS_EVENT.configure_for_timer(priority);
679
680 // Note: The embassy_time_driver macro automatically registers the driver
681 // The frequency parameter is accepted for future compatibility
682 let _ = frequency_hz; // Suppress unused parameter warning
683 }
684
685 // Export the global time driver expected by embassy-time
686 embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver);
687
688 /// To be called from the OS_EVENT IRQ.
689 pub fn on_interrupt() {
690 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
691
692 // Critical section to prevent races with schedule_wake
693 critical_section::with(|_| {
694 // Check if interrupt is actually pending and handle it atomically
695 if r.osevent_ctrl().read().ostimer_intrflag().bit_is_set() {
696 // Clear flag and disable interrupt atomically
697 r.osevent_ctrl().write(|w| {
698 w.ostimer_intrflag()
699 .clear_bit_by_one() // Write-1-to-clear using safe helper
700 .ostimer_intena()
701 .clear_bit()
702 });
703
704 // Wake the waiting task
705 TIMER_WAKER.wake();
706
707 // Handle alarm callback if active and this interrupt is for the alarm
708 if ALARM_ACTIVE.load(Ordering::SeqCst) {
709 let current_time = now_ticks_read();
710 let target_time = unsafe { ALARM_TARGET_TIME };
711
712 // Check if current time is close to alarm target time (within 1000 ticks for timing variations)
713 if current_time >= target_time && current_time <= target_time + 1000 {
714 ALARM_ACTIVE.store(false, Ordering::SeqCst);
715 unsafe { ALARM_TARGET_TIME = 0 };
716
717 // Execute callback if set
718 unsafe {
719 if let Some(callback) = ALARM_CALLBACK {
720 callback();
721 }
722 }
723
724 // Set flag if provided
725 unsafe {
726 if let Some(flag) = ALARM_FLAG {
727 (*flag).store(true, Ordering::SeqCst);
728 }
729 }
730 }
731 }
732 }
733 });
734 }
735}
736
737#[cfg(feature = "time")]
738use crate::pac::interrupt;
739
740#[cfg(feature = "time")]
741#[allow(non_snake_case)]
742#[interrupt]
743fn OS_EVENT() {
744 time_driver::on_interrupt()
745}
diff --git a/embassy-mcxa/src/rtc.rs b/embassy-mcxa/src/rtc.rs
new file mode 100644
index 000000000..f975d9c9f
--- /dev/null
+++ b/embassy-mcxa/src/rtc.rs
@@ -0,0 +1,453 @@
1//! RTC DateTime driver.
2use core::marker::PhantomData;
3
4use embassy_hal_internal::{Peri, PeripheralType};
5use maitake_sync::WaitCell;
6
7use crate::clocks::with_clocks;
8use crate::interrupt::typelevel::{Handler, Interrupt};
9use crate::pac;
10use crate::pac::rtc0::cr::Um;
11
12type Regs = pac::rtc0::RegisterBlock;
13
14/// Global wait cell for alarm notifications
15static WAKER: WaitCell = WaitCell::new();
16
17/// RTC interrupt handler.
18pub struct InterruptHandler<I: Instance> {
19 _phantom: PhantomData<I>,
20}
21
22/// Trait for RTC peripheral instances
23pub trait Instance: PeripheralType {
24 type Interrupt: Interrupt;
25 fn ptr() -> *const Regs;
26}
27
28/// Token for RTC0
29pub type Rtc0 = crate::peripherals::RTC0;
30impl Instance for crate::peripherals::RTC0 {
31 type Interrupt = crate::interrupt::typelevel::RTC;
32 #[inline(always)]
33 fn ptr() -> *const Regs {
34 pac::Rtc0::ptr()
35 }
36}
37
38/// Number of days in a standard year
39const DAYS_IN_A_YEAR: u32 = 365;
40/// Number of seconds in a day
41const SECONDS_IN_A_DAY: u32 = 86400;
42/// Number of seconds in an hour
43const SECONDS_IN_A_HOUR: u32 = 3600;
44/// Number of seconds in a minute
45const SECONDS_IN_A_MINUTE: u32 = 60;
46/// Unix epoch start year
47const YEAR_RANGE_START: u16 = 1970;
48
49/// Date and time structure for RTC operations
50#[derive(Debug, Clone, Copy)]
51pub struct RtcDateTime {
52 pub year: u16,
53 pub month: u8,
54 pub day: u8,
55 pub hour: u8,
56 pub minute: u8,
57 pub second: u8,
58}
59#[derive(Copy, Clone)]
60pub struct RtcConfig {
61 #[allow(dead_code)]
62 wakeup_select: bool,
63 update_mode: Um,
64 #[allow(dead_code)]
65 supervisor_access: bool,
66 compensation_interval: u8,
67 compensation_time: u8,
68}
69
70/// RTC interrupt enable flags
71#[derive(Copy, Clone)]
72pub struct RtcInterruptEnable;
73impl RtcInterruptEnable {
74 pub const RTC_TIME_INVALID_INTERRUPT_ENABLE: u32 = 1 << 0;
75 pub const RTC_TIME_OVERFLOW_INTERRUPT_ENABLE: u32 = 1 << 1;
76 pub const RTC_ALARM_INTERRUPT_ENABLE: u32 = 1 << 2;
77 pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4;
78}
79
80/// Converts a DateTime structure to Unix timestamp (seconds since 1970-01-01)
81///
82/// # Arguments
83///
84/// * `datetime` - The date and time to convert
85///
86/// # Returns
87///
88/// Unix timestamp as u32
89///
90/// # Note
91///
92/// This function handles leap years correctly.
93pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 {
94 let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
95
96 let mut seconds = (datetime.year as u32 - 1970) * DAYS_IN_A_YEAR;
97 seconds += (datetime.year as u32 / 4) - (1970 / 4);
98 seconds += month_days[datetime.month as usize] as u32;
99 seconds += datetime.day as u32 - 1;
100
101 if (datetime.year & 3 == 0) && (datetime.month <= 2) {
102 seconds -= 1;
103 }
104
105 seconds = seconds * SECONDS_IN_A_DAY
106 + (datetime.hour as u32 * SECONDS_IN_A_HOUR)
107 + (datetime.minute as u32 * SECONDS_IN_A_MINUTE)
108 + datetime.second as u32;
109
110 seconds
111}
112
113/// Converts Unix timestamp to DateTime structure
114///
115/// # Arguments
116///
117/// * `seconds` - Unix timestamp (seconds since 1970-01-01)
118///
119/// # Returns
120///
121/// RtcDateTime structure with the converted date and time
122///
123/// # Note
124///
125/// This function handles leap years correctly.
126pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime {
127 let mut seconds_remaining = seconds;
128 let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1;
129 seconds_remaining %= SECONDS_IN_A_DAY;
130
131 let hour = (seconds_remaining / SECONDS_IN_A_HOUR) as u8;
132 seconds_remaining %= SECONDS_IN_A_HOUR;
133 let minute = (seconds_remaining / SECONDS_IN_A_MINUTE) as u8;
134 let second = (seconds_remaining % SECONDS_IN_A_MINUTE) as u8;
135
136 let mut year = YEAR_RANGE_START;
137 let mut days_in_year = DAYS_IN_A_YEAR;
138
139 while days > days_in_year {
140 days -= days_in_year;
141 year += 1;
142
143 days_in_year = if year.is_multiple_of(4) {
144 DAYS_IN_A_YEAR + 1
145 } else {
146 DAYS_IN_A_YEAR
147 };
148 }
149
150 let days_per_month = [
151 31,
152 if year.is_multiple_of(4) { 29 } else { 28 },
153 31,
154 30,
155 31,
156 30,
157 31,
158 31,
159 30,
160 31,
161 30,
162 31,
163 ];
164
165 let mut month = 1;
166 for (m, month_days) in days_per_month.iter().enumerate() {
167 let m = m + 1;
168 if days <= *month_days as u32 {
169 month = m;
170 break;
171 } else {
172 days -= *month_days as u32;
173 }
174 }
175
176 let day = days as u8;
177
178 RtcDateTime {
179 year,
180 month: month as u8,
181 day,
182 hour,
183 minute,
184 second,
185 }
186}
187
188/// Returns default RTC configuration
189///
190/// # Returns
191///
192/// RtcConfig with sensible default values:
193/// - No wakeup selection
194/// - Update mode 0 (immediate updates)
195/// - No supervisor access restriction
196/// - No compensation
197pub fn get_default_config() -> RtcConfig {
198 RtcConfig {
199 wakeup_select: false,
200 update_mode: Um::Um0,
201 supervisor_access: false,
202 compensation_interval: 0,
203 compensation_time: 0,
204 }
205}
206/// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy)
207pub struct Rtc<'a, I: Instance> {
208 _inst: core::marker::PhantomData<&'a mut I>,
209}
210
211impl<'a, I: Instance> Rtc<'a, I> {
212 /// Create a new instance of the real time clock.
213 pub fn new(
214 _inst: Peri<'a, I>,
215 _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a,
216 config: RtcConfig,
217 ) -> Self {
218 let rtc = unsafe { &*I::ptr() };
219
220 // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock
221 // on the vsys domain is active
222 let clocks = with_clocks(|c| c.clk_16k_vsys.clone());
223 match clocks {
224 None => panic!("Clocks have not been initialized"),
225 Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"),
226 Some(Some(_)) => {}
227 }
228
229 // RTC reset
230 rtc.cr().modify(|_, w| w.swr().set_bit());
231 rtc.cr().modify(|_, w| w.swr().clear_bit());
232 rtc.tsr().write(|w| unsafe { w.bits(1) });
233
234 rtc.cr().modify(|_, w| w.um().variant(config.update_mode));
235
236 rtc.tcr().modify(|_, w| unsafe {
237 w.cir()
238 .bits(config.compensation_interval)
239 .tcr()
240 .bits(config.compensation_time)
241 });
242
243 // Enable RTC interrupt
244 I::Interrupt::unpend();
245 unsafe { I::Interrupt::enable() };
246
247 Self {
248 _inst: core::marker::PhantomData,
249 }
250 }
251
252 /// Set the current date and time
253 ///
254 /// # Arguments
255 ///
256 /// * `datetime` - The date and time to set
257 ///
258 /// # Note
259 ///
260 /// The datetime is converted to Unix timestamp and written to the time seconds register.
261 pub fn set_datetime(&self, datetime: RtcDateTime) {
262 let rtc = unsafe { &*I::ptr() };
263 let seconds = convert_datetime_to_seconds(&datetime);
264 rtc.tsr().write(|w| unsafe { w.bits(seconds) });
265 }
266
267 /// Get the current date and time
268 ///
269 /// # Returns
270 ///
271 /// Current date and time as RtcDateTime
272 ///
273 /// # Note
274 ///
275 /// Reads the current Unix timestamp from the time seconds register and converts it.
276 pub fn get_datetime(&self) -> RtcDateTime {
277 let rtc = unsafe { &*I::ptr() };
278 let seconds = rtc.tsr().read().bits();
279 convert_seconds_to_datetime(seconds)
280 }
281
282 /// Set the alarm date and time
283 ///
284 /// # Arguments
285 ///
286 /// * `alarm` - The date and time when the alarm should trigger
287 ///
288 /// # Note
289 ///
290 /// This function:
291 /// - Clears any existing alarm by writing 0 to the alarm register
292 /// - Waits for the clear operation to complete
293 /// - Sets the new alarm time
294 /// - Waits for the write operation to complete
295 /// - Uses timeouts to prevent infinite loops
296 /// - Enables the alarm interrupt after setting
297 pub fn set_alarm(&self, alarm: RtcDateTime) {
298 let rtc = unsafe { &*I::ptr() };
299 let seconds = convert_datetime_to_seconds(&alarm);
300
301 rtc.tar().write(|w| unsafe { w.bits(0) });
302 let mut timeout = 10000;
303 while rtc.tar().read().bits() != 0 && timeout > 0 {
304 timeout -= 1;
305 }
306
307 rtc.tar().write(|w| unsafe { w.bits(seconds) });
308
309 let mut timeout = 10000;
310 while rtc.tar().read().bits() != seconds && timeout > 0 {
311 timeout -= 1;
312 }
313
314 self.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
315 }
316
317 /// Get the current alarm date and time
318 ///
319 /// # Returns
320 ///
321 /// Alarm date and time as RtcDateTime
322 ///
323 /// # Note
324 ///
325 /// Reads the alarm timestamp from the time alarm register and converts it.
326 pub fn get_alarm(&self) -> RtcDateTime {
327 let rtc = unsafe { &*I::ptr() };
328 let alarm_seconds = rtc.tar().read().bits();
329 convert_seconds_to_datetime(alarm_seconds)
330 }
331
332 /// Start the RTC time counter
333 ///
334 /// # Note
335 ///
336 /// Sets the Time Counter Enable (TCE) bit in the status register.
337 pub fn start(&self) {
338 let rtc = unsafe { &*I::ptr() };
339 rtc.sr().modify(|_, w| w.tce().set_bit());
340 }
341
342 /// Stop the RTC time counter
343 ///
344 /// # Note
345 ///
346 /// Clears the Time Counter Enable (TCE) bit in the status register.
347 pub fn stop(&self) {
348 let rtc = unsafe { &*I::ptr() };
349 rtc.sr().modify(|_, w| w.tce().clear_bit());
350 }
351
352 /// Enable specific RTC interrupts
353 ///
354 /// # Arguments
355 ///
356 /// * `mask` - Bitmask of interrupts to enable (use RtcInterruptEnable constants)
357 ///
358 /// # Note
359 ///
360 /// This function enables the specified interrupt types and resets the alarm occurred flag.
361 /// Available interrupts:
362 /// - Time Invalid Interrupt
363 /// - Time Overflow Interrupt
364 /// - Alarm Interrupt
365 /// - Seconds Interrupt
366 pub fn set_interrupt(&self, mask: u32) {
367 let rtc = unsafe { &*I::ptr() };
368
369 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
370 rtc.ier().modify(|_, w| w.tiie().tiie_1());
371 }
372 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
373 rtc.ier().modify(|_, w| w.toie().toie_1());
374 }
375 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
376 rtc.ier().modify(|_, w| w.taie().taie_1());
377 }
378 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
379 rtc.ier().modify(|_, w| w.tsie().tsie_1());
380 }
381 }
382
383 /// Disable specific RTC interrupts
384 ///
385 /// # Arguments
386 ///
387 /// * `mask` - Bitmask of interrupts to disable (use RtcInterruptEnable constants)
388 ///
389 /// # Note
390 ///
391 /// This function disables the specified interrupt types.
392 pub fn disable_interrupt(&self, mask: u32) {
393 let rtc = unsafe { &*I::ptr() };
394
395 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
396 rtc.ier().modify(|_, w| w.tiie().tiie_0());
397 }
398 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
399 rtc.ier().modify(|_, w| w.toie().toie_0());
400 }
401 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
402 rtc.ier().modify(|_, w| w.taie().taie_0());
403 }
404 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
405 rtc.ier().modify(|_, w| w.tsie().tsie_0());
406 }
407 }
408
409 /// Clear the alarm interrupt flag
410 ///
411 /// # Note
412 ///
413 /// This function clears the Time Alarm Interrupt Enable bit.
414 pub fn clear_alarm_flag(&self) {
415 let rtc = unsafe { &*I::ptr() };
416 rtc.ier().modify(|_, w| w.taie().clear_bit());
417 }
418
419 /// Wait for an RTC alarm to trigger.
420 ///
421 /// # Arguments
422 ///
423 /// * `alarm` - The date and time when the alarm should trigger
424 /// This function will wait until the RTC alarm is triggered.
425 /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered.
426 pub async fn wait_for_alarm(&mut self, alarm: RtcDateTime) {
427 let wait = WAKER.subscribe().await;
428
429 self.set_alarm(alarm);
430 self.start();
431
432 // REVISIT: propagate error?
433 let _ = wait.await;
434
435 // Clear the interrupt and disable the alarm after waking up
436 self.disable_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
437 }
438}
439
440/// RTC interrupt handler
441///
442/// This struct implements the interrupt handler for RTC events.
443impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {
444 unsafe fn on_interrupt() {
445 let rtc = &*pac::Rtc0::ptr();
446 // Check if this is actually a time alarm interrupt
447 let sr = rtc.sr().read();
448 if sr.taf().bit_is_set() {
449 rtc.ier().modify(|_, w| w.taie().clear_bit());
450 WAKER.wake();
451 }
452 }
453}
diff --git a/embassy-net-esp-hosted/CHANGELOG.md b/embassy-net-esp-hosted/CHANGELOG.md
index d8b912295..6991b39fd 100644
--- a/embassy-net-esp-hosted/CHANGELOG.md
+++ b/embassy-net-esp-hosted/CHANGELOG.md
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11- Add an `Interface` trait to allow using other interface transports. 11- Add an `Interface` trait to allow using other interface transports.
12- Switch to `micropb` for protobuf. 12- Switch to `micropb` for protobuf.
13- Update protos to latest `esp-hosted-fg`. 13- Update protos to latest `esp-hosted-fg`.
14- Add support for OTA firmware updates.
14 15
15## 0.2.1 - 2025-08-26 16## 0.2.1 - 2025-08-26
16 17
diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs
index 38ec648b4..eb79593f6 100644
--- a/embassy-net-esp-hosted/src/control.rs
+++ b/embassy-net-esp-hosted/src/control.rs
@@ -146,6 +146,43 @@ impl<'a> Control<'a> {
146 Ok(()) 146 Ok(())
147 } 147 }
148 148
149 /// Initiate a firmware update.
150 pub async fn ota_begin(&mut self) -> Result<(), Error> {
151 let req = proto::CtrlMsg_Req_OTABegin {};
152 ioctl!(self, ReqOtaBegin, RespOtaBegin, req, resp);
153 Ok(())
154 }
155
156 /// Write slice of firmware to a device.
157 ///
158 /// [`ota_begin`] must be called first.
159 ///
160 /// The slice is split into chunks that can be sent across
161 /// the ioctl protocol to the wifi adapter.
162 pub async fn ota_write(&mut self, data: &[u8]) -> Result<(), Error> {
163 for chunk in data.chunks(256) {
164 let req = proto::CtrlMsg_Req_OTAWrite {
165 ota_data: heapless::Vec::from_slice(chunk).unwrap(),
166 };
167 ioctl!(self, ReqOtaWrite, RespOtaWrite, req, resp);
168 }
169 Ok(())
170 }
171
172 /// End the OTA session.
173 ///
174 /// [`ota_begin`] must be called first.
175 ///
176 /// NOTE: Will reset the wifi adapter after 5 seconds.
177 pub async fn ota_end(&mut self) -> Result<(), Error> {
178 let req = proto::CtrlMsg_Req_OTAEnd {};
179 ioctl!(self, ReqOtaEnd, RespOtaEnd, req, resp);
180 self.shared.ota_done();
181 // Wait for re-init
182 self.init().await?;
183 Ok(())
184 }
185
149 /// duration in seconds, clamped to [10, 3600] 186 /// duration in seconds, clamped to [10, 3600]
150 async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> { 187 async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> {
151 let req = proto::CtrlMsg_Req_ConfigHeartbeat { 188 let req = proto::CtrlMsg_Req_ConfigHeartbeat {
@@ -175,7 +212,8 @@ impl<'a> Control<'a> {
175 async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> { 212 async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> {
176 debug!("ioctl req: {:?}", &msg); 213 debug!("ioctl req: {:?}", &msg);
177 214
178 let mut buf = [0u8; 128]; 215 // Theoretical max overhead is 29 bytes. Biggest message is OTA write with 256 bytes.
216 let mut buf = [0u8; 256 + 29];
179 let buf_len = buf.len(); 217 let buf_len = buf.len();
180 218
181 let mut encoder = PbEncoder::new(&mut buf[..]); 219 let mut encoder = PbEncoder::new(&mut buf[..]);
diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs
index a516f80c7..de0f867e8 100644
--- a/embassy-net-esp-hosted/src/ioctl.rs
+++ b/embassy-net-esp-hosted/src/ioctl.rs
@@ -23,16 +23,23 @@ pub struct Shared(RefCell<SharedInner>);
23 23
24struct SharedInner { 24struct SharedInner {
25 ioctl: IoctlState, 25 ioctl: IoctlState,
26 is_init: bool, 26 state: ControlState,
27 control_waker: WakerRegistration, 27 control_waker: WakerRegistration,
28 runner_waker: WakerRegistration, 28 runner_waker: WakerRegistration,
29} 29}
30 30
31#[derive(Clone, Copy)]
32pub(crate) enum ControlState {
33 Init,
34 Reboot,
35 Ready,
36}
37
31impl Shared { 38impl Shared {
32 pub fn new() -> Self { 39 pub fn new() -> Self {
33 Self(RefCell::new(SharedInner { 40 Self(RefCell::new(SharedInner {
34 ioctl: IoctlState::Done { resp_len: 0 }, 41 ioctl: IoctlState::Done { resp_len: 0 },
35 is_init: false, 42 state: ControlState::Init,
36 control_waker: WakerRegistration::new(), 43 control_waker: WakerRegistration::new(),
37 runner_waker: WakerRegistration::new(), 44 runner_waker: WakerRegistration::new(),
38 })) 45 }))
@@ -99,18 +106,30 @@ impl Shared {
99 } 106 }
100 } 107 }
101 108
109 // ota
110 pub fn ota_done(&self) {
111 let mut this = self.0.borrow_mut();
112 this.state = ControlState::Reboot;
113 }
114
102 // // // // // // // // // // // // // // // // // // // // 115 // // // // // // // // // // // // // // // // // // // //
116 //
117 // check if ota is in progress
118 pub(crate) fn state(&self) -> ControlState {
119 let this = self.0.borrow();
120 this.state
121 }
103 122
104 pub fn init_done(&self) { 123 pub fn init_done(&self) {
105 let mut this = self.0.borrow_mut(); 124 let mut this = self.0.borrow_mut();
106 this.is_init = true; 125 this.state = ControlState::Ready;
107 this.control_waker.wake(); 126 this.control_waker.wake();
108 } 127 }
109 128
110 pub fn init_wait(&self) -> impl Future<Output = ()> + '_ { 129 pub fn init_wait(&self) -> impl Future<Output = ()> + '_ {
111 poll_fn(|cx| { 130 poll_fn(|cx| {
112 let mut this = self.0.borrow_mut(); 131 let mut this = self.0.borrow_mut();
113 if this.is_init { 132 if let ControlState::Ready = this.state {
114 Poll::Ready(()) 133 Poll::Ready(())
115 } else { 134 } else {
116 this.control_waker.register(cx.waker()); 135 this.control_waker.register(cx.waker());
diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs
index d882af8cf..2c7377281 100644
--- a/embassy-net-esp-hosted/src/lib.rs
+++ b/embassy-net-esp-hosted/src/lib.rs
@@ -234,6 +234,11 @@ where
234 tx_buf[..PayloadHeader::SIZE].fill(0); 234 tx_buf[..PayloadHeader::SIZE].fill(0);
235 } 235 }
236 Either4::Fourth(()) => { 236 Either4::Fourth(()) => {
237 // Extend the deadline if initializing
238 if let ioctl::ControlState::Reboot = self.shared.state() {
239 self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP;
240 continue;
241 }
237 panic!("heartbeat from esp32 stopped") 242 panic!("heartbeat from esp32 stopped")
238 } 243 }
239 } 244 }
diff --git a/embassy-net-esp-hosted/src/proto.rs b/embassy-net-esp-hosted/src/proto.rs
index 74c67bd61..09bec8984 100644
--- a/embassy-net-esp-hosted/src/proto.rs
+++ b/embassy-net-esp-hosted/src/proto.rs
@@ -16,7 +16,7 @@ Switch to a proper script when https://github.com/YuhanLiin/micropb/issues/30 is
16 // Special config for things that need to be larger 16 // Special config for things that need to be larger
17 g.configure( 17 g.configure(
18 ".CtrlMsg_Req_OTAWrite.ota_data", 18 ".CtrlMsg_Req_OTAWrite.ota_data",
19 micropb_gen::Config::new().max_bytes(1024), 19 micropb_gen::Config::new().max_bytes(256),
20 ); 20 );
21 g.configure( 21 g.configure(
22 ".CtrlMsg_Event_ESPInit.init_data", 22 ".CtrlMsg_Event_ESPInit.init_data",
@@ -4296,28 +4296,28 @@ impl ::micropb::MessageEncode for CtrlMsg_Resp_OTABegin {
4296#[derive(Debug, Default, PartialEq, Clone)] 4296#[derive(Debug, Default, PartialEq, Clone)]
4297#[cfg_attr(feature = "defmt", derive(defmt::Format))] 4297#[cfg_attr(feature = "defmt", derive(defmt::Format))]
4298pub struct CtrlMsg_Req_OTAWrite { 4298pub struct CtrlMsg_Req_OTAWrite {
4299 pub r#ota_data: ::micropb::heapless::Vec<u8, 1024>, 4299 pub r#ota_data: ::micropb::heapless::Vec<u8, 256>,
4300} 4300}
4301impl CtrlMsg_Req_OTAWrite { 4301impl CtrlMsg_Req_OTAWrite {
4302 ///Return a reference to `ota_data` 4302 ///Return a reference to `ota_data`
4303 #[inline] 4303 #[inline]
4304 pub fn r#ota_data(&self) -> &::micropb::heapless::Vec<u8, 1024> { 4304 pub fn r#ota_data(&self) -> &::micropb::heapless::Vec<u8, 256> {
4305 &self.r#ota_data 4305 &self.r#ota_data
4306 } 4306 }
4307 ///Return a mutable reference to `ota_data` 4307 ///Return a mutable reference to `ota_data`
4308 #[inline] 4308 #[inline]
4309 pub fn mut_ota_data(&mut self) -> &mut ::micropb::heapless::Vec<u8, 1024> { 4309 pub fn mut_ota_data(&mut self) -> &mut ::micropb::heapless::Vec<u8, 256> {
4310 &mut self.r#ota_data 4310 &mut self.r#ota_data
4311 } 4311 }
4312 ///Set the value of `ota_data` 4312 ///Set the value of `ota_data`
4313 #[inline] 4313 #[inline]
4314 pub fn set_ota_data(&mut self, value: ::micropb::heapless::Vec<u8, 1024>) -> &mut Self { 4314 pub fn set_ota_data(&mut self, value: ::micropb::heapless::Vec<u8, 256>) -> &mut Self {
4315 self.r#ota_data = value.into(); 4315 self.r#ota_data = value.into();
4316 self 4316 self
4317 } 4317 }
4318 ///Builder method that sets the value of `ota_data`. Useful for initializing the message. 4318 ///Builder method that sets the value of `ota_data`. Useful for initializing the message.
4319 #[inline] 4319 #[inline]
4320 pub fn init_ota_data(mut self, value: ::micropb::heapless::Vec<u8, 1024>) -> Self { 4320 pub fn init_ota_data(mut self, value: ::micropb::heapless::Vec<u8, 256>) -> Self {
4321 self.r#ota_data = value.into(); 4321 self.r#ota_data = value.into();
4322 self 4322 self
4323 } 4323 }
diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml
index 8cf11f1fb..cd9bf2987 100644
--- a/embassy-net-nrf91/Cargo.toml
+++ b/embassy-net-nrf91/Cargo.toml
@@ -18,7 +18,7 @@ log = ["dep:log"]
18defmt = { version = "1.0.1", optional = true } 18defmt = { version = "1.0.1", optional = true }
19log = { version = "0.4.14", optional = true } 19log = { version = "0.4.14", optional = true }
20 20
21nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "176dc4afe1dd8df78f3cbda4479ab5151aa32252" } 21nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "52fd51ce762a3d3a81660dea62947e6d2d1e9d91" }
22cortex-m = "0.7.7" 22cortex-m = "0.7.7"
23 23
24embassy-time = { version = "0.5.0", path = "../embassy-time" } 24embassy-time = { version = "0.5.0", path = "../embassy-time" }
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md
index 5657ddcfb..9c53e66a7 100644
--- a/embassy-nrf/CHANGELOG.md
+++ b/embassy-nrf/CHANGELOG.md
@@ -29,6 +29,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
29- changed: add workaround for anomaly 66 on nrf52 29- changed: add workaround for anomaly 66 on nrf52
30- added: expose PPI events available on SPIS peripheral 30- added: expose PPI events available on SPIS peripheral
31- added: add basic GRTC time driver support for nRF54L 31- added: add basic GRTC time driver support for nRF54L
32* added: support for nrf54l10 and nrf54l05
33* added: expose uicr write functions
34* added: support for nrf54lm20a
32 35
33## 0.8.0 - 2025-09-30 36## 0.8.0 - 2025-09-30
34 37
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index f73772682..ee070f0c0 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -25,6 +25,11 @@ build = [
25 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf5340-net", "time", "time-driver-rtc1"]}, 25 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf5340-net", "time", "time-driver-rtc1"]},
26 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-s", "time", "time-driver-grtc"]}, 26 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-s", "time", "time-driver-grtc"]},
27 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-ns", "time", "time-driver-grtc"]}, 27 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-ns", "time", "time-driver-grtc"]},
28 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l10-app-s", "time", "time-driver-grtc"]},
29 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l10-app-ns", "time", "time-driver-grtc"]},
30 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l05-app-s", "time", "time-driver-grtc"]},
31 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l05-app-ns", "time", "time-driver-grtc"]},
32 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54lm20-app-s", "time", "time-driver-grtc"]},
28 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time"]}, 33 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time"]},
29 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time-driver-rtc1"]}, 34 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time-driver-rtc1"]},
30 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time", "time-driver-rtc1"]}, 35 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time", "time-driver-rtc1"]},
@@ -130,6 +135,16 @@ nrf5340-net = ["_nrf5340-net"]
130nrf54l15-app-s = ["_nrf54l15-app", "_s", "_multi_wdt"] 135nrf54l15-app-s = ["_nrf54l15-app", "_s", "_multi_wdt"]
131## nRF54L15 application core in Non-Secure mode 136## nRF54L15 application core in Non-Secure mode
132nrf54l15-app-ns = ["_nrf54l15-app", "_ns"] 137nrf54l15-app-ns = ["_nrf54l15-app", "_ns"]
138## nRF54L10 application core in Secure mode
139nrf54l10-app-s = ["_nrf54l10-app", "_s", "_multi_wdt"]
140## nRF54L10 application core in Non-Secure mode
141nrf54l10-app-ns = ["_nrf54l10-app", "_ns"]
142## nRF54L05 application core in Secure mode
143nrf54l05-app-s = ["_nrf54l05-app", "_s", "_multi_wdt"]
144## nRF54L05 application core in Non-Secure mode
145nrf54l05-app-ns = ["_nrf54l05-app", "_ns"]
146## nRF54LM20 application core in Secure mode
147nrf54lm20-app-s = ["_nrf54lm20-app", "_s", "_multi_wdt"]
133 148
134## nRF9160 in Secure mode 149## nRF9160 in Secure mode
135nrf9160-s = ["_nrf9160", "_s", "_nrf91"] 150nrf9160-s = ["_nrf9160", "_s", "_nrf91"]
@@ -153,6 +168,12 @@ _nrf5340-net = ["_nrf5340", "nrf-pac/nrf5340-net"]
153_nrf5340 = ["_gpio-p1", "_dppi"] 168_nrf5340 = ["_gpio-p1", "_dppi"]
154_nrf54l15-app = ["_nrf54l15", "nrf-pac/nrf54l15-app"] 169_nrf54l15-app = ["_nrf54l15", "nrf-pac/nrf54l15-app"]
155_nrf54l15 = ["_nrf54l", "_gpio-p1", "_gpio-p2"] 170_nrf54l15 = ["_nrf54l", "_gpio-p1", "_gpio-p2"]
171_nrf54l10-app = ["_nrf54l10", "nrf-pac/nrf54l10-app"]
172_nrf54l10 = ["_nrf54l", "_gpio-p1", "_gpio-p2"]
173_nrf54l05-app = ["_nrf54l05", "nrf-pac/nrf54l05-app"]
174_nrf54l05 = ["_nrf54l", "_gpio-p1", "_gpio-p2"]
175_nrf54lm20-app = ["_nrf54lm20", "nrf-pac/nrf54lm20a-app"]
176_nrf54lm20 = ["_nrf54l", "_gpio-p1", "_gpio-p2"]
156_nrf54l = ["_dppi", "_grtc"] 177_nrf54l = ["_dppi", "_grtc"]
157 178
158_nrf9160 = ["nrf-pac/nrf9160", "_dppi", "_spi-v1"] 179_nrf9160 = ["nrf-pac/nrf9160", "_dppi", "_spi-v1"]
@@ -202,7 +223,7 @@ embedded-io-async = { version = "0.6.1" }
202rand-core-06 = { package = "rand_core", version = "0.6" } 223rand-core-06 = { package = "rand_core", version = "0.6" }
203rand-core-09 = { package = "rand_core", version = "0.9" } 224rand-core-09 = { package = "rand_core", version = "0.9" }
204 225
205nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "176dc4afe1dd8df78f3cbda4479ab5151aa32252" } 226nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "52fd51ce762a3d3a81660dea62947e6d2d1e9d91" }
206 227
207defmt = { version = "1.0.1", optional = true } 228defmt = { version = "1.0.1", optional = true }
208bitflags = "2.4.2" 229bitflags = "2.4.2"
diff --git a/embassy-nrf/src/chips/nrf54l05_app.rs b/embassy-nrf/src/chips/nrf54l05_app.rs
new file mode 100644
index 000000000..8e6595248
--- /dev/null
+++ b/embassy-nrf/src/chips/nrf54l05_app.rs
@@ -0,0 +1,746 @@
1/// Peripheral Access Crate
2#[allow(unused_imports)]
3#[rustfmt::skip]
4pub mod pac {
5 pub use nrf_pac::*;
6
7 #[cfg(feature = "_ns")]
8 #[doc(no_inline)]
9 pub use nrf_pac::{
10 FICR_NS as FICR,
11 DPPIC00_NS as DPPIC00,
12 PPIB00_NS as PPIB00,
13 PPIB01_NS as PPIB01,
14 AAR00_NS as AAR00,
15 CCM00_NS as CCM00,
16 ECB00_NS as ECB00,
17 SPIM00_NS as SPIM00,
18 SPIS00_NS as SPIS00,
19 UARTE00_NS as UARTE00,
20 VPR00_NS as VPR00,
21 P2_NS as P2,
22 CTRLAP_NS as CTRLAP,
23 TAD_NS as TAD,
24 TIMER00_NS as TIMER00,
25 DPPIC10_NS as DPPIC10,
26 PPIB10_NS as PPIB10,
27 PPIB11_NS as PPIB11,
28 TIMER10_NS as TIMER10,
29 EGU10_NS as EGU10,
30 RADIO_NS as RADIO,
31 DPPIC20_NS as DPPIC20,
32 PPIB20_NS as PPIB20,
33 PPIB21_NS as PPIB21,
34 PPIB22_NS as PPIB22,
35 SPIM20_NS as SPIM20,
36 SPIS20_NS as SPIS20,
37 TWIM20_NS as TWIM20,
38 TWIS20_NS as TWIS20,
39 UARTE20_NS as UARTE20,
40 SPIM21_NS as SPIM21,
41 SPIS21_NS as SPIS21,
42 TWIM21_NS as TWIM21,
43 TWIS21_NS as TWIS21,
44 UARTE21_NS as UARTE21,
45 SPIM22_NS as SPIM22,
46 SPIS22_NS as SPIS22,
47 TWIM22_NS as TWIM22,
48 TWIS22_NS as TWIS22,
49 UARTE22_NS as UARTE22,
50 EGU20_NS as EGU20,
51 TIMER20_NS as TIMER20,
52 TIMER21_NS as TIMER21,
53 TIMER22_NS as TIMER22,
54 TIMER23_NS as TIMER23,
55 TIMER24_NS as TIMER24,
56 MEMCONF_NS as MEMCONF,
57 PDM20_NS as PDM20,
58 PDM21_NS as PDM21,
59 PWM20_NS as PWM20,
60 PWM21_NS as PWM21,
61 PWM22_NS as PWM22,
62 SAADC_NS as SAADC,
63 NFCT_NS as NFCT,
64 TEMP_NS as TEMP,
65 P1_NS as P1,
66 GPIOTE20_NS as GPIOTE20,
67 I2S20_NS as I2S20,
68 QDEC20_NS as QDEC20,
69 QDEC21_NS as QDEC21,
70 GRTC_NS as GRTC,
71 DPPIC30_NS as DPPIC30,
72 PPIB30_NS as PPIB30,
73 SPIM30_NS as SPIM30,
74 SPIS30_NS as SPIS30,
75 TWIM30_NS as TWIM30,
76 TWIS30_NS as TWIS30,
77 UARTE30_NS as UARTE30,
78 COMP_NS as COMP,
79 LPCOMP_NS as LPCOMP,
80 WDT31_NS as WDT31,
81 P0_NS as P0,
82 GPIOTE30_NS as GPIOTE30,
83 CLOCK_NS as CLOCK,
84 POWER_NS as POWER,
85 RESET_NS as RESET,
86 OSCILLATORS_NS as OSCILLATORS,
87 REGULATORS_NS as REGULATORS,
88 TPIU_NS as TPIU,
89 ETM_NS as ETM,
90 };
91
92 #[cfg(feature = "_s")]
93 #[doc(no_inline)]
94 pub use nrf_pac::{
95 FICR_NS as FICR,
96 SICR_S as SICR,
97 ICACHEDATA_S as ICACHEDATA,
98 ICACHEINFO_S as ICACHEINFO,
99 SWI00_S as SWI00,
100 SWI01_S as SWI01,
101 SWI02_S as SWI02,
102 SWI03_S as SWI03,
103 SPU00_S as SPU00,
104 MPC00_S as MPC00,
105 DPPIC00_S as DPPIC00,
106 PPIB00_S as PPIB00,
107 PPIB01_S as PPIB01,
108 KMU_S as KMU,
109 AAR00_S as AAR00,
110 CCM00_S as CCM00,
111 ECB00_S as ECB00,
112 CRACEN_S as CRACEN,
113 SPIM00_S as SPIM00,
114 SPIS00_S as SPIS00,
115 UARTE00_S as UARTE00,
116 GLITCHDET_S as GLITCHDET,
117 RRAMC_S as RRAMC,
118 VPR00_S as VPR00,
119 P2_S as P2,
120 CTRLAP_S as CTRLAP,
121 TAD_S as TAD,
122 TIMER00_S as TIMER00,
123 SPU10_S as SPU10,
124 DPPIC10_S as DPPIC10,
125 PPIB10_S as PPIB10,
126 PPIB11_S as PPIB11,
127 TIMER10_S as TIMER10,
128 EGU10_S as EGU10,
129 RADIO_S as RADIO,
130 SPU20_S as SPU20,
131 DPPIC20_S as DPPIC20,
132 PPIB20_S as PPIB20,
133 PPIB21_S as PPIB21,
134 PPIB22_S as PPIB22,
135 SPIM20_S as SPIM20,
136 SPIS20_S as SPIS20,
137 TWIM20_S as TWIM20,
138 TWIS20_S as TWIS20,
139 UARTE20_S as UARTE20,
140 SPIM21_S as SPIM21,
141 SPIS21_S as SPIS21,
142 TWIM21_S as TWIM21,
143 TWIS21_S as TWIS21,
144 UARTE21_S as UARTE21,
145 SPIM22_S as SPIM22,
146 SPIS22_S as SPIS22,
147 TWIM22_S as TWIM22,
148 TWIS22_S as TWIS22,
149 UARTE22_S as UARTE22,
150 EGU20_S as EGU20,
151 TIMER20_S as TIMER20,
152 TIMER21_S as TIMER21,
153 TIMER22_S as TIMER22,
154 TIMER23_S as TIMER23,
155 TIMER24_S as TIMER24,
156 MEMCONF_S as MEMCONF,
157 PDM20_S as PDM20,
158 PDM21_S as PDM21,
159 PWM20_S as PWM20,
160 PWM21_S as PWM21,
161 PWM22_S as PWM22,
162 SAADC_S as SAADC,
163 NFCT_S as NFCT,
164 TEMP_S as TEMP,
165 P1_S as P1,
166 GPIOTE20_S as GPIOTE20,
167 TAMPC_S as TAMPC,
168 I2S20_S as I2S20,
169 QDEC20_S as QDEC20,
170 QDEC21_S as QDEC21,
171 GRTC_S as GRTC,
172 SPU30_S as SPU30,
173 DPPIC30_S as DPPIC30,
174 PPIB30_S as PPIB30,
175 SPIM30_S as SPIM30,
176 SPIS30_S as SPIS30,
177 TWIM30_S as TWIM30,
178 TWIS30_S as TWIS30,
179 UARTE30_S as UARTE30,
180 COMP_S as COMP,
181 LPCOMP_S as LPCOMP,
182 WDT30_S as WDT30,
183 WDT31_S as WDT31,
184 P0_S as P0,
185 GPIOTE30_S as GPIOTE30,
186 CLOCK_S as CLOCK,
187 POWER_S as POWER,
188 RESET_S as RESET,
189 OSCILLATORS_S as OSCILLATORS,
190 REGULATORS_S as REGULATORS,
191 CRACENCORE_S as CRACENCORE,
192 CPUC_S as CPUC,
193 ICACHE_S as ICACHE,
194 };
195}
196
197/// The maximum buffer size that the EasyDMA can send/recv in one operation.
198pub const EASY_DMA_SIZE: usize = (1 << 16) - 1;
199pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
200
201// 1.5 MB NVM
202#[allow(unused)]
203pub const FLASH_SIZE: usize = 1524 * 1024;
204
205embassy_hal_internal::peripherals! {
206 // PPI
207 PPI00_CH0,
208 PPI00_CH1,
209 PPI00_CH2,
210 PPI00_CH3,
211 PPI00_CH4,
212 PPI00_CH5,
213 PPI00_CH6,
214 PPI00_CH7,
215
216 PPI10_CH0,
217 PPI10_CH1,
218 PPI10_CH2,
219 PPI10_CH3,
220 PPI10_CH4,
221 PPI10_CH5,
222 PPI10_CH6,
223 PPI10_CH7,
224 PPI10_CH8,
225 PPI10_CH9,
226 PPI10_CH10,
227 PPI10_CH11,
228 PPI10_CH12,
229 PPI10_CH13,
230 PPI10_CH14,
231 PPI10_CH15,
232 PPI10_CH16,
233 PPI10_CH17,
234 PPI10_CH18,
235 PPI10_CH19,
236 PPI10_CH20,
237 PPI10_CH21,
238 PPI10_CH22,
239 PPI10_CH23,
240
241 PPI20_CH0,
242 PPI20_CH1,
243 PPI20_CH2,
244 PPI20_CH3,
245 PPI20_CH4,
246 PPI20_CH5,
247 PPI20_CH6,
248 PPI20_CH7,
249 PPI20_CH8,
250 PPI20_CH9,
251 PPI20_CH10,
252 PPI20_CH11,
253 PPI20_CH12,
254 PPI20_CH13,
255 PPI20_CH14,
256 PPI20_CH15,
257
258 PPI30_CH0,
259 PPI30_CH1,
260 PPI30_CH2,
261 PPI30_CH3,
262
263 PPI00_GROUP0,
264 PPI00_GROUP1,
265
266 PPI10_GROUP0,
267 PPI10_GROUP1,
268 PPI10_GROUP2,
269 PPI10_GROUP3,
270 PPI10_GROUP4,
271 PPI10_GROUP5,
272
273 PPI20_GROUP0,
274 PPI20_GROUP1,
275 PPI20_GROUP2,
276 PPI20_GROUP3,
277 PPI20_GROUP4,
278 PPI20_GROUP5,
279
280 PPI30_GROUP0,
281 PPI30_GROUP1,
282
283 // PPI BRIDGE channels
284 PPIB00_CH0,
285 PPIB00_CH1,
286 PPIB00_CH2,
287 PPIB00_CH3,
288 PPIB00_CH4,
289 PPIB00_CH5,
290 PPIB00_CH6,
291 PPIB00_CH7,
292
293 PPIB01_CH0,
294 PPIB01_CH1,
295 PPIB01_CH2,
296 PPIB01_CH3,
297 PPIB01_CH4,
298 PPIB01_CH5,
299 PPIB01_CH6,
300 PPIB01_CH7,
301
302 PPIB10_CH0,
303 PPIB10_CH1,
304 PPIB10_CH2,
305 PPIB10_CH3,
306 PPIB10_CH4,
307 PPIB10_CH5,
308 PPIB10_CH6,
309 PPIB10_CH7,
310
311 PPIB11_CH0,
312 PPIB11_CH1,
313 PPIB11_CH2,
314 PPIB11_CH3,
315 PPIB11_CH4,
316 PPIB11_CH5,
317 PPIB11_CH6,
318 PPIB11_CH7,
319 PPIB11_CH8,
320 PPIB11_CH9,
321 PPIB11_CH10,
322 PPIB11_CH11,
323 PPIB11_CH12,
324 PPIB11_CH13,
325 PPIB11_CH14,
326 PPIB11_CH15,
327
328 PPIB20_CH0,
329 PPIB20_CH1,
330 PPIB20_CH2,
331 PPIB20_CH3,
332 PPIB20_CH4,
333 PPIB20_CH5,
334 PPIB20_CH6,
335 PPIB20_CH7,
336
337 PPIB21_CH0,
338 PPIB21_CH1,
339 PPIB21_CH2,
340 PPIB21_CH3,
341 PPIB21_CH4,
342 PPIB21_CH5,
343 PPIB21_CH6,
344 PPIB21_CH7,
345 PPIB21_CH8,
346 PPIB21_CH9,
347 PPIB21_CH10,
348 PPIB21_CH11,
349 PPIB21_CH12,
350 PPIB21_CH13,
351 PPIB21_CH14,
352 PPIB21_CH15,
353
354 PPIB22_CH0,
355 PPIB22_CH1,
356 PPIB22_CH2,
357 PPIB22_CH3,
358
359 PPIB30_CH0,
360 PPIB30_CH1,
361 PPIB30_CH2,
362 PPIB30_CH3,
363
364 // Timers
365 TIMER00,
366 TIMER10,
367 TIMER20,
368 TIMER21,
369 TIMER22,
370 TIMER23,
371 TIMER24,
372
373 // GPIO port 0
374 P0_00,
375 P0_01,
376 P0_02,
377 P0_03,
378 P0_04,
379 P0_05,
380 P0_06,
381
382 // GPIO port 1
383 P1_00,
384 P1_01,
385 P1_02,
386 P1_03,
387 P1_04,
388 P1_05,
389 P1_06,
390 P1_07,
391 P1_08,
392 P1_09,
393 P1_10,
394 P1_11,
395 P1_12,
396 P1_13,
397 P1_14,
398 P1_15,
399 P1_16,
400
401
402 // GPIO port 2
403 P2_00,
404 P2_01,
405 P2_02,
406 P2_03,
407 P2_04,
408 P2_05,
409 P2_06,
410 P2_07,
411 P2_08,
412 P2_09,
413 P2_10,
414
415 // GRTC
416 GRTC_CH0,
417 #[cfg(not(feature = "time-driver-grtc"))]
418 GRTC_CH1,
419 GRTC_CH2,
420 GRTC_CH3,
421 GRTC_CH4,
422 GRTC_CH5,
423 GRTC_CH6,
424 GRTC_CH7,
425 GRTC_CH8,
426 GRTC_CH9,
427 GRTC_CH10,
428 GRTC_CH11,
429
430 // PWM
431 PWM20,
432 PWM21,
433 PWM22,
434
435 // SERIAL
436 SERIAL00,
437 SERIAL20,
438 SERIAL21,
439 SERIAL22,
440 SERIAL30,
441
442 // SAADC
443 SAADC,
444
445 // RADIO
446 RADIO,
447
448
449 // GPIOTE instances
450 GPIOTE20,
451 GPIOTE30,
452
453 // GPIOTE channels
454 GPIOTE20_CH0,
455 GPIOTE20_CH1,
456 GPIOTE20_CH2,
457 GPIOTE20_CH3,
458 GPIOTE20_CH4,
459 GPIOTE20_CH5,
460 GPIOTE20_CH6,
461 GPIOTE20_CH7,
462 GPIOTE30_CH0,
463 GPIOTE30_CH1,
464 GPIOTE30_CH2,
465 GPIOTE30_CH3,
466
467 // CRACEN
468 #[cfg(feature = "_s")]
469 CRACEN,
470
471 #[cfg(feature = "_s")]
472 // RRAMC
473 RRAMC,
474
475 // TEMP
476 TEMP,
477
478 // WDT
479 #[cfg(feature = "_ns")]
480 WDT,
481 #[cfg(feature = "_s")]
482 WDT0,
483 #[cfg(feature = "_s")]
484 WDT1,
485}
486
487impl_pin!(P0_00, 0, 0);
488impl_pin!(P0_01, 0, 1);
489impl_pin!(P0_02, 0, 2);
490impl_pin!(P0_03, 0, 3);
491impl_pin!(P0_04, 0, 4);
492impl_pin!(P0_05, 0, 5);
493impl_pin!(P0_06, 0, 6);
494
495impl_pin!(P1_00, 1, 0);
496impl_pin!(P1_01, 1, 1);
497impl_pin!(P1_02, 1, 2);
498impl_pin!(P1_03, 1, 3);
499impl_pin!(P1_04, 1, 4);
500impl_pin!(P1_05, 1, 5);
501impl_pin!(P1_06, 1, 6);
502impl_pin!(P1_07, 1, 7);
503impl_pin!(P1_08, 1, 8);
504impl_pin!(P1_09, 1, 9);
505impl_pin!(P1_10, 1, 10);
506impl_pin!(P1_11, 1, 11);
507impl_pin!(P1_12, 1, 12);
508impl_pin!(P1_13, 1, 13);
509impl_pin!(P1_14, 1, 14);
510impl_pin!(P1_15, 1, 15);
511impl_pin!(P1_16, 1, 16);
512
513impl_pin!(P2_00, 2, 0);
514impl_pin!(P2_01, 2, 1);
515impl_pin!(P2_02, 2, 2);
516impl_pin!(P2_03, 2, 3);
517impl_pin!(P2_04, 2, 4);
518impl_pin!(P2_05, 2, 5);
519impl_pin!(P2_06, 2, 6);
520impl_pin!(P2_07, 2, 7);
521impl_pin!(P2_08, 2, 8);
522impl_pin!(P2_09, 2, 9);
523impl_pin!(P2_10, 2, 10);
524
525cfg_if::cfg_if! {
526 if #[cfg(feature = "gpiote")] {
527 impl_gpiote_pin!(P0_00, GPIOTE30);
528 impl_gpiote_pin!(P0_01, GPIOTE30);
529 impl_gpiote_pin!(P0_02, GPIOTE30);
530 impl_gpiote_pin!(P0_03, GPIOTE30);
531 impl_gpiote_pin!(P0_04, GPIOTE30);
532 impl_gpiote_pin!(P0_05, GPIOTE30);
533 impl_gpiote_pin!(P0_06, GPIOTE30);
534
535 impl_gpiote_pin!(P1_00, GPIOTE20);
536 impl_gpiote_pin!(P1_01, GPIOTE20);
537 impl_gpiote_pin!(P1_02, GPIOTE20);
538 impl_gpiote_pin!(P1_03, GPIOTE20);
539 impl_gpiote_pin!(P1_04, GPIOTE20);
540 impl_gpiote_pin!(P1_05, GPIOTE20);
541 impl_gpiote_pin!(P1_06, GPIOTE20);
542 impl_gpiote_pin!(P1_07, GPIOTE20);
543 impl_gpiote_pin!(P1_08, GPIOTE20);
544 impl_gpiote_pin!(P1_09, GPIOTE20);
545 impl_gpiote_pin!(P1_10, GPIOTE20);
546 impl_gpiote_pin!(P1_11, GPIOTE20);
547 impl_gpiote_pin!(P1_12, GPIOTE20);
548 impl_gpiote_pin!(P1_13, GPIOTE20);
549 impl_gpiote_pin!(P1_14, GPIOTE20);
550 impl_gpiote_pin!(P1_15, GPIOTE20);
551 impl_gpiote_pin!(P1_16, GPIOTE20);
552 }
553}
554
555#[cfg(feature = "_ns")]
556impl_wdt!(WDT, WDT31, WDT31, 0);
557#[cfg(feature = "_s")]
558impl_wdt!(WDT0, WDT31, WDT31, 0);
559#[cfg(feature = "_s")]
560impl_wdt!(WDT1, WDT30, WDT30, 1);
561// DPPI00 channels
562impl_ppi_channel!(PPI00_CH0, DPPIC00, 0 => configurable);
563impl_ppi_channel!(PPI00_CH1, DPPIC00, 1 => configurable);
564impl_ppi_channel!(PPI00_CH2, DPPIC00, 2 => configurable);
565impl_ppi_channel!(PPI00_CH3, DPPIC00, 3 => configurable);
566impl_ppi_channel!(PPI00_CH4, DPPIC00, 4 => configurable);
567impl_ppi_channel!(PPI00_CH5, DPPIC00, 5 => configurable);
568impl_ppi_channel!(PPI00_CH6, DPPIC00, 6 => configurable);
569impl_ppi_channel!(PPI00_CH7, DPPIC00, 7 => configurable);
570
571// DPPI10 channels
572impl_ppi_channel!(PPI10_CH0, DPPIC10, 0 => static);
573
574// DPPI20 channels
575impl_ppi_channel!(PPI20_CH0, DPPIC20, 0 => configurable);
576impl_ppi_channel!(PPI20_CH1, DPPIC20, 1 => configurable);
577impl_ppi_channel!(PPI20_CH2, DPPIC20, 2 => configurable);
578impl_ppi_channel!(PPI20_CH3, DPPIC20, 3 => configurable);
579impl_ppi_channel!(PPI20_CH4, DPPIC20, 4 => configurable);
580impl_ppi_channel!(PPI20_CH5, DPPIC20, 5 => configurable);
581impl_ppi_channel!(PPI20_CH6, DPPIC20, 6 => configurable);
582impl_ppi_channel!(PPI20_CH7, DPPIC20, 7 => configurable);
583impl_ppi_channel!(PPI20_CH8, DPPIC20, 8 => configurable);
584impl_ppi_channel!(PPI20_CH9, DPPIC20, 9 => configurable);
585impl_ppi_channel!(PPI20_CH10, DPPIC20, 10 => configurable);
586impl_ppi_channel!(PPI20_CH11, DPPIC20, 11 => configurable);
587impl_ppi_channel!(PPI20_CH12, DPPIC20, 12 => configurable);
588impl_ppi_channel!(PPI20_CH13, DPPIC20, 13 => configurable);
589impl_ppi_channel!(PPI20_CH14, DPPIC20, 14 => configurable);
590impl_ppi_channel!(PPI20_CH15, DPPIC20, 15 => configurable);
591
592// DPPI30 channels
593impl_ppi_channel!(PPI30_CH0, DPPIC30, 0 => configurable);
594impl_ppi_channel!(PPI30_CH1, DPPIC30, 1 => configurable);
595impl_ppi_channel!(PPI30_CH2, DPPIC30, 2 => configurable);
596impl_ppi_channel!(PPI30_CH3, DPPIC30, 3 => configurable);
597
598// DPPI00 groups
599impl_ppi_group!(PPI00_GROUP0, DPPIC00, 0);
600impl_ppi_group!(PPI00_GROUP1, DPPIC00, 1);
601
602// DPPI10 groups
603impl_ppi_group!(PPI10_GROUP0, DPPIC10, 0);
604
605// DPPI20 groups
606impl_ppi_group!(PPI20_GROUP0, DPPIC20, 0);
607impl_ppi_group!(PPI20_GROUP1, DPPIC20, 1);
608impl_ppi_group!(PPI20_GROUP2, DPPIC20, 2);
609impl_ppi_group!(PPI20_GROUP3, DPPIC20, 3);
610impl_ppi_group!(PPI20_GROUP4, DPPIC20, 4);
611impl_ppi_group!(PPI20_GROUP5, DPPIC20, 5);
612
613// DPPI30 groups
614impl_ppi_group!(PPI30_GROUP0, DPPIC30, 0);
615impl_ppi_group!(PPI30_GROUP1, DPPIC30, 1);
616
617impl_timer!(TIMER00, TIMER00, TIMER00);
618impl_timer!(TIMER10, TIMER10, TIMER10);
619impl_timer!(TIMER20, TIMER20, TIMER20);
620impl_timer!(TIMER21, TIMER21, TIMER21);
621impl_timer!(TIMER22, TIMER22, TIMER22);
622impl_timer!(TIMER23, TIMER23, TIMER23);
623impl_timer!(TIMER24, TIMER24, TIMER24);
624
625impl_twim!(SERIAL20, TWIM20, SERIAL20);
626impl_twim!(SERIAL21, TWIM21, SERIAL21);
627impl_twim!(SERIAL22, TWIM22, SERIAL22);
628impl_twim!(SERIAL30, TWIM30, SERIAL30);
629
630impl_twis!(SERIAL20, TWIS20, SERIAL20);
631impl_twis!(SERIAL21, TWIS21, SERIAL21);
632impl_twis!(SERIAL22, TWIS22, SERIAL22);
633impl_twis!(SERIAL30, TWIS30, SERIAL30);
634
635impl_pwm!(PWM20, PWM20, PWM20);
636impl_pwm!(PWM21, PWM21, PWM21);
637impl_pwm!(PWM22, PWM22, PWM22);
638
639#[cfg(feature = "_s")]
640impl_spim!(
641 SERIAL00,
642 SPIM00,
643 SERIAL00,
644 match pac::OSCILLATORS_S.pll().currentfreq().read().currentfreq() {
645 pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,
646 pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,
647 _ => unreachable!(),
648 }
649);
650#[cfg(feature = "_ns")]
651impl_spim!(
652 SERIAL00,
653 SPIM00,
654 SERIAL00,
655 match pac::OSCILLATORS_NS.pll().currentfreq().read().currentfreq() {
656 pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,
657 pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,
658 _ => unreachable!(),
659 }
660);
661impl_spim!(SERIAL20, SPIM20, SERIAL20, 16_000_000);
662impl_spim!(SERIAL21, SPIM21, SERIAL21, 16_000_000);
663impl_spim!(SERIAL22, SPIM22, SERIAL22, 16_000_000);
664impl_spim!(SERIAL30, SPIM30, SERIAL30, 16_000_000);
665
666impl_spis!(SERIAL20, SPIS20, SERIAL20);
667impl_spis!(SERIAL21, SPIS21, SERIAL21);
668impl_spis!(SERIAL22, SPIS22, SERIAL22);
669impl_spis!(SERIAL30, SPIS30, SERIAL30);
670
671impl_uarte!(SERIAL00, UARTE00, SERIAL00);
672impl_uarte!(SERIAL20, UARTE20, SERIAL20);
673impl_uarte!(SERIAL21, UARTE21, SERIAL21);
674impl_uarte!(SERIAL22, UARTE22, SERIAL22);
675impl_uarte!(SERIAL30, UARTE30, SERIAL30);
676
677// NB: SAADC uses "pin" abstraction, not "AIN"
678impl_saadc_input!(P1_04, 1, 4);
679impl_saadc_input!(P1_05, 1, 5);
680impl_saadc_input!(P1_06, 1, 6);
681impl_saadc_input!(P1_07, 1, 7);
682impl_saadc_input!(P1_11, 1, 11);
683impl_saadc_input!(P1_12, 1, 12);
684impl_saadc_input!(P1_13, 1, 13);
685impl_saadc_input!(P1_14, 1, 14);
686
687#[cfg(feature = "_s")]
688impl_cracen!(CRACEN, CRACEN, CRACEN);
689
690embassy_hal_internal::interrupt_mod!(
691 SWI00,
692 SWI01,
693 SWI02,
694 SWI03,
695 SPU00,
696 MPC00,
697 AAR00_CCM00,
698 ECB00,
699 CRACEN,
700 SERIAL00,
701 RRAMC,
702 VPR00,
703 CTRLAP,
704 TIMER00,
705 SPU10,
706 TIMER10,
707 EGU10,
708 RADIO_0,
709 RADIO_1,
710 SPU20,
711 SERIAL20,
712 SERIAL21,
713 SERIAL22,
714 EGU20,
715 TIMER20,
716 TIMER21,
717 TIMER22,
718 TIMER23,
719 TIMER24,
720 PDM20,
721 PDM21,
722 PWM20,
723 PWM21,
724 PWM22,
725 SAADC,
726 NFCT,
727 TEMP,
728 GPIOTE20_0,
729 GPIOTE20_1,
730 TAMPC,
731 I2S20,
732 QDEC20,
733 QDEC21,
734 GRTC_0,
735 GRTC_1,
736 GRTC_2,
737 GRTC_3,
738 SPU30,
739 SERIAL30,
740 COMP_LPCOMP,
741 WDT30,
742 WDT31,
743 GPIOTE30_0,
744 GPIOTE30_1,
745 CLOCK_POWER,
746);
diff --git a/embassy-nrf/src/chips/nrf54l10_app.rs b/embassy-nrf/src/chips/nrf54l10_app.rs
new file mode 100644
index 000000000..8e6595248
--- /dev/null
+++ b/embassy-nrf/src/chips/nrf54l10_app.rs
@@ -0,0 +1,746 @@
1/// Peripheral Access Crate
2#[allow(unused_imports)]
3#[rustfmt::skip]
4pub mod pac {
5 pub use nrf_pac::*;
6
7 #[cfg(feature = "_ns")]
8 #[doc(no_inline)]
9 pub use nrf_pac::{
10 FICR_NS as FICR,
11 DPPIC00_NS as DPPIC00,
12 PPIB00_NS as PPIB00,
13 PPIB01_NS as PPIB01,
14 AAR00_NS as AAR00,
15 CCM00_NS as CCM00,
16 ECB00_NS as ECB00,
17 SPIM00_NS as SPIM00,
18 SPIS00_NS as SPIS00,
19 UARTE00_NS as UARTE00,
20 VPR00_NS as VPR00,
21 P2_NS as P2,
22 CTRLAP_NS as CTRLAP,
23 TAD_NS as TAD,
24 TIMER00_NS as TIMER00,
25 DPPIC10_NS as DPPIC10,
26 PPIB10_NS as PPIB10,
27 PPIB11_NS as PPIB11,
28 TIMER10_NS as TIMER10,
29 EGU10_NS as EGU10,
30 RADIO_NS as RADIO,
31 DPPIC20_NS as DPPIC20,
32 PPIB20_NS as PPIB20,
33 PPIB21_NS as PPIB21,
34 PPIB22_NS as PPIB22,
35 SPIM20_NS as SPIM20,
36 SPIS20_NS as SPIS20,
37 TWIM20_NS as TWIM20,
38 TWIS20_NS as TWIS20,
39 UARTE20_NS as UARTE20,
40 SPIM21_NS as SPIM21,
41 SPIS21_NS as SPIS21,
42 TWIM21_NS as TWIM21,
43 TWIS21_NS as TWIS21,
44 UARTE21_NS as UARTE21,
45 SPIM22_NS as SPIM22,
46 SPIS22_NS as SPIS22,
47 TWIM22_NS as TWIM22,
48 TWIS22_NS as TWIS22,
49 UARTE22_NS as UARTE22,
50 EGU20_NS as EGU20,
51 TIMER20_NS as TIMER20,
52 TIMER21_NS as TIMER21,
53 TIMER22_NS as TIMER22,
54 TIMER23_NS as TIMER23,
55 TIMER24_NS as TIMER24,
56 MEMCONF_NS as MEMCONF,
57 PDM20_NS as PDM20,
58 PDM21_NS as PDM21,
59 PWM20_NS as PWM20,
60 PWM21_NS as PWM21,
61 PWM22_NS as PWM22,
62 SAADC_NS as SAADC,
63 NFCT_NS as NFCT,
64 TEMP_NS as TEMP,
65 P1_NS as P1,
66 GPIOTE20_NS as GPIOTE20,
67 I2S20_NS as I2S20,
68 QDEC20_NS as QDEC20,
69 QDEC21_NS as QDEC21,
70 GRTC_NS as GRTC,
71 DPPIC30_NS as DPPIC30,
72 PPIB30_NS as PPIB30,
73 SPIM30_NS as SPIM30,
74 SPIS30_NS as SPIS30,
75 TWIM30_NS as TWIM30,
76 TWIS30_NS as TWIS30,
77 UARTE30_NS as UARTE30,
78 COMP_NS as COMP,
79 LPCOMP_NS as LPCOMP,
80 WDT31_NS as WDT31,
81 P0_NS as P0,
82 GPIOTE30_NS as GPIOTE30,
83 CLOCK_NS as CLOCK,
84 POWER_NS as POWER,
85 RESET_NS as RESET,
86 OSCILLATORS_NS as OSCILLATORS,
87 REGULATORS_NS as REGULATORS,
88 TPIU_NS as TPIU,
89 ETM_NS as ETM,
90 };
91
92 #[cfg(feature = "_s")]
93 #[doc(no_inline)]
94 pub use nrf_pac::{
95 FICR_NS as FICR,
96 SICR_S as SICR,
97 ICACHEDATA_S as ICACHEDATA,
98 ICACHEINFO_S as ICACHEINFO,
99 SWI00_S as SWI00,
100 SWI01_S as SWI01,
101 SWI02_S as SWI02,
102 SWI03_S as SWI03,
103 SPU00_S as SPU00,
104 MPC00_S as MPC00,
105 DPPIC00_S as DPPIC00,
106 PPIB00_S as PPIB00,
107 PPIB01_S as PPIB01,
108 KMU_S as KMU,
109 AAR00_S as AAR00,
110 CCM00_S as CCM00,
111 ECB00_S as ECB00,
112 CRACEN_S as CRACEN,
113 SPIM00_S as SPIM00,
114 SPIS00_S as SPIS00,
115 UARTE00_S as UARTE00,
116 GLITCHDET_S as GLITCHDET,
117 RRAMC_S as RRAMC,
118 VPR00_S as VPR00,
119 P2_S as P2,
120 CTRLAP_S as CTRLAP,
121 TAD_S as TAD,
122 TIMER00_S as TIMER00,
123 SPU10_S as SPU10,
124 DPPIC10_S as DPPIC10,
125 PPIB10_S as PPIB10,
126 PPIB11_S as PPIB11,
127 TIMER10_S as TIMER10,
128 EGU10_S as EGU10,
129 RADIO_S as RADIO,
130 SPU20_S as SPU20,
131 DPPIC20_S as DPPIC20,
132 PPIB20_S as PPIB20,
133 PPIB21_S as PPIB21,
134 PPIB22_S as PPIB22,
135 SPIM20_S as SPIM20,
136 SPIS20_S as SPIS20,
137 TWIM20_S as TWIM20,
138 TWIS20_S as TWIS20,
139 UARTE20_S as UARTE20,
140 SPIM21_S as SPIM21,
141 SPIS21_S as SPIS21,
142 TWIM21_S as TWIM21,
143 TWIS21_S as TWIS21,
144 UARTE21_S as UARTE21,
145 SPIM22_S as SPIM22,
146 SPIS22_S as SPIS22,
147 TWIM22_S as TWIM22,
148 TWIS22_S as TWIS22,
149 UARTE22_S as UARTE22,
150 EGU20_S as EGU20,
151 TIMER20_S as TIMER20,
152 TIMER21_S as TIMER21,
153 TIMER22_S as TIMER22,
154 TIMER23_S as TIMER23,
155 TIMER24_S as TIMER24,
156 MEMCONF_S as MEMCONF,
157 PDM20_S as PDM20,
158 PDM21_S as PDM21,
159 PWM20_S as PWM20,
160 PWM21_S as PWM21,
161 PWM22_S as PWM22,
162 SAADC_S as SAADC,
163 NFCT_S as NFCT,
164 TEMP_S as TEMP,
165 P1_S as P1,
166 GPIOTE20_S as GPIOTE20,
167 TAMPC_S as TAMPC,
168 I2S20_S as I2S20,
169 QDEC20_S as QDEC20,
170 QDEC21_S as QDEC21,
171 GRTC_S as GRTC,
172 SPU30_S as SPU30,
173 DPPIC30_S as DPPIC30,
174 PPIB30_S as PPIB30,
175 SPIM30_S as SPIM30,
176 SPIS30_S as SPIS30,
177 TWIM30_S as TWIM30,
178 TWIS30_S as TWIS30,
179 UARTE30_S as UARTE30,
180 COMP_S as COMP,
181 LPCOMP_S as LPCOMP,
182 WDT30_S as WDT30,
183 WDT31_S as WDT31,
184 P0_S as P0,
185 GPIOTE30_S as GPIOTE30,
186 CLOCK_S as CLOCK,
187 POWER_S as POWER,
188 RESET_S as RESET,
189 OSCILLATORS_S as OSCILLATORS,
190 REGULATORS_S as REGULATORS,
191 CRACENCORE_S as CRACENCORE,
192 CPUC_S as CPUC,
193 ICACHE_S as ICACHE,
194 };
195}
196
197/// The maximum buffer size that the EasyDMA can send/recv in one operation.
198pub const EASY_DMA_SIZE: usize = (1 << 16) - 1;
199pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
200
201// 1.5 MB NVM
202#[allow(unused)]
203pub const FLASH_SIZE: usize = 1524 * 1024;
204
205embassy_hal_internal::peripherals! {
206 // PPI
207 PPI00_CH0,
208 PPI00_CH1,
209 PPI00_CH2,
210 PPI00_CH3,
211 PPI00_CH4,
212 PPI00_CH5,
213 PPI00_CH6,
214 PPI00_CH7,
215
216 PPI10_CH0,
217 PPI10_CH1,
218 PPI10_CH2,
219 PPI10_CH3,
220 PPI10_CH4,
221 PPI10_CH5,
222 PPI10_CH6,
223 PPI10_CH7,
224 PPI10_CH8,
225 PPI10_CH9,
226 PPI10_CH10,
227 PPI10_CH11,
228 PPI10_CH12,
229 PPI10_CH13,
230 PPI10_CH14,
231 PPI10_CH15,
232 PPI10_CH16,
233 PPI10_CH17,
234 PPI10_CH18,
235 PPI10_CH19,
236 PPI10_CH20,
237 PPI10_CH21,
238 PPI10_CH22,
239 PPI10_CH23,
240
241 PPI20_CH0,
242 PPI20_CH1,
243 PPI20_CH2,
244 PPI20_CH3,
245 PPI20_CH4,
246 PPI20_CH5,
247 PPI20_CH6,
248 PPI20_CH7,
249 PPI20_CH8,
250 PPI20_CH9,
251 PPI20_CH10,
252 PPI20_CH11,
253 PPI20_CH12,
254 PPI20_CH13,
255 PPI20_CH14,
256 PPI20_CH15,
257
258 PPI30_CH0,
259 PPI30_CH1,
260 PPI30_CH2,
261 PPI30_CH3,
262
263 PPI00_GROUP0,
264 PPI00_GROUP1,
265
266 PPI10_GROUP0,
267 PPI10_GROUP1,
268 PPI10_GROUP2,
269 PPI10_GROUP3,
270 PPI10_GROUP4,
271 PPI10_GROUP5,
272
273 PPI20_GROUP0,
274 PPI20_GROUP1,
275 PPI20_GROUP2,
276 PPI20_GROUP3,
277 PPI20_GROUP4,
278 PPI20_GROUP5,
279
280 PPI30_GROUP0,
281 PPI30_GROUP1,
282
283 // PPI BRIDGE channels
284 PPIB00_CH0,
285 PPIB00_CH1,
286 PPIB00_CH2,
287 PPIB00_CH3,
288 PPIB00_CH4,
289 PPIB00_CH5,
290 PPIB00_CH6,
291 PPIB00_CH7,
292
293 PPIB01_CH0,
294 PPIB01_CH1,
295 PPIB01_CH2,
296 PPIB01_CH3,
297 PPIB01_CH4,
298 PPIB01_CH5,
299 PPIB01_CH6,
300 PPIB01_CH7,
301
302 PPIB10_CH0,
303 PPIB10_CH1,
304 PPIB10_CH2,
305 PPIB10_CH3,
306 PPIB10_CH4,
307 PPIB10_CH5,
308 PPIB10_CH6,
309 PPIB10_CH7,
310
311 PPIB11_CH0,
312 PPIB11_CH1,
313 PPIB11_CH2,
314 PPIB11_CH3,
315 PPIB11_CH4,
316 PPIB11_CH5,
317 PPIB11_CH6,
318 PPIB11_CH7,
319 PPIB11_CH8,
320 PPIB11_CH9,
321 PPIB11_CH10,
322 PPIB11_CH11,
323 PPIB11_CH12,
324 PPIB11_CH13,
325 PPIB11_CH14,
326 PPIB11_CH15,
327
328 PPIB20_CH0,
329 PPIB20_CH1,
330 PPIB20_CH2,
331 PPIB20_CH3,
332 PPIB20_CH4,
333 PPIB20_CH5,
334 PPIB20_CH6,
335 PPIB20_CH7,
336
337 PPIB21_CH0,
338 PPIB21_CH1,
339 PPIB21_CH2,
340 PPIB21_CH3,
341 PPIB21_CH4,
342 PPIB21_CH5,
343 PPIB21_CH6,
344 PPIB21_CH7,
345 PPIB21_CH8,
346 PPIB21_CH9,
347 PPIB21_CH10,
348 PPIB21_CH11,
349 PPIB21_CH12,
350 PPIB21_CH13,
351 PPIB21_CH14,
352 PPIB21_CH15,
353
354 PPIB22_CH0,
355 PPIB22_CH1,
356 PPIB22_CH2,
357 PPIB22_CH3,
358
359 PPIB30_CH0,
360 PPIB30_CH1,
361 PPIB30_CH2,
362 PPIB30_CH3,
363
364 // Timers
365 TIMER00,
366 TIMER10,
367 TIMER20,
368 TIMER21,
369 TIMER22,
370 TIMER23,
371 TIMER24,
372
373 // GPIO port 0
374 P0_00,
375 P0_01,
376 P0_02,
377 P0_03,
378 P0_04,
379 P0_05,
380 P0_06,
381
382 // GPIO port 1
383 P1_00,
384 P1_01,
385 P1_02,
386 P1_03,
387 P1_04,
388 P1_05,
389 P1_06,
390 P1_07,
391 P1_08,
392 P1_09,
393 P1_10,
394 P1_11,
395 P1_12,
396 P1_13,
397 P1_14,
398 P1_15,
399 P1_16,
400
401
402 // GPIO port 2
403 P2_00,
404 P2_01,
405 P2_02,
406 P2_03,
407 P2_04,
408 P2_05,
409 P2_06,
410 P2_07,
411 P2_08,
412 P2_09,
413 P2_10,
414
415 // GRTC
416 GRTC_CH0,
417 #[cfg(not(feature = "time-driver-grtc"))]
418 GRTC_CH1,
419 GRTC_CH2,
420 GRTC_CH3,
421 GRTC_CH4,
422 GRTC_CH5,
423 GRTC_CH6,
424 GRTC_CH7,
425 GRTC_CH8,
426 GRTC_CH9,
427 GRTC_CH10,
428 GRTC_CH11,
429
430 // PWM
431 PWM20,
432 PWM21,
433 PWM22,
434
435 // SERIAL
436 SERIAL00,
437 SERIAL20,
438 SERIAL21,
439 SERIAL22,
440 SERIAL30,
441
442 // SAADC
443 SAADC,
444
445 // RADIO
446 RADIO,
447
448
449 // GPIOTE instances
450 GPIOTE20,
451 GPIOTE30,
452
453 // GPIOTE channels
454 GPIOTE20_CH0,
455 GPIOTE20_CH1,
456 GPIOTE20_CH2,
457 GPIOTE20_CH3,
458 GPIOTE20_CH4,
459 GPIOTE20_CH5,
460 GPIOTE20_CH6,
461 GPIOTE20_CH7,
462 GPIOTE30_CH0,
463 GPIOTE30_CH1,
464 GPIOTE30_CH2,
465 GPIOTE30_CH3,
466
467 // CRACEN
468 #[cfg(feature = "_s")]
469 CRACEN,
470
471 #[cfg(feature = "_s")]
472 // RRAMC
473 RRAMC,
474
475 // TEMP
476 TEMP,
477
478 // WDT
479 #[cfg(feature = "_ns")]
480 WDT,
481 #[cfg(feature = "_s")]
482 WDT0,
483 #[cfg(feature = "_s")]
484 WDT1,
485}
486
487impl_pin!(P0_00, 0, 0);
488impl_pin!(P0_01, 0, 1);
489impl_pin!(P0_02, 0, 2);
490impl_pin!(P0_03, 0, 3);
491impl_pin!(P0_04, 0, 4);
492impl_pin!(P0_05, 0, 5);
493impl_pin!(P0_06, 0, 6);
494
495impl_pin!(P1_00, 1, 0);
496impl_pin!(P1_01, 1, 1);
497impl_pin!(P1_02, 1, 2);
498impl_pin!(P1_03, 1, 3);
499impl_pin!(P1_04, 1, 4);
500impl_pin!(P1_05, 1, 5);
501impl_pin!(P1_06, 1, 6);
502impl_pin!(P1_07, 1, 7);
503impl_pin!(P1_08, 1, 8);
504impl_pin!(P1_09, 1, 9);
505impl_pin!(P1_10, 1, 10);
506impl_pin!(P1_11, 1, 11);
507impl_pin!(P1_12, 1, 12);
508impl_pin!(P1_13, 1, 13);
509impl_pin!(P1_14, 1, 14);
510impl_pin!(P1_15, 1, 15);
511impl_pin!(P1_16, 1, 16);
512
513impl_pin!(P2_00, 2, 0);
514impl_pin!(P2_01, 2, 1);
515impl_pin!(P2_02, 2, 2);
516impl_pin!(P2_03, 2, 3);
517impl_pin!(P2_04, 2, 4);
518impl_pin!(P2_05, 2, 5);
519impl_pin!(P2_06, 2, 6);
520impl_pin!(P2_07, 2, 7);
521impl_pin!(P2_08, 2, 8);
522impl_pin!(P2_09, 2, 9);
523impl_pin!(P2_10, 2, 10);
524
525cfg_if::cfg_if! {
526 if #[cfg(feature = "gpiote")] {
527 impl_gpiote_pin!(P0_00, GPIOTE30);
528 impl_gpiote_pin!(P0_01, GPIOTE30);
529 impl_gpiote_pin!(P0_02, GPIOTE30);
530 impl_gpiote_pin!(P0_03, GPIOTE30);
531 impl_gpiote_pin!(P0_04, GPIOTE30);
532 impl_gpiote_pin!(P0_05, GPIOTE30);
533 impl_gpiote_pin!(P0_06, GPIOTE30);
534
535 impl_gpiote_pin!(P1_00, GPIOTE20);
536 impl_gpiote_pin!(P1_01, GPIOTE20);
537 impl_gpiote_pin!(P1_02, GPIOTE20);
538 impl_gpiote_pin!(P1_03, GPIOTE20);
539 impl_gpiote_pin!(P1_04, GPIOTE20);
540 impl_gpiote_pin!(P1_05, GPIOTE20);
541 impl_gpiote_pin!(P1_06, GPIOTE20);
542 impl_gpiote_pin!(P1_07, GPIOTE20);
543 impl_gpiote_pin!(P1_08, GPIOTE20);
544 impl_gpiote_pin!(P1_09, GPIOTE20);
545 impl_gpiote_pin!(P1_10, GPIOTE20);
546 impl_gpiote_pin!(P1_11, GPIOTE20);
547 impl_gpiote_pin!(P1_12, GPIOTE20);
548 impl_gpiote_pin!(P1_13, GPIOTE20);
549 impl_gpiote_pin!(P1_14, GPIOTE20);
550 impl_gpiote_pin!(P1_15, GPIOTE20);
551 impl_gpiote_pin!(P1_16, GPIOTE20);
552 }
553}
554
555#[cfg(feature = "_ns")]
556impl_wdt!(WDT, WDT31, WDT31, 0);
557#[cfg(feature = "_s")]
558impl_wdt!(WDT0, WDT31, WDT31, 0);
559#[cfg(feature = "_s")]
560impl_wdt!(WDT1, WDT30, WDT30, 1);
561// DPPI00 channels
562impl_ppi_channel!(PPI00_CH0, DPPIC00, 0 => configurable);
563impl_ppi_channel!(PPI00_CH1, DPPIC00, 1 => configurable);
564impl_ppi_channel!(PPI00_CH2, DPPIC00, 2 => configurable);
565impl_ppi_channel!(PPI00_CH3, DPPIC00, 3 => configurable);
566impl_ppi_channel!(PPI00_CH4, DPPIC00, 4 => configurable);
567impl_ppi_channel!(PPI00_CH5, DPPIC00, 5 => configurable);
568impl_ppi_channel!(PPI00_CH6, DPPIC00, 6 => configurable);
569impl_ppi_channel!(PPI00_CH7, DPPIC00, 7 => configurable);
570
571// DPPI10 channels
572impl_ppi_channel!(PPI10_CH0, DPPIC10, 0 => static);
573
574// DPPI20 channels
575impl_ppi_channel!(PPI20_CH0, DPPIC20, 0 => configurable);
576impl_ppi_channel!(PPI20_CH1, DPPIC20, 1 => configurable);
577impl_ppi_channel!(PPI20_CH2, DPPIC20, 2 => configurable);
578impl_ppi_channel!(PPI20_CH3, DPPIC20, 3 => configurable);
579impl_ppi_channel!(PPI20_CH4, DPPIC20, 4 => configurable);
580impl_ppi_channel!(PPI20_CH5, DPPIC20, 5 => configurable);
581impl_ppi_channel!(PPI20_CH6, DPPIC20, 6 => configurable);
582impl_ppi_channel!(PPI20_CH7, DPPIC20, 7 => configurable);
583impl_ppi_channel!(PPI20_CH8, DPPIC20, 8 => configurable);
584impl_ppi_channel!(PPI20_CH9, DPPIC20, 9 => configurable);
585impl_ppi_channel!(PPI20_CH10, DPPIC20, 10 => configurable);
586impl_ppi_channel!(PPI20_CH11, DPPIC20, 11 => configurable);
587impl_ppi_channel!(PPI20_CH12, DPPIC20, 12 => configurable);
588impl_ppi_channel!(PPI20_CH13, DPPIC20, 13 => configurable);
589impl_ppi_channel!(PPI20_CH14, DPPIC20, 14 => configurable);
590impl_ppi_channel!(PPI20_CH15, DPPIC20, 15 => configurable);
591
592// DPPI30 channels
593impl_ppi_channel!(PPI30_CH0, DPPIC30, 0 => configurable);
594impl_ppi_channel!(PPI30_CH1, DPPIC30, 1 => configurable);
595impl_ppi_channel!(PPI30_CH2, DPPIC30, 2 => configurable);
596impl_ppi_channel!(PPI30_CH3, DPPIC30, 3 => configurable);
597
598// DPPI00 groups
599impl_ppi_group!(PPI00_GROUP0, DPPIC00, 0);
600impl_ppi_group!(PPI00_GROUP1, DPPIC00, 1);
601
602// DPPI10 groups
603impl_ppi_group!(PPI10_GROUP0, DPPIC10, 0);
604
605// DPPI20 groups
606impl_ppi_group!(PPI20_GROUP0, DPPIC20, 0);
607impl_ppi_group!(PPI20_GROUP1, DPPIC20, 1);
608impl_ppi_group!(PPI20_GROUP2, DPPIC20, 2);
609impl_ppi_group!(PPI20_GROUP3, DPPIC20, 3);
610impl_ppi_group!(PPI20_GROUP4, DPPIC20, 4);
611impl_ppi_group!(PPI20_GROUP5, DPPIC20, 5);
612
613// DPPI30 groups
614impl_ppi_group!(PPI30_GROUP0, DPPIC30, 0);
615impl_ppi_group!(PPI30_GROUP1, DPPIC30, 1);
616
617impl_timer!(TIMER00, TIMER00, TIMER00);
618impl_timer!(TIMER10, TIMER10, TIMER10);
619impl_timer!(TIMER20, TIMER20, TIMER20);
620impl_timer!(TIMER21, TIMER21, TIMER21);
621impl_timer!(TIMER22, TIMER22, TIMER22);
622impl_timer!(TIMER23, TIMER23, TIMER23);
623impl_timer!(TIMER24, TIMER24, TIMER24);
624
625impl_twim!(SERIAL20, TWIM20, SERIAL20);
626impl_twim!(SERIAL21, TWIM21, SERIAL21);
627impl_twim!(SERIAL22, TWIM22, SERIAL22);
628impl_twim!(SERIAL30, TWIM30, SERIAL30);
629
630impl_twis!(SERIAL20, TWIS20, SERIAL20);
631impl_twis!(SERIAL21, TWIS21, SERIAL21);
632impl_twis!(SERIAL22, TWIS22, SERIAL22);
633impl_twis!(SERIAL30, TWIS30, SERIAL30);
634
635impl_pwm!(PWM20, PWM20, PWM20);
636impl_pwm!(PWM21, PWM21, PWM21);
637impl_pwm!(PWM22, PWM22, PWM22);
638
639#[cfg(feature = "_s")]
640impl_spim!(
641 SERIAL00,
642 SPIM00,
643 SERIAL00,
644 match pac::OSCILLATORS_S.pll().currentfreq().read().currentfreq() {
645 pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,
646 pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,
647 _ => unreachable!(),
648 }
649);
650#[cfg(feature = "_ns")]
651impl_spim!(
652 SERIAL00,
653 SPIM00,
654 SERIAL00,
655 match pac::OSCILLATORS_NS.pll().currentfreq().read().currentfreq() {
656 pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,
657 pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,
658 _ => unreachable!(),
659 }
660);
661impl_spim!(SERIAL20, SPIM20, SERIAL20, 16_000_000);
662impl_spim!(SERIAL21, SPIM21, SERIAL21, 16_000_000);
663impl_spim!(SERIAL22, SPIM22, SERIAL22, 16_000_000);
664impl_spim!(SERIAL30, SPIM30, SERIAL30, 16_000_000);
665
666impl_spis!(SERIAL20, SPIS20, SERIAL20);
667impl_spis!(SERIAL21, SPIS21, SERIAL21);
668impl_spis!(SERIAL22, SPIS22, SERIAL22);
669impl_spis!(SERIAL30, SPIS30, SERIAL30);
670
671impl_uarte!(SERIAL00, UARTE00, SERIAL00);
672impl_uarte!(SERIAL20, UARTE20, SERIAL20);
673impl_uarte!(SERIAL21, UARTE21, SERIAL21);
674impl_uarte!(SERIAL22, UARTE22, SERIAL22);
675impl_uarte!(SERIAL30, UARTE30, SERIAL30);
676
677// NB: SAADC uses "pin" abstraction, not "AIN"
678impl_saadc_input!(P1_04, 1, 4);
679impl_saadc_input!(P1_05, 1, 5);
680impl_saadc_input!(P1_06, 1, 6);
681impl_saadc_input!(P1_07, 1, 7);
682impl_saadc_input!(P1_11, 1, 11);
683impl_saadc_input!(P1_12, 1, 12);
684impl_saadc_input!(P1_13, 1, 13);
685impl_saadc_input!(P1_14, 1, 14);
686
687#[cfg(feature = "_s")]
688impl_cracen!(CRACEN, CRACEN, CRACEN);
689
690embassy_hal_internal::interrupt_mod!(
691 SWI00,
692 SWI01,
693 SWI02,
694 SWI03,
695 SPU00,
696 MPC00,
697 AAR00_CCM00,
698 ECB00,
699 CRACEN,
700 SERIAL00,
701 RRAMC,
702 VPR00,
703 CTRLAP,
704 TIMER00,
705 SPU10,
706 TIMER10,
707 EGU10,
708 RADIO_0,
709 RADIO_1,
710 SPU20,
711 SERIAL20,
712 SERIAL21,
713 SERIAL22,
714 EGU20,
715 TIMER20,
716 TIMER21,
717 TIMER22,
718 TIMER23,
719 TIMER24,
720 PDM20,
721 PDM21,
722 PWM20,
723 PWM21,
724 PWM22,
725 SAADC,
726 NFCT,
727 TEMP,
728 GPIOTE20_0,
729 GPIOTE20_1,
730 TAMPC,
731 I2S20,
732 QDEC20,
733 QDEC21,
734 GRTC_0,
735 GRTC_1,
736 GRTC_2,
737 GRTC_3,
738 SPU30,
739 SERIAL30,
740 COMP_LPCOMP,
741 WDT30,
742 WDT31,
743 GPIOTE30_0,
744 GPIOTE30_1,
745 CLOCK_POWER,
746);
diff --git a/embassy-nrf/src/chips/nrf54lm20_app.rs b/embassy-nrf/src/chips/nrf54lm20_app.rs
new file mode 100644
index 000000000..c5fdf02e4
--- /dev/null
+++ b/embassy-nrf/src/chips/nrf54lm20_app.rs
@@ -0,0 +1,841 @@
1/// Peripheral Access Crate
2#[allow(unused_imports)]
3#[rustfmt::skip]
4pub mod pac {
5 pub use nrf_pac::*;
6
7 #[cfg(feature = "_ns")]
8 #[doc(no_inline)]
9 pub use nrf_pac::{
10 FICR_NS as FICR,
11 DPPIC00_NS as DPPIC00,
12 PPIB00_NS as PPIB00,
13 PPIB01_NS as PPIB01,
14 AAR00_NS as AAR00,
15 CCM00_NS as CCM00,
16 ECB00_NS as ECB00,
17 SPIM00_NS as SPIM00,
18 SPIS00_NS as SPIS00,
19 UARTE00_NS as UARTE00,
20 VPR00_NS as VPR00,
21 P2_NS as P2,
22 CTRLAP_NS as CTRLAP,
23 TAD_NS as TAD,
24 TIMER00_NS as TIMER00,
25 DPPIC10_NS as DPPIC10,
26 PPIB10_NS as PPIB10,
27 PPIB11_NS as PPIB11,
28 TIMER10_NS as TIMER10,
29 EGU10_NS as EGU10,
30 RADIO_NS as RADIO,
31 DPPIC20_NS as DPPIC20,
32 PPIB20_NS as PPIB20,
33 PPIB21_NS as PPIB21,
34 PPIB22_NS as PPIB22,
35 SPIM20_NS as SPIM20,
36 SPIS20_NS as SPIS20,
37 TWIM20_NS as TWIM20,
38 TWIS20_NS as TWIS20,
39 UARTE20_NS as UARTE20,
40 SPIM21_NS as SPIM21,
41 SPIS21_NS as SPIS21,
42 TWIM21_NS as TWIM21,
43 TWIS21_NS as TWIS21,
44 UARTE21_NS as UARTE21,
45 SPIM22_NS as SPIM22,
46 SPIS22_NS as SPIS22,
47 TWIM22_NS as TWIM22,
48 TWIS22_NS as TWIS22,
49 UARTE22_NS as UARTE22,
50 EGU20_NS as EGU20,
51 TIMER20_NS as TIMER20,
52 TIMER21_NS as TIMER21,
53 TIMER22_NS as TIMER22,
54 TIMER23_NS as TIMER23,
55 TIMER24_NS as TIMER24,
56 MEMCONF_NS as MEMCONF,
57 PDM20_NS as PDM20,
58 PDM21_NS as PDM21,
59 PWM20_NS as PWM20,
60 PWM21_NS as PWM21,
61 PWM22_NS as PWM22,
62 SAADC_NS as SAADC,
63 NFCT_NS as NFCT,
64 TEMP_NS as TEMP,
65 P1_NS as P1,
66 GPIOTE20_NS as GPIOTE20,
67 I2S20_NS as I2S20,
68 QDEC20_NS as QDEC20,
69 QDEC21_NS as QDEC21,
70 GRTC_NS as GRTC,
71 DPPIC30_NS as DPPIC30,
72 PPIB30_NS as PPIB30,
73 SPIM30_NS as SPIM30,
74 SPIS30_NS as SPIS30,
75 TWIM30_NS as TWIM30,
76 TWIS30_NS as TWIS30,
77 UARTE30_NS as UARTE30,
78 COMP_NS as COMP,
79 LPCOMP_NS as LPCOMP,
80 WDT31_NS as WDT31,
81 P0_NS as P0,
82 GPIOTE30_NS as GPIOTE30,
83 CLOCK_NS as CLOCK,
84 POWER_NS as POWER,
85 RESET_NS as RESET,
86 OSCILLATORS_NS as OSCILLATORS,
87 REGULATORS_NS as REGULATORS,
88 TPIU_NS as TPIU,
89 ETM_NS as ETM,
90 };
91
92 #[cfg(feature = "_s")]
93 #[doc(no_inline)]
94 pub use nrf_pac::{
95 FICR_NS as FICR,
96 SICR_S as SICR,
97 ICACHEDATA_S as ICACHEDATA,
98 ICACHEINFO_S as ICACHEINFO,
99 SWI00_S as SWI00,
100 SWI01_S as SWI01,
101 SWI02_S as SWI02,
102 SWI03_S as SWI03,
103 SPU00_S as SPU00,
104 MPC00_S as MPC00,
105 DPPIC00_S as DPPIC00,
106 PPIB00_S as PPIB00,
107 PPIB01_S as PPIB01,
108 KMU_S as KMU,
109 AAR00_S as AAR00,
110 CCM00_S as CCM00,
111 ECB00_S as ECB00,
112 CRACEN_S as CRACEN,
113 SPIM00_S as SPIM00,
114 SPIS00_S as SPIS00,
115 UARTE00_S as UARTE00,
116 GLITCHDET_S as GLITCHDET,
117 RRAMC_S as RRAMC,
118 VPR00_S as VPR00,
119 P2_S as P2,
120 CTRLAP_S as CTRLAP,
121 TAD_S as TAD,
122 TIMER00_S as TIMER00,
123 SPU10_S as SPU10,
124 DPPIC10_S as DPPIC10,
125 PPIB10_S as PPIB10,
126 PPIB11_S as PPIB11,
127 TIMER10_S as TIMER10,
128 EGU10_S as EGU10,
129 RADIO_S as RADIO,
130 SPU20_S as SPU20,
131 DPPIC20_S as DPPIC20,
132 PPIB20_S as PPIB20,
133 PPIB21_S as PPIB21,
134 PPIB22_S as PPIB22,
135 SPIM20_S as SPIM20,
136 SPIS20_S as SPIS20,
137 TWIM20_S as TWIM20,
138 TWIS20_S as TWIS20,
139 UARTE20_S as UARTE20,
140 SPIM21_S as SPIM21,
141 SPIS21_S as SPIS21,
142 TWIM21_S as TWIM21,
143 TWIS21_S as TWIS21,
144 UARTE21_S as UARTE21,
145 SPIM22_S as SPIM22,
146 SPIS22_S as SPIS22,
147 TWIM22_S as TWIM22,
148 TWIS22_S as TWIS22,
149 UARTE22_S as UARTE22,
150 EGU20_S as EGU20,
151 TIMER20_S as TIMER20,
152 TIMER21_S as TIMER21,
153 TIMER22_S as TIMER22,
154 TIMER23_S as TIMER23,
155 TIMER24_S as TIMER24,
156 MEMCONF_S as MEMCONF,
157 PDM20_S as PDM20,
158 PDM21_S as PDM21,
159 PWM20_S as PWM20,
160 PWM21_S as PWM21,
161 PWM22_S as PWM22,
162 SAADC_S as SAADC,
163 NFCT_S as NFCT,
164 TEMP_S as TEMP,
165 P1_S as P1,
166 GPIOTE20_S as GPIOTE20,
167 TAMPC_S as TAMPC,
168 QDEC20_S as QDEC20,
169 QDEC21_S as QDEC21,
170 GRTC_S as GRTC,
171 SPU30_S as SPU30,
172 DPPIC30_S as DPPIC30,
173 PPIB30_S as PPIB30,
174 SPIM30_S as SPIM30,
175 SPIS30_S as SPIS30,
176 TWIM30_S as TWIM30,
177 TWIS30_S as TWIS30,
178 UARTE30_S as UARTE30,
179 COMP_S as COMP,
180 LPCOMP_S as LPCOMP,
181 WDT30_S as WDT30,
182 WDT31_S as WDT31,
183 P0_S as P0,
184 GPIOTE30_S as GPIOTE30,
185 CLOCK_S as CLOCK,
186 POWER_S as POWER,
187 RESET_S as RESET,
188 OSCILLATORS_S as OSCILLATORS,
189 REGULATORS_S as REGULATORS,
190 CRACENCORE_S as CRACENCORE,
191 CPUC_S as CPUC,
192 ICACHE_S as ICACHE,
193 };
194}
195
196/// The maximum buffer size that the EasyDMA can send/recv in one operation.
197pub const EASY_DMA_SIZE: usize = (1 << 16) - 1;
198pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
199
200// 2 MB NVM
201#[allow(unused)]
202pub const FLASH_SIZE: usize = 2048 * 1024;
203
204embassy_hal_internal::peripherals! {
205 // PPI
206 PPI00_CH0,
207 PPI00_CH1,
208 PPI00_CH2,
209 PPI00_CH3,
210 PPI00_CH4,
211 PPI00_CH5,
212 PPI00_CH6,
213 PPI00_CH7,
214
215 PPI10_CH0,
216 PPI10_CH1,
217 PPI10_CH2,
218 PPI10_CH3,
219 PPI10_CH4,
220 PPI10_CH5,
221 PPI10_CH6,
222 PPI10_CH7,
223 PPI10_CH8,
224 PPI10_CH9,
225 PPI10_CH10,
226 PPI10_CH11,
227 PPI10_CH12,
228 PPI10_CH13,
229 PPI10_CH14,
230 PPI10_CH15,
231 PPI10_CH16,
232 PPI10_CH17,
233 PPI10_CH18,
234 PPI10_CH19,
235 PPI10_CH20,
236 PPI10_CH21,
237 PPI10_CH22,
238 PPI10_CH23,
239
240 PPI20_CH0,
241 PPI20_CH1,
242 PPI20_CH2,
243 PPI20_CH3,
244 PPI20_CH4,
245 PPI20_CH5,
246 PPI20_CH6,
247 PPI20_CH7,
248 PPI20_CH8,
249 PPI20_CH9,
250 PPI20_CH10,
251 PPI20_CH11,
252 PPI20_CH12,
253 PPI20_CH13,
254 PPI20_CH14,
255 PPI20_CH15,
256
257 PPI30_CH0,
258 PPI30_CH1,
259 PPI30_CH2,
260 PPI30_CH3,
261
262 PPI00_GROUP0,
263 PPI00_GROUP1,
264
265 PPI10_GROUP0,
266 PPI10_GROUP1,
267 PPI10_GROUP2,
268 PPI10_GROUP3,
269 PPI10_GROUP4,
270 PPI10_GROUP5,
271
272 PPI20_GROUP0,
273 PPI20_GROUP1,
274 PPI20_GROUP2,
275 PPI20_GROUP3,
276 PPI20_GROUP4,
277 PPI20_GROUP5,
278
279 PPI30_GROUP0,
280 PPI30_GROUP1,
281
282 // PPI BRIDGE channels
283 PPIB00_CH0,
284 PPIB00_CH1,
285 PPIB00_CH2,
286 PPIB00_CH3,
287 PPIB00_CH4,
288 PPIB00_CH5,
289 PPIB00_CH6,
290 PPIB00_CH7,
291
292 PPIB01_CH0,
293 PPIB01_CH1,
294 PPIB01_CH2,
295 PPIB01_CH3,
296 PPIB01_CH4,
297 PPIB01_CH5,
298 PPIB01_CH6,
299 PPIB01_CH7,
300
301 PPIB10_CH0,
302 PPIB10_CH1,
303 PPIB10_CH2,
304 PPIB10_CH3,
305 PPIB10_CH4,
306 PPIB10_CH5,
307 PPIB10_CH6,
308 PPIB10_CH7,
309
310 PPIB11_CH0,
311 PPIB11_CH1,
312 PPIB11_CH2,
313 PPIB11_CH3,
314 PPIB11_CH4,
315 PPIB11_CH5,
316 PPIB11_CH6,
317 PPIB11_CH7,
318 PPIB11_CH8,
319 PPIB11_CH9,
320 PPIB11_CH10,
321 PPIB11_CH11,
322 PPIB11_CH12,
323 PPIB11_CH13,
324 PPIB11_CH14,
325 PPIB11_CH15,
326
327 PPIB20_CH0,
328 PPIB20_CH1,
329 PPIB20_CH2,
330 PPIB20_CH3,
331 PPIB20_CH4,
332 PPIB20_CH5,
333 PPIB20_CH6,
334 PPIB20_CH7,
335
336 PPIB21_CH0,
337 PPIB21_CH1,
338 PPIB21_CH2,
339 PPIB21_CH3,
340 PPIB21_CH4,
341 PPIB21_CH5,
342 PPIB21_CH6,
343 PPIB21_CH7,
344 PPIB21_CH8,
345 PPIB21_CH9,
346 PPIB21_CH10,
347 PPIB21_CH11,
348 PPIB21_CH12,
349 PPIB21_CH13,
350 PPIB21_CH14,
351 PPIB21_CH15,
352
353 PPIB22_CH0,
354 PPIB22_CH1,
355 PPIB22_CH2,
356 PPIB22_CH3,
357
358 PPIB30_CH0,
359 PPIB30_CH1,
360 PPIB30_CH2,
361 PPIB30_CH3,
362
363 // Timers
364 TIMER00,
365 TIMER10,
366 TIMER20,
367 TIMER21,
368 TIMER22,
369 TIMER23,
370 TIMER24,
371
372 // GPIO port 0
373 P0_00,
374 P0_01,
375 P0_02,
376 P0_03,
377 P0_04,
378 P0_05,
379 P0_06,
380 P0_07,
381 P0_08,
382 P0_09,
383
384 // GPIO port 1
385 P1_00,
386 P1_01,
387 P1_02,
388 P1_03,
389 P1_04,
390 P1_05,
391 P1_06,
392 P1_07,
393 P1_08,
394 P1_09,
395 P1_10,
396 P1_11,
397 P1_12,
398 P1_13,
399 P1_14,
400 P1_15,
401 P1_16,
402 P1_17,
403 P1_18,
404 P1_19,
405 P1_20,
406 P1_21,
407 P1_22,
408 P1_23,
409 P1_24,
410 P1_25,
411 P1_26,
412 P1_27,
413 P1_28,
414 P1_29,
415 P1_30,
416 P1_31,
417
418
419 // GPIO port 2
420 P2_00,
421 P2_01,
422 P2_02,
423 P2_03,
424 P2_04,
425 P2_05,
426 P2_06,
427 P2_07,
428 P2_08,
429 P2_09,
430 P2_10,
431
432 // GPIO port 3
433 P3_00,
434 P3_01,
435 P3_02,
436 P3_03,
437 P3_04,
438 P3_05,
439 P3_06,
440 P3_07,
441 P3_08,
442 P3_09,
443 P3_10,
444 P3_11,
445 P3_12,
446
447 // GRTC
448 GRTC_CH0,
449 #[cfg(not(feature = "time-driver-grtc"))]
450 GRTC_CH1,
451 GRTC_CH2,
452 GRTC_CH3,
453 GRTC_CH4,
454 GRTC_CH5,
455 GRTC_CH6,
456 GRTC_CH7,
457 GRTC_CH8,
458 GRTC_CH9,
459 GRTC_CH10,
460 GRTC_CH11,
461
462 // PWM
463 PWM20,
464 PWM21,
465 PWM22,
466
467 // SERIAL
468 SERIAL00,
469 SERIAL20,
470 SERIAL21,
471 SERIAL22,
472 SERIAL30,
473
474 // SAADC
475 SAADC,
476
477 // RADIO
478 RADIO,
479
480
481 // GPIOTE instances
482 GPIOTE20,
483 GPIOTE30,
484
485 // GPIOTE channels
486 GPIOTE20_CH0,
487 GPIOTE20_CH1,
488 GPIOTE20_CH2,
489 GPIOTE20_CH3,
490 GPIOTE20_CH4,
491 GPIOTE20_CH5,
492 GPIOTE20_CH6,
493 GPIOTE20_CH7,
494 GPIOTE30_CH0,
495 GPIOTE30_CH1,
496 GPIOTE30_CH2,
497 GPIOTE30_CH3,
498
499 // CRACEN
500 #[cfg(feature = "_s")]
501 CRACEN,
502
503 #[cfg(feature = "_s")]
504 // RRAMC
505 RRAMC,
506
507 // TEMP
508 TEMP,
509
510 // WDT
511 #[cfg(feature = "_ns")]
512 WDT,
513 #[cfg(feature = "_s")]
514 WDT0,
515 #[cfg(feature = "_s")]
516 WDT1,
517}
518
519impl_pin!(P0_00, 0, 0);
520impl_pin!(P0_01, 0, 1);
521impl_pin!(P0_02, 0, 2);
522impl_pin!(P0_03, 0, 3);
523impl_pin!(P0_04, 0, 4);
524impl_pin!(P0_05, 0, 5);
525impl_pin!(P0_06, 0, 6);
526impl_pin!(P0_07, 0, 7);
527impl_pin!(P0_08, 0, 8);
528impl_pin!(P0_09, 0, 9);
529
530impl_pin!(P1_00, 1, 0);
531impl_pin!(P1_01, 1, 1);
532impl_pin!(P1_02, 1, 2);
533impl_pin!(P1_03, 1, 3);
534impl_pin!(P1_04, 1, 4);
535impl_pin!(P1_05, 1, 5);
536impl_pin!(P1_06, 1, 6);
537impl_pin!(P1_07, 1, 7);
538impl_pin!(P1_08, 1, 8);
539impl_pin!(P1_09, 1, 9);
540impl_pin!(P1_10, 1, 10);
541impl_pin!(P1_11, 1, 11);
542impl_pin!(P1_12, 1, 12);
543impl_pin!(P1_13, 1, 13);
544impl_pin!(P1_14, 1, 14);
545impl_pin!(P1_15, 1, 15);
546impl_pin!(P1_16, 1, 16);
547impl_pin!(P1_17, 1, 17);
548impl_pin!(P1_18, 1, 18);
549impl_pin!(P1_19, 1, 19);
550impl_pin!(P1_20, 1, 20);
551impl_pin!(P1_21, 1, 21);
552impl_pin!(P1_22, 1, 22);
553impl_pin!(P1_23, 1, 23);
554impl_pin!(P1_24, 1, 24);
555impl_pin!(P1_25, 1, 25);
556impl_pin!(P1_26, 1, 26);
557impl_pin!(P1_27, 1, 27);
558impl_pin!(P1_28, 1, 28);
559impl_pin!(P1_29, 1, 29);
560impl_pin!(P1_30, 1, 30);
561impl_pin!(P1_31, 1, 31);
562
563impl_pin!(P2_00, 2, 0);
564impl_pin!(P2_01, 2, 1);
565impl_pin!(P2_02, 2, 2);
566impl_pin!(P2_03, 2, 3);
567impl_pin!(P2_04, 2, 4);
568impl_pin!(P2_05, 2, 5);
569impl_pin!(P2_06, 2, 6);
570impl_pin!(P2_07, 2, 7);
571impl_pin!(P2_08, 2, 8);
572impl_pin!(P2_09, 2, 9);
573impl_pin!(P2_10, 2, 10);
574
575impl_pin!(P3_00, 3, 0);
576impl_pin!(P3_01, 3, 1);
577impl_pin!(P3_02, 3, 2);
578impl_pin!(P3_03, 3, 3);
579impl_pin!(P3_04, 3, 4);
580impl_pin!(P3_05, 3, 5);
581impl_pin!(P3_06, 3, 6);
582impl_pin!(P3_07, 3, 7);
583impl_pin!(P3_08, 3, 8);
584impl_pin!(P3_09, 3, 9);
585impl_pin!(P3_10, 3, 10);
586impl_pin!(P3_11, 3, 11);
587impl_pin!(P3_12, 3, 12);
588
589cfg_if::cfg_if! {
590 if #[cfg(feature = "gpiote")] {
591 impl_gpiote_pin!(P0_00, GPIOTE30);
592 impl_gpiote_pin!(P0_01, GPIOTE30);
593 impl_gpiote_pin!(P0_02, GPIOTE30);
594 impl_gpiote_pin!(P0_03, GPIOTE30);
595 impl_gpiote_pin!(P0_04, GPIOTE30);
596 impl_gpiote_pin!(P0_05, GPIOTE30);
597 impl_gpiote_pin!(P0_06, GPIOTE30);
598 impl_gpiote_pin!(P0_07, GPIOTE30);
599 impl_gpiote_pin!(P0_08, GPIOTE30);
600 impl_gpiote_pin!(P0_09, GPIOTE30);
601
602 impl_gpiote_pin!(P1_00, GPIOTE20);
603 impl_gpiote_pin!(P1_01, GPIOTE20);
604 impl_gpiote_pin!(P1_02, GPIOTE20);
605 impl_gpiote_pin!(P1_03, GPIOTE20);
606 impl_gpiote_pin!(P1_04, GPIOTE20);
607 impl_gpiote_pin!(P1_05, GPIOTE20);
608 impl_gpiote_pin!(P1_06, GPIOTE20);
609 impl_gpiote_pin!(P1_07, GPIOTE20);
610 impl_gpiote_pin!(P1_08, GPIOTE20);
611 impl_gpiote_pin!(P1_09, GPIOTE20);
612 impl_gpiote_pin!(P1_10, GPIOTE20);
613 impl_gpiote_pin!(P1_11, GPIOTE20);
614 impl_gpiote_pin!(P1_12, GPIOTE20);
615 impl_gpiote_pin!(P1_13, GPIOTE20);
616 impl_gpiote_pin!(P1_14, GPIOTE20);
617 impl_gpiote_pin!(P1_15, GPIOTE20);
618 impl_gpiote_pin!(P1_16, GPIOTE20);
619 impl_gpiote_pin!(P1_17, GPIOTE20);
620 impl_gpiote_pin!(P1_18, GPIOTE20);
621 impl_gpiote_pin!(P1_19, GPIOTE20);
622 impl_gpiote_pin!(P1_20, GPIOTE20);
623 impl_gpiote_pin!(P1_21, GPIOTE20);
624 impl_gpiote_pin!(P1_22, GPIOTE20);
625 impl_gpiote_pin!(P1_23, GPIOTE20);
626 impl_gpiote_pin!(P1_24, GPIOTE20);
627 impl_gpiote_pin!(P1_25, GPIOTE20);
628 impl_gpiote_pin!(P1_26, GPIOTE20);
629 impl_gpiote_pin!(P1_27, GPIOTE20);
630 impl_gpiote_pin!(P1_28, GPIOTE20);
631 impl_gpiote_pin!(P1_29, GPIOTE20);
632 impl_gpiote_pin!(P1_30, GPIOTE20);
633 impl_gpiote_pin!(P1_31, GPIOTE20);
634
635 impl_gpiote_pin!(P3_00, GPIOTE20);
636 impl_gpiote_pin!(P3_01, GPIOTE20);
637 impl_gpiote_pin!(P3_02, GPIOTE20);
638 impl_gpiote_pin!(P3_03, GPIOTE20);
639 impl_gpiote_pin!(P3_04, GPIOTE20);
640 impl_gpiote_pin!(P3_05, GPIOTE20);
641 impl_gpiote_pin!(P3_06, GPIOTE20);
642 impl_gpiote_pin!(P3_07, GPIOTE20);
643 impl_gpiote_pin!(P3_08, GPIOTE20);
644 impl_gpiote_pin!(P3_09, GPIOTE20);
645 impl_gpiote_pin!(P3_10, GPIOTE20);
646 impl_gpiote_pin!(P3_11, GPIOTE20);
647 impl_gpiote_pin!(P3_12, GPIOTE20);
648 }
649}
650
651#[cfg(feature = "_ns")]
652impl_wdt!(WDT, WDT31, WDT31, 0);
653#[cfg(feature = "_s")]
654impl_wdt!(WDT0, WDT31, WDT31, 0);
655#[cfg(feature = "_s")]
656impl_wdt!(WDT1, WDT30, WDT30, 1);
657// DPPI00 channels
658impl_ppi_channel!(PPI00_CH0, DPPIC00, 0 => configurable);
659impl_ppi_channel!(PPI00_CH1, DPPIC00, 1 => configurable);
660impl_ppi_channel!(PPI00_CH2, DPPIC00, 2 => configurable);
661impl_ppi_channel!(PPI00_CH3, DPPIC00, 3 => configurable);
662impl_ppi_channel!(PPI00_CH4, DPPIC00, 4 => configurable);
663impl_ppi_channel!(PPI00_CH5, DPPIC00, 5 => configurable);
664impl_ppi_channel!(PPI00_CH6, DPPIC00, 6 => configurable);
665impl_ppi_channel!(PPI00_CH7, DPPIC00, 7 => configurable);
666
667// DPPI10 channels
668impl_ppi_channel!(PPI10_CH0, DPPIC10, 0 => static);
669
670// DPPI20 channels
671impl_ppi_channel!(PPI20_CH0, DPPIC20, 0 => configurable);
672impl_ppi_channel!(PPI20_CH1, DPPIC20, 1 => configurable);
673impl_ppi_channel!(PPI20_CH2, DPPIC20, 2 => configurable);
674impl_ppi_channel!(PPI20_CH3, DPPIC20, 3 => configurable);
675impl_ppi_channel!(PPI20_CH4, DPPIC20, 4 => configurable);
676impl_ppi_channel!(PPI20_CH5, DPPIC20, 5 => configurable);
677impl_ppi_channel!(PPI20_CH6, DPPIC20, 6 => configurable);
678impl_ppi_channel!(PPI20_CH7, DPPIC20, 7 => configurable);
679impl_ppi_channel!(PPI20_CH8, DPPIC20, 8 => configurable);
680impl_ppi_channel!(PPI20_CH9, DPPIC20, 9 => configurable);
681impl_ppi_channel!(PPI20_CH10, DPPIC20, 10 => configurable);
682impl_ppi_channel!(PPI20_CH11, DPPIC20, 11 => configurable);
683impl_ppi_channel!(PPI20_CH12, DPPIC20, 12 => configurable);
684impl_ppi_channel!(PPI20_CH13, DPPIC20, 13 => configurable);
685impl_ppi_channel!(PPI20_CH14, DPPIC20, 14 => configurable);
686impl_ppi_channel!(PPI20_CH15, DPPIC20, 15 => configurable);
687
688// DPPI30 channels
689impl_ppi_channel!(PPI30_CH0, DPPIC30, 0 => configurable);
690impl_ppi_channel!(PPI30_CH1, DPPIC30, 1 => configurable);
691impl_ppi_channel!(PPI30_CH2, DPPIC30, 2 => configurable);
692impl_ppi_channel!(PPI30_CH3, DPPIC30, 3 => configurable);
693
694// DPPI00 groups
695impl_ppi_group!(PPI00_GROUP0, DPPIC00, 0);
696impl_ppi_group!(PPI00_GROUP1, DPPIC00, 1);
697
698// DPPI10 groups
699impl_ppi_group!(PPI10_GROUP0, DPPIC10, 0);
700
701// DPPI20 groups
702impl_ppi_group!(PPI20_GROUP0, DPPIC20, 0);
703impl_ppi_group!(PPI20_GROUP1, DPPIC20, 1);
704impl_ppi_group!(PPI20_GROUP2, DPPIC20, 2);
705impl_ppi_group!(PPI20_GROUP3, DPPIC20, 3);
706impl_ppi_group!(PPI20_GROUP4, DPPIC20, 4);
707impl_ppi_group!(PPI20_GROUP5, DPPIC20, 5);
708
709// DPPI30 groups
710impl_ppi_group!(PPI30_GROUP0, DPPIC30, 0);
711impl_ppi_group!(PPI30_GROUP1, DPPIC30, 1);
712
713impl_timer!(TIMER00, TIMER00, TIMER00);
714impl_timer!(TIMER10, TIMER10, TIMER10);
715impl_timer!(TIMER20, TIMER20, TIMER20);
716impl_timer!(TIMER21, TIMER21, TIMER21);
717impl_timer!(TIMER22, TIMER22, TIMER22);
718impl_timer!(TIMER23, TIMER23, TIMER23);
719impl_timer!(TIMER24, TIMER24, TIMER24);
720
721impl_twim!(SERIAL20, TWIM20, SERIAL20);
722impl_twim!(SERIAL21, TWIM21, SERIAL21);
723impl_twim!(SERIAL22, TWIM22, SERIAL22);
724impl_twim!(SERIAL30, TWIM30, SERIAL30);
725
726impl_twis!(SERIAL20, TWIS20, SERIAL20);
727impl_twis!(SERIAL21, TWIS21, SERIAL21);
728impl_twis!(SERIAL22, TWIS22, SERIAL22);
729impl_twis!(SERIAL30, TWIS30, SERIAL30);
730
731impl_pwm!(PWM20, PWM20, PWM20);
732impl_pwm!(PWM21, PWM21, PWM21);
733impl_pwm!(PWM22, PWM22, PWM22);
734
735#[cfg(feature = "_s")]
736impl_spim!(
737 SERIAL00,
738 SPIM00,
739 SERIAL00,
740 match pac::OSCILLATORS_S.pll().currentfreq().read().currentfreq() {
741 pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,
742 pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,
743 _ => unreachable!(),
744 }
745);
746#[cfg(feature = "_ns")]
747impl_spim!(
748 SERIAL00,
749 SPIM00,
750 SERIAL00,
751 match pac::OSCILLATORS_NS.pll().currentfreq().read().currentfreq() {
752 pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,
753 pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,
754 _ => unreachable!(),
755 }
756);
757impl_spim!(SERIAL20, SPIM20, SERIAL20, 16_000_000);
758impl_spim!(SERIAL21, SPIM21, SERIAL21, 16_000_000);
759impl_spim!(SERIAL22, SPIM22, SERIAL22, 16_000_000);
760impl_spim!(SERIAL30, SPIM30, SERIAL30, 16_000_000);
761
762impl_spis!(SERIAL20, SPIS20, SERIAL20);
763impl_spis!(SERIAL21, SPIS21, SERIAL21);
764impl_spis!(SERIAL22, SPIS22, SERIAL22);
765impl_spis!(SERIAL30, SPIS30, SERIAL30);
766
767impl_uarte!(SERIAL00, UARTE00, SERIAL00);
768impl_uarte!(SERIAL20, UARTE20, SERIAL20);
769impl_uarte!(SERIAL21, UARTE21, SERIAL21);
770impl_uarte!(SERIAL22, UARTE22, SERIAL22);
771impl_uarte!(SERIAL30, UARTE30, SERIAL30);
772
773// NB: SAADC uses "pin" abstraction, not "AIN"
774impl_saadc_input!(P1_04, 1, 4);
775impl_saadc_input!(P1_05, 1, 5);
776impl_saadc_input!(P1_06, 1, 6);
777impl_saadc_input!(P1_07, 1, 7);
778impl_saadc_input!(P1_11, 1, 11);
779impl_saadc_input!(P1_12, 1, 12);
780impl_saadc_input!(P1_13, 1, 13);
781impl_saadc_input!(P1_14, 1, 14);
782
783#[cfg(feature = "_s")]
784impl_cracen!(CRACEN, CRACEN, CRACEN);
785
786embassy_hal_internal::interrupt_mod!(
787 SWI00,
788 SWI01,
789 SWI02,
790 SWI03,
791 SPU00,
792 MPC00,
793 AAR00_CCM00,
794 ECB00,
795 CRACEN,
796 SERIAL00,
797 RRAMC,
798 VPR00,
799 CTRLAP,
800 TIMER00,
801 SPU10,
802 TIMER10,
803 EGU10,
804 RADIO_0,
805 RADIO_1,
806 SPU20,
807 SERIAL20,
808 SERIAL21,
809 SERIAL22,
810 EGU20,
811 TIMER20,
812 TIMER21,
813 TIMER22,
814 TIMER23,
815 TIMER24,
816 PDM20,
817 PDM21,
818 PWM20,
819 PWM21,
820 PWM22,
821 SAADC,
822 NFCT,
823 TEMP,
824 GPIOTE20_0,
825 GPIOTE20_1,
826 TAMPC,
827 QDEC20,
828 QDEC21,
829 GRTC_0,
830 GRTC_1,
831 GRTC_2,
832 GRTC_3,
833 SPU30,
834 SERIAL30,
835 COMP_LPCOMP,
836 WDT30,
837 WDT31,
838 GPIOTE30_0,
839 GPIOTE30_1,
840 CLOCK_POWER,
841);
diff --git a/embassy-nrf/src/cracen.rs b/embassy-nrf/src/cracen.rs
index 47ef1cd87..6381701c0 100644
--- a/embassy-nrf/src/cracen.rs
+++ b/embassy-nrf/src/cracen.rs
@@ -18,10 +18,7 @@ pub struct Cracen<'d, M: Mode> {
18impl<'d> Cracen<'d, Blocking> { 18impl<'d> Cracen<'d, Blocking> {
19 /// Create a new CRACEN driver. 19 /// Create a new CRACEN driver.
20 pub fn new_blocking(_peri: Peri<'d, peripherals::CRACEN>) -> Self { 20 pub fn new_blocking(_peri: Peri<'d, peripherals::CRACEN>) -> Self {
21 let me = Self { _peri, _p: PhantomData }; 21 Self { _peri, _p: PhantomData }
22
23 me.stop();
24 me
25 } 22 }
26} 23}
27 24
@@ -48,7 +45,14 @@ impl<'d, M: Mode> Cracen<'d, M> {
48 while r.rngcontrol().status().read().state() == pac::cracencore::vals::State::STARTUP {} 45 while r.rngcontrol().status().read().state() == pac::cracencore::vals::State::STARTUP {}
49 } 46 }
50 47
51 fn stop(&self) { 48 fn stop_rng(&self) {
49 let r = Self::core();
50 r.rngcontrol().control().write(|w| {
51 w.set_enable(false);
52 });
53
54 while r.rngcontrol().status().read().state() != pac::cracencore::vals::State::RESET {}
55
52 let r = Self::regs(); 56 let r = Self::regs();
53 r.enable().write(|w| { 57 r.enable().write(|w| {
54 w.set_cryptomaster(false); 58 w.set_cryptomaster(false);
@@ -69,7 +73,7 @@ impl<'d, M: Mode> Cracen<'d, M> {
69 chunk[..to_copy].copy_from_slice(&word[..to_copy]); 73 chunk[..to_copy].copy_from_slice(&word[..to_copy]);
70 } 74 }
71 75
72 self.stop(); 76 self.stop_rng();
73 } 77 }
74 78
75 /// Generate a random u32 79 /// Generate a random u32
@@ -90,19 +94,7 @@ impl<'d, M: Mode> Cracen<'d, M> {
90 94
91impl<'d, M: Mode> Drop for Cracen<'d, M> { 95impl<'d, M: Mode> Drop for Cracen<'d, M> {
92 fn drop(&mut self) { 96 fn drop(&mut self) {
93 let r = Self::core(); 97 // nothing to do here, since we stop+disable rng for each operation.
94 r.rngcontrol().control().write(|w| {
95 w.set_enable(false);
96 });
97
98 while r.rngcontrol().status().read().state() != pac::cracencore::vals::State::RESET {}
99
100 let r = Self::regs();
101 r.enable().write(|w| {
102 w.set_cryptomaster(false);
103 w.set_rng(false);
104 w.set_pkeikg(false);
105 });
106 } 98 }
107} 99}
108 100
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs
index d4f6668f3..1f6000b13 100644
--- a/embassy-nrf/src/gpiote.rs
+++ b/embassy-nrf/src/gpiote.rs
@@ -33,14 +33,18 @@ const CHANNELS_PER_PORT: usize = 8;
33 feature = "nrf52833", 33 feature = "nrf52833",
34 feature = "nrf52840", 34 feature = "nrf52840",
35 feature = "_nrf5340", 35 feature = "_nrf5340",
36 feature = "_nrf54l" 36 feature = "_nrf54l15",
37 feature = "_nrf54l10",
38 feature = "_nrf54l05"
37))] 39))]
38const PIN_COUNT: usize = 48; 40const PIN_COUNT: usize = 48;
41#[cfg(feature = "_nrf54lm20")]
42const PIN_COUNT: usize = 66;
39#[cfg(not(any( 43#[cfg(not(any(
40 feature = "nrf52833", 44 feature = "nrf52833",
41 feature = "nrf52840", 45 feature = "nrf52840",
42 feature = "_nrf5340", 46 feature = "_nrf5340",
43 feature = "_nrf54l" 47 feature = "_nrf54l",
44)))] 48)))]
45const PIN_COUNT: usize = 32; 49const PIN_COUNT: usize = 32;
46 50
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index e8762b00d..db71dee10 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -25,6 +25,11 @@
25 feature = "nrf5340-net", 25 feature = "nrf5340-net",
26 feature = "nrf54l15-app-s", 26 feature = "nrf54l15-app-s",
27 feature = "nrf54l15-app-ns", 27 feature = "nrf54l15-app-ns",
28 feature = "nrf54l10-app-s",
29 feature = "nrf54l10-app-ns",
30 feature = "nrf54l05-app-s",
31 feature = "nrf54l05-app-ns",
32 feature = "nrf54lm20-app-s",
28 feature = "nrf9160-s", 33 feature = "nrf9160-s",
29 feature = "nrf9160-ns", 34 feature = "nrf9160-ns",
30 feature = "nrf9120-s", 35 feature = "nrf9120-s",
@@ -49,6 +54,11 @@ compile_error!(
49 nrf5340-net, 54 nrf5340-net,
50 nrf54l15-app-s, 55 nrf54l15-app-s,
51 nrf54l15-app-ns, 56 nrf54l15-app-ns,
57 nrf54l10-app-s,
58 nrf54l10-app-ns,
59 nrf54l05-app-s,
60 nrf54l05-app-ns,
61 nrf54lm20-app-s,
52 nrf9160-s, 62 nrf9160-s,
53 nrf9160-ns, 63 nrf9160-ns,
54 nrf9120-s, 64 nrf9120-s,
@@ -99,9 +109,9 @@ pub mod ipc;
99pub mod nfct; 109pub mod nfct;
100#[cfg(not(feature = "_nrf54l"))] 110#[cfg(not(feature = "_nrf54l"))]
101pub mod nvmc; 111pub mod nvmc;
102#[cfg(feature = "nrf54l15-app-s")] 112#[cfg(all(feature = "_nrf54l", feature = "_s"))]
103pub mod rramc; 113pub mod rramc;
104#[cfg(feature = "nrf54l15-app-s")] 114#[cfg(all(feature = "_nrf54l", feature = "_s"))]
105pub use rramc as nvmc; 115pub use rramc as nvmc;
106#[cfg(not(feature = "_nrf54l"))] // TODO 116#[cfg(not(feature = "_nrf54l"))] // TODO
107#[cfg(any( 117#[cfg(any(
@@ -192,6 +202,9 @@ pub mod wdt;
192#[cfg_attr(feature = "_nrf5340-app", path = "chips/nrf5340_app.rs")] 202#[cfg_attr(feature = "_nrf5340-app", path = "chips/nrf5340_app.rs")]
193#[cfg_attr(feature = "_nrf5340-net", path = "chips/nrf5340_net.rs")] 203#[cfg_attr(feature = "_nrf5340-net", path = "chips/nrf5340_net.rs")]
194#[cfg_attr(feature = "_nrf54l15-app", path = "chips/nrf54l15_app.rs")] 204#[cfg_attr(feature = "_nrf54l15-app", path = "chips/nrf54l15_app.rs")]
205#[cfg_attr(feature = "_nrf54l10-app", path = "chips/nrf54l10_app.rs")]
206#[cfg_attr(feature = "_nrf54l05-app", path = "chips/nrf54l05_app.rs")]
207#[cfg_attr(feature = "_nrf54lm20-app", path = "chips/nrf54lm20_app.rs")]
195#[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")] 208#[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")]
196#[cfg_attr(feature = "_nrf9120", path = "chips/nrf9120.rs")] 209#[cfg_attr(feature = "_nrf9120", path = "chips/nrf9120.rs")]
197mod chip; 210mod chip;
@@ -657,10 +670,11 @@ mod consts {
657 pub const APPROTECT_DISABLED: u32 = 0x0000_005a; 670 pub const APPROTECT_DISABLED: u32 = 0x0000_005a;
658} 671}
659 672
673/// Result from writing UICR.
660#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] 674#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))]
661#[derive(Debug, Copy, Clone, Eq, PartialEq)] 675#[derive(Debug, Copy, Clone, Eq, PartialEq)]
662#[cfg_attr(feature = "defmt", derive(defmt::Format))] 676#[cfg_attr(feature = "defmt", derive(defmt::Format))]
663enum WriteResult { 677pub enum WriteResult {
664 /// Word was written successfully, needs reset. 678 /// Word was written successfully, needs reset.
665 Written, 679 Written,
666 /// Word was already set to the value we wanted to write, nothing was done. 680 /// Word was already set to the value we wanted to write, nothing was done.
@@ -669,13 +683,21 @@ enum WriteResult {
669 Failed, 683 Failed,
670} 684}
671 685
686/// Write the UICR value at the provided address, ensuring that flash
687/// settings are correctly apply to persist the value.
688///
689/// Safety: the address must be a valid UICR register.
672#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] 690#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))]
673unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult { 691pub unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult {
674 uicr_write_masked(address, value, 0xFFFF_FFFF) 692 uicr_write_masked(address, value, 0xFFFF_FFFF)
675} 693}
676 694
677#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] 695#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))]
678unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteResult { 696/// Write the UICR value at the provided address, ensuring that flash
697/// settings are correctly apply to persist the value.
698///
699/// Safety: the address must be a valid UICR register.
700pub unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteResult {
679 let curr_val = address.read_volatile(); 701 let curr_val = address.read_volatile();
680 if curr_val & mask == value & mask { 702 if curr_val & mask == value & mask {
681 return WriteResult::Noop; 703 return WriteResult::Noop;
diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs
index ab0e2b86a..e89338416 100644
--- a/embassy-nrf/src/saadc.rs
+++ b/embassy-nrf/src/saadc.rs
@@ -195,6 +195,7 @@ impl<'d, const N: usize> Saadc<'d, N> {
195 w.set_resp(cc.resistor.into()); 195 w.set_resp(cc.resistor.into());
196 #[cfg(not(feature = "_nrf54l"))] 196 #[cfg(not(feature = "_nrf54l"))]
197 w.set_resn(vals::Resn::BYPASS); 197 w.set_resn(vals::Resn::BYPASS);
198 #[cfg(not(feature = "_nrf54l"))]
198 w.set_burst(!matches!(oversample, Oversample::BYPASS)); 199 w.set_burst(!matches!(oversample, Oversample::BYPASS));
199 }); 200 });
200 } 201 }
diff --git a/embassy-stm32-wpan/CHANGELOG.md b/embassy-stm32-wpan/CHANGELOG.md
index eb17856fc..a6089d6cf 100644
--- a/embassy-stm32-wpan/CHANGELOG.md
+++ b/embassy-stm32-wpan/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 10
11- refactor into wb55 crate and add feature for wba
11- wpan: restructure hil and test wpan mac 12- wpan: restructure hil and test wpan mac
12- restructure to allow embassy net driver to work. 13- restructure to allow embassy net driver to work.
13- First release with changelog. 14- First release with changelog.
diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml
index 05d76f4a6..103dedead 100644
--- a/embassy-stm32-wpan/Cargo.toml
+++ b/embassy-stm32-wpan/Cargo.toml
@@ -20,10 +20,10 @@ build = [
20src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/" 20src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/"
21src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/" 21src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/"
22target = "thumbv7em-none-eabihf" 22target = "thumbv7em-none-eabihf"
23features = ["stm32wb55rg", "ble", "mac"] 23features = ["stm32wb55rg", "wb55_ble", "wb55_mac"]
24 24
25[package.metadata.docs.rs] 25[package.metadata.docs.rs]
26features = ["stm32wb55rg", "ble", "mac"] 26features = ["stm32wb55rg", "wb55_ble", "wb55_mac"]
27 27
28[dependencies] 28[dependencies]
29embassy-stm32 = { version = "0.4.0", path = "../embassy-stm32" } 29embassy-stm32 = { version = "0.4.0", path = "../embassy-stm32" }
@@ -34,6 +34,7 @@ embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" }
34embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } 34embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" }
35embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } 35embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true }
36smoltcp = { version = "0.12.0", optional=true, default-features = false } 36smoltcp = { version = "0.12.0", optional=true, default-features = false }
37stm32-bindings = { version = "0.1.3", optional=true, default-features = false }
37 38
38defmt = { version = "1.0.1", optional = true } 39defmt = { version = "1.0.1", optional = true }
39log = { version = "0.4.17", optional = true } 40log = { version = "0.4.17", optional = true }
@@ -52,24 +53,63 @@ bitflags = { version = "2.3.3", optional = true }
52[features] 53[features]
53defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"] 54defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"]
54 55
55ble = ["dep:stm32wb-hci"] 56wb55 = []
56mac = ["dep:bitflags", "dep:embassy-net-driver", "dep:smoltcp", "smoltcp/medium-ieee802154"] 57wb55_ble = ["dep:stm32wb-hci"]
58wb55_mac = ["dep:bitflags", "dep:embassy-net-driver", "dep:smoltcp", "smoltcp/medium-ieee802154"]
59
60wba = [ "dep:stm32-bindings" ]
61wba_ble = [ "stm32-bindings/wba_wpan_mac" , "stm32-bindings/wba_wpan" ]
62wba_mac = [ "stm32-bindings/wba_wpan_mac", "stm32-bindings/wba_wpan_ble" , "stm32-bindings/lib_wba5_linklayer15_4", "stm32-bindings/lib_wba_mac_lib" , "stm32-bindings/wba_wpan" ]
57 63
58extended = [] 64extended = []
59 65
60stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] 66stm32wb10cc = [ "embassy-stm32/stm32wb10cc" , "wb55" ]
61stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ] 67stm32wb15cc = [ "embassy-stm32/stm32wb15cc" , "wb55" ]
62stm32wb30ce = [ "embassy-stm32/stm32wb30ce" ] 68stm32wb30ce = [ "embassy-stm32/stm32wb30ce" , "wb55" ]
63stm32wb35cc = [ "embassy-stm32/stm32wb35cc" ] 69stm32wb35cc = [ "embassy-stm32/stm32wb35cc" , "wb55" ]
64stm32wb35ce = [ "embassy-stm32/stm32wb35ce" ] 70stm32wb35ce = [ "embassy-stm32/stm32wb35ce" , "wb55" ]
65stm32wb50cg = [ "embassy-stm32/stm32wb50cg" ] 71stm32wb50cg = [ "embassy-stm32/stm32wb50cg" , "wb55" ]
66stm32wb55cc = [ "embassy-stm32/stm32wb55cc" ] 72stm32wb55cc = [ "embassy-stm32/stm32wb55cc" , "wb55" ]
67stm32wb55ce = [ "embassy-stm32/stm32wb55ce" ] 73stm32wb55ce = [ "embassy-stm32/stm32wb55ce" , "wb55" ]
68stm32wb55cg = [ "embassy-stm32/stm32wb55cg" ] 74stm32wb55cg = [ "embassy-stm32/stm32wb55cg" , "wb55" ]
69stm32wb55rc = [ "embassy-stm32/stm32wb55rc" ] 75stm32wb55rc = [ "embassy-stm32/stm32wb55rc" , "wb55" ]
70stm32wb55re = [ "embassy-stm32/stm32wb55re" ] 76stm32wb55re = [ "embassy-stm32/stm32wb55re" , "wb55" ]
71stm32wb55rg = [ "embassy-stm32/stm32wb55rg" ] 77stm32wb55rg = [ "embassy-stm32/stm32wb55rg" , "wb55" ]
72stm32wb55vc = [ "embassy-stm32/stm32wb55vc" ] 78stm32wb55vc = [ "embassy-stm32/stm32wb55vc" , "wb55" ]
73stm32wb55ve = [ "embassy-stm32/stm32wb55ve" ] 79stm32wb55ve = [ "embassy-stm32/stm32wb55ve" , "wb55" ]
74stm32wb55vg = [ "embassy-stm32/stm32wb55vg" ] 80stm32wb55vg = [ "embassy-stm32/stm32wb55vg" , "wb55" ]
75stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ] 81stm32wb55vy = [ "embassy-stm32/stm32wb55vy" , "wb55" ]
82stm32wba50ke = [ "embassy-stm32/stm32wba50ke", "wba" ]
83stm32wba50kg = [ "embassy-stm32/stm32wba50kg", "wba" ]
84stm32wba52ce = [ "embassy-stm32/stm32wba52ce", "wba" ]
85stm32wba52cg = [ "embassy-stm32/stm32wba52cg", "wba" ]
86stm32wba52ke = [ "embassy-stm32/stm32wba52ke", "wba" ]
87stm32wba52kg = [ "embassy-stm32/stm32wba52kg", "wba" ]
88stm32wba54ce = [ "embassy-stm32/stm32wba54ce", "wba" ]
89stm32wba54cg = [ "embassy-stm32/stm32wba54cg", "wba" ]
90stm32wba54ke = [ "embassy-stm32/stm32wba54ke", "wba" ]
91stm32wba54kg = [ "embassy-stm32/stm32wba54kg", "wba" ]
92stm32wba55ce = [ "embassy-stm32/stm32wba55ce", "wba" ]
93stm32wba55cg = [ "embassy-stm32/stm32wba55cg", "wba" ]
94stm32wba55he = [ "embassy-stm32/stm32wba55he", "wba" ]
95stm32wba55hg = [ "embassy-stm32/stm32wba55hg", "wba" ]
96stm32wba55ue = [ "embassy-stm32/stm32wba55ue", "wba" ]
97stm32wba55ug = [ "embassy-stm32/stm32wba55ug", "wba" ]
98stm32wba62cg = [ "embassy-stm32/stm32wba62cg", "wba" ]
99stm32wba62ci = [ "embassy-stm32/stm32wba62ci", "wba" ]
100stm32wba62mg = [ "embassy-stm32/stm32wba62mg", "wba" ]
101stm32wba62mi = [ "embassy-stm32/stm32wba62mi", "wba" ]
102stm32wba62pg = [ "embassy-stm32/stm32wba62pg", "wba" ]
103stm32wba62pi = [ "embassy-stm32/stm32wba62pi", "wba" ]
104stm32wba63cg = [ "embassy-stm32/stm32wba63cg", "wba" ]
105stm32wba63ci = [ "embassy-stm32/stm32wba63ci", "wba" ]
106stm32wba64cg = [ "embassy-stm32/stm32wba64cg", "wba" ]
107stm32wba64ci = [ "embassy-stm32/stm32wba64ci", "wba" ]
108stm32wba65cg = [ "embassy-stm32/stm32wba65cg", "wba" ]
109stm32wba65ci = [ "embassy-stm32/stm32wba65ci", "wba" ]
110stm32wba65mg = [ "embassy-stm32/stm32wba65mg", "wba" ]
111stm32wba65mi = [ "embassy-stm32/stm32wba65mi", "wba" ]
112stm32wba65pg = [ "embassy-stm32/stm32wba65pg", "wba" ]
113stm32wba65pi = [ "embassy-stm32/stm32wba65pi", "wba" ]
114stm32wba65rg = [ "embassy-stm32/stm32wba65rg", "wba" ]
115stm32wba65ri = [ "embassy-stm32/stm32wba65ri", "wba" ]
diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs
index 18b0feada..3fabe112a 100644
--- a/embassy-stm32-wpan/src/lib.rs
+++ b/embassy-stm32-wpan/src/lib.rs
@@ -18,201 +18,14 @@
18// #![warn(missing_docs)] 18// #![warn(missing_docs)]
19#![allow(static_mut_refs)] // TODO: Fix 19#![allow(static_mut_refs)] // TODO: Fix
20 20
21// This must go FIRST so that all the other modules see its macros. 21#[cfg(feature = "wb55")]
22mod fmt; 22mod wb55;
23 23
24use core::mem::MaybeUninit; 24#[cfg(feature = "wb55")]
25use core::sync::atomic::{Ordering, compiler_fence}; 25pub use wb55::*;
26 26
27use embassy_hal_internal::Peri; 27#[cfg(feature = "wba")]
28use embassy_stm32::interrupt; 28mod wba;
29use embassy_stm32::ipcc::{Config, Ipcc, IpccRxChannel, ReceiveInterruptHandler, TransmitInterruptHandler};
30use embassy_stm32::peripherals::IPCC;
31use sub::mm::MemoryManager;
32use sub::sys::Sys;
33use tables::*;
34use unsafe_linked_list::LinkedListNode;
35 29
36pub mod channels; 30#[cfg(feature = "wba")]
37pub mod cmd; 31pub use wba::*;
38pub mod consts;
39pub mod evt;
40pub mod lhci;
41pub mod shci;
42pub mod sub;
43pub mod tables;
44pub mod unsafe_linked_list;
45
46#[cfg(feature = "mac")]
47pub mod mac;
48
49#[cfg(feature = "ble")]
50pub use crate::sub::ble::hci;
51
52type PacketHeader = LinkedListNode;
53
54/// Transport Layer for the Mailbox interface
55pub struct TlMbox<'d> {
56 pub sys_subsystem: Sys<'d>,
57 pub mm_subsystem: MemoryManager<'d>,
58 #[cfg(feature = "ble")]
59 pub ble_subsystem: sub::ble::Ble<'d>,
60 #[cfg(feature = "mac")]
61 pub mac_subsystem: sub::mac::Mac<'d>,
62 pub traces: IpccRxChannel<'d>,
63}
64
65impl<'d> TlMbox<'d> {
66 /// Initialise the Transport Layer, and creates and returns a wrapper around it.
67 ///
68 /// This method performs the initialisation laid out in AN5289 annex 14.1. However, it differs
69 /// from the implementation documented in Figure 64, to avoid needing to reference any C
70 /// function pointers.
71 ///
72 /// Annex 14.1 lays out the following methods that should be called:
73 /// 1. tl_mbox.c/TL_Init, which initialises the reference table that is shared between CPU1
74 /// and CPU2.
75 /// 2. shci_tl.c/shci_init(), which initialises the system transport layer, and in turn
76 /// calls tl_mbox.c/TL_SYS_Init, which initialises SYSTEM_EVT_QUEUE channel.
77 /// 3. tl_mbox.c/TL_MM_Init(), which initialises the channel used for sending memory
78 /// manager commands.
79 /// 4. tl_mbox.c/TL_Enable(), which enables the IPCC, and starts CPU2.
80 /// This implementation initialises all of the shared refernce tables and all IPCC channel that
81 /// would be initialised by this process. The developer should therefore treat this method as
82 /// completing all steps in Figure 64.
83 ///
84 /// Once this method has been called, no system commands may be sent until the CPU2 ready
85 /// signal is received, via [sys_subsystem.read]; this completes the procedure laid out in
86 /// Figure 65.
87 ///
88 /// If the `ble` feature is enabled, at this point, the user should call
89 /// [sys_subsystem.shci_c2_ble_init], before any commands are written to the
90 /// [TlMbox.ble_subsystem] ([sub::ble::Ble::new()] completes the process that would otherwise
91 /// be handled by `TL_BLE_Init`; see Figure 66). This completes the procedure laid out in
92 /// Figure 66.
93 // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem
94 pub async fn init(
95 ipcc: Peri<'d, IPCC>,
96 _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler>
97 + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler>,
98 config: Config,
99 ) -> Self {
100 // this is an inlined version of TL_Init from the STM32WB firmware as requested by AN5289.
101 // HW_IPCC_Init is not called, and its purpose is (presumably?) covered by this
102 // implementation
103 unsafe {
104 TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable {
105 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(),
106 ble_table: TL_BLE_TABLE.as_ptr(),
107 thread_table: TL_THREAD_TABLE.as_ptr(),
108 sys_table: TL_SYS_TABLE.as_ptr(),
109 mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(),
110 traces_table: TL_TRACES_TABLE.as_ptr(),
111 mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(),
112 zigbee_table: TL_ZIGBEE_TABLE.as_ptr(),
113 lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(),
114 ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(),
115 });
116
117 TL_SYS_TABLE
118 .as_mut_ptr()
119 .write_volatile(MaybeUninit::zeroed().assume_init());
120 TL_DEVICE_INFO_TABLE
121 .as_mut_ptr()
122 .write_volatile(MaybeUninit::zeroed().assume_init());
123 TL_BLE_TABLE
124 .as_mut_ptr()
125 .write_volatile(MaybeUninit::zeroed().assume_init());
126 TL_THREAD_TABLE
127 .as_mut_ptr()
128 .write_volatile(MaybeUninit::zeroed().assume_init());
129 TL_MEM_MANAGER_TABLE
130 .as_mut_ptr()
131 .write_volatile(MaybeUninit::zeroed().assume_init());
132
133 TL_TRACES_TABLE
134 .as_mut_ptr()
135 .write_volatile(MaybeUninit::zeroed().assume_init());
136 TL_MAC_802_15_4_TABLE
137 .as_mut_ptr()
138 .write_volatile(MaybeUninit::zeroed().assume_init());
139 TL_ZIGBEE_TABLE
140 .as_mut_ptr()
141 .write_volatile(MaybeUninit::zeroed().assume_init());
142 TL_LLD_TESTS_TABLE
143 .as_mut_ptr()
144 .write_volatile(MaybeUninit::zeroed().assume_init());
145 TL_BLE_LLD_TABLE
146 .as_mut_ptr()
147 .write_volatile(MaybeUninit::zeroed().assume_init());
148
149 EVT_POOL
150 .as_mut_ptr()
151 .write_volatile(MaybeUninit::zeroed().assume_init());
152 SYS_SPARE_EVT_BUF
153 .as_mut_ptr()
154 .write_volatile(MaybeUninit::zeroed().assume_init());
155 CS_BUFFER
156 .as_mut_ptr()
157 .write_volatile(MaybeUninit::zeroed().assume_init());
158
159 #[cfg(feature = "ble")]
160 {
161 BLE_SPARE_EVT_BUF
162 .as_mut_ptr()
163 .write_volatile(MaybeUninit::zeroed().assume_init());
164
165 BLE_CMD_BUFFER
166 .as_mut_ptr()
167 .write_volatile(MaybeUninit::zeroed().assume_init());
168 HCI_ACL_DATA_BUFFER
169 .as_mut_ptr()
170 .write_volatile(MaybeUninit::zeroed().assume_init());
171 }
172
173 #[cfg(feature = "mac")]
174 {
175 MAC_802_15_4_CMD_BUFFER
176 .as_mut_ptr()
177 .write_volatile(MaybeUninit::zeroed().assume_init());
178 MAC_802_15_4_NOTIF_RSP_EVT_BUFFER
179 .as_mut_ptr()
180 .write_volatile(MaybeUninit::zeroed().assume_init());
181 }
182 }
183
184 compiler_fence(Ordering::SeqCst);
185
186 // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable`
187 let [
188 (_hw_ipcc_ble_cmd_channel, _ipcc_ble_event_channel),
189 (ipcc_system_cmd_rsp_channel, ipcc_system_event_channel),
190 (_ipcc_mac_802_15_4_cmd_rsp_channel, _ipcc_mac_802_15_4_notification_ack_channel),
191 (ipcc_mm_release_buffer_channel, _ipcc_traces_channel),
192 (_ipcc_ble_lld_cmd_channel, _ipcc_ble_lld_rsp_channel),
193 (_ipcc_hci_acl_data_channel, _),
194 ] = Ipcc::new(ipcc, _irqs, config).split();
195
196 let mm = sub::mm::MemoryManager::new(ipcc_mm_release_buffer_channel);
197 let mut sys = sub::sys::Sys::new(ipcc_system_cmd_rsp_channel, ipcc_system_event_channel);
198
199 debug!("sys event: {}", sys.read().await.payload());
200
201 Self {
202 sys_subsystem: sys,
203 #[cfg(feature = "ble")]
204 ble_subsystem: sub::ble::Ble::new(
205 _hw_ipcc_ble_cmd_channel,
206 _ipcc_ble_event_channel,
207 _ipcc_hci_acl_data_channel,
208 ),
209 #[cfg(feature = "mac")]
210 mac_subsystem: sub::mac::Mac::new(
211 _ipcc_mac_802_15_4_cmd_rsp_channel,
212 _ipcc_mac_802_15_4_notification_ack_channel,
213 ),
214 mm_subsystem: mm,
215 traces: _ipcc_traces_channel,
216 }
217 }
218}
diff --git a/embassy-stm32-wpan/src/sub/mod.rs b/embassy-stm32-wpan/src/sub/mod.rs
deleted file mode 100644
index bee3dbdf1..000000000
--- a/embassy-stm32-wpan/src/sub/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
1#[cfg(feature = "ble")]
2pub mod ble;
3#[cfg(feature = "mac")]
4pub mod mac;
5pub mod mm;
6pub mod sys;
diff --git a/embassy-stm32-wpan/src/channels.rs b/embassy-stm32-wpan/src/wb55/channels.rs
index 58f857136..58f857136 100644
--- a/embassy-stm32-wpan/src/channels.rs
+++ b/embassy-stm32-wpan/src/wb55/channels.rs
diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/wb55/cmd.rs
index 787c22c4b..34f02d6e7 100644
--- a/embassy-stm32-wpan/src/cmd.rs
+++ b/embassy-stm32-wpan/src/wb55/cmd.rs
@@ -1,8 +1,8 @@
1use core::ptr; 1use core::ptr;
2use core::sync::atomic::{Ordering, compiler_fence}; 2use core::sync::atomic::{Ordering, compiler_fence};
3 3
4use crate::PacketHeader;
5use crate::consts::TlPacketType; 4use crate::consts::TlPacketType;
5use crate::wb55::PacketHeader;
6 6
7#[derive(Copy, Clone)] 7#[derive(Copy, Clone)]
8#[repr(C, packed)] 8#[repr(C, packed)]
diff --git a/embassy-stm32-wpan/src/consts.rs b/embassy-stm32-wpan/src/wb55/consts.rs
index 7ecb22974..659e74e69 100644
--- a/embassy-stm32-wpan/src/consts.rs
+++ b/embassy-stm32-wpan/src/wb55/consts.rs
@@ -1,5 +1,5 @@
1use crate::PacketHeader;
2use crate::evt::CsEvt; 1use crate::evt::CsEvt;
2use crate::wb55::PacketHeader;
3 3
4#[derive(Debug)] 4#[derive(Debug)]
5#[repr(C)] 5#[repr(C)]
diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/wb55/evt.rs
index f32821269..f32821269 100644
--- a/embassy-stm32-wpan/src/evt.rs
+++ b/embassy-stm32-wpan/src/wb55/evt.rs
diff --git a/embassy-stm32-wpan/src/fmt.rs b/embassy-stm32-wpan/src/wb55/fmt.rs
index 8ca61bc39..8ca61bc39 100644
--- a/embassy-stm32-wpan/src/fmt.rs
+++ b/embassy-stm32-wpan/src/wb55/fmt.rs
diff --git a/embassy-stm32-wpan/src/lhci.rs b/embassy-stm32-wpan/src/wb55/lhci.rs
index 59c8bfb5d..59c8bfb5d 100644
--- a/embassy-stm32-wpan/src/lhci.rs
+++ b/embassy-stm32-wpan/src/wb55/lhci.rs
diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/wb55/mac/commands.rs
index d96f0094a..d96f0094a 100644
--- a/embassy-stm32-wpan/src/mac/commands.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/commands.rs
diff --git a/embassy-stm32-wpan/src/mac/consts.rs b/embassy-stm32-wpan/src/wb55/mac/consts.rs
index 56903d980..56903d980 100644
--- a/embassy-stm32-wpan/src/mac/consts.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/consts.rs
diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/wb55/mac/control.rs
index 14c6fdd2b..14c6fdd2b 100644
--- a/embassy-stm32-wpan/src/mac/control.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/control.rs
diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/wb55/mac/driver.rs
index 41171ce3d..41171ce3d 100644
--- a/embassy-stm32-wpan/src/mac/driver.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/driver.rs
diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/wb55/mac/event.rs
index 39856e185..39856e185 100644
--- a/embassy-stm32-wpan/src/mac/event.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/event.rs
diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/wb55/mac/indications.rs
index 5673514c9..5673514c9 100644
--- a/embassy-stm32-wpan/src/mac/indications.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/indications.rs
diff --git a/embassy-stm32-wpan/src/mac/macros.rs b/embassy-stm32-wpan/src/wb55/mac/macros.rs
index 1a988a779..1a988a779 100644
--- a/embassy-stm32-wpan/src/mac/macros.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/macros.rs
diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/wb55/mac/mod.rs
index ac50a6b29..ac50a6b29 100644
--- a/embassy-stm32-wpan/src/mac/mod.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/mod.rs
diff --git a/embassy-stm32-wpan/src/mac/opcodes.rs b/embassy-stm32-wpan/src/wb55/mac/opcodes.rs
index fd7011873..fd7011873 100644
--- a/embassy-stm32-wpan/src/mac/opcodes.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/opcodes.rs
diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/wb55/mac/responses.rs
index 544fdaae8..544fdaae8 100644
--- a/embassy-stm32-wpan/src/mac/responses.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/responses.rs
diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/wb55/mac/runner.rs
index 3b7d895df..3b7d895df 100644
--- a/embassy-stm32-wpan/src/mac/runner.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/runner.rs
diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/wb55/mac/typedefs.rs
index 175d4a37d..175d4a37d 100644
--- a/embassy-stm32-wpan/src/mac/typedefs.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/typedefs.rs
diff --git a/embassy-stm32-wpan/src/wb55/mod.rs b/embassy-stm32-wpan/src/wb55/mod.rs
new file mode 100644
index 000000000..95cfe09f1
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mod.rs
@@ -0,0 +1,198 @@
1// This must go FIRST so that all the other modules see its macros.
2mod fmt;
3
4use core::mem::MaybeUninit;
5use core::sync::atomic::{Ordering, compiler_fence};
6
7use embassy_hal_internal::Peri;
8use embassy_stm32::interrupt;
9use embassy_stm32::ipcc::{Config, Ipcc, IpccRxChannel, ReceiveInterruptHandler, TransmitInterruptHandler};
10use embassy_stm32::peripherals::IPCC;
11use sub::mm::MemoryManager;
12use sub::sys::Sys;
13use tables::*;
14use unsafe_linked_list::LinkedListNode;
15
16pub mod channels;
17pub mod cmd;
18pub mod consts;
19pub mod evt;
20pub mod lhci;
21pub mod shci;
22pub mod sub;
23pub mod tables;
24pub mod unsafe_linked_list;
25
26#[cfg(feature = "wb55_mac")]
27pub mod mac;
28
29#[cfg(feature = "wb55_ble")]
30pub use crate::sub::ble::hci;
31
32type PacketHeader = LinkedListNode;
33
34/// Transport Layer for the Mailbox interface
35pub struct TlMbox<'d> {
36 pub sys_subsystem: Sys<'d>,
37 pub mm_subsystem: MemoryManager<'d>,
38 #[cfg(feature = "wb55_ble")]
39 pub ble_subsystem: sub::ble::Ble<'d>,
40 #[cfg(feature = "wb55_mac")]
41 pub mac_subsystem: sub::mac::Mac<'d>,
42 pub traces: IpccRxChannel<'d>,
43}
44
45impl<'d> TlMbox<'d> {
46 /// Initialise the Transport Layer, and creates and returns a wrapper around it.
47 ///
48 /// This method performs the initialisation laid out in AN5289 annex 14.1. However, it differs
49 /// from the implementation documented in Figure 64, to avoid needing to reference any C
50 /// function pointers.
51 ///
52 /// Annex 14.1 lays out the following methods that should be called:
53 /// 1. tl_mbox.c/TL_Init, which initialises the reference table that is shared between CPU1
54 /// and CPU2.
55 /// 2. shci_tl.c/shci_init(), which initialises the system transport layer, and in turn
56 /// calls tl_mbox.c/TL_SYS_Init, which initialises SYSTEM_EVT_QUEUE channel.
57 /// 3. tl_mbox.c/TL_MM_Init(), which initialises the channel used for sending memory
58 /// manager commands.
59 /// 4. tl_mbox.c/TL_Enable(), which enables the IPCC, and starts CPU2.
60 /// This implementation initialises all of the shared refernce tables and all IPCC channel that
61 /// would be initialised by this process. The developer should therefore treat this method as
62 /// completing all steps in Figure 64.
63 ///
64 /// Once this method has been called, no system commands may be sent until the CPU2 ready
65 /// signal is received, via [sys_subsystem.read]; this completes the procedure laid out in
66 /// Figure 65.
67 ///
68 /// If the `ble` feature is enabled, at this point, the user should call
69 /// [sys_subsystem.shci_c2_ble_init], before any commands are written to the
70 /// [TlMbox.ble_subsystem] ([sub::ble::Ble::new()] completes the process that would otherwise
71 /// be handled by `TL_BLE_Init`; see Figure 66). This completes the procedure laid out in
72 /// Figure 66.
73 // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem
74 pub async fn init(
75 ipcc: Peri<'d, IPCC>,
76 _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler>
77 + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler>,
78 config: Config,
79 ) -> Self {
80 // this is an inlined version of TL_Init from the STM32WB firmware as requested by AN5289.
81 // HW_IPCC_Init is not called, and its purpose is (presumably?) covered by this
82 // implementation
83 unsafe {
84 TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable {
85 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(),
86 ble_table: TL_BLE_TABLE.as_ptr(),
87 thread_table: TL_THREAD_TABLE.as_ptr(),
88 sys_table: TL_SYS_TABLE.as_ptr(),
89 mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(),
90 traces_table: TL_TRACES_TABLE.as_ptr(),
91 mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(),
92 zigbee_table: TL_ZIGBEE_TABLE.as_ptr(),
93 lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(),
94 ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(),
95 });
96
97 TL_SYS_TABLE
98 .as_mut_ptr()
99 .write_volatile(MaybeUninit::zeroed().assume_init());
100 TL_DEVICE_INFO_TABLE
101 .as_mut_ptr()
102 .write_volatile(MaybeUninit::zeroed().assume_init());
103 TL_BLE_TABLE
104 .as_mut_ptr()
105 .write_volatile(MaybeUninit::zeroed().assume_init());
106 TL_THREAD_TABLE
107 .as_mut_ptr()
108 .write_volatile(MaybeUninit::zeroed().assume_init());
109 TL_MEM_MANAGER_TABLE
110 .as_mut_ptr()
111 .write_volatile(MaybeUninit::zeroed().assume_init());
112
113 TL_TRACES_TABLE
114 .as_mut_ptr()
115 .write_volatile(MaybeUninit::zeroed().assume_init());
116 TL_MAC_802_15_4_TABLE
117 .as_mut_ptr()
118 .write_volatile(MaybeUninit::zeroed().assume_init());
119 TL_ZIGBEE_TABLE
120 .as_mut_ptr()
121 .write_volatile(MaybeUninit::zeroed().assume_init());
122 TL_LLD_TESTS_TABLE
123 .as_mut_ptr()
124 .write_volatile(MaybeUninit::zeroed().assume_init());
125 TL_BLE_LLD_TABLE
126 .as_mut_ptr()
127 .write_volatile(MaybeUninit::zeroed().assume_init());
128
129 EVT_POOL
130 .as_mut_ptr()
131 .write_volatile(MaybeUninit::zeroed().assume_init());
132 SYS_SPARE_EVT_BUF
133 .as_mut_ptr()
134 .write_volatile(MaybeUninit::zeroed().assume_init());
135 CS_BUFFER
136 .as_mut_ptr()
137 .write_volatile(MaybeUninit::zeroed().assume_init());
138
139 #[cfg(feature = "wb55_ble")]
140 {
141 BLE_SPARE_EVT_BUF
142 .as_mut_ptr()
143 .write_volatile(MaybeUninit::zeroed().assume_init());
144
145 BLE_CMD_BUFFER
146 .as_mut_ptr()
147 .write_volatile(MaybeUninit::zeroed().assume_init());
148 HCI_ACL_DATA_BUFFER
149 .as_mut_ptr()
150 .write_volatile(MaybeUninit::zeroed().assume_init());
151 }
152
153 #[cfg(feature = "wb55_mac")]
154 {
155 MAC_802_15_4_CMD_BUFFER
156 .as_mut_ptr()
157 .write_volatile(MaybeUninit::zeroed().assume_init());
158 MAC_802_15_4_NOTIF_RSP_EVT_BUFFER
159 .as_mut_ptr()
160 .write_volatile(MaybeUninit::zeroed().assume_init());
161 }
162 }
163
164 compiler_fence(Ordering::SeqCst);
165
166 // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable`
167 let [
168 (_hw_ipcc_ble_cmd_channel, _ipcc_ble_event_channel),
169 (ipcc_system_cmd_rsp_channel, ipcc_system_event_channel),
170 (_ipcc_mac_802_15_4_cmd_rsp_channel, _ipcc_mac_802_15_4_notification_ack_channel),
171 (ipcc_mm_release_buffer_channel, _ipcc_traces_channel),
172 (_ipcc_ble_lld_cmd_channel, _ipcc_ble_lld_rsp_channel),
173 (_ipcc_hci_acl_data_channel, _),
174 ] = Ipcc::new(ipcc, _irqs, config).split();
175
176 let mm = sub::mm::MemoryManager::new(ipcc_mm_release_buffer_channel);
177 let mut sys = sub::sys::Sys::new(ipcc_system_cmd_rsp_channel, ipcc_system_event_channel);
178
179 debug!("sys event: {}", sys.read().await.payload());
180
181 Self {
182 sys_subsystem: sys,
183 #[cfg(feature = "wb55_ble")]
184 ble_subsystem: sub::ble::Ble::new(
185 _hw_ipcc_ble_cmd_channel,
186 _ipcc_ble_event_channel,
187 _ipcc_hci_acl_data_channel,
188 ),
189 #[cfg(feature = "wb55_mac")]
190 mac_subsystem: sub::mac::Mac::new(
191 _ipcc_mac_802_15_4_cmd_rsp_channel,
192 _ipcc_mac_802_15_4_notification_ack_channel,
193 ),
194 mm_subsystem: mm,
195 traces: _ipcc_traces_channel,
196 }
197 }
198}
diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/wb55/shci.rs
index 2d94a9cda..3faa79209 100644
--- a/embassy-stm32-wpan/src/shci.rs
+++ b/embassy-stm32-wpan/src/wb55/shci.rs
@@ -1,10 +1,10 @@
1use core::sync::atomic::{Ordering, compiler_fence}; 1use core::sync::atomic::{Ordering, compiler_fence};
2use core::{mem, ptr, slice}; 2use core::{mem, ptr, slice};
3 3
4use crate::PacketHeader;
5use crate::cmd::CmdPacket; 4use crate::cmd::CmdPacket;
6use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; 5use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
7use crate::evt::{CcEvt, EvtStub}; 6use crate::evt::{CcEvt, EvtStub};
7use crate::wb55::PacketHeader;
8 8
9const SHCI_OGF: u16 = 0x3F; 9const SHCI_OGF: u16 = 0x3F;
10 10
diff --git a/embassy-stm32-wpan/src/sub/ble.rs b/embassy-stm32-wpan/src/wb55/sub/ble.rs
index afc4a510a..afc4a510a 100644
--- a/embassy-stm32-wpan/src/sub/ble.rs
+++ b/embassy-stm32-wpan/src/wb55/sub/ble.rs
diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/wb55/sub/mac.rs
index ce2903e61..ce2903e61 100644
--- a/embassy-stm32-wpan/src/sub/mac.rs
+++ b/embassy-stm32-wpan/src/wb55/sub/mac.rs
diff --git a/embassy-stm32-wpan/src/sub/mm.rs b/embassy-stm32-wpan/src/wb55/sub/mm.rs
index aac252929..cbb5f130b 100644
--- a/embassy-stm32-wpan/src/sub/mm.rs
+++ b/embassy-stm32-wpan/src/wb55/sub/mm.rs
@@ -11,7 +11,7 @@ use embassy_sync::waitqueue::AtomicWaker;
11use crate::consts::POOL_SIZE; 11use crate::consts::POOL_SIZE;
12use crate::evt; 12use crate::evt;
13use crate::evt::EvtPacket; 13use crate::evt::EvtPacket;
14#[cfg(feature = "ble")] 14#[cfg(feature = "wb55_ble")]
15use crate::tables::BLE_SPARE_EVT_BUF; 15use crate::tables::BLE_SPARE_EVT_BUF;
16use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; 16use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE};
17use crate::unsafe_linked_list::LinkedListNode; 17use crate::unsafe_linked_list::LinkedListNode;
@@ -30,9 +30,9 @@ impl<'a> MemoryManager<'a> {
30 LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); 30 LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
31 31
32 TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable { 32 TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable {
33 #[cfg(feature = "ble")] 33 #[cfg(feature = "wb55_ble")]
34 spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), 34 spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(),
35 #[cfg(not(feature = "ble"))] 35 #[cfg(not(feature = "wb55_ble"))]
36 spare_ble_buffer: core::ptr::null(), 36 spare_ble_buffer: core::ptr::null(),
37 spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(), 37 spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(),
38 blepool: EVT_POOL.as_ptr().cast(), 38 blepool: EVT_POOL.as_ptr().cast(),
diff --git a/embassy-stm32-wpan/src/wb55/sub/mod.rs b/embassy-stm32-wpan/src/wb55/sub/mod.rs
new file mode 100644
index 000000000..d3ebd822a
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/sub/mod.rs
@@ -0,0 +1,6 @@
1#[cfg(feature = "wb55_ble")]
2pub mod ble;
3#[cfg(feature = "wb55_mac")]
4pub mod mac;
5pub mod mm;
6pub mod sys;
diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/wb55/sub/sys.rs
index 3ee539bb9..4376314c7 100644
--- a/embassy-stm32-wpan/src/sub/sys.rs
+++ b/embassy-stm32-wpan/src/wb55/sub/sys.rs
@@ -3,13 +3,13 @@ use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel};
3use crate::cmd::CmdPacket; 3use crate::cmd::CmdPacket;
4use crate::consts::TlPacketType; 4use crate::consts::TlPacketType;
5use crate::evt::EvtBox; 5use crate::evt::EvtBox;
6#[cfg(feature = "ble")] 6#[cfg(feature = "wb55_ble")]
7use crate::shci::ShciBleInitCmdParam; 7use crate::shci::ShciBleInitCmdParam;
8use crate::shci::{SchiCommandStatus, ShciOpcode}; 8use crate::shci::{SchiCommandStatus, ShciOpcode};
9use crate::sub::mm; 9use crate::sub::mm;
10use crate::tables::{SysTable, WirelessFwInfoTable}; 10use crate::tables::{SysTable, WirelessFwInfoTable};
11use crate::unsafe_linked_list::LinkedListNode; 11use crate::unsafe_linked_list::LinkedListNode;
12use crate::{SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; 12use crate::wb55::{SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE};
13 13
14/// A guard that, once constructed, allows for sys commands to be sent to CPU2. 14/// A guard that, once constructed, allows for sys commands to be sent to CPU2.
15pub struct Sys<'a> { 15pub struct Sys<'a> {
@@ -66,7 +66,7 @@ impl<'a> Sys<'a> {
66 unsafe { SchiCommandStatus::from_packet(SYS_CMD_BUF.as_ptr()) } 66 unsafe { SchiCommandStatus::from_packet(SYS_CMD_BUF.as_ptr()) }
67 } 67 }
68 68
69 #[cfg(feature = "mac")] 69 #[cfg(feature = "wb55_mac")]
70 pub async fn shci_c2_mac_802_15_4_init(&mut self) -> Result<SchiCommandStatus, ()> { 70 pub async fn shci_c2_mac_802_15_4_init(&mut self) -> Result<SchiCommandStatus, ()> {
71 self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await 71 self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await
72 } 72 }
@@ -77,7 +77,7 @@ impl<'a> Sys<'a> {
77 /// AN5289, Figures 65 and 66). It should only be called after CPU2 sends a system event, via 77 /// AN5289, Figures 65 and 66). It should only be called after CPU2 sends a system event, via
78 /// `HW_IPCC_SYS_EvtNot`, aka `IoBusCallBackUserEvt` (as detailed in Figure 65), aka 78 /// `HW_IPCC_SYS_EvtNot`, aka `IoBusCallBackUserEvt` (as detailed in Figure 65), aka
79 /// [crate::sub::ble::hci::host::uart::UartHci::read]. 79 /// [crate::sub::ble::hci::host::uart::UartHci::read].
80 #[cfg(feature = "ble")] 80 #[cfg(feature = "wb55_ble")]
81 pub async fn shci_c2_ble_init(&mut self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> { 81 pub async fn shci_c2_ble_init(&mut self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> {
82 self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await 82 self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await
83 } 83 }
diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/wb55/tables.rs
index 20d2c190f..2e6a9199b 100644
--- a/embassy-stm32-wpan/src/tables.rs
+++ b/embassy-stm32-wpan/src/wb55/tables.rs
@@ -4,7 +4,7 @@ use aligned::{A4, Aligned};
4use bit_field::BitField; 4use bit_field::BitField;
5 5
6use crate::cmd::{AclDataPacket, CmdPacket}; 6use crate::cmd::{AclDataPacket, CmdPacket};
7#[cfg(feature = "mac")] 7#[cfg(feature = "wb55_mac")]
8use crate::consts::C_SIZE_CMD_STRING; 8use crate::consts::C_SIZE_CMD_STRING;
9use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; 9use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
10use crate::unsafe_linked_list::LinkedListNode; 10use crate::unsafe_linked_list::LinkedListNode;
@@ -242,11 +242,11 @@ pub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(May
242pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed()); 242pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());
243 243
244// --------------------- app tables --------------------- 244// --------------------- app tables ---------------------
245#[cfg(feature = "mac")] 245#[cfg(feature = "wb55_mac")]
246#[unsafe(link_section = "MB_MEM2")] 246#[unsafe(link_section = "MB_MEM2")]
247pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed()); 247pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed());
248 248
249#[cfg(feature = "mac")] 249#[cfg(feature = "wb55_mac")]
250#[unsafe(link_section = "MB_MEM2")] 250#[unsafe(link_section = "MB_MEM2")]
251pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< 251pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<
252 Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>, 252 Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>,
@@ -262,21 +262,21 @@ pub static mut SYS_CMD_BUF: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeU
262pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = 262pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
263 Aligned(MaybeUninit::zeroed()); 263 Aligned(MaybeUninit::zeroed());
264 264
265#[cfg(feature = "mac")] 265#[cfg(feature = "wb55_mac")]
266#[unsafe(link_section = "MB_MEM2")] 266#[unsafe(link_section = "MB_MEM2")]
267pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> = 267pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> =
268 Aligned(MaybeUninit::zeroed()); 268 Aligned(MaybeUninit::zeroed());
269 269
270#[cfg(feature = "ble")] 270#[cfg(feature = "wb55_ble")]
271#[unsafe(link_section = "MB_MEM1")] 271#[unsafe(link_section = "MB_MEM1")]
272pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed()); 272pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed());
273 273
274#[cfg(feature = "ble")] 274#[cfg(feature = "wb55_ble")]
275#[unsafe(link_section = "MB_MEM2")] 275#[unsafe(link_section = "MB_MEM2")]
276pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = 276pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
277 Aligned(MaybeUninit::zeroed()); 277 Aligned(MaybeUninit::zeroed());
278 278
279#[cfg(feature = "ble")] 279#[cfg(feature = "wb55_ble")]
280#[unsafe(link_section = "MB_MEM2")] 280#[unsafe(link_section = "MB_MEM2")]
281// fuck these "magic" numbers from ST ---v---v 281// fuck these "magic" numbers from ST ---v---v
282pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> = 282pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> =
diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs
index d8bc29763..d8bc29763 100644
--- a/embassy-stm32-wpan/src/unsafe_linked_list.rs
+++ b/embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs
diff --git a/embassy-stm32-wpan/src/wba/bindings.rs b/embassy-stm32-wpan/src/wba/bindings.rs
new file mode 100644
index 000000000..d2030cfb8
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/bindings.rs
@@ -0,0 +1 @@
pub use stm32_bindings::bindings::{mac, wba_ble_stack as ble, wba_link_layer as link_layer};
diff --git a/embassy-stm32-wpan/src/wba/linklayer_plat.rs b/embassy-stm32-wpan/src/wba/linklayer_plat.rs
new file mode 100644
index 000000000..c011b3bcb
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/linklayer_plat.rs
@@ -0,0 +1,645 @@
1#[allow(dead_code)]
2fn test_fn() {}
3
4// /* USER CODE BEGIN Header */
5// /**
6// ******************************************************************************
7// * @file linklayer_plat.c
8// * @author MCD Application Team
9// * @brief Source file for the linklayer plateform adaptation layer
10// ******************************************************************************
11// * @attention
12// *
13// * Copyright (c) 2024 STMicroelectronics.
14// * All rights reserved.
15// *
16// * This software is licensed under terms that can be found in the LICENSE file
17// * in the root directory of this software component.
18// * If no LICENSE file comes with this software, it is provided AS-IS.
19// *
20// ******************************************************************************
21// */
22// /* USER CODE END Header */
23//
24// #include "stm32wbaxx_hal.h"
25// #include "stm32wbaxx_hal_conf.h"
26// #include "stm32wbaxx_ll_rcc.h"
27//
28// #include "app_common.h"
29// #include "app_conf.h"
30// #include "linklayer_plat.h"
31// #include "scm.h"
32// #include "log_module.h"
33// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
34// #include "adc_ctrl.h"
35// #endif /* (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1) */
36//
37// #if (CFG_LPM_LEVEL != 0)
38// #include "stm32_lpm.h"
39// #include "stm32_lpm_if.h"
40// #endif /* (CFG_LPM_LEVEL != 0) */
41//
42// /* USER CODE BEGIN Includes */
43//
44// /* USER CODE END Includes */
45//
46// #define max(a,b) ((a) > (b) ? a : b)
47//
48// /* 2.4GHz RADIO ISR callbacks */
49// void (*radio_callback)(void) = NULL;
50// void (*low_isr_callback)(void) = NULL;
51//
52// /* RNG handle */
53// extern RNG_HandleTypeDef hrng;
54//
55// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
56// /* Link Layer temperature request from background */
57// extern void ll_sys_bg_temperature_measurement(void);
58// #endif /* (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1) */
59//
60// /* Radio critical sections */
61// static uint32_t primask_bit = 0;
62// volatile int32_t prio_high_isr_counter = 0;
63// volatile int32_t prio_low_isr_counter = 0;
64// volatile int32_t prio_sys_isr_counter = 0;
65// volatile int32_t irq_counter = 0;
66// volatile uint32_t local_basepri_value = 0;
67//
68// /* Radio SW low ISR global variable */
69// volatile uint8_t radio_sw_low_isr_is_running_high_prio = 0;
70//
71// /* Radio bus clock control variables */
72// uint8_t AHB5_SwitchedOff = 0;
73// uint32_t radio_sleep_timer_val = 0;
74//
75// /**
76// * @brief Configure the necessary clock sources for the radio.
77// * @param None
78// * @retval None
79// */
80// void LINKLAYER_PLAT_ClockInit()
81// {
82// uint32_t linklayer_slp_clk_src = LL_RCC_RADIOSLEEPSOURCE_NONE;
83//
84// /* Get the Link Layer sleep timer clock source */
85// linklayer_slp_clk_src = LL_RCC_RADIO_GetSleepTimerClockSource();
86// if(linklayer_slp_clk_src == LL_RCC_RADIOSLEEPSOURCE_NONE)
87// {
88// /* If there is no clock source defined, should be selected before */
89// assert_param(0);
90// }
91//
92// /* Enable AHB5ENR peripheral clock (bus CLK) */
93// __HAL_RCC_RADIO_CLK_ENABLE();
94// }
95//
96// /**
97// * @brief Link Layer active waiting loop.
98// * @param delay: delay in us
99// * @retval None
100// */
101// void LINKLAYER_PLAT_DelayUs(uint32_t delay)
102// {
103// static uint8_t lock = 0;
104// uint32_t t0;
105// uint32_t primask_bit;
106//
107// /* Enter critical section */
108// primask_bit= __get_PRIMASK();
109// __disable_irq();
110//
111// if (lock == 0U)
112// {
113// /* Initialize counter */
114// /* Reset cycle counter to prevent overflow
115// As a us counter, it is assumed than even with re-entrancy,
116// overflow will never happen before re-initializing this counter */
117// DWT->CYCCNT = 0U;
118// /* Enable DWT by safety but should be useless (as already set) */
119// SET_BIT(DCB->DEMCR, DCB_DEMCR_TRCENA_Msk);
120// /* Enable counter */
121// SET_BIT(DWT->CTRL, DWT_CTRL_CYCCNTENA_Msk);
122// }
123// /* Increment 're-entrance' counter */
124// lock++;
125// /* Get starting time stamp */
126// t0 = DWT->CYCCNT;
127// /* Exit critical section */
128// __set_PRIMASK(primask_bit);
129//
130// /* Turn us into cycles */
131// delay = delay * (SystemCoreClock / 1000000U);
132// delay += t0;
133//
134// /* Busy waiting loop */
135// while (DWT->CYCCNT < delay)
136// {
137// };
138//
139// /* Enter critical section */
140// primask_bit= __get_PRIMASK();
141// __disable_irq();
142// if (lock == 1U)
143// {
144// /* Disable counter */
145// CLEAR_BIT(DWT->CTRL, DWT_CTRL_CYCCNTENA_Msk);
146// }
147// /* Decrement 're-entrance' counter */
148// lock--;
149// /* Exit critical section */
150// __set_PRIMASK(primask_bit);
151//
152// }
153//
154// /**
155// * @brief Link Layer assertion API
156// * @param condition: conditional statement to be checked.
157// * @retval None
158// */
159// void LINKLAYER_PLAT_Assert(uint8_t condition)
160// {
161// assert_param(condition);
162// }
163//
164// /**
165// * @brief Enable/disable the Link Layer active clock (baseband clock).
166// * @param enable: boolean value to enable (1) or disable (0) the clock.
167// * @retval None
168// */
169// void LINKLAYER_PLAT_WaitHclkRdy(void)
170// {
171// /* Wait on radio bus clock readiness if it has been turned of */
172// if (AHB5_SwitchedOff == 1)
173// {
174// AHB5_SwitchedOff = 0;
175// while (radio_sleep_timer_val == ll_intf_cmn_get_slptmr_value());
176// }
177// }
178//
179// /**
180// * @brief Notify the Link Layer platform layer the system will enter in WFI
181// * and AHB5 clock may be turned of regarding the 2.4Ghz radio state.
182// * @param None
183// * @retval None
184// */
185// void LINKLAYER_PLAT_NotifyWFIEnter(void)
186// {
187// /* Check if Radio state will allow the AHB5 clock to be cut */
188//
189// /* AHB5 clock will be cut in the following cases:
190// * - 2.4GHz radio is not in ACTIVE mode (in SLEEP or DEEPSLEEP mode).
191// * - RADIOSMEN and STRADIOCLKON bits are at 0.
192// */
193// if((LL_PWR_GetRadioMode() != LL_PWR_RADIO_ACTIVE_MODE) ||
194// ((__HAL_RCC_RADIO_IS_CLK_SLEEP_ENABLED() == 0) && (LL_RCC_RADIO_IsEnabledSleepTimerClock() == 0)))
195// {
196// AHB5_SwitchedOff = 1;
197// }
198// }
199//
200// /**
201// * @brief Notify the Link Layer platform layer the system exited WFI and AHB5
202// * clock may be resynchronized as is may have been turned of during
203// * low power mode entry.
204// * @param None
205// * @retval None
206// */
207// void LINKLAYER_PLAT_NotifyWFIExit(void)
208// {
209// /* Check if AHB5 clock has been turned of and needs resynchronisation */
210// if (AHB5_SwitchedOff)
211// {
212// /* Read sleep register as earlier as possible */
213// radio_sleep_timer_val = ll_intf_cmn_get_slptmr_value();
214// }
215// }
216//
217// /**
218// * @brief Active wait on bus clock readiness.
219// * @param None
220// * @retval None
221// */
222// void LINKLAYER_PLAT_AclkCtrl(uint8_t enable)
223// {
224// if(enable != 0u)
225// {
226// #if (CFG_SCM_SUPPORTED == 1)
227// /* SCM HSE BEGIN */
228// /* Polling on HSE32 activation */
229// SCM_HSE_WaitUntilReady();
230// /* Enable RADIO baseband clock (active CLK) */
231// HAL_RCCEx_EnableRadioBBClock();
232// /* SCM HSE END */
233// #else
234// /* Enable RADIO baseband clock (active CLK) */
235// HAL_RCCEx_EnableRadioBBClock();
236// /* Polling on HSE32 activation */
237// while ( LL_RCC_HSE_IsReady() == 0);
238// #endif /* CFG_SCM_SUPPORTED */
239// }
240// else
241// {
242// /* Disable RADIO baseband clock (active CLK) */
243// HAL_RCCEx_DisableRadioBBClock();
244// }
245// }
246//
247// /**
248// * @brief Link Layer RNG request.
249// * @param ptr_rnd: pointer to the variable that hosts the number.
250// * @param len: number of byte of anthropy to get.
251// * @retval None
252// */
253// void LINKLAYER_PLAT_GetRNG(uint8_t *ptr_rnd, uint32_t len)
254// {
255// uint32_t nb_remaining_rng = len;
256// uint32_t generated_rng;
257//
258// /* Get the requested RNGs (4 bytes by 4bytes) */
259// while(nb_remaining_rng >= 4)
260// {
261// generated_rng = 0;
262// HW_RNG_Get(1, &generated_rng);
263// memcpy((ptr_rnd+(len-nb_remaining_rng)), &generated_rng, 4);
264// nb_remaining_rng -=4;
265// }
266//
267// /* Get the remaining number of RNGs */
268// if(nb_remaining_rng>0){
269// generated_rng = 0;
270// HW_RNG_Get(1, &generated_rng);
271// memcpy((ptr_rnd+(len-nb_remaining_rng)), &generated_rng, nb_remaining_rng);
272// }
273// }
274//
275// /**
276// * @brief Initialize Link Layer radio high priority interrupt.
277// * @param intr_cb: function pointer to assign for the radio high priority ISR routine.
278// * @retval None
279// */
280// void LINKLAYER_PLAT_SetupRadioIT(void (*intr_cb)())
281// {
282// radio_callback = intr_cb;
283// HAL_NVIC_SetPriority((IRQn_Type) RADIO_INTR_NUM, RADIO_INTR_PRIO_HIGH, 0);
284// HAL_NVIC_EnableIRQ((IRQn_Type) RADIO_INTR_NUM);
285// }
286//
287// /**
288// * @brief Initialize Link Layer SW low priority interrupt.
289// * @param intr_cb: function pointer to assign for the SW low priority ISR routine.
290// * @retval None
291// */
292// void LINKLAYER_PLAT_SetupSwLowIT(void (*intr_cb)())
293// {
294// low_isr_callback = intr_cb;
295//
296// HAL_NVIC_SetPriority((IRQn_Type) RADIO_SW_LOW_INTR_NUM, RADIO_SW_LOW_INTR_PRIO, 0);
297// HAL_NVIC_EnableIRQ((IRQn_Type) RADIO_SW_LOW_INTR_NUM);
298// }
299//
300// /**
301// * @brief Trigger the link layer SW low interrupt.
302// * @param None
303// * @retval None
304// */
305// void LINKLAYER_PLAT_TriggerSwLowIT(uint8_t priority)
306// {
307// uint8_t low_isr_priority = RADIO_INTR_PRIO_LOW;
308//
309// /* Check if a SW low interrupt as already been raised.
310// * Nested call far radio low isr are not supported
311// **/
312//
313// if(NVIC_GetActive(RADIO_SW_LOW_INTR_NUM) == 0)
314// {
315// /* No nested SW low ISR, default behavior */
316//
317// if(priority == 0)
318// {
319// low_isr_priority = RADIO_SW_LOW_INTR_PRIO;
320// }
321//
322// HAL_NVIC_SetPriority((IRQn_Type) RADIO_SW_LOW_INTR_NUM, low_isr_priority, 0);
323// }
324// else
325// {
326// /* Nested call detected */
327// /* No change for SW radio low interrupt priority for the moment */
328//
329// if(priority != 0)
330// {
331// /* At the end of current SW radio low ISR, this pending SW low interrupt
332// * will run with RADIO_INTR_PRIO_LOW priority
333// **/
334// radio_sw_low_isr_is_running_high_prio = 1;
335// }
336// }
337//
338// HAL_NVIC_SetPendingIRQ((IRQn_Type) RADIO_SW_LOW_INTR_NUM);
339// }
340//
341// /**
342// * @brief Enable interrupts.
343// * @param None
344// * @retval None
345// */
346// void LINKLAYER_PLAT_EnableIRQ(void)
347// {
348// irq_counter = max(0,irq_counter-1);
349//
350// if(irq_counter == 0)
351// {
352// /* When irq_counter reaches 0, restore primask bit */
353// __set_PRIMASK(primask_bit);
354// }
355// }
356//
357// /**
358// * @brief Disable interrupts.
359// * @param None
360// * @retval None
361// */
362// void LINKLAYER_PLAT_DisableIRQ(void)
363// {
364// if(irq_counter == 0)
365// {
366// /* Save primask bit at first interrupt disablement */
367// primask_bit= __get_PRIMASK();
368// }
369// __disable_irq();
370// irq_counter ++;
371// }
372//
373// /**
374// * @brief Enable specific interrupt group.
375// * @param isr_type: mask for interrupt group to enable.
376// * This parameter can be one of the following:
377// * @arg LL_HIGH_ISR_ONLY: enable link layer high priority ISR.
378// * @arg LL_LOW_ISR_ONLY: enable link layer SW low priority ISR.
379// * @arg SYS_LOW_ISR: mask interrupts for all the other system ISR with
380// * lower priority that link layer SW low interrupt.
381// * @retval None
382// */
383// void LINKLAYER_PLAT_EnableSpecificIRQ(uint8_t isr_type)
384// {
385// if( (isr_type & LL_HIGH_ISR_ONLY) != 0 )
386// {
387// prio_high_isr_counter--;
388// if(prio_high_isr_counter == 0)
389// {
390// /* When specific counter for link layer high ISR reaches 0, interrupt is enabled */
391// HAL_NVIC_EnableIRQ(RADIO_INTR_NUM);
392// /* USER CODE BEGIN LINKLAYER_PLAT_EnableSpecificIRQ_1 */
393//
394// /* USER CODE END LINKLAYER_PLAT_EnableSpecificIRQ_1 */
395// }
396// }
397//
398// if( (isr_type & LL_LOW_ISR_ONLY) != 0 )
399// {
400// prio_low_isr_counter--;
401// if(prio_low_isr_counter == 0)
402// {
403// /* When specific counter for link layer SW low ISR reaches 0, interrupt is enabled */
404// HAL_NVIC_EnableIRQ(RADIO_SW_LOW_INTR_NUM);
405// }
406//
407// }
408//
409// if( (isr_type & SYS_LOW_ISR) != 0 )
410// {
411// prio_sys_isr_counter--;
412// if(prio_sys_isr_counter == 0)
413// {
414// /* Restore basepri value */
415// __set_BASEPRI(local_basepri_value);
416// }
417// }
418// }
419//
420// /**
421// * @brief Disable specific interrupt group.
422// * @param isr_type: mask for interrupt group to disable.
423// * This parameter can be one of the following:
424// * @arg LL_HIGH_ISR_ONLY: disable link layer high priority ISR.
425// * @arg LL_LOW_ISR_ONLY: disable link layer SW low priority ISR.
426// * @arg SYS_LOW_ISR: unmask interrupts for all the other system ISR with
427// * lower priority that link layer SW low interrupt.
428// * @retval None
429// */
430// void LINKLAYER_PLAT_DisableSpecificIRQ(uint8_t isr_type)
431// {
432// if( (isr_type & LL_HIGH_ISR_ONLY) != 0 )
433// {
434// prio_high_isr_counter++;
435// if(prio_high_isr_counter == 1)
436// {
437// /* USER CODE BEGIN LINKLAYER_PLAT_DisableSpecificIRQ_1 */
438//
439// /* USER CODE END LINKLAYER_PLAT_DisableSpecificIRQ_1 */
440// /* When specific counter for link layer high ISR value is 1, interrupt is disabled */
441// HAL_NVIC_DisableIRQ(RADIO_INTR_NUM);
442// }
443// }
444//
445// if( (isr_type & LL_LOW_ISR_ONLY) != 0 )
446// {
447// prio_low_isr_counter++;
448// if(prio_low_isr_counter == 1)
449// {
450// /* When specific counter for link layer SW low ISR value is 1, interrupt is disabled */
451// HAL_NVIC_DisableIRQ(RADIO_SW_LOW_INTR_NUM);
452// }
453// }
454//
455// if( (isr_type & SYS_LOW_ISR) != 0 )
456// {
457// prio_sys_isr_counter++;
458// if(prio_sys_isr_counter == 1)
459// {
460// /* Save basepri register value */
461// local_basepri_value = __get_BASEPRI();
462//
463// /* Mask all other interrupts with lower priority that link layer SW low ISR */
464// __set_BASEPRI_MAX(RADIO_INTR_PRIO_LOW<<4);
465// }
466// }
467// }
468//
469// /**
470// * @brief Enable link layer high priority ISR only.
471// * @param None
472// * @retval None
473// */
474// void LINKLAYER_PLAT_EnableRadioIT(void)
475// {
476// /* USER CODE BEGIN LINKLAYER_PLAT_EnableRadioIT_1 */
477//
478// /* USER CODE END LINKLAYER_PLAT_EnableRadioIT_1 */
479//
480// HAL_NVIC_EnableIRQ((IRQn_Type) RADIO_INTR_NUM);
481//
482// /* USER CODE BEGIN LINKLAYER_PLAT_EnableRadioIT_2 */
483//
484// /* USER CODE END LINKLAYER_PLAT_EnableRadioIT_2 */
485// }
486//
487// /**
488// * @brief Disable link layer high priority ISR only.
489// * @param None
490// * @retval None
491// */
492// void LINKLAYER_PLAT_DisableRadioIT(void)
493// {
494// /* USER CODE BEGIN LINKLAYER_PLAT_DisableRadioIT_1 */
495//
496// /* USER CODE END LINKLAYER_PLAT_DisableRadioIT_1 */
497//
498// HAL_NVIC_DisableIRQ((IRQn_Type) RADIO_INTR_NUM);
499//
500// /* USER CODE BEGIN LINKLAYER_PLAT_DisableRadioIT_2 */
501//
502// /* USER CODE END LINKLAYER_PLAT_DisableRadioIT_2 */
503// }
504//
505// /**
506// * @brief Link Layer notification for radio activity start.
507// * @param None
508// * @retval None
509// */
510// void LINKLAYER_PLAT_StartRadioEvt(void)
511// {
512// __HAL_RCC_RADIO_CLK_SLEEP_ENABLE();
513// NVIC_SetPriority(RADIO_INTR_NUM, RADIO_INTR_PRIO_HIGH);
514// #if (CFG_SCM_SUPPORTED == 1)
515// scm_notifyradiostate(SCM_RADIO_ACTIVE);
516// #endif /* CFG_SCM_SUPPORTED */
517// }
518//
519// /**
520// * @brief Link Layer notification for radio activity end.
521// * @param None
522// * @retval None
523// */
524// void LINKLAYER_PLAT_StopRadioEvt(void)
525// {
526// __HAL_RCC_RADIO_CLK_SLEEP_DISABLE();
527// NVIC_SetPriority(RADIO_INTR_NUM, RADIO_INTR_PRIO_LOW);
528// #if (CFG_SCM_SUPPORTED == 1)
529// scm_notifyradiostate(SCM_RADIO_NOT_ACTIVE);
530// #endif /* CFG_SCM_SUPPORTED */
531// }
532//
533// /**
534// * @brief Link Layer notification for RCO calibration start.
535// * @param None
536// * @retval None
537// */
538// void LINKLAYER_PLAT_RCOStartClbr(void)
539// {
540// #if (CFG_LPM_LEVEL != 0)
541// PWR_DisableSleepMode();
542// /* Disabling stop mode prevents also from entering in standby */
543// UTIL_LPM_SetStopMode(1U << CFG_LPM_LL_HW_RCO_CLBR, UTIL_LPM_DISABLE);
544// #endif /* (CFG_LPM_LEVEL != 0) */
545// #if (CFG_SCM_SUPPORTED == 1)
546// scm_setsystemclock(SCM_USER_LL_HW_RCO_CLBR, HSE_32MHZ);
547// while (LL_PWR_IsActiveFlag_VOS() == 0);
548// #endif /* (CFG_SCM_SUPPORTED == 1) */
549// }
550//
551// /**
552// * @brief Link Layer notification for RCO calibration end.
553// * @param None
554// * @retval None
555// */
556// void LINKLAYER_PLAT_RCOStopClbr(void)
557// {
558// #if (CFG_LPM_LEVEL != 0)
559// PWR_EnableSleepMode();
560// UTIL_LPM_SetStopMode(1U << CFG_LPM_LL_HW_RCO_CLBR, UTIL_LPM_ENABLE);
561// #endif /* (CFG_LPM_LEVEL != 0) */
562// #if (CFG_SCM_SUPPORTED == 1)
563// scm_setsystemclock(SCM_USER_LL_HW_RCO_CLBR, HSE_16MHZ);
564// #endif /* (CFG_SCM_SUPPORTED == 1) */
565// }
566//
567// /**
568// * @brief Link Layer requests temperature.
569// * @param None
570// * @retval None
571// */
572// void LINKLAYER_PLAT_RequestTemperature(void)
573// {
574// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
575// ll_sys_bg_temperature_measurement();
576// #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */
577// }
578//
579// /**
580// * @brief PHY Start calibration.
581// * @param None
582// * @retval None
583// */
584// void LINKLAYER_PLAT_PhyStartClbr(void)
585// {
586// /* USER CODE BEGIN LINKLAYER_PLAT_PhyStartClbr_0 */
587//
588// /* USER CODE END LINKLAYER_PLAT_PhyStartClbr_0 */
589//
590// /* USER CODE BEGIN LINKLAYER_PLAT_PhyStartClbr_1 */
591//
592// /* USER CODE END LINKLAYER_PLAT_PhyStartClbr_1 */
593// }
594//
595// /**
596// * @brief PHY Stop calibration.
597// * @param None
598// * @retval None
599// */
600// void LINKLAYER_PLAT_PhyStopClbr(void)
601// {
602// /* USER CODE BEGIN LINKLAYER_PLAT_PhyStopClbr_0 */
603//
604// /* USER CODE END LINKLAYER_PLAT_PhyStopClbr_0 */
605//
606// /* USER CODE BEGIN LINKLAYER_PLAT_PhyStopClbr_1 */
607//
608// /* USER CODE END LINKLAYER_PLAT_PhyStopClbr_1 */
609// }
610//
611// /**
612// * @brief Notify the upper layer that new Link Layer timings have been applied.
613// * @param evnt_timing[in]: Evnt_timing_t pointer to structure contains drift time , execution time and scheduling time
614// * @retval None.
615// */
616// void LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT(Evnt_timing_t * p_evnt_timing)
617// {
618// /* USER CODE BEGIN LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT_0 */
619//
620// /* USER CODE END LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT_0 */
621// }
622//
623// /**
624// * @brief Get the ST company ID.
625// * @param None
626// * @retval Company ID
627// */
628// uint32_t LINKLAYER_PLAT_GetSTCompanyID(void)
629// {
630// return LL_FLASH_GetSTCompanyID();
631// }
632//
633// /**
634// * @brief Get the Unique Device Number (UDN).
635// * @param None
636// * @retval UDN
637// */
638// uint32_t LINKLAYER_PLAT_GetUDN(void)
639// {
640// return LL_FLASH_GetUDN();
641// }
642//
643// /* USER CODE BEGIN LINKLAYER_PLAT 0 */
644//
645// /* USER CODE END LINKLAYER_PLAT 0 */
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_cs.rs b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_cs.rs
new file mode 100644
index 000000000..30103ba27
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_cs.rs
@@ -0,0 +1,77 @@
1use crate::bindings::link_layer::{
2 LINKLAYER_PLAT_DisableIRQ, LINKLAYER_PLAT_DisableSpecificIRQ, LINKLAYER_PLAT_EnableIRQ,
3 LINKLAYER_PLAT_EnableSpecificIRQ, LINKLAYER_PLAT_PhyStartClbr, LINKLAYER_PLAT_PhyStopClbr,
4};
5
6// /**
7// ******************************************************************************
8// * @file ll_sys_cs.c
9// * @author MCD Application Team
10// * @brief Link Layer IP system interface critical sections management
11// ******************************************************************************
12// * @attention
13// *
14// * Copyright (c) 2022 STMicroelectronics.
15// * All rights reserved.
16// *
17// * This software is licensed under terms that can be found in the LICENSE file
18// * in the root directory of this software component.
19// * If no LICENSE file comes with this software, it is provided AS-IS.
20// *
21// ******************************************************************************
22// */
23//
24// #include "linklayer_plat.h"
25// #include "ll_sys.h"
26// #include <stdint.h>
27//
28/**
29 * @brief Enable interrupts
30 * @param None
31 * @retval None
32 */
33#[unsafe(no_mangle)]
34unsafe extern "C" fn ll_sys_enable_irq() {
35 LINKLAYER_PLAT_EnableIRQ();
36}
37//
38// /**
39// * @brief Disable interrupts
40// * @param None
41// * @retval None
42// */
43#[unsafe(no_mangle)]
44unsafe extern "C" fn ll_sys_disable_irq() {
45 LINKLAYER_PLAT_DisableIRQ();
46}
47//
48// /**
49// * @brief Set the Current Interrupt Priority Mask.
50// * All interrupts with low priority level will be masked.
51// * @param None
52// * @retval None
53// */
54#[unsafe(no_mangle)]
55unsafe extern "C" fn ll_sys_enable_specific_irq(isr_type: u8) {
56 LINKLAYER_PLAT_EnableSpecificIRQ(isr_type);
57}
58//
59// /**
60// * @brief Restore the previous interrupt priority level
61// * @param None
62// * @retval None
63// */
64#[unsafe(no_mangle)]
65unsafe extern "C" fn ll_sys_disable_specific_irq(isr_type: u8) {
66 LINKLAYER_PLAT_DisableSpecificIRQ(isr_type);
67}
68//
69#[unsafe(no_mangle)]
70unsafe extern "C" fn ll_sys_phy_start_clbr() {
71 LINKLAYER_PLAT_PhyStartClbr();
72}
73//
74#[unsafe(no_mangle)]
75unsafe extern "C" fn ll_sys_phy_stop_clbr() {
76 LINKLAYER_PLAT_PhyStopClbr();
77}
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_dp_slp.rs b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_dp_slp.rs
new file mode 100644
index 000000000..ae8223a5a
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_dp_slp.rs
@@ -0,0 +1,163 @@
1use crate::bindings::link_layer::{
2 _NULL as NULL, DPSLP_STATE_DEEP_SLEEP_DISABLE, DPSLP_STATE_DEEP_SLEEP_ENABLE, LINKLAYER_PLAT_DisableRadioIT,
3 LINKLAYER_PLAT_EnableRadioIT, LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED,
4 LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_ENABLED, LL_SYS_STATUS_T_LL_SYS_ERROR, LL_SYS_STATUS_T_LL_SYS_OK,
5 OS_TIMER_PRIO_HG_PRIO_TMR, OS_TIMER_STATE_OSTIMERSTOPPED, OS_TIMER_TYPE_OS_TIMER_ONCE, SUCCESS, ble_stat_t,
6 ll_intf_cmn_le_set_dp_slp_mode, ll_sys_dp_slp_state_t, ll_sys_status_t, os_get_tmr_state, os_timer_create,
7 os_timer_id, os_timer_set_prio, os_timer_start, os_timer_stop,
8};
9
10macro_rules! LL_DP_SLP_NO_WAKEUP {
11 () => {
12 !0u32
13 };
14}
15
16macro_rules! LL_INTERNAL_TMR_US_TO_STEPS {
17 ($us:expr) => {
18 ((($us) * 4) / 125)
19 };
20}
21
22// /**
23// ******************************************************************************
24// * @file ll_sys_dp_slp.c
25// * @author MCD Application Team
26// * @brief Link Layer IP system interface deep sleep management
27// ******************************************************************************
28// * @attention
29// *
30// * Copyright (c) 2022 STMicroelectronics.
31// * All rights reserved.
32// *
33// * This software is licensed under terms that can be found in the LICENSE file
34// * in the root directory of this software component.
35// * If no LICENSE file comes with this software, it is provided AS-IS.
36// *
37// ******************************************************************************
38// */
39//
40// #include "linklayer_plat.h"
41// #include "ll_sys.h"
42// #include "ll_intf_cmn.h"
43//
44// /* Link Layer deep sleep timer */
45static mut RADIO_DP_SLP_TMR_ID: os_timer_id = NULL as *mut _;
46//
47// /* Link Layer deep sleep state */
48static mut LINKLAYER_DP_SLP_STATE: ll_sys_dp_slp_state_t = LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED;
49//
50// /**
51// * @brief Initialize resources to handle deep sleep entry/exit
52// * @param None
53// * @retval LL_SYS status
54// */
55#[unsafe(no_mangle)]
56unsafe extern "C" fn ll_sys_dp_slp_init() -> ll_sys_status_t {
57 let mut return_status: ll_sys_status_t = LL_SYS_STATUS_T_LL_SYS_ERROR;
58
59 /* Create link layer timer for handling IP DEEP SLEEP mode */
60 RADIO_DP_SLP_TMR_ID = os_timer_create(
61 Some(ll_sys_dp_slp_wakeup_evt_clbk),
62 OS_TIMER_TYPE_OS_TIMER_ONCE,
63 NULL as *mut _,
64 );
65
66 /* Set priority of deep sleep timer */
67 os_timer_set_prio(RADIO_DP_SLP_TMR_ID, OS_TIMER_PRIO_HG_PRIO_TMR);
68
69 if RADIO_DP_SLP_TMR_ID != NULL as *mut _ {
70 return_status = LL_SYS_STATUS_T_LL_SYS_OK;
71 }
72
73 return return_status;
74}
75//
76// /**
77// * @brief Link Layer deep sleep status getter
78// * @param None
79// * @retval Link Layer deep sleep state
80// */
81#[unsafe(no_mangle)]
82unsafe extern "C" fn ll_sys_dp_slp_get_state() -> ll_sys_dp_slp_state_t {
83 return LINKLAYER_DP_SLP_STATE;
84}
85//
86// /**
87// * @brief The Link Layer IP enters deep sleep mode
88// * @param dp_slp_duration deep sleep duration in us
89// * @retval LL_SYS status
90// */
91#[unsafe(no_mangle)]
92unsafe extern "C" fn ll_sys_dp_slp_enter(dp_slp_duration: u32) -> ll_sys_status_t {
93 let cmd_status: ble_stat_t;
94 let os_status: i32;
95 let mut return_status: ll_sys_status_t = LL_SYS_STATUS_T_LL_SYS_ERROR;
96
97 /* Check if deep sleep timer has to be started */
98 if dp_slp_duration < LL_DP_SLP_NO_WAKEUP!() {
99 /* Start deep sleep timer */
100 os_status = os_timer_start(RADIO_DP_SLP_TMR_ID, LL_INTERNAL_TMR_US_TO_STEPS!(dp_slp_duration));
101 } else {
102 /* No timer started */
103 os_status = SUCCESS as i32;
104 }
105
106 if os_status == SUCCESS as i32 {
107 /* Switch Link Layer IP to DEEP SLEEP mode */
108 cmd_status = ll_intf_cmn_le_set_dp_slp_mode(DPSLP_STATE_DEEP_SLEEP_ENABLE as u8);
109 if cmd_status == SUCCESS {
110 LINKLAYER_DP_SLP_STATE = LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_ENABLED;
111 return_status = LL_SYS_STATUS_T_LL_SYS_OK;
112 }
113 }
114
115 return return_status;
116}
117//
118// /**
119// * @brief The Link Layer IP exits deep sleep mode
120// * @param None
121// * @retval LL_SYS status
122// */
123#[unsafe(no_mangle)]
124unsafe extern "C" fn ll_sys_dp_slp_exit() -> ll_sys_status_t {
125 let cmd_status: ble_stat_t;
126 let mut return_status: ll_sys_status_t = LL_SYS_STATUS_T_LL_SYS_ERROR;
127
128 /* Disable radio interrupt */
129 LINKLAYER_PLAT_DisableRadioIT();
130
131 if LINKLAYER_DP_SLP_STATE == LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED {
132 /* Radio not in sleep mode */
133 return_status = LL_SYS_STATUS_T_LL_SYS_OK;
134 } else {
135 /* Switch Link Layer IP to SLEEP mode (by deactivate DEEP SLEEP mode) */
136 cmd_status = ll_intf_cmn_le_set_dp_slp_mode(DPSLP_STATE_DEEP_SLEEP_DISABLE as u8);
137 if cmd_status == SUCCESS {
138 LINKLAYER_DP_SLP_STATE = LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED;
139 return_status = LL_SYS_STATUS_T_LL_SYS_OK;
140 }
141
142 /* Stop the deep sleep wake-up timer if running */
143 if os_get_tmr_state(RADIO_DP_SLP_TMR_ID) != OS_TIMER_STATE_OSTIMERSTOPPED {
144 os_timer_stop(RADIO_DP_SLP_TMR_ID);
145 }
146 }
147
148 /* Re-enable radio interrupt */
149 LINKLAYER_PLAT_EnableRadioIT();
150
151 return return_status;
152}
153
154/**
155 * @brief Link Layer deep sleep wake-up timer callback
156 * @param ptr_arg pointer passed through the callback
157 * @retval LL_SYS status
158 */
159#[unsafe(no_mangle)]
160unsafe extern "C" fn ll_sys_dp_slp_wakeup_evt_clbk(_ptr_arg: *const ::core::ffi::c_void) {
161 /* Link Layer IP exits from DEEP SLEEP mode */
162 ll_sys_dp_slp_exit();
163}
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_intf.rs b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_intf.rs
new file mode 100644
index 000000000..0b4b0b37f
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_intf.rs
@@ -0,0 +1,199 @@
1use crate::bindings::link_layer::{
2 Evnt_timing_t, HostStack_Process, LINKLAYER_PLAT_AclkCtrl, LINKLAYER_PLAT_Assert, LINKLAYER_PLAT_ClockInit,
3 LINKLAYER_PLAT_DelayUs, LINKLAYER_PLAT_GetRNG, LINKLAYER_PLAT_RCOStartClbr, LINKLAYER_PLAT_RCOStopClbr,
4 LINKLAYER_PLAT_RequestTemperature, LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT, LINKLAYER_PLAT_SetupRadioIT,
5 LINKLAYER_PLAT_SetupSwLowIT, LINKLAYER_PLAT_StartRadioEvt, LINKLAYER_PLAT_StopRadioEvt,
6 LINKLAYER_PLAT_TriggerSwLowIT, LINKLAYER_PLAT_WaitHclkRdy, MAX_NUM_CNCRT_STAT_MCHNS, emngr_can_mcu_sleep,
7 emngr_handle_all_events, ll_sys_schedule_bg_process,
8};
9
10// /**
11// ******************************************************************************
12// * @file ll_sys_intf.c
13// * @author MCD Application Team
14// * @brief Link Layer IP general system interface
15// ******************************************************************************
16// * @attention
17// *
18// * Copyright (c) 2022 STMicroelectronics.
19// * All rights reserved.
20// *
21// * This software is licensed under terms that can be found in the LICENSE file
22// * in the root directory of this software component.
23// * If no LICENSE file comes with this software, it is provided AS-IS.
24// *
25// ******************************************************************************
26// */
27// #include <stdint.h>
28//
29// #include "ll_sys.h"
30// #include "linklayer_plat.h"
31// #include "event_manager.h"
32// #include "ll_intf.h"
33//
34/**
35 * @brief Initialize the Link Layer SoC dependencies
36 * @param None
37 * @retval None
38 */
39#[unsafe(no_mangle)]
40unsafe extern "C" fn ll_sys_init() {
41 LINKLAYER_PLAT_ClockInit();
42}
43//
44/**
45 * @brief Blocking delay in us
46 * @param None
47 * @retval None
48 */
49#[unsafe(no_mangle)]
50unsafe extern "C" fn ll_sys_delay_us(delay: u32) {
51 LINKLAYER_PLAT_DelayUs(delay);
52}
53
54/**
55 * @brief Assert checking
56 * @param None
57 * @retval None
58 */
59#[unsafe(no_mangle)]
60unsafe extern "C" fn ll_sys_assert(condition: u8) {
61 LINKLAYER_PLAT_Assert(condition);
62}
63
64/**
65 * @brief Radio active clock management
66 * @param None
67 * @retval None
68 */
69#[unsafe(no_mangle)]
70unsafe extern "C" fn ll_sys_radio_ack_ctrl(enable: u8) {
71 LINKLAYER_PLAT_AclkCtrl(enable);
72}
73
74/**
75 * @brief Link Layer waits for radio bus clock ready
76 * @param None
77 * @retval None
78 */
79#[unsafe(no_mangle)]
80unsafe extern "C" fn ll_sys_radio_wait_for_busclkrdy() {
81 LINKLAYER_PLAT_WaitHclkRdy();
82}
83
84/**
85 * @brief Get RNG number for the Link Layer IP
86 * @param None
87 * @retval None
88 */
89#[unsafe(no_mangle)]
90unsafe extern "C" fn ll_sys_get_rng(ptr_rnd: *mut u8, len: u32) {
91 LINKLAYER_PLAT_GetRNG(ptr_rnd, len);
92}
93
94/**
95 * @brief Initialize the main radio interrupt
96 * @param intr_cb radio interrupt callback to link with the radio IRQ
97 * @retval None
98 */
99#[unsafe(no_mangle)]
100unsafe extern "C" fn ll_sys_setup_radio_intr(intr_cb: ::core::option::Option<unsafe extern "C" fn()>) {
101 LINKLAYER_PLAT_SetupRadioIT(intr_cb);
102}
103
104/**
105 * @brief Initialize the radio SW low interrupt
106 * @param intr_cb radio SW low interrupt interrupt callback to link
107 * with the defined interrupt vector
108 * @retval None
109 */
110#[unsafe(no_mangle)]
111unsafe extern "C" fn ll_sys_setup_radio_sw_low_intr(intr_cb: ::core::option::Option<unsafe extern "C" fn()>) {
112 LINKLAYER_PLAT_SetupSwLowIT(intr_cb);
113}
114
115/**
116 * @brief Trigger the radio SW low interrupt
117 * @param None
118 * @retval None
119 */
120#[unsafe(no_mangle)]
121unsafe extern "C" fn ll_sys_radio_sw_low_intr_trigger(priority: u8) {
122 LINKLAYER_PLAT_TriggerSwLowIT(priority);
123}
124
125/**
126 * @brief Link Layer radio activity event notification
127 * @param start start/end of radio event
128 * @retval None
129 */
130#[unsafe(no_mangle)]
131unsafe extern "C" fn ll_sys_radio_evt_not(start: u8) {
132 if start != 0 {
133 LINKLAYER_PLAT_StartRadioEvt();
134 } else {
135 LINKLAYER_PLAT_StopRadioEvt();
136 }
137}
138
139/**
140 * @brief Link Layer RCO calibration notification
141 * @param start start/end of RCO calibration
142 * @retval None
143 */
144#[unsafe(no_mangle)]
145unsafe extern "C" fn ll_sys_rco_clbr_not(start: u8) {
146 if start != 0 {
147 LINKLAYER_PLAT_RCOStartClbr();
148 } else {
149 LINKLAYER_PLAT_RCOStopClbr();
150 }
151}
152
153/**
154 * @brief Link Layer temperature request
155 * @param None
156 * @retval None
157 */
158#[unsafe(no_mangle)]
159unsafe extern "C" fn ll_sys_request_temperature() {
160 LINKLAYER_PLAT_RequestTemperature();
161}
162
163/**
164 * @brief Link Layer background task pcoessing procedure
165 * @param None
166 * @retval None
167 */
168#[unsafe(no_mangle)]
169unsafe extern "C" fn ll_sys_bg_process() {
170 if emngr_can_mcu_sleep() == 0 {
171 emngr_handle_all_events();
172
173 HostStack_Process();
174 }
175
176 if emngr_can_mcu_sleep() == 0 {
177 ll_sys_schedule_bg_process();
178 }
179}
180
181#[unsafe(no_mangle)]
182unsafe extern "C" fn ll_sys_schldr_timing_update_not(p_evnt_timing: *mut Evnt_timing_t) {
183 LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT(p_evnt_timing);
184}
185
186/**
187 * @brief Get the number of concurrent state machines for the Link Layer
188 * @param None
189 * @retval Supported number of concurrent state machines
190 */
191#[unsafe(no_mangle)]
192unsafe extern "C" fn ll_sys_get_concurrent_state_machines_num() -> u8 {
193 return MAX_NUM_CNCRT_STAT_MCHNS as u8;
194}
195//
196// __WEAK void HostStack_Process(void)
197// {
198//
199// }
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_startup.rs b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_startup.rs
new file mode 100644
index 000000000..074aaeafe
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_startup.rs
@@ -0,0 +1,125 @@
1use crate::bindings::link_layer::{
2 _NULL as NULL, LL_SYS_STATUS_T_LL_SYS_OK, ble_buff_hdr_p, hci_dispatch_tbl, hci_get_dis_tbl, hst_cbk, ll_intf_init,
3 ll_intf_rgstr_hst_cbk, ll_intf_rgstr_hst_cbk_ll_queue_full, ll_sys_assert, ll_sys_bg_process_init,
4 ll_sys_config_params, ll_sys_dp_slp_init, ll_sys_status_t,
5};
6use crate::bindings::mac::ST_MAC_preInit;
7// /**
8// ******************************************************************************
9// * @file ll_sys_startup.c
10// * @author MCD Application Team
11// * @brief Link Layer IP system interface startup module
12// ******************************************************************************
13// * @attention
14// *
15// * Copyright (c) 2022 STMicroelectronics.
16// * All rights reserved.
17// *
18// * This software is licensed under terms that can be found in the LICENSE file
19// * in the root directory of this software component.
20// * If no LICENSE file comes with this software, it is provided AS-IS.
21// *
22// ******************************************************************************
23// */
24//
25// #include "ll_fw_config.h"
26// #include "ll_sys.h"
27// #include "ll_intf.h"
28// #include "ll_sys_startup.h"
29// #include "common_types.h"
30// #if defined(MAC)
31// #ifndef OPENTHREAD_CONFIG_FILE
32// /* Projects with MAC Layer (i.e. 15.4 except Thread) */
33// #include "st_mac_802_15_4_sap.h"
34// #endif /* OPENTHREAD_CONFIG_FILE */
35// #endif /* MAC */
36//
37
38#[allow(dead_code)]
39/**
40 * @brief Missed HCI event flag
41 */
42static mut MISSED_HCI_EVENT_FLAG: u8 = 0;
43
44// static void ll_sys_dependencies_init(void);
45// #if SUPPORT_BLE
46
47#[cfg(feature = "wba_ble")]
48#[allow(dead_code)]
49unsafe extern "C" fn ll_sys_event_missed_cb(_ptr_evnt_hdr: ble_buff_hdr_p) {
50 MISSED_HCI_EVENT_FLAG = 1;
51}
52
53#[cfg(feature = "wba_ble")]
54/**
55 * @brief Initialize the Link Layer IP BLE controller
56 * @param None
57 * @retval None
58 */
59#[unsafe(no_mangle)]
60unsafe extern "C" fn ll_sys_ble_cntrl_init(host_callback: hst_cbk) {
61 let p_hci_dis_tbl: *const hci_dispatch_tbl = NULL as *const _;
62
63 hci_get_dis_tbl(&p_hci_dis_tbl as *const *const _ as *mut *const _);
64
65 ll_intf_init(p_hci_dis_tbl);
66
67 ll_intf_rgstr_hst_cbk(host_callback);
68
69 ll_intf_rgstr_hst_cbk_ll_queue_full(Some(ll_sys_event_missed_cb));
70
71 ll_sys_dependencies_init();
72}
73// #endif /* SUPPORT_BLE */
74// #if defined(MAC)
75// #ifndef OPENTHREAD_CONFIG_FILE
76#[cfg(feature = "wba_mac")]
77/**
78 * @brief Initialize the Link Layer IP 802.15.4 MAC controller
79 * @param None
80 * @retval None
81 */
82#[unsafe(no_mangle)]
83unsafe extern "C" fn ll_sys_mac_cntrl_init() {
84 ST_MAC_preInit();
85 ll_sys_dependencies_init();
86}
87// #endif /* OPENTHREAD_CONFIG_FILE */
88// #endif /* MAC */
89/**
90 * @brief Start the Link Layer IP in OpenThread configuration
91 * @param None
92 * @retval None
93 */
94#[unsafe(no_mangle)]
95unsafe extern "C" fn ll_sys_thread_init() {
96 ll_sys_dependencies_init();
97}
98
99/**
100 * @brief Initialize the Link Layer resources for startup.
101 * This includes: - Deep Sleep feature resources
102 * - Link Layer background task
103 * @param None
104 * @retval None
105 */
106unsafe fn ll_sys_dependencies_init() {
107 static mut IS_LL_INITIALIZED: u8 = 0;
108 let dp_slp_status: ll_sys_status_t;
109
110 /* Ensure Link Layer resources are created only once */
111 if IS_LL_INITIALIZED == 1 {
112 return;
113 }
114 IS_LL_INITIALIZED = 1;
115
116 /* Deep sleep feature initialization */
117 dp_slp_status = ll_sys_dp_slp_init();
118 ll_sys_assert((dp_slp_status == LL_SYS_STATUS_T_LL_SYS_OK) as u8);
119
120 /* Background task initialization */
121 ll_sys_bg_process_init();
122
123 /* Link Layer user parameters application */
124 ll_sys_config_params();
125}
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/ll_version.rs b/embassy-stm32-wpan/src/wba/ll_sys/ll_version.rs
new file mode 100644
index 000000000..a42e8cc67
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/ll_version.rs
@@ -0,0 +1,115 @@
1use crate::bindings::link_layer::{
2 LL_SYS_BRIEF_VERSION_MAJOR, LL_SYS_BRIEF_VERSION_MAJOR_MASK, LL_SYS_BRIEF_VERSION_MAJOR_POS,
3 LL_SYS_BRIEF_VERSION_MINOR, LL_SYS_BRIEF_VERSION_MINOR_MASK, LL_SYS_BRIEF_VERSION_MINOR_POS,
4 LL_SYS_BRIEF_VERSION_PATCH, LL_SYS_BRIEF_VERSION_PATCH_MASK, LL_SYS_BRIEF_VERSION_PATCH_POS,
5};
6
7// /**
8// ******************************************************************************
9// * @file ll_version.c
10// * @author MCD Application Team
11// * @brief Link Layer version interface
12// ******************************************************************************
13// * @attention
14// *
15// * Copyright (c) 2025 STMicroelectronics.
16// * All rights reserved.
17// *
18// * This software is licensed under terms that can be found in the LICENSE file
19// * in the root directory of this software component.
20// * If no LICENSE file comes with this software, it is provided AS-IS.
21// *
22// ******************************************************************************
23// */
24//
25// /* Includes ------------------------------------------------------------------*/
26// /* Integer types */
27// #include <stdint.h>
28//
29// /* Own header file */
30// #include "ll_version.h"
31//
32// /* Temporary header file for version tracking */
33// #include "ll_tmp_version.h"
34//
35// /* Private defines -----------------------------------------------------------*/
36// /**
37// * @brief Magic keyword to identify the system version when debugging
38// */
39// #define LL_SYS_MAGIC_KEYWORD 0xDEADBEEF
40
41const LL_SYS_MAGIC_KEYWORD: u32 = 0xDEADBEEF;
42
43//
44// /* Private macros ------------------------------------------------------------*/
45// /* Macro to set a specific field value */
46// #define LL_SYS_SET_FIELD_VALUE(value, mask, pos) \
47// (((value) << (pos)) & (mask))
48
49macro_rules! LL_SYS_SET_FIELD_VALUE {
50 ($value:expr, $mask:expr, $pos:expr) => {
51 ((($value) << ($pos)) & ($mask))
52 };
53}
54
55//
56// /* Private typedef -----------------------------------------------------------*/
57// /**
58// * @brief Link Layer system version structure definition
59// */
60#[allow(non_camel_case_types)]
61struct ll_sys_version_t {
62 #[allow(unused)]
63 magic_key_word: u32, /* Magic key word to identify the system version */
64 version: u32, /* System version - i.e.: short hash of latest commit */
65}
66//
67// /* Private variables ---------------------------------------------------------*/
68// /**
69// * @brief Link Layer brief version definition
70// */
71const LL_SYS_BRIEF_VERSION: u8 = LL_SYS_SET_FIELD_VALUE!(
72 LL_SYS_BRIEF_VERSION_MAJOR as u8,
73 LL_SYS_BRIEF_VERSION_MAJOR_MASK as u8,
74 LL_SYS_BRIEF_VERSION_MAJOR_POS as u8
75) | LL_SYS_SET_FIELD_VALUE!(
76 LL_SYS_BRIEF_VERSION_MINOR as u8,
77 LL_SYS_BRIEF_VERSION_MINOR_MASK as u8,
78 LL_SYS_BRIEF_VERSION_MINOR_POS as u8
79) | LL_SYS_SET_FIELD_VALUE!(
80 LL_SYS_BRIEF_VERSION_PATCH as u8,
81 LL_SYS_BRIEF_VERSION_PATCH_MASK as u8,
82 LL_SYS_BRIEF_VERSION_PATCH_POS as u8
83);
84//
85// /**
86// * @brief Link Layer system version structure definition
87// */
88const LL_SYS_SYSTEM_VERSION: ll_sys_version_t = ll_sys_version_t {
89 magic_key_word: LL_SYS_MAGIC_KEYWORD,
90 version: 0, // LL_SYS_SYSTEM_VERSION,
91};
92//
93// /**
94// * @brief Link Layer source version structure definition
95// */
96const LL_SYS_SOURCE_VERSION: ll_sys_version_t = ll_sys_version_t {
97 magic_key_word: LL_SYS_MAGIC_KEYWORD,
98 version: 0, // LL_SYS_SOURCE_VERSION
99};
100//
101// /* Functions Definition ------------------------------------------------------*/
102#[unsafe(no_mangle)]
103unsafe extern "C" fn ll_sys_get_brief_fw_version() -> u8 {
104 return LL_SYS_BRIEF_VERSION;
105}
106
107#[unsafe(no_mangle)]
108unsafe extern "C" fn ll_sys_get_system_fw_version() -> u32 {
109 return LL_SYS_SYSTEM_VERSION.version;
110}
111
112#[unsafe(no_mangle)]
113unsafe extern "C" fn ll_sys_get_source_fw_version() -> u32 {
114 return LL_SYS_SOURCE_VERSION.version;
115}
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/mod.rs b/embassy-stm32-wpan/src/wba/ll_sys/mod.rs
new file mode 100644
index 000000000..45e196c96
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/mod.rs
@@ -0,0 +1,5 @@
1mod ll_sys_cs;
2mod ll_sys_dp_slp;
3mod ll_sys_intf;
4mod ll_sys_startup;
5mod ll_version;
diff --git a/embassy-stm32-wpan/src/wba/ll_sys_if.rs b/embassy-stm32-wpan/src/wba/ll_sys_if.rs
new file mode 100644
index 000000000..7218b69c4
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys_if.rs
@@ -0,0 +1,416 @@
1#![cfg(feature = "wba")]
2// /* USER CODE BEGIN Header */
3// /**
4// ******************************************************************************
5// * @file ll_sys_if.c
6// * @author MCD Application Team
7// * @brief Source file for initiating system
8// ******************************************************************************
9// * @attention
10// *
11// * Copyright (c) 2022 STMicroelectronics.
12// * All rights reserved.
13// *
14// * This software is licensed under terms that can be found in the LICENSE file
15// * in the root directory of this software component.
16// * If no LICENSE file comes with this software, it is provided AS-IS.
17// *
18// ******************************************************************************
19// */
20// /* USER CODE END Header */
21//
22// #include "main.h"
23// #include "app_common.h"
24// #include "app_conf.h"
25// #include "log_module.h"
26// #include "ll_intf_cmn.h"
27// #include "ll_sys.h"
28// #include "ll_sys_if.h"
29// #include "stm32_rtos.h"
30// #include "utilities_common.h"
31// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
32// #include "temp_measurement.h"
33// #endif /* (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1) */
34// #if (CFG_LPM_STANDBY_SUPPORTED == 0)
35// extern void profile_reset(void);
36// #endif
37// /* Private defines -----------------------------------------------------------*/
38// /* Radio event scheduling method - must be set at 1 */
39// #define USE_RADIO_LOW_ISR (1)
40// #define NEXT_EVENT_SCHEDULING_FROM_ISR (1)
41//
42// /* USER CODE BEGIN PD */
43//
44// /* USER CODE END PD */
45//
46// /* Private macros ------------------------------------------------------------*/
47// /* USER CODE BEGIN PM */
48//
49// /* USER CODE END PM */
50//
51// /* Private constants ---------------------------------------------------------*/
52// /* USER CODE BEGIN PC */
53//
54// /* USER CODE END PC */
55//
56// /* Private variables ---------------------------------------------------------*/
57// /* USER CODE BEGIN PV */
58//
59// /* USER CODE END PV */
60//
61// /* Global variables ----------------------------------------------------------*/
62//
63// /* USER CODE BEGIN GV */
64//
65// /* USER CODE END GV */
66//
67// /* Private functions prototypes-----------------------------------------------*/
68// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
69// static void ll_sys_bg_temperature_measurement_init(void);
70// #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */
71// static void ll_sys_sleep_clock_source_selection(void);
72// static uint8_t ll_sys_BLE_sleep_clock_accuracy_selection(void);
73// void ll_sys_reset(void);
74//
75// /* USER CODE BEGIN PFP */
76//
77// /* USER CODE END PFP */
78//
79// /* External variables --------------------------------------------------------*/
80//
81// /* USER CODE BEGIN EV */
82//
83// /* USER CODE END EV */
84//
85// /* Functions Definition ------------------------------------------------------*/
86//
87// /**
88// * @brief Link Layer background process initialization
89// * @param None
90// * @retval None
91// */
92// void ll_sys_bg_process_init(void)
93// {
94// /* Register Link Layer task */
95// UTIL_SEQ_RegTask(1U << CFG_TASK_LINK_LAYER, UTIL_SEQ_RFU, ll_sys_bg_process);
96// }
97//
98// /**
99// * @brief Link Layer background process next iteration scheduling
100// * @param None
101// * @retval None
102// */
103// void ll_sys_schedule_bg_process(void)
104// {
105// UTIL_SEQ_SetTask(1U << CFG_TASK_LINK_LAYER, TASK_PRIO_LINK_LAYER);
106// }
107//
108// /**
109// * @brief Link Layer background process next iteration scheduling from ISR
110// * @param None
111// * @retval None
112// */
113// void ll_sys_schedule_bg_process_isr(void)
114// {
115// UTIL_SEQ_SetTask(1U << CFG_TASK_LINK_LAYER, TASK_PRIO_LINK_LAYER);
116// }
117//
118// /**
119// * @brief Link Layer configuration phase before application startup.
120// * @param None
121// * @retval None
122// */
123// void ll_sys_config_params(void)
124// {
125// /* USER CODE BEGIN ll_sys_config_params_0 */
126//
127// /* USER CODE END ll_sys_config_params_0 */
128//
129// /* Configure link layer behavior for low ISR use and next event scheduling method:
130// * - SW low ISR is used.
131// * - Next event is scheduled from ISR.
132// */
133// ll_intf_cmn_config_ll_ctx_params(USE_RADIO_LOW_ISR, NEXT_EVENT_SCHEDULING_FROM_ISR);
134// /* Apply the selected link layer sleep timer source */
135// ll_sys_sleep_clock_source_selection();
136//
137// /* USER CODE BEGIN ll_sys_config_params_1 */
138//
139// /* USER CODE END ll_sys_config_params_1 */
140//
141// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
142// /* Initialize link layer temperature measurement background task */
143// ll_sys_bg_temperature_measurement_init();
144//
145// /* Link layer IP uses temperature based calibration instead of periodic one */
146// ll_intf_cmn_set_temperature_sensor_state();
147// #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */
148//
149// /* Link Layer power table */
150// ll_intf_cmn_select_tx_power_table(CFG_RF_TX_POWER_TABLE_ID);
151//
152// #if (USE_CTE_DEGRADATION == 1u)
153// /* Apply CTE degradation */
154// ll_sys_apply_cte_settings ();
155// #endif /* (USE_CTE_DEGRADATION == 1u) */
156//
157// /* USER CODE BEGIN ll_sys_config_params_2 */
158//
159// /* USER CODE END ll_sys_config_params_2 */
160// }
161//
162// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
163//
164// /**
165// * @brief Link Layer temperature request background process initialization
166// * @param None
167// * @retval None
168// */
169// void ll_sys_bg_temperature_measurement_init(void)
170// {
171// /* Register Temperature Measurement task */
172// UTIL_SEQ_RegTask(1U << CFG_TASK_TEMP_MEAS, UTIL_SEQ_RFU, TEMPMEAS_RequestTemperatureMeasurement);
173// }
174//
175// /**
176// * @brief Request backroud task processing for temperature measurement
177// * @param None
178// * @retval None
179// */
180// void ll_sys_bg_temperature_measurement(void)
181// {
182// static uint8_t initial_temperature_acquisition = 0;
183//
184// if(initial_temperature_acquisition == 0)
185// {
186// TEMPMEAS_RequestTemperatureMeasurement();
187// initial_temperature_acquisition = 1;
188// }
189// else
190// {
191// UTIL_SEQ_SetTask(1U << CFG_TASK_TEMP_MEAS, CFG_SEQ_PRIO_0);
192// }
193// }
194//
195// #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */
196//
197// uint8_t ll_sys_BLE_sleep_clock_accuracy_selection(void)
198// {
199// uint8_t BLE_sleep_clock_accuracy = 0;
200// #if (CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE == 0)
201// uint32_t RevID = LL_DBGMCU_GetRevisionID();
202// #endif
203// uint32_t linklayer_slp_clk_src = LL_RCC_RADIO_GetSleepTimerClockSource();
204//
205// if(linklayer_slp_clk_src == LL_RCC_RADIOSLEEPSOURCE_LSE)
206// {
207// /* LSE selected as Link Layer sleep clock source.
208// Sleep clock accuracy is different regarding the WBA device ID and revision
209// */
210// #if (CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE == 0)
211// #if defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx)
212// if(RevID == REV_ID_A)
213// {
214// BLE_sleep_clock_accuracy = STM32WBA5x_REV_ID_A_SCA_RANGE;
215// }
216// else if(RevID == REV_ID_B)
217// {
218// BLE_sleep_clock_accuracy = STM32WBA5x_REV_ID_B_SCA_RANGE;
219// }
220// else
221// {
222// /* Revision ID not supported, default value of 500ppm applied */
223// BLE_sleep_clock_accuracy = STM32WBA5x_DEFAULT_SCA_RANGE;
224// }
225// #elif defined(STM32WBA65xx)
226// BLE_sleep_clock_accuracy = STM32WBA6x_SCA_RANGE;
227// UNUSED(RevID);
228// #else
229// UNUSED(RevID);
230// #endif /* defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx) */
231// #else /* CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE */
232// BLE_sleep_clock_accuracy = CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE;
233// #endif /* CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE */
234// }
235// else
236// {
237// /* LSE is not the Link Layer sleep clock source, sleep clock accurcay default value is 500 ppm */
238// BLE_sleep_clock_accuracy = STM32WBA5x_DEFAULT_SCA_RANGE;
239// }
240//
241// return BLE_sleep_clock_accuracy;
242// }
243//
244// void ll_sys_sleep_clock_source_selection(void)
245// {
246// uint16_t freq_value = 0;
247// uint32_t linklayer_slp_clk_src = LL_RCC_RADIOSLEEPSOURCE_NONE;
248//
249// linklayer_slp_clk_src = LL_RCC_RADIO_GetSleepTimerClockSource();
250// switch(linklayer_slp_clk_src)
251// {
252// case LL_RCC_RADIOSLEEPSOURCE_LSE:
253// linklayer_slp_clk_src = RTC_SLPTMR;
254// break;
255//
256// case LL_RCC_RADIOSLEEPSOURCE_LSI:
257// linklayer_slp_clk_src = RCO_SLPTMR;
258// break;
259//
260// case LL_RCC_RADIOSLEEPSOURCE_HSE_DIV1000:
261// linklayer_slp_clk_src = CRYSTAL_OSCILLATOR_SLPTMR;
262// break;
263//
264// case LL_RCC_RADIOSLEEPSOURCE_NONE:
265// /* No Link Layer sleep clock source selected */
266// assert_param(0);
267// break;
268// }
269// ll_intf_cmn_le_select_slp_clk_src((uint8_t)linklayer_slp_clk_src, &freq_value);
270// }
271//
272// void ll_sys_reset(void)
273// {
274// uint8_t bsca = 0;
275// /* Link layer timings */
276// uint8_t drift_time = DRIFT_TIME_DEFAULT;
277// uint8_t exec_time = EXEC_TIME_DEFAULT;
278//
279// /* USER CODE BEGIN ll_sys_reset_0 */
280//
281// /* USER CODE END ll_sys_reset_0 */
282//
283// /* Apply the selected link layer sleep timer source */
284// ll_sys_sleep_clock_source_selection();
285//
286// /* Configure the link layer sleep clock accuracy */
287// bsca = ll_sys_BLE_sleep_clock_accuracy_selection();
288// ll_intf_le_set_sleep_clock_accuracy(bsca);
289//
290// /* Update link layer timings depending on selected configuration */
291// if(LL_RCC_RADIO_GetSleepTimerClockSource() == LL_RCC_RADIOSLEEPSOURCE_LSI)
292// {
293// drift_time += DRIFT_TIME_EXTRA_LSI2;
294// exec_time += EXEC_TIME_EXTRA_LSI2;
295// }
296// else
297// {
298// #if defined(__GNUC__) && defined(DEBUG)
299// drift_time += DRIFT_TIME_EXTRA_GCC_DEBUG;
300// exec_time += EXEC_TIME_EXTRA_GCC_DEBUG;
301// #endif
302// }
303//
304// /* USER CODE BEGIN ll_sys_reset_1 */
305//
306// /* USER CODE END ll_sys_reset_1 */
307//
308// if((drift_time != DRIFT_TIME_DEFAULT) || (exec_time != EXEC_TIME_DEFAULT))
309// {
310// ll_sys_config_BLE_schldr_timings(drift_time, exec_time);
311// }
312// /* USER CODE BEGIN ll_sys_reset_2 */
313//
314// /* USER CODE END ll_sys_reset_2 */
315// }
316// #if defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx) || defined(STM32WBA65xx)
317// void ll_sys_apply_cte_settings(void)
318// {
319// ll_intf_apply_cte_degrad_change();
320// }
321// #endif /* defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx) || defined(STM32WBA65xx) */
322//
323// #if (CFG_LPM_STANDBY_SUPPORTED == 0)
324// void ll_sys_get_ble_profile_statistics(uint32_t* exec_time, uint32_t* drift_time, uint32_t* average_drift_time, uint8_t reset)
325// {
326// if (reset != 0U)
327// {
328// profile_reset();
329// }
330// ll_intf_get_profile_statistics(exec_time, drift_time, average_drift_time);
331// }
332// #endif
333//
334use super::bindings::{link_layer, mac};
335use super::util_seq;
336
337const UTIL_SEQ_RFU: u32 = 0;
338const TASK_LINK_LAYER_MASK: u32 = 1 << mac::CFG_TASK_ID_T_CFG_TASK_LINK_LAYER;
339const TASK_PRIO_LINK_LAYER: u32 = mac::CFG_SEQ_PRIO_ID_T_CFG_SEQ_PRIO_0 as u32;
340
341/**
342 * @brief Link Layer background process initialization
343 * @param None
344 * @retval None
345 */
346#[unsafe(no_mangle)]
347pub unsafe extern "C" fn ll_sys_bg_process_init() {
348 util_seq::UTIL_SEQ_RegTask(TASK_LINK_LAYER_MASK, UTIL_SEQ_RFU, Some(link_layer::ll_sys_bg_process));
349}
350
351/**
352 * @brief Link Layer background process next iteration scheduling
353 * @param None
354 * @retval None
355 */
356#[unsafe(no_mangle)]
357pub unsafe extern "C" fn ll_sys_schedule_bg_process() {
358 util_seq::UTIL_SEQ_SetTask(TASK_LINK_LAYER_MASK, TASK_PRIO_LINK_LAYER);
359}
360
361/**
362 * @brief Link Layer background process next iteration scheduling from ISR
363 * @param None
364 * @retval None
365 */
366#[unsafe(no_mangle)]
367pub unsafe extern "C" fn ll_sys_schedule_bg_process_isr() {
368 util_seq::UTIL_SEQ_SetTask(TASK_LINK_LAYER_MASK, TASK_PRIO_LINK_LAYER);
369}
370
371/**
372 * @brief Link Layer configuration phase before application startup.
373 * @param None
374 * @retval None
375 */
376#[unsafe(no_mangle)]
377pub unsafe extern "C" fn ll_sys_config_params() {
378 let allow_low_isr = mac::USE_RADIO_LOW_ISR as u8;
379 let run_from_isr = mac::NEXT_EVENT_SCHEDULING_FROM_ISR as u8;
380 let _ = link_layer::ll_intf_cmn_config_ll_ctx_params(allow_low_isr, run_from_isr);
381
382 ll_sys_sleep_clock_source_selection();
383 let _ = link_layer::ll_intf_cmn_select_tx_power_table(mac::CFG_RF_TX_POWER_TABLE_ID as u8);
384}
385
386/**
387 * @brief Reset Link Layer timing parameters to their default configuration.
388 * @param None
389 * @retval None
390 */
391#[unsafe(no_mangle)]
392pub unsafe extern "C" fn ll_sys_reset() {
393 ll_sys_sleep_clock_source_selection();
394
395 let sleep_accuracy = ll_sys_BLE_sleep_clock_accuracy_selection();
396 let _ = link_layer::ll_intf_le_set_sleep_clock_accuracy(sleep_accuracy);
397}
398
399/// Select the sleep-clock source used by the Link Layer.
400/// Defaults to the crystal oscillator when no explicit configuration is available.
401#[unsafe(no_mangle)]
402pub unsafe extern "C" fn ll_sys_sleep_clock_source_selection() {
403 let mut frequency: u16 = 0;
404 let _ = link_layer::ll_intf_cmn_le_select_slp_clk_src(
405 link_layer::_SLPTMR_SRC_TYPE_E_CRYSTAL_OSCILLATOR_SLPTMR as u8,
406 &mut frequency as *mut u16,
407 );
408}
409
410/// Determine the BLE sleep-clock accuracy used by the stack.
411/// Returns zero when board-specific calibration data is unavailable.
412#[unsafe(no_mangle)]
413pub unsafe extern "C" fn ll_sys_BLE_sleep_clock_accuracy_selection() -> u8 {
414 // TODO: derive the board-specific sleep clock accuracy once calibration data is available.
415 0
416}
diff --git a/embassy-stm32-wpan/src/wba/mac_sys_if.rs b/embassy-stm32-wpan/src/wba/mac_sys_if.rs
new file mode 100644
index 000000000..273399a19
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/mac_sys_if.rs
@@ -0,0 +1,188 @@
1#![cfg(feature = "wba")]
2#![allow(non_snake_case)]
3
4//
5// /* USER CODE BEGIN Header */
6// /**
7// ******************************************************************************
8// * @file mac_sys_if.c
9// * @author MCD Application Team
10// * @brief Source file for using MAC Layer with a RTOS
11// ******************************************************************************
12// * @attention
13// *
14// * Copyright (c) 2025 STMicroelectronics.
15// * All rights reserved.
16// *
17// * This software is licensed under terms that can be found in the LICENSE file
18// * in the root directory of this software component.
19// * If no LICENSE file comes with this software, it is provided AS-IS.
20// *
21// ******************************************************************************
22// */
23// /* USER CODE END Header */
24//
25// #include "main.h"
26// #include "app_common.h"
27// #include "app_conf.h"
28// #include "log_module.h"
29// #include "stm32_rtos.h"
30// #include "st_mac_802_15_4_sys.h"
31//
32// extern void mac_baremetal_run(void);
33//
34// /* Private defines -----------------------------------------------------------*/
35// /* USER CODE BEGIN PD */
36//
37// /* USER CODE END PD */
38//
39// /* Private macros ------------------------------------------------------------*/
40// /* USER CODE BEGIN PM */
41//
42// /* USER CODE END PM */
43//
44// /* Private variables ---------------------------------------------------------*/
45// /* USER CODE BEGIN PV */
46//
47// /* USER CODE END PV */
48//
49// /* Global variables ----------------------------------------------------------*/
50// /* USER CODE BEGIN GV */
51//
52// /* USER CODE END GV */
53//
54// /* Functions Definition ------------------------------------------------------*/
55//
56// /**
57// * @brief Mac Layer Initialisation
58// * @param None
59// * @retval None
60// */
61// void MacSys_Init(void)
62// {
63// /* Register tasks */
64// UTIL_SEQ_RegTask( TASK_MAC_LAYER, UTIL_SEQ_RFU, mac_baremetal_run);
65// }
66//
67// /**
68// * @brief Mac Layer Resume
69// * @param None
70// * @retval None
71// */
72// void MacSys_Resume(void)
73// {
74// UTIL_SEQ_ResumeTask( TASK_MAC_LAYER );
75// }
76//
77// /**
78// * @brief MAC Layer set Task.
79// * @param None
80// * @retval None
81// */
82// void MacSys_SemaphoreSet(void)
83// {
84// UTIL_SEQ_SetTask( TASK_MAC_LAYER, TASK_PRIO_MAC_LAYER );
85// }
86//
87// /**
88// * @brief MAC Layer Task wait.
89// * @param None
90// * @retval None
91// */
92// void MacSys_SemaphoreWait( void )
93// {
94// /* Not used */
95// }
96//
97// /**
98// * @brief MAC Layer set Event.
99// * @param None
100// * @retval None
101// */
102// void MacSys_EventSet( void )
103// {
104// UTIL_SEQ_SetEvt( EVENT_MAC_LAYER );
105// }
106//
107// /**
108// * @brief MAC Layer wait Event.
109// * @param None
110// * @retval None
111// */
112// void MacSys_EventWait( void )
113// {
114// UTIL_SEQ_WaitEvt( EVENT_MAC_LAYER );
115// }
116//
117
118use super::util_seq;
119use crate::bindings::mac;
120
121/// Placeholder value used by the original ST middleware when registering tasks.
122const UTIL_SEQ_RFU: u32 = 0;
123
124/// Bit mask identifying the MAC layer task within the sequencer.
125const TASK_MAC_LAYER_MASK: u32 = 1 << mac::CFG_TASK_ID_T_CFG_TASK_MAC_LAYER;
126
127/// Sequencer priority assigned to the MAC layer task.
128const TASK_PRIO_MAC_LAYER: u32 = mac::CFG_SEQ_PRIO_ID_T_CFG_SEQ_PRIO_0 as u32;
129
130/// Event flag consumed by the MAC task while waiting on notifications.
131const EVENT_MAC_LAYER_MASK: u32 = 1 << 0;
132
133/// Registers the MAC bare-metal runner with the lightweight sequencer.
134///
135/// Mirrors the behaviour of the reference implementation:
136/// `UTIL_SEQ_RegTask(TASK_MAC_LAYER, UTIL_SEQ_RFU, mac_baremetal_run);`
137#[unsafe(no_mangle)]
138pub unsafe extern "C" fn MacSys_Init() {
139 util_seq::UTIL_SEQ_RegTask(TASK_MAC_LAYER_MASK, UTIL_SEQ_RFU, Some(mac::mac_baremetal_run));
140}
141
142/**
143 * @brief Mac Layer Resume
144 * @param None
145 * @retval None
146 */
147#[unsafe(no_mangle)]
148pub unsafe extern "C" fn MacSys_Resume() {
149 util_seq::UTIL_SEQ_ResumeTask(TASK_MAC_LAYER_MASK);
150}
151
152/**
153 * @brief MAC Layer set Task.
154 * @param None
155 * @retval None
156 */
157#[unsafe(no_mangle)]
158pub unsafe extern "C" fn MacSys_SemaphoreSet() {
159 util_seq::UTIL_SEQ_SetTask(TASK_MAC_LAYER_MASK, TASK_PRIO_MAC_LAYER);
160}
161
162/**
163 * @brief MAC Layer Task wait.
164 * @param None
165 * @retval None
166 */
167#[unsafe(no_mangle)]
168pub unsafe extern "C" fn MacSys_SemaphoreWait() {}
169
170/**
171 * @brief MAC Layer set Event.
172 * @param None
173 * @retval None
174 */
175#[unsafe(no_mangle)]
176pub unsafe extern "C" fn MacSys_EventSet() {
177 util_seq::UTIL_SEQ_SetEvt(EVENT_MAC_LAYER_MASK);
178}
179
180/**
181 * @brief MAC Layer wait Event.
182 * @param None
183 * @retval None
184 */
185#[unsafe(no_mangle)]
186pub unsafe extern "C" fn MacSys_EventWait() {
187 util_seq::UTIL_SEQ_WaitEvt(EVENT_MAC_LAYER_MASK);
188}
diff --git a/embassy-stm32-wpan/src/wba/mod.rs b/embassy-stm32-wpan/src/wba/mod.rs
new file mode 100644
index 000000000..3161b578e
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/mod.rs
@@ -0,0 +1,6 @@
1pub mod bindings;
2pub mod linklayer_plat;
3pub mod ll_sys;
4pub mod ll_sys_if;
5pub mod mac_sys_if;
6pub mod util_seq;
diff --git a/embassy-stm32-wpan/src/wba/util_seq.rs b/embassy-stm32-wpan/src/wba/util_seq.rs
new file mode 100644
index 000000000..b596df908
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/util_seq.rs
@@ -0,0 +1,243 @@
1#![cfg(feature = "wba")]
2
3use core::cell::UnsafeCell;
4use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
5
6use critical_section::with as critical;
7
8type TaskFn = unsafe extern "C" fn();
9
10const MAX_TASKS: usize = 32;
11const DEFAULT_PRIORITY: u8 = u8::MAX;
12
13struct TaskTable {
14 funcs: UnsafeCell<[Option<TaskFn>; MAX_TASKS]>,
15 priorities: UnsafeCell<[u8; MAX_TASKS]>,
16}
17
18impl TaskTable {
19 const fn new() -> Self {
20 Self {
21 funcs: UnsafeCell::new([None; MAX_TASKS]),
22 priorities: UnsafeCell::new([DEFAULT_PRIORITY; MAX_TASKS]),
23 }
24 }
25
26 unsafe fn set_task(&self, idx: usize, func: Option<TaskFn>, priority: u8) {
27 (*self.funcs.get())[idx] = func;
28 (*self.priorities.get())[idx] = priority;
29 }
30
31 unsafe fn update_priority(&self, idx: usize, priority: u8) {
32 (*self.priorities.get())[idx] = priority;
33 }
34
35 unsafe fn task(&self, idx: usize) -> Option<TaskFn> {
36 (*self.funcs.get())[idx]
37 }
38
39 unsafe fn priority(&self, idx: usize) -> u8 {
40 (*self.priorities.get())[idx]
41 }
42}
43
44unsafe impl Sync for TaskTable {}
45
46#[inline(always)]
47fn wake_event() {
48 #[cfg(target_arch = "arm")]
49 {
50 cortex_m::asm::sev();
51 }
52
53 #[cfg(not(target_arch = "arm"))]
54 {
55 // No-op on architectures without SEV support.
56 }
57}
58
59#[inline(always)]
60fn wait_event() {
61 #[cfg(target_arch = "arm")]
62 {
63 cortex_m::asm::wfe();
64 }
65
66 #[cfg(not(target_arch = "arm"))]
67 {
68 core::hint::spin_loop();
69 }
70}
71
72static TASKS: TaskTable = TaskTable::new();
73static PENDING_TASKS: AtomicU32 = AtomicU32::new(0);
74static EVENTS: AtomicU32 = AtomicU32::new(0);
75static SCHEDULING: AtomicBool = AtomicBool::new(false);
76
77fn mask_to_index(mask: u32) -> Option<usize> {
78 if mask == 0 {
79 return None;
80 }
81 let idx = mask.trailing_zeros() as usize;
82 if idx < MAX_TASKS { Some(idx) } else { None }
83}
84
85fn drain_pending_tasks() {
86 loop {
87 if SCHEDULING
88 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
89 .is_err()
90 {
91 return;
92 }
93
94 loop {
95 let next = critical(|_| select_next_task());
96 match next {
97 Some((idx, task)) => unsafe {
98 task();
99 // Force a fresh read of the pending bitmask after each task completion.
100 let _ = idx;
101 },
102 None => break,
103 }
104 }
105
106 SCHEDULING.store(false, Ordering::Release);
107
108 if PENDING_TASKS.load(Ordering::Acquire) == 0 {
109 break;
110 }
111 }
112}
113
114/// Poll and execute any tasks that have been scheduled via the UTIL sequencer API.
115pub fn poll_pending_tasks() {
116 drain_pending_tasks();
117}
118
119fn select_next_task() -> Option<(usize, TaskFn)> {
120 let pending = PENDING_TASKS.load(Ordering::Acquire);
121 if pending == 0 {
122 return None;
123 }
124
125 let mut remaining = pending;
126 let mut best_idx: Option<usize> = None;
127 let mut best_priority = DEFAULT_PRIORITY;
128 let mut best_fn: Option<TaskFn> = None;
129
130 while remaining != 0 {
131 let idx = remaining.trailing_zeros() as usize;
132 remaining &= remaining - 1;
133
134 if idx >= MAX_TASKS {
135 continue;
136 }
137
138 unsafe {
139 if let Some(func) = TASKS.task(idx) {
140 let prio = TASKS.priority(idx);
141 if prio <= best_priority {
142 if prio < best_priority || best_idx.map_or(true, |current| idx < current) {
143 best_priority = prio;
144 best_idx = Some(idx);
145 best_fn = Some(func);
146 }
147 }
148 } else {
149 PENDING_TASKS.fetch_and(!(1u32 << idx), Ordering::AcqRel);
150 }
151 }
152 }
153
154 if let (Some(idx), Some(func)) = (best_idx, best_fn) {
155 PENDING_TASKS.fetch_and(!(1u32 << idx), Ordering::AcqRel);
156 Some((idx, func))
157 } else {
158 None
159 }
160}
161
162#[unsafe(no_mangle)]
163pub extern "C" fn UTIL_SEQ_RegTask(task_mask: u32, _flags: u32, task: Option<TaskFn>) {
164 if let Some(idx) = mask_to_index(task_mask) {
165 critical(|_| unsafe {
166 TASKS.set_task(idx, task, DEFAULT_PRIORITY);
167 });
168 }
169}
170
171#[unsafe(no_mangle)]
172pub extern "C" fn UTIL_SEQ_UnregTask(task_mask: u32) {
173 if let Some(idx) = mask_to_index(task_mask) {
174 critical(|_| unsafe {
175 TASKS.set_task(idx, None, DEFAULT_PRIORITY);
176 });
177 PENDING_TASKS.fetch_and(!(task_mask), Ordering::AcqRel);
178 }
179}
180
181#[unsafe(no_mangle)]
182pub extern "C" fn UTIL_SEQ_SetTask(task_mask: u32, priority: u32) {
183 let prio = (priority & 0xFF) as u8;
184
185 if let Some(idx) = mask_to_index(task_mask) {
186 let registered = critical(|_| unsafe {
187 if TASKS.task(idx).is_some() {
188 TASKS.update_priority(idx, prio);
189 true
190 } else {
191 false
192 }
193 });
194
195 if registered {
196 PENDING_TASKS.fetch_or(task_mask, Ordering::Release);
197 wake_event();
198 }
199 }
200}
201
202#[unsafe(no_mangle)]
203pub extern "C" fn UTIL_SEQ_ResumeTask(task_mask: u32) {
204 PENDING_TASKS.fetch_or(task_mask, Ordering::Release);
205 wake_event();
206}
207
208#[unsafe(no_mangle)]
209pub extern "C" fn UTIL_SEQ_PauseTask(task_mask: u32) {
210 PENDING_TASKS.fetch_and(!task_mask, Ordering::AcqRel);
211}
212
213#[unsafe(no_mangle)]
214pub extern "C" fn UTIL_SEQ_SetEvt(event_mask: u32) {
215 EVENTS.fetch_or(event_mask, Ordering::Release);
216 wake_event();
217}
218
219#[unsafe(no_mangle)]
220pub extern "C" fn UTIL_SEQ_ClrEvt(event_mask: u32) {
221 EVENTS.fetch_and(!event_mask, Ordering::AcqRel);
222}
223
224#[unsafe(no_mangle)]
225pub extern "C" fn UTIL_SEQ_IsEvtSet(event_mask: u32) -> u32 {
226 let state = EVENTS.load(Ordering::Acquire);
227 if (state & event_mask) == event_mask { 1 } else { 0 }
228}
229
230#[unsafe(no_mangle)]
231pub extern "C" fn UTIL_SEQ_WaitEvt(event_mask: u32) {
232 loop {
233 poll_pending_tasks();
234
235 let current = EVENTS.load(Ordering::Acquire);
236 if (current & event_mask) == event_mask {
237 EVENTS.fetch_and(!event_mask, Ordering::AcqRel);
238 break;
239 }
240
241 wait_event();
242 }
243}
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 6c36bc108..38f22b1c3 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7 7
8## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
9 9
10- fix: stm32: GPDMA driver reset ignored during channel configuration
11- fix: stm32: SPI driver SSOE and SSM manegment, add `nss_output_disable` to SPI Config
12- change: stm32: use typelevel timer type to allow dma for 32 bit timers
10- fix: fix incorrect handling of split interrupts in timer driver 13- fix: fix incorrect handling of split interrupts in timer driver
11- feat: allow granular stop for regular usart 14- feat: allow granular stop for regular usart
12- feat: Add continuous waveform method to SimplePWM 15- feat: Add continuous waveform method to SimplePWM
@@ -85,8 +88,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
85- fix: build script ensures EXTI2_TSC is listed as the IRQ of EXTI2 even if the PAC doesn't 88- fix: build script ensures EXTI2_TSC is listed as the IRQ of EXTI2 even if the PAC doesn't
86- feat: stm32/lcd: added implementation 89- feat: stm32/lcd: added implementation
87- change: add error messages to can timing calculations ([#4961](https://github.com/embassy-rs/embassy/pull/4961)) 90- change: add error messages to can timing calculations ([#4961](https://github.com/embassy-rs/embassy/pull/4961))
91- feat: stm32/spi bidirectional mode
88- fix: stm32/i2c v2: add stop flag on stop received 92- fix: stm32/i2c v2: add stop flag on stop received
89- stm32: Add blocking_listen for blocking I2C driver 93- stm32: Add blocking_listen for blocking I2C driver
94- fix: stm32l47*/stm32l48* adc analog pin setup
95- fix: keep stm32/sai: make NODIV independent of MCKDIV
90 96
91## 0.4.0 - 2025-08-26 97## 0.4.0 - 2025-08-26
92 98
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index e10409112..7989fc5d7 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -200,11 +200,11 @@ aligned = "0.4.1"
200heapless = "0.9.1" 200heapless = "0.9.1"
201 201
202#stm32-metapac = { version = "18" } 202#stm32-metapac = { version = "18" }
203stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb" } 203stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-497fb3042b49b765d8974aac87b8ab4fa3566d74" }
204 204
205[build-dependencies] 205[build-dependencies]
206#stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} 206#stm32-metapac = { version = "18", default-features = false, features = ["metadata"]}
207stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb", default-features = false, features = ["metadata"] } 207stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-497fb3042b49b765d8974aac87b8ab4fa3566d74", default-features = false, features = ["metadata"] }
208 208
209proc-macro2 = "1.0.36" 209proc-macro2 = "1.0.36"
210quote = "1.0.15" 210quote = "1.0.15"
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs
index 453513309..43509873f 100644
--- a/embassy-stm32/src/adc/adc4.rs
+++ b/embassy-stm32/src/adc/adc4.rs
@@ -5,7 +5,7 @@ use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingR
5use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; 5use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel};
6 6
7use super::blocking_delay_us; 7use super::blocking_delay_us;
8use crate::adc::ConversionMode; 8use crate::adc::{AdcRegs, ConversionMode, Instance};
9#[cfg(stm32u5)] 9#[cfg(stm32u5)]
10pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; 10pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr;
11#[cfg(stm32wba)] 11#[cfg(stm32wba)]
@@ -90,135 +90,112 @@ fn from_ker_ck(frequency: Hertz) -> Presc {
90 } 90 }
91} 91}
92 92
93pub trait SealedInstance { 93impl AdcRegs for crate::pac::adc::Adc4 {
94 #[allow(unused)] 94 fn data(&self) -> *mut u16 {
95 fn regs() -> crate::pac::adc::Adc4; 95 crate::pac::adc::Adc4::dr(*self).as_ptr() as *mut u16
96} 96 }
97 97
98pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { 98 fn enable(&self) {
99 type Interrupt: crate::interrupt::typelevel::Interrupt; 99 if !self.cr().read().aden() || !self.isr().read().adrdy() {
100} 100 self.isr().write(|w| w.set_adrdy(true));
101 self.cr().modify(|w| w.set_aden(true));
102 while !self.isr().read().adrdy() {}
103 }
104 }
101 105
102foreach_adc!( 106 fn start(&self) {
103 (ADC4, $common_inst:ident, $clock:ident) => { 107 // Start conversion
104 use crate::peripherals::ADC4; 108 self.cr().modify(|reg| {
109 reg.set_adstart(true);
110 });
111 }
105 112
106 impl super::BasicAnyInstance for ADC4 { 113 fn stop(&self) {
107 type SampleTime = SampleTime; 114 let cr = self.cr().read();
115 if cr.adstart() {
116 self.cr().modify(|w| w.set_adstp(true));
117 while self.cr().read().adstart() {}
108 } 118 }
109 119
110 impl super::SealedAnyInstance for ADC4 { 120 if cr.aden() || cr.adstart() {
111 fn dr() -> *mut u16 { 121 self.cr().modify(|w| w.set_addis(true));
112 ADC4::regs().dr().as_ptr() as *mut u16 122 while self.cr().read().aden() {}
113 } 123 }
114 124
115 fn enable() { 125 // Reset configuration.
116 if !ADC4::regs().cr().read().aden() || !ADC4::regs().isr().read().adrdy() { 126 self.cfgr1().modify(|reg| {
117 ADC4::regs().isr().write(|w| w.set_adrdy(true)); 127 reg.set_dmaen(false);
118 ADC4::regs().cr().modify(|w| w.set_aden(true)); 128 });
119 while !ADC4::regs().isr().read().adrdy() {} 129 }
120 }
121 }
122 130
123 fn start() { 131 fn configure_dma(&self, conversion_mode: ConversionMode) {
124 // Start conversion 132 match conversion_mode {
125 ADC4::regs().cr().modify(|reg| { 133 ConversionMode::Singular => {
126 reg.set_adstart(true); 134 self.isr().modify(|reg| {
135 reg.set_ovr(true);
136 reg.set_eos(true);
137 reg.set_eoc(true);
127 }); 138 });
128 }
129 139
130 fn stop() { 140 self.cfgr1().modify(|reg| {
131 let cr = ADC4::regs().cr().read(); 141 reg.set_dmaen(true);
132 if cr.adstart() { 142 reg.set_dmacfg(Dmacfg::ONE_SHOT);
133 ADC4::regs().cr().modify(|w| w.set_adstp(true)); 143 #[cfg(stm32u5)]
134 while ADC4::regs().cr().read().adstart() {} 144 reg.set_chselrmod(false);
135 } 145 #[cfg(stm32wba)]
136 146 reg.set_chselrmod(Chselrmod::ENABLE_INPUT)
137 if cr.aden() || cr.adstart() {
138 ADC4::regs().cr().modify(|w| w.set_addis(true));
139 while ADC4::regs().cr().read().aden() {}
140 }
141
142 // Reset configuration.
143 ADC4::regs().cfgr1().modify(|reg| {
144 reg.set_dmaen(false);
145 }); 147 });
146 } 148 }
149 #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))]
150 _ => unreachable!(),
151 }
152 }
147 153
148 fn configure_dma(conversion_mode: ConversionMode) { 154 fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {
149 match conversion_mode { 155 let mut prev_channel: i16 = -1;
150 ConversionMode::Singular => { 156 #[cfg(stm32wba)]
151 ADC4::regs().isr().modify(|reg| { 157 self.chselr().write_value(Chselr(0_u32));
152 reg.set_ovr(true); 158 #[cfg(stm32u5)]
153 reg.set_eos(true); 159 self.chselrmod0().write_value(Chselr(0_u32));
154 reg.set_eoc(true); 160 for (_i, ((channel, _), sample_time)) in sequence.enumerate() {
155 }); 161 self.smpr().modify(|w| {
156 162 w.set_smp(_i, sample_time);
157 ADC4::regs().cfgr1().modify(|reg| { 163 });
158 reg.set_dmaen(true); 164
159 reg.set_dmacfg(Dmacfg::ONE_SHOT); 165 let channel_num = channel;
160 #[cfg(stm32u5)] 166 if channel_num as i16 <= prev_channel {
161 reg.set_chselrmod(false); 167 return;
162 #[cfg(stm32wba)] 168 };
163 reg.set_chselrmod(Chselrmod::ENABLE_INPUT) 169 prev_channel = channel_num as i16;
164 });
165 }
166 #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))]
167 _ => unreachable!(),
168 }
169 }
170
171 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {
172 let mut prev_channel: i16 = -1;
173 #[cfg(stm32wba)]
174 ADC4::regs().chselr().write_value(Chselr(0_u32));
175 #[cfg(stm32u5)]
176 ADC4::regs().chselrmod0().write_value(Chselr(0_u32));
177 for (_i, ((channel, _), sample_time)) in sequence.enumerate() {
178 ADC4::regs().smpr().modify(|w| {
179 w.set_smp(_i, sample_time);
180 });
181
182 let channel_num = channel;
183 if channel_num as i16 <= prev_channel {
184 return;
185 };
186 prev_channel = channel_num as i16;
187
188 #[cfg(stm32wba)]
189 ADC4::regs().chselr().modify(|w| {
190 w.set_chsel0(channel as usize, true);
191 });
192 #[cfg(stm32u5)]
193 ADC4::regs().chselrmod0().modify(|w| {
194 w.set_chsel(channel as usize, true);
195 });
196 }
197 }
198 170
199 fn convert() -> u16 { 171 #[cfg(stm32wba)]
200 // Reset interrupts 172 self.chselr().modify(|w| {
201 ADC4::regs().isr().modify(|reg| { 173 w.set_chsel0(channel as usize, true);
202 reg.set_eos(true); 174 });
203 reg.set_eoc(true); 175 #[cfg(stm32u5)]
204 }); 176 self.chselrmod0().modify(|w| {
177 w.set_chsel(channel as usize, true);
178 });
179 }
180 }
205 181
206 // Start conversion 182 fn convert(&self) {
207 ADC4::regs().cr().modify(|reg| { 183 // Reset interrupts
208 reg.set_adstart(true); 184 self.isr().modify(|reg| {
209 }); 185 reg.set_eos(true);
186 reg.set_eoc(true);
187 });
210 188
211 while !ADC4::regs().isr().read().eos() { 189 // Start conversion
212 // spin 190 self.cr().modify(|reg| {
213 } 191 reg.set_adstart(true);
192 });
214 193
215 ADC4::regs().dr().read().0 as u16 194 while !self.isr().read().eos() {
216 } 195 // spin
217 } 196 }
218 197 }
219 impl super::AnyInstance for ADC4 {} 198}
220 };
221);
222 199
223pub struct Adc4<'d, T: Instance> { 200pub struct Adc4<'d, T: Instance> {
224 #[allow(unused)] 201 #[allow(unused)]
@@ -231,7 +208,7 @@ pub enum Adc4Error {
231 DMAError, 208 DMAError,
232} 209}
233 210
234impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> { 211impl<'d, T: Instance<Regs = crate::pac::adc::Adc4>> super::Adc<'d, T> {
235 /// Create a new ADC driver. 212 /// Create a new ADC driver.
236 pub fn new_adc4(adc: Peri<'d, T>) -> Self { 213 pub fn new_adc4(adc: Peri<'d, T>) -> Self {
237 rcc::enable_and_reset::<T>(); 214 rcc::enable_and_reset::<T>();
@@ -267,7 +244,7 @@ impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> {
267 244
268 blocking_delay_us(1); 245 blocking_delay_us(1);
269 246
270 T::enable(); 247 T::regs().enable();
271 248
272 // single conversion mode, software trigger 249 // single conversion mode, software trigger
273 T::regs().cfgr1().modify(|w| { 250 T::regs().cfgr1().modify(|w| {
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs
index 3e109e429..2f0f326af 100644
--- a/embassy-stm32/src/adc/c0.rs
+++ b/embassy-stm32/src/adc/c0.rs
@@ -4,7 +4,7 @@ use pac::adccommon::vals::Presc;
4use stm32_metapac::adc::vals::{SampleTime, Scandir}; 4use stm32_metapac::adc::vals::{SampleTime, Scandir};
5 5
6use super::{Adc, Instance, Resolution, blocking_delay_us}; 6use super::{Adc, Instance, Resolution, blocking_delay_us};
7use crate::adc::{AnyInstance, ConversionMode}; 7use crate::adc::{AdcRegs, ConversionMode};
8use crate::time::Hertz; 8use crate::time::Hertz;
9use crate::{Peri, pac, rcc}; 9use crate::{Peri, pac, rcc};
10 10
@@ -43,52 +43,52 @@ fn from_ker_ck(frequency: Hertz) -> Presc {
43 } 43 }
44} 44}
45 45
46impl<T: Instance> super::SealedAnyInstance for T { 46impl AdcRegs for crate::pac::adc::Adc {
47 fn dr() -> *mut u16 { 47 fn data(&self) -> *mut u16 {
48 T::regs().dr().as_ptr() as *mut u16 48 crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16
49 } 49 }
50 50
51 fn enable() { 51 fn enable(&self) {
52 T::regs().isr().modify(|w| w.set_adrdy(true)); 52 self.isr().modify(|w| w.set_adrdy(true));
53 T::regs().cr().modify(|w| w.set_aden(true)); 53 self.cr().modify(|w| w.set_aden(true));
54 // ADRDY is "ADC ready". Wait until it will be True. 54 // ADRDY is "ADC ready". Wait until it will be True.
55 while !T::regs().isr().read().adrdy() {} 55 while !self.isr().read().adrdy() {}
56 } 56 }
57 57
58 fn start() { 58 fn start(&self) {
59 // Start conversion 59 // Start conversion
60 T::regs().cr().modify(|reg| { 60 self.cr().modify(|reg| {
61 reg.set_adstart(true); 61 reg.set_adstart(true);
62 }); 62 });
63 } 63 }
64 64
65 fn stop() { 65 fn stop(&self) {
66 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { 66 if self.cr().read().adstart() && !self.cr().read().addis() {
67 T::regs().cr().modify(|reg| { 67 self.cr().modify(|reg| {
68 reg.set_adstp(Adstp::STOP); 68 reg.set_adstp(Adstp::STOP);
69 }); 69 });
70 while T::regs().cr().read().adstart() {} 70 while self.cr().read().adstart() {}
71 } 71 }
72 72
73 // Reset configuration. 73 // Reset configuration.
74 T::regs().cfgr1().modify(|reg| { 74 self.cfgr1().modify(|reg| {
75 reg.set_cont(false); 75 reg.set_cont(false);
76 reg.set_dmacfg(Dmacfg::from_bits(0)); 76 reg.set_dmacfg(Dmacfg::from_bits(0));
77 reg.set_dmaen(false); 77 reg.set_dmaen(false);
78 }); 78 });
79 } 79 }
80 80
81 fn configure_dma(conversion_mode: super::ConversionMode) { 81 fn configure_dma(&self, conversion_mode: super::ConversionMode) {
82 match conversion_mode { 82 match conversion_mode {
83 ConversionMode::Singular => { 83 ConversionMode::Singular => {
84 // Enable overrun control, so no new DMA requests will be generated until 84 // Enable overrun control, so no new DMA requests will be generated until
85 // previous DR values is read. 85 // previous DR values is read.
86 T::regs().isr().modify(|reg| { 86 self.isr().modify(|reg| {
87 reg.set_ovr(true); 87 reg.set_ovr(true);
88 }); 88 });
89 89
90 // Set continuous mode with oneshot dma. 90 // Set continuous mode with oneshot dma.
91 T::regs().cfgr1().modify(|reg| { 91 self.cfgr1().modify(|reg| {
92 reg.set_discen(false); 92 reg.set_discen(false);
93 reg.set_cont(true); 93 reg.set_cont(true);
94 reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); 94 reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT);
@@ -99,7 +99,7 @@ impl<T: Instance> super::SealedAnyInstance for T {
99 } 99 }
100 } 100 }
101 101
102 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>) { 102 fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>) {
103 let mut needs_hw = sequence.len() == 1 || sequence.len() > CHSELR_SQ_SIZE; 103 let mut needs_hw = sequence.len() == 1 || sequence.len() > CHSELR_SQ_SIZE;
104 let mut is_ordered_up = true; 104 let mut is_ordered_up = true;
105 let mut is_ordered_down = true; 105 let mut is_ordered_down = true;
@@ -109,7 +109,7 @@ impl<T: Instance> super::SealedAnyInstance for T {
109 let mut last_channel: u8 = 0; 109 let mut last_channel: u8 = 0;
110 let mut sample_time: Self::SampleTime = SampleTime::CYCLES2_5; 110 let mut sample_time: Self::SampleTime = SampleTime::CYCLES2_5;
111 111
112 T::regs().chselr_sq().write(|w| { 112 self.chselr_sq().write(|w| {
113 for (i, ((channel, _), _sample_time)) in sequence.enumerate() { 113 for (i, ((channel, _), _sample_time)) in sequence.enumerate() {
114 assert!( 114 assert!(
115 sample_time == _sample_time || i == 0, 115 sample_time == _sample_time || i == 0,
@@ -146,42 +146,40 @@ impl<T: Instance> super::SealedAnyInstance for T {
146 ); 146 );
147 147
148 // Set required channels for multi-convert. 148 // Set required channels for multi-convert.
149 unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } 149 unsafe { (self.chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) }
150 } 150 }
151 151
152 T::regs().smpr().modify(|w| { 152 self.smpr().modify(|w| {
153 w.smpsel(0); 153 w.smpsel(0);
154 w.set_smp1(sample_time); 154 w.set_smp1(sample_time);
155 }); 155 });
156 156
157 T::regs().cfgr1().modify(|reg| { 157 self.cfgr1().modify(|reg| {
158 reg.set_chselrmod(!needs_hw); 158 reg.set_chselrmod(!needs_hw);
159 reg.set_align(Align::RIGHT); 159 reg.set_align(Align::RIGHT);
160 reg.set_scandir(if is_ordered_up { Scandir::UP } else { Scandir::BACK }); 160 reg.set_scandir(if is_ordered_up { Scandir::UP } else { Scandir::BACK });
161 }); 161 });
162 162
163 // Trigger and wait for the channel selection procedure to complete. 163 // Trigger and wait for the channel selection procedure to complete.
164 T::regs().isr().modify(|w| w.set_ccrdy(false)); 164 self.isr().modify(|w| w.set_ccrdy(false));
165 while !T::regs().isr().read().ccrdy() {} 165 while !self.isr().read().ccrdy() {}
166 } 166 }
167 167
168 fn convert() -> u16 { 168 fn convert(&self) {
169 // Set single conversion mode. 169 // Set single conversion mode.
170 T::regs().cfgr1().modify(|w| w.set_cont(false)); 170 self.cfgr1().modify(|w| w.set_cont(false));
171 171
172 // Start conversion 172 // Start conversion
173 T::regs().cr().modify(|reg| { 173 self.cr().modify(|reg| {
174 reg.set_adstart(true); 174 reg.set_adstart(true);
175 }); 175 });
176 176
177 // Waiting for End Of Conversion (EOC). 177 // Waiting for End Of Conversion (EOC).
178 while !T::regs().isr().read().eoc() {} 178 while !self.isr().read().eoc() {}
179
180 T::regs().dr().read().data() as u16
181 } 179 }
182} 180}
183 181
184impl<'d, T: AnyInstance> Adc<'d, T> { 182impl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> {
185 /// Create a new ADC driver. 183 /// Create a new ADC driver.
186 pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { 184 pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self {
187 rcc::enable_and_reset::<T>(); 185 rcc::enable_and_reset::<T>();
@@ -225,7 +223,7 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
225 223
226 T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); 224 T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value));
227 225
228 T::enable(); 226 T::regs().enable();
229 227
230 // single conversion mode, software trigger 228 // single conversion mode, software trigger
231 T::regs().cfgr1().modify(|w| { 229 T::regs().cfgr1().modify(|w| {
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs
index 1767a3bb3..e93ed945f 100644
--- a/embassy-stm32/src/adc/g4.rs
+++ b/embassy-stm32/src/adc/g4.rs
@@ -12,7 +12,8 @@ use super::{
12 Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, 12 Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime,
13 blocking_delay_us, 13 blocking_delay_us,
14}; 14};
15use crate::adc::{AnyInstance, SealedAdcChannel}; 15use crate::adc::{AdcRegs, BasicAdcRegs, SealedAdcChannel};
16use crate::pac::adc::regs::{Smpr, Smpr2, Sqr1, Sqr2, Sqr3, Sqr4};
16use crate::time::Hertz; 17use crate::time::Hertz;
17use crate::{Peri, pac, rcc}; 18use crate::{Peri, pac, rcc};
18 19
@@ -68,79 +69,77 @@ pub struct ConversionTrigger {
68 pub edge: Exten, 69 pub edge: Exten,
69} 70}
70 71
71impl<T: Instance> super::SealedAnyInstance for T { 72impl super::AdcRegs for crate::pac::adc::Adc {
72 fn dr() -> *mut u16 { 73 fn data(&self) -> *mut u16 {
73 T::regs().dr().as_ptr() as *mut u16 74 crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16
74 } 75 }
75 76
76 fn enable() { 77 fn enable(&self) {
77 // Make sure bits are off 78 // Make sure bits are off
78 while T::regs().cr().read().addis() { 79 while self.cr().read().addis() {
79 // spin 80 // spin
80 } 81 }
81 82
82 if !T::regs().cr().read().aden() { 83 if !self.cr().read().aden() {
83 // Enable ADC 84 // Enable ADC
84 T::regs().isr().modify(|reg| { 85 self.isr().modify(|reg| {
85 reg.set_adrdy(true); 86 reg.set_adrdy(true);
86 }); 87 });
87 T::regs().cr().modify(|reg| { 88 self.cr().modify(|reg| {
88 reg.set_aden(true); 89 reg.set_aden(true);
89 }); 90 });
90 91
91 while !T::regs().isr().read().adrdy() { 92 while !self.isr().read().adrdy() {
92 // spin 93 // spin
93 } 94 }
94 } 95 }
95 } 96 }
96 97
97 fn start() { 98 fn start(&self) {
98 T::regs().cr().modify(|reg| { 99 self.cr().modify(|reg| {
99 reg.set_adstart(true); 100 reg.set_adstart(true);
100 }); 101 });
101 } 102 }
102 103
103 fn stop() { 104 fn stop(&self) {
104 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { 105 if self.cr().read().adstart() && !self.cr().read().addis() {
105 T::regs().cr().modify(|reg| { 106 self.cr().modify(|reg| {
106 reg.set_adstp(Adstp::STOP); 107 reg.set_adstp(Adstp::STOP);
107 }); 108 });
108 // The software must poll ADSTART until the bit is reset before assuming the 109 // The software must poll ADSTART until the bit is reset before assuming the
109 // ADC is completely stopped 110 // ADC is completely stopped
110 while T::regs().cr().read().adstart() {} 111 while self.cr().read().adstart() {}
111 } 112 }
112 113
113 // Disable dma control and continuous conversion, if enabled 114 // Disable dma control and continuous conversion, if enabled
114 T::regs().cfgr().modify(|reg| { 115 self.cfgr().modify(|reg| {
115 reg.set_cont(false); 116 reg.set_cont(false);
116 reg.set_dmaen(Dmaen::DISABLE); 117 reg.set_dmaen(Dmaen::DISABLE);
117 }); 118 });
118 } 119 }
119 120
120 fn convert() -> u16 { 121 fn convert(&self) {
121 T::regs().isr().modify(|reg| { 122 self.isr().modify(|reg| {
122 reg.set_eos(true); 123 reg.set_eos(true);
123 reg.set_eoc(true); 124 reg.set_eoc(true);
124 }); 125 });
125 126
126 // Start conversion 127 // Start conversion
127 T::regs().cr().modify(|reg| { 128 self.cr().modify(|reg| {
128 reg.set_adstart(true); 129 reg.set_adstart(true);
129 }); 130 });
130 131
131 while !T::regs().isr().read().eos() { 132 while !self.isr().read().eos() {
132 // spin 133 // spin
133 } 134 }
134
135 T::regs().dr().read().0 as u16
136 } 135 }
137 136
138 fn configure_dma(conversion_mode: ConversionMode) { 137 fn configure_dma(&self, conversion_mode: ConversionMode) {
139 T::regs().isr().modify(|reg| { 138 self.isr().modify(|reg| {
140 reg.set_ovr(true); 139 reg.set_ovr(true);
141 }); 140 });
142 141
143 T::regs().cfgr().modify(|reg| { 142 self.cfgr().modify(|reg| {
144 reg.set_discen(false); // Convert all channels for each trigger 143 reg.set_discen(false); // Convert all channels for each trigger
145 reg.set_dmacfg(match conversion_mode { 144 reg.set_dmacfg(match conversion_mode {
146 ConversionMode::Singular => Dmacfg::ONE_SHOT, 145 ConversionMode::Singular => Dmacfg::ONE_SHOT,
@@ -152,43 +151,41 @@ impl<T: Instance> super::SealedAnyInstance for T {
152 if let ConversionMode::Repeated(mode) = conversion_mode { 151 if let ConversionMode::Repeated(mode) = conversion_mode {
153 match mode { 152 match mode {
154 RegularConversionMode::Continuous => { 153 RegularConversionMode::Continuous => {
155 T::regs().cfgr().modify(|reg| { 154 self.cfgr().modify(|reg| {
156 reg.set_cont(true); 155 reg.set_cont(true);
157 }); 156 });
158 } 157 }
159 RegularConversionMode::Triggered(trigger) => { 158 RegularConversionMode::Triggered(trigger) => {
160 T::regs().cfgr().modify(|r| { 159 self.cfgr().modify(|r| {
161 r.set_cont(false); // New trigger is neede for each sample to be read 160 r.set_cont(false); // New trigger is neede for each sample to be read
162 }); 161 });
163 162
164 T::regs().cfgr().modify(|r| { 163 self.cfgr().modify(|r| {
165 r.set_extsel(trigger.channel); 164 r.set_extsel(trigger.channel);
166 r.set_exten(trigger.edge); 165 r.set_exten(trigger.edge);
167 }); 166 });
168 167
169 // Regular conversions uses DMA so no need to generate interrupt 168 // Regular conversions uses DMA so no need to generate interrupt
170 T::regs().ier().modify(|r| r.set_eosie(false)); 169 self.ier().modify(|r| r.set_eosie(false));
171 } 170 }
172 } 171 }
173 } 172 }
174 } 173 }
175 174
176 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { 175 fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {
177 T::regs().cr().modify(|w| w.set_aden(false)); 176 self.cr().modify(|w| w.set_aden(false));
178
179 // Set sequence length
180 T::regs().sqr1().modify(|w| {
181 w.set_l(sequence.len() as u8 - 1);
182 });
183 177
184 #[cfg(stm32g4)] 178 #[cfg(stm32g4)]
185 let mut difsel = DifselReg::default(); 179 let mut difsel = DifselReg::default();
186 let mut smpr = T::regs().smpr().read(); 180 let mut smpr = Smpr::default();
187 let mut smpr2 = T::regs().smpr2().read(); 181 let mut smpr2 = Smpr2::default();
188 let mut sqr1 = T::regs().sqr1().read(); 182 let mut sqr1 = Sqr1::default();
189 let mut sqr2 = T::regs().sqr2().read(); 183 let mut sqr2 = Sqr2::default();
190 let mut sqr3 = T::regs().sqr3().read(); 184 let mut sqr3 = Sqr3::default();
191 let mut sqr4 = T::regs().sqr4().read(); 185 let mut sqr4 = Sqr4::default();
186
187 // Set sequence length
188 sqr1.set_l(sequence.len() as u8 - 1);
192 189
193 // Configure channels and ranks 190 // Configure channels and ranks
194 for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { 191 for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() {
@@ -230,18 +227,18 @@ impl<T: Instance> super::SealedAnyInstance for T {
230 } 227 }
231 } 228 }
232 229
233 T::regs().smpr().write_value(smpr); 230 self.smpr().write_value(smpr);
234 T::regs().smpr2().write_value(smpr2); 231 self.smpr2().write_value(smpr2);
235 T::regs().sqr1().write_value(sqr1); 232 self.sqr1().write_value(sqr1);
236 T::regs().sqr2().write_value(sqr2); 233 self.sqr2().write_value(sqr2);
237 T::regs().sqr3().write_value(sqr3); 234 self.sqr3().write_value(sqr3);
238 T::regs().sqr4().write_value(sqr4); 235 self.sqr4().write_value(sqr4);
239 #[cfg(stm32g4)] 236 #[cfg(stm32g4)]
240 T::regs().difsel().write_value(difsel); 237 self.difsel().write_value(difsel);
241 } 238 }
242} 239}
243 240
244impl<'d, T: Instance + AnyInstance> Adc<'d, T> { 241impl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> {
245 /// Create a new ADC driver. 242 /// Create a new ADC driver.
246 pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { 243 pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self {
247 rcc::enable_and_reset::<T>(); 244 rcc::enable_and_reset::<T>();
@@ -293,7 +290,7 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> {
293 290
294 blocking_delay_us(20); 291 blocking_delay_us(20);
295 292
296 T::enable(); 293 T::regs().enable();
297 294
298 // single conversion mode, software trigger 295 // single conversion mode, software trigger
299 T::regs().cfgr().modify(|w| { 296 T::regs().cfgr().modify(|w| {
@@ -409,10 +406,10 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> {
409 /// `InjectedAdc<T, N>` to enforce bounds at compile time. 406 /// `InjectedAdc<T, N>` to enforce bounds at compile time.
410 pub fn setup_injected_conversions<'a, const N: usize>( 407 pub fn setup_injected_conversions<'a, const N: usize>(
411 self, 408 self,
412 sequence: [(AnyAdcChannel<T>, SampleTime); N], 409 sequence: [(AnyAdcChannel<'a, T>, SampleTime); N],
413 trigger: ConversionTrigger, 410 trigger: ConversionTrigger,
414 interrupt: bool, 411 interrupt: bool,
415 ) -> InjectedAdc<T, N> { 412 ) -> InjectedAdc<'a, T, N> {
416 assert!(N != 0, "Read sequence cannot be empty"); 413 assert!(N != 0, "Read sequence cannot be empty");
417 assert!( 414 assert!(
418 N <= NR_INJECTED_RANKS, 415 N <= NR_INJECTED_RANKS,
@@ -420,12 +417,12 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> {
420 NR_INJECTED_RANKS 417 NR_INJECTED_RANKS
421 ); 418 );
422 419
423 T::enable(); 420 T::regs().enable();
424 421
425 T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); 422 T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1));
426 423
427 for (n, (channel, sample_time)) in sequence.into_iter().enumerate() { 424 for (n, (channel, sample_time)) in sequence.iter().enumerate() {
428 let sample_time = sample_time.into(); 425 let sample_time = sample_time.clone().into();
429 if channel.channel() <= 9 { 426 if channel.channel() <= 9 {
430 T::regs() 427 T::regs()
431 .smpr() 428 .smpr()
@@ -487,16 +484,16 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> {
487 /// This function is `unsafe` because it clones the ADC peripheral handle unchecked. Both the 484 /// This function is `unsafe` because it clones the ADC peripheral handle unchecked. Both the
488 /// `RingBufferedAdc` and `InjectedAdc` take ownership of the handle and drop it independently. 485 /// `RingBufferedAdc` and `InjectedAdc` take ownership of the handle and drop it independently.
489 /// Ensure no other code concurrently accesses the same ADC instance in a conflicting way. 486 /// Ensure no other code concurrently accesses the same ADC instance in a conflicting way.
490 pub fn into_ring_buffered_and_injected<'a, const N: usize>( 487 pub fn into_ring_buffered_and_injected<'a, 'b, const N: usize>(
491 self, 488 self,
492 dma: Peri<'a, impl RxDma<T>>, 489 dma: Peri<'a, impl RxDma<T>>,
493 dma_buf: &'a mut [u16], 490 dma_buf: &'a mut [u16],
494 regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, T::SampleTime)>, 491 regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<'b, T>, <T::Regs as BasicAdcRegs>::SampleTime)>,
495 regular_conversion_mode: RegularConversionMode, 492 regular_conversion_mode: RegularConversionMode,
496 injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], 493 injected_sequence: [(AnyAdcChannel<'b, T>, SampleTime); N],
497 injected_trigger: ConversionTrigger, 494 injected_trigger: ConversionTrigger,
498 injected_interrupt: bool, 495 injected_interrupt: bool,
499 ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { 496 ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<'b, T, N>) {
500 unsafe { 497 unsafe {
501 ( 498 (
502 Self { 499 Self {
@@ -531,7 +528,7 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> {
531 } 528 }
532} 529}
533 530
534impl<T: Instance, const N: usize> InjectedAdc<T, N> { 531impl<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> InjectedAdc<'a, T, N> {
535 /// Read sampled data from all injected ADC injected ranks 532 /// Read sampled data from all injected ADC injected ranks
536 /// Clear the JEOS flag to allow a new injected sequence 533 /// Clear the JEOS flag to allow a new injected sequence
537 pub(super) fn read_injected_data() -> [u16; N] { 534 pub(super) fn read_injected_data() -> [u16; N] {
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs
index ccaa5d1b2..029722b84 100644
--- a/embassy-stm32/src/adc/injected.rs
+++ b/embassy-stm32/src/adc/injected.rs
@@ -4,19 +4,19 @@ use core::sync::atomic::{Ordering, compiler_fence};
4#[allow(unused_imports)] 4#[allow(unused_imports)]
5use embassy_hal_internal::Peri; 5use embassy_hal_internal::Peri;
6 6
7use super::{AnyAdcChannel, SampleTime}; 7use super::{AdcRegs, AnyAdcChannel, SampleTime};
8use crate::adc::Adc;
8#[allow(unused_imports)] 9#[allow(unused_imports)]
9use crate::adc::Instance; 10use crate::adc::Instance;
10use crate::adc::{Adc, AnyInstance};
11 11
12/// Injected ADC sequence with owned channels. 12/// Injected ADC sequence with owned channels.
13pub struct InjectedAdc<T: Instance, const N: usize> { 13pub struct InjectedAdc<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> {
14 _channels: [(AnyAdcChannel<T>, SampleTime); N], 14 _channels: [(AnyAdcChannel<'a, T>, SampleTime); N],
15 _phantom: PhantomData<T>, 15 _phantom: PhantomData<T>,
16} 16}
17 17
18impl<T: Instance, const N: usize> InjectedAdc<T, N> { 18impl<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> InjectedAdc<'a, T, N> {
19 pub(crate) fn new(channels: [(AnyAdcChannel<T>, SampleTime); N]) -> Self { 19 pub(crate) fn new(channels: [(AnyAdcChannel<'a, T>, SampleTime); N]) -> Self {
20 Self { 20 Self {
21 _channels: channels, 21 _channels: channels,
22 _phantom: PhantomData, 22 _phantom: PhantomData,
@@ -36,9 +36,9 @@ impl<T: Instance, const N: usize> InjectedAdc<T, N> {
36 } 36 }
37} 37}
38 38
39impl<T: Instance + AnyInstance, const N: usize> Drop for InjectedAdc<T, N> { 39impl<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> Drop for InjectedAdc<'a, T, N> {
40 fn drop(&mut self) { 40 fn drop(&mut self) {
41 T::stop(); 41 T::regs().stop();
42 compiler_fence(Ordering::SeqCst); 42 compiler_fence(Ordering::SeqCst);
43 } 43 }
44} 44}
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 6d53d9b91..a6af1175a 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -25,12 +25,18 @@ use core::marker::PhantomData;
25#[allow(unused)] 25#[allow(unused)]
26#[cfg(not(any(adc_f3v3, adc_wba)))] 26#[cfg(not(any(adc_f3v3, adc_wba)))]
27pub use _version::*; 27pub use _version::*;
28use embassy_hal_internal::{PeripheralType, impl_peripheral}; 28#[allow(unused)]
29use embassy_hal_internal::PeripheralType;
29#[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] 30#[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
30use embassy_sync::waitqueue::AtomicWaker; 31use embassy_sync::waitqueue::AtomicWaker;
31#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] 32#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))]
32pub use ringbuffered::RingBufferedAdc; 33pub use ringbuffered::RingBufferedAdc;
33 34
35#[cfg(adc_u5)]
36use crate::pac::adc::vals::Adc4SampleTime;
37#[cfg(adc_wba)]
38use crate::pac::adc::vals::SampleTime as Adc4SampleTime;
39
34#[cfg(any(adc_u5, adc_wba))] 40#[cfg(any(adc_u5, adc_wba))]
35#[path = "adc4.rs"] 41#[path = "adc4.rs"]
36pub mod adc4; 42pub mod adc4;
@@ -43,10 +49,10 @@ pub use crate::pac::adc::vals::Res as Resolution;
43pub use crate::pac::adc::vals::SampleTime; 49pub use crate::pac::adc::vals::SampleTime;
44use crate::peripherals; 50use crate::peripherals;
45 51
46dma_trait!(RxDma, AnyInstance); 52dma_trait!(RxDma, Instance);
47 53
48/// Analog to Digital driver. 54/// Analog to Digital driver.
49pub struct Adc<'d, T: AnyInstance> { 55pub struct Adc<'d, T: Instance> {
50 #[allow(unused)] 56 #[allow(unused)]
51 adc: crate::Peri<'d, T>, 57 adc: crate::Peri<'d, T>,
52} 58}
@@ -65,79 +71,72 @@ impl State {
65 } 71 }
66} 72}
67 73
68trait SealedInstance { 74#[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))]
69 #[cfg(not(adc_wba))] 75trait_set::trait_set! {
70 #[allow(unused)] 76 pub trait DefaultInstance = Instance;
71 fn regs() -> crate::pac::adc::Adc;
72 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0)))]
73 #[allow(unused)]
74 fn common_regs() -> crate::pac::adccommon::AdcCommon;
75 #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
76 fn state() -> &'static State;
77} 77}
78 78
79pub(crate) trait SealedAdcChannel<T> { 79#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_g4, adc_c0))]
80 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] 80trait_set::trait_set! {
81 fn setup(&mut self) {} 81 pub trait DefaultInstance = Instance<Regs = crate::pac::adc::Adc>;
82
83 #[allow(unused)]
84 fn channel(&self) -> u8;
85
86 #[allow(unused)]
87 fn is_differential(&self) -> bool {
88 false
89 }
90} 82}
91 83
92// Temporary patch for ADCs that have not implemented the standard iface yet 84#[cfg(adc_wba)]
93#[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1))]
94trait_set::trait_set! { 85trait_set::trait_set! {
95 pub trait AnyInstance = Instance; 86 pub trait DefaultInstance = Instance<Regs = crate::pac::adc::Adc4>;
96} 87}
97 88
98#[cfg(any( 89pub trait BasicAdcRegs {
99 adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0
100))]
101pub trait BasicAnyInstance {
102 type SampleTime; 90 type SampleTime;
103} 91}
104 92
105#[cfg(any( 93#[cfg(any(
106 adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 94 adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0
107))] 95))]
108pub(self) trait SealedAnyInstance: BasicAnyInstance { 96trait AdcRegs: BasicAdcRegs {
109 fn enable(); 97 fn enable(&self);
110 fn start(); 98 fn start(&self);
111 fn stop(); 99 fn stop(&self);
112 fn convert() -> u16; 100 fn convert(&self);
113 fn configure_dma(conversion_mode: ConversionMode); 101 fn configure_dma(&self, conversion_mode: ConversionMode);
114 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); 102 fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>);
115 #[allow(dead_code)] 103 fn data(&self) -> *mut u16;
116 fn dr() -> *mut u16;
117} 104}
118 105
119// On chips without ADC4, AnyInstance is an Instance
120#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_g4, adc_c0))]
121#[allow(private_bounds)] 106#[allow(private_bounds)]
122pub trait AnyInstance: SealedAnyInstance + Instance {} 107pub trait BasicInstance {
123 108 #[cfg(any(
124// On chips with ADC4, AnyInstance is an Instance or adc4::Instance 109 adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0
125#[cfg(any(adc_v4, adc_u5, adc_wba))] 110 ))]
126#[allow(private_bounds)] 111 type Regs: AdcRegs;
127pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} 112}
128 113
129// Implement AnyInstance automatically for SealedAnyInstance 114trait SealedInstance: BasicInstance {
130#[cfg(any( 115 #[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))]
131 adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 116 fn regs() -> crate::pac::adc::Adc;
132))] 117 #[cfg(any(
133impl<T: SealedAnyInstance + Instance> BasicAnyInstance for T { 118 adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0
134 type SampleTime = SampleTime; 119 ))]
120 fn regs() -> Self::Regs;
121 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0)))]
122 #[allow(unused)]
123 fn common_regs() -> crate::pac::adccommon::AdcCommon;
124 #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
125 fn state() -> &'static State;
135} 126}
136 127
137#[cfg(any( 128pub(crate) trait SealedAdcChannel<T> {
138 adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 129 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))]
139))] 130 fn setup(&mut self) {}
140impl<T: SealedAnyInstance + Instance> AnyInstance for T {} 131
132 #[allow(unused)]
133 fn channel(&self) -> u8;
134
135 #[allow(unused)]
136 fn is_differential(&self) -> bool {
137 false
138 }
139}
141 140
142#[cfg(any(adc_c0, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] 141#[cfg(any(adc_c0, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))]
143/// Number of samples used for averaging. 142/// Number of samples used for averaging.
@@ -183,28 +182,33 @@ pub enum RegularConversionMode {
183 Triggered(ConversionTrigger), 182 Triggered(ConversionTrigger),
184} 183}
185 184
186impl<'d, T: AnyInstance> Adc<'d, T> { 185impl<'d, T: Instance> Adc<'d, T> {
187 #[cfg(any( 186 #[cfg(any(
188 adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4, adc_wba, adc_c0 187 adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v3, adc_v4, adc_wba, adc_c0
189 ))] 188 ))]
190 /// Read an ADC pin. 189 /// Read an ADC pin.
191 pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: T::SampleTime) -> u16 { 190 pub fn blocking_read(
192 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] 191 &mut self,
192 channel: &mut impl AdcChannel<T>,
193 sample_time: <T::Regs as BasicAdcRegs>::SampleTime,
194 ) -> u16 {
195 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))]
193 channel.setup(); 196 channel.setup();
194 197
195 // Ensure no conversions are ongoing 198 // Ensure no conversions are ongoing
196 T::stop(); 199 T::regs().stop();
197 #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h7rs, adc_u0, adc_u5, adc_wba, adc_c0))] 200 #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h7rs, adc_u0, adc_u5, adc_wba, adc_c0))]
198 T::enable(); 201 T::regs().enable();
199 T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); 202 T::regs().configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter());
200 203
201 // On chips with differential channels, enable after configure_sequence to allow setting differential channels 204 // On chips with differential channels, enable after configure_sequence to allow setting differential channels
202 // 205 //
203 // TODO: If hardware allows, enable after configure_sequence on all chips 206 // TODO: If hardware allows, enable after configure_sequence on all chips
204 #[cfg(any(adc_g4, adc_h5))] 207 #[cfg(any(adc_g4, adc_h5))]
205 T::enable(); 208 T::regs().enable();
209 T::regs().convert();
206 210
207 T::convert() 211 unsafe { *T::regs().data() }
208 } 212 }
209 213
210 #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] 214 #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))]
@@ -241,10 +245,10 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
241 /// in order or require the sequence to have the same sample time for all channnels, depending 245 /// in order or require the sequence to have the same sample time for all channnels, depending
242 /// on the number and properties of the channels in the sequence. This method will panic if 246 /// on the number and properties of the channels in the sequence. This method will panic if
243 /// the hardware cannot deliver the requested configuration. 247 /// the hardware cannot deliver the requested configuration.
244 pub async fn read( 248 pub async fn read<'a, 'b: 'a>(
245 &mut self, 249 &mut self,
246 rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, 250 rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>,
247 sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, T::SampleTime)>, 251 sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<'b, T>, <T::Regs as BasicAdcRegs>::SampleTime)>,
248 readings: &mut [u16], 252 readings: &mut [u16],
249 ) { 253 ) {
250 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); 254 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
@@ -258,11 +262,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
258 ); 262 );
259 263
260 // Ensure no conversions are ongoing 264 // Ensure no conversions are ongoing
261 T::stop(); 265 T::regs().stop();
262 #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] 266 #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))]
263 T::enable(); 267 T::regs().enable();
264 268
265 T::configure_sequence( 269 T::regs().configure_sequence(
266 sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), 270 sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)),
267 ); 271 );
268 272
@@ -270,20 +274,20 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
270 // 274 //
271 // TODO: If hardware allows, enable after configure_sequence on all chips 275 // TODO: If hardware allows, enable after configure_sequence on all chips
272 #[cfg(any(adc_g4, adc_h5))] 276 #[cfg(any(adc_g4, adc_h5))]
273 T::enable(); 277 T::regs().enable();
274 T::configure_dma(ConversionMode::Singular); 278 T::regs().configure_dma(ConversionMode::Singular);
275 279
276 let request = rx_dma.request(); 280 let request = rx_dma.request();
277 let transfer = 281 let transfer =
278 unsafe { crate::dma::Transfer::new_read(rx_dma, request, T::dr(), readings, Default::default()) }; 282 unsafe { crate::dma::Transfer::new_read(rx_dma, request, T::regs().data(), readings, Default::default()) };
279 283
280 T::start(); 284 T::regs().start();
281 285
282 // Wait for conversion sequence to finish. 286 // Wait for conversion sequence to finish.
283 transfer.await; 287 transfer.await;
284 288
285 // Ensure conversions are finished. 289 // Ensure conversions are finished.
286 T::stop(); 290 T::regs().stop();
287 } 291 }
288 292
289 #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] 293 #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))]
@@ -313,11 +317,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
313 /// in order or require the sequence to have the same sample time for all channnels, depending 317 /// in order or require the sequence to have the same sample time for all channnels, depending
314 /// on the number and properties of the channels in the sequence. This method will panic if 318 /// on the number and properties of the channels in the sequence. This method will panic if
315 /// the hardware cannot deliver the requested configuration. 319 /// the hardware cannot deliver the requested configuration.
316 pub fn into_ring_buffered<'a>( 320 pub fn into_ring_buffered<'a, 'b>(
317 self, 321 self,
318 dma: embassy_hal_internal::Peri<'a, impl RxDma<T>>, 322 dma: embassy_hal_internal::Peri<'a, impl RxDma<T>>,
319 dma_buf: &'a mut [u16], 323 dma_buf: &'a mut [u16],
320 sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, T::SampleTime)>, 324 sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<'b, T>, <T::Regs as BasicAdcRegs>::SampleTime)>,
321 mode: RegularConversionMode, 325 mode: RegularConversionMode,
322 ) -> RingBufferedAdc<'a, T> { 326 ) -> RingBufferedAdc<'a, T> {
323 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); 327 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
@@ -327,11 +331,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
327 "Asynchronous read sequence cannot be more than 16 in length" 331 "Asynchronous read sequence cannot be more than 16 in length"
328 ); 332 );
329 // Ensure no conversions are ongoing 333 // Ensure no conversions are ongoing
330 T::stop(); 334 T::regs().stop();
331 #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] 335 #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))]
332 T::enable(); 336 T::regs().enable();
333 337
334 T::configure_sequence( 338 T::regs().configure_sequence(
335 sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), 339 sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)),
336 ); 340 );
337 341
@@ -339,8 +343,8 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
339 // 343 //
340 // TODO: If hardware allows, enable after configure_sequence on all chips 344 // TODO: If hardware allows, enable after configure_sequence on all chips
341 #[cfg(any(adc_g4, adc_h5))] 345 #[cfg(any(adc_g4, adc_h5))]
342 T::enable(); 346 T::regs().enable();
343 T::configure_dma(ConversionMode::Repeated(mode)); 347 T::regs().configure_dma(ConversionMode::Repeated(mode));
344 348
345 core::mem::forget(self); 349 core::mem::forget(self);
346 350
@@ -417,8 +421,11 @@ pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeri
417#[allow(private_bounds)] 421#[allow(private_bounds)]
418pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { 422pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized {
419 #[allow(unused_mut)] 423 #[allow(unused_mut)]
420 fn degrade_adc(mut self) -> AnyAdcChannel<T> { 424 fn degrade_adc<'a>(mut self) -> AnyAdcChannel<'a, T>
421 #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] 425 where
426 Self: 'a,
427 {
428 #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))]
422 self.setup(); 429 self.setup();
423 430
424 AnyAdcChannel { 431 AnyAdcChannel {
@@ -433,14 +440,13 @@ pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized {
433/// 440///
434/// This is useful in scenarios where you need the ADC channels to have the same type, such as 441/// This is useful in scenarios where you need the ADC channels to have the same type, such as
435/// storing them in an array. 442/// storing them in an array.
436pub struct AnyAdcChannel<T> { 443pub struct AnyAdcChannel<'a, T> {
437 channel: u8, 444 channel: u8,
438 is_differential: bool, 445 is_differential: bool,
439 _phantom: PhantomData<T>, 446 _phantom: PhantomData<&'a mut T>,
440} 447}
441impl_peripheral!(AnyAdcChannel<T: AnyInstance>); 448impl<T: Instance> AdcChannel<T> for AnyAdcChannel<'_, T> {}
442impl<T: AnyInstance> AdcChannel<T> for AnyAdcChannel<T> {} 449impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<'_, T> {
443impl<T: AnyInstance> SealedAdcChannel<T> for AnyAdcChannel<T> {
444 fn channel(&self) -> u8 { 450 fn channel(&self) -> u8 {
445 self.channel 451 self.channel
446 } 452 }
@@ -450,22 +456,41 @@ impl<T: AnyInstance> SealedAdcChannel<T> for AnyAdcChannel<T> {
450 } 456 }
451} 457}
452 458
453impl<T> AnyAdcChannel<T> { 459impl<T> AnyAdcChannel<'_, T> {
454 #[allow(unused)] 460 #[allow(unused)]
455 pub fn get_hw_channel(&self) -> u8 { 461 pub fn get_hw_channel(&self) -> u8 {
456 self.channel 462 self.channel
457 } 463 }
458} 464}
465
466#[cfg(not(adc_wba))]
467impl BasicAdcRegs for crate::pac::adc::Adc {
468 type SampleTime = SampleTime;
469}
470
471#[cfg(any(adc_wba, adc_u5))]
472impl BasicAdcRegs for crate::pac::adc::Adc4 {
473 type SampleTime = Adc4SampleTime;
474}
475
459#[cfg(adc_wba)] 476#[cfg(adc_wba)]
460foreach_adc!( 477foreach_adc!(
461 (ADC4, $common_inst:ident, $clock:ident) => { 478 (ADC4, $common_inst:ident, $clock:ident) => {
462 impl crate::adc::adc4::SealedInstance for peripherals::ADC4 { 479 impl crate::adc::BasicInstance for peripherals::ADC4 {
463 fn regs() -> crate::pac::adc::Adc4 { 480 type Regs = crate::pac::adc::Adc4;
481 }
482
483 impl crate::adc::SealedInstance for peripherals::ADC4 {
484 fn regs() -> Self::Regs {
464 crate::pac::ADC4 485 crate::pac::ADC4
465 } 486 }
487
488 fn common_regs() -> crate::pac::adccommon::AdcCommon {
489 return crate::pac::$common_inst
490 }
466 } 491 }
467 492
468 impl crate::adc::adc4::Instance for peripherals::ADC4 { 493 impl crate::adc::Instance for peripherals::ADC4 {
469 type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL; 494 type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL;
470 } 495 }
471 }; 496 };
@@ -490,20 +515,32 @@ foreach_adc!(
490#[cfg(adc_u5)] 515#[cfg(adc_u5)]
491foreach_adc!( 516foreach_adc!(
492 (ADC4, $common_inst:ident, $clock:ident) => { 517 (ADC4, $common_inst:ident, $clock:ident) => {
493 impl crate::adc::adc4::SealedInstance for peripherals::ADC4 { 518 impl crate::adc::BasicInstance for peripherals::ADC4 {
494 fn regs() -> crate::pac::adc::Adc4 { 519 type Regs = crate::pac::adc::Adc4;
520 }
521
522 impl crate::adc::SealedInstance for peripherals::ADC4 {
523 fn regs() -> Self::Regs {
495 crate::pac::ADC4 524 crate::pac::ADC4
496 } 525 }
526
527 fn common_regs() -> crate::pac::adccommon::AdcCommon {
528 return crate::pac::$common_inst
529 }
497 } 530 }
498 531
499 impl crate::adc::adc4::Instance for peripherals::ADC4 { 532 impl crate::adc::Instance for peripherals::ADC4 {
500 type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL; 533 type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL;
501 } 534 }
502 }; 535 };
503 536
504 ($inst:ident, $common_inst:ident, $clock:ident) => { 537 ($inst:ident, $common_inst:ident, $clock:ident) => {
538 impl crate::adc::BasicInstance for peripherals::$inst {
539 type Regs = crate::pac::adc::Adc;
540 }
541
505 impl crate::adc::SealedInstance for peripherals::$inst { 542 impl crate::adc::SealedInstance for peripherals::$inst {
506 fn regs() -> crate::pac::adc::Adc { 543 fn regs() -> Self::Regs {
507 crate::pac::$inst 544 crate::pac::$inst
508 } 545 }
509 546
@@ -521,14 +558,23 @@ foreach_adc!(
521#[cfg(not(any(adc_u5, adc_wba)))] 558#[cfg(not(any(adc_u5, adc_wba)))]
522foreach_adc!( 559foreach_adc!(
523 ($inst:ident, $common_inst:ident, $clock:ident) => { 560 ($inst:ident, $common_inst:ident, $clock:ident) => {
561 impl crate::adc::BasicInstance for peripherals::$inst {
562 #[cfg(any(
563 adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0
564 ))]
565 type Regs = crate::pac::adc::Adc;
566 }
567
524 impl crate::adc::SealedInstance for peripherals::$inst { 568 impl crate::adc::SealedInstance for peripherals::$inst {
525 #[cfg(not(adc_wba))] 569 #[cfg(any(
526 fn regs() -> crate::pac::adc::Adc { 570 adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0
571 ))]
572 fn regs() -> Self::Regs {
527 crate::pac::$inst 573 crate::pac::$inst
528 } 574 }
529 575
530 #[cfg(adc_wba)] 576 #[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))]
531 fn regs() -> crate::pac::adc::Adc4 { 577 fn regs() -> crate::pac::adc::Adc {
532 crate::pac::$inst 578 crate::pac::$inst
533 } 579 }
534 580
@@ -554,7 +600,7 @@ macro_rules! impl_adc_pin {
554 ($inst:ident, $pin:ident, $ch:expr) => { 600 ($inst:ident, $pin:ident, $ch:expr) => {
555 impl crate::adc::AdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {} 601 impl crate::adc::AdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {}
556 impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> { 602 impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {
557 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] 603 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))]
558 fn setup(&mut self) { 604 fn setup(&mut self) {
559 <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(self); 605 <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(self);
560 } 606 }
@@ -582,7 +628,7 @@ macro_rules! impl_adc_pair {
582 crate::Peri<'_, crate::peripherals::$npin>, 628 crate::Peri<'_, crate::peripherals::$npin>,
583 ) 629 )
584 { 630 {
585 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] 631 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))]
586 fn setup(&mut self) { 632 fn setup(&mut self) {
587 <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(&mut self.0); 633 <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(&mut self.0);
588 <crate::peripherals::$npin as crate::gpio::SealedPin>::set_as_analog(&mut self.1); 634 <crate::peripherals::$npin as crate::gpio::SealedPin>::set_as_analog(&mut self.1);
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs
index 5437866d3..242a1a89c 100644
--- a/embassy-stm32/src/adc/ringbuffered.rs
+++ b/embassy-stm32/src/adc/ringbuffered.rs
@@ -4,7 +4,7 @@ use core::sync::atomic::{Ordering, compiler_fence};
4#[allow(unused_imports)] 4#[allow(unused_imports)]
5use embassy_hal_internal::Peri; 5use embassy_hal_internal::Peri;
6 6
7use crate::adc::AnyInstance; 7use super::AdcRegs;
8#[allow(unused_imports)] 8#[allow(unused_imports)]
9use crate::adc::{Instance, RxDma}; 9use crate::adc::{Instance, RxDma};
10#[allow(unused_imports)] 10#[allow(unused_imports)]
@@ -19,7 +19,7 @@ pub struct RingBufferedAdc<'d, T: Instance> {
19 ring_buf: ReadableRingBuffer<'d, u16>, 19 ring_buf: ReadableRingBuffer<'d, u16>,
20} 20}
21 21
22impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { 22impl<'d, T: Instance> RingBufferedAdc<'d, T> {
23 pub(crate) fn new(dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> Self { 23 pub(crate) fn new(dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> Self {
24 //dma side setup 24 //dma side setup
25 let opts = TransferOptions { 25 let opts = TransferOptions {
@@ -31,8 +31,7 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> {
31 // Safety: we forget the struct before this function returns. 31 // Safety: we forget the struct before this function returns.
32 let request = dma.request(); 32 let request = dma.request();
33 33
34 let ring_buf = 34 let ring_buf = unsafe { ReadableRingBuffer::new(dma, request, T::regs().data(), dma_buf, opts) };
35 unsafe { ReadableRingBuffer::new(dma, request, T::regs().dr().as_ptr() as *mut u16, dma_buf, opts) };
36 35
37 Self { 36 Self {
38 _phantom: PhantomData, 37 _phantom: PhantomData,
@@ -45,7 +44,7 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> {
45 compiler_fence(Ordering::SeqCst); 44 compiler_fence(Ordering::SeqCst);
46 self.ring_buf.start(); 45 self.ring_buf.start();
47 46
48 T::start(); 47 T::regs().start();
49 } 48 }
50 49
51 pub fn stop(&mut self) { 50 pub fn stop(&mut self) {
@@ -117,15 +116,15 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> {
117 self.start(); 116 self.start();
118 } 117 }
119 118
120 #[cfg(adc_v2)] 119 // #[cfg(adc_v2)]
121 { 120 // {
122 // Clear overrun flag if set. 121 // // Clear overrun flag if set.
123 if T::regs().sr().read().ovr() { 122 // if T::regs().sr().read().ovr() {
124 self.stop(); 123 // self.stop();
125 124 //
126 return Err(OverrunError); 125 // return Err(OverrunError);
127 } 126 // }
128 } 127 // }
129 128
130 self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError) 129 self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError)
131 } 130 }
@@ -143,15 +142,16 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> {
143 self.start(); 142 self.start();
144 } 143 }
145 144
146 #[cfg(adc_v2)] 145 // #[cfg(adc_v2)]
147 { 146 // {
148 // Clear overrun flag if set. 147 // // Clear overrun flag if set.
149 if T::regs().sr().read().ovr() { 148 // if T::regs().sr().read().ovr() {
150 self.stop(); 149 // self.stop();
150 //
151 // return Err(OverrunError);
152 // }
153 // }
151 154
152 return Err(OverrunError);
153 }
154 }
155 loop { 155 loop {
156 match self.ring_buf.read(buf) { 156 match self.ring_buf.read(buf) {
157 Ok((0, _)) => {} 157 Ok((0, _)) => {}
@@ -168,9 +168,9 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> {
168 } 168 }
169} 169}
170 170
171impl<T: Instance + AnyInstance> Drop for RingBufferedAdc<'_, T> { 171impl<T: Instance> Drop for RingBufferedAdc<'_, T> {
172 fn drop(&mut self) { 172 fn drop(&mut self) {
173 T::stop(); 173 T::regs().stop();
174 174
175 compiler_fence(Ordering::SeqCst); 175 compiler_fence(Ordering::SeqCst);
176 176
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs
index 3c4431ae0..b026383d5 100644
--- a/embassy-stm32/src/adc/v2.rs
+++ b/embassy-stm32/src/adc/v2.rs
@@ -1,7 +1,7 @@
1use core::sync::atomic::{Ordering, compiler_fence}; 1use core::sync::atomic::{Ordering, compiler_fence};
2 2
3use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; 3use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us};
4use crate::adc::{Adc, Instance, Resolution, SampleTime}; 4use crate::adc::{Adc, AdcRegs, Instance, Resolution, SampleTime};
5use crate::pac::adc::vals; 5use crate::pac::adc::vals;
6pub use crate::pac::adccommon::vals::Adcpre; 6pub use crate::pac::adccommon::vals::Adcpre;
7use crate::time::Hertz; 7use crate::time::Hertz;
@@ -71,31 +71,31 @@ fn from_pclk2(freq: Hertz) -> Adcpre {
71/// ADC configuration 71/// ADC configuration
72#[derive(Default)] 72#[derive(Default)]
73pub struct AdcConfig { 73pub struct AdcConfig {
74 resolution: Option<Resolution>, 74 pub resolution: Option<Resolution>,
75} 75}
76 76
77impl<T: Instance> super::SealedAnyInstance for T { 77impl super::AdcRegs for crate::pac::adc::Adc {
78 fn dr() -> *mut u16 { 78 fn data(&self) -> *mut u16 {
79 T::regs().dr().as_ptr() as *mut u16 79 crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16
80 } 80 }
81 81
82 fn enable() { 82 fn enable(&self) {
83 T::regs().cr2().modify(|reg| { 83 self.cr2().modify(|reg| {
84 reg.set_adon(true); 84 reg.set_adon(true);
85 }); 85 });
86 86
87 blocking_delay_us(3); 87 blocking_delay_us(3);
88 } 88 }
89 89
90 fn start() { 90 fn start(&self) {
91 // Begin ADC conversions 91 // Begin ADC conversions
92 T::regs().cr2().modify(|reg| { 92 self.cr2().modify(|reg| {
93 reg.set_swstart(true); 93 reg.set_swstart(true);
94 }); 94 });
95 } 95 }
96 96
97 fn stop() { 97 fn stop(&self) {
98 let r = T::regs(); 98 let r = self;
99 99
100 // Stop ADC 100 // Stop ADC
101 r.cr2().modify(|reg| { 101 r.cr2().modify(|reg| {
@@ -114,36 +114,34 @@ impl<T: Instance> super::SealedAnyInstance for T {
114 w.set_ovrie(false); 114 w.set_ovrie(false);
115 }); 115 });
116 116
117 clear_interrupt_flags(r); 117 clear_interrupt_flags(*r);
118 118
119 compiler_fence(Ordering::SeqCst); 119 compiler_fence(Ordering::SeqCst);
120 } 120 }
121 121
122 fn convert() -> u16 { 122 fn convert(&self) {
123 // clear end of conversion flag 123 // clear end of conversion flag
124 T::regs().sr().modify(|reg| { 124 self.sr().modify(|reg| {
125 reg.set_eoc(false); 125 reg.set_eoc(false);
126 }); 126 });
127 127
128 // Start conversion 128 // Start conversion
129 T::regs().cr2().modify(|reg| { 129 self.cr2().modify(|reg| {
130 reg.set_swstart(true); 130 reg.set_swstart(true);
131 }); 131 });
132 132
133 while T::regs().sr().read().strt() == false { 133 while self.sr().read().strt() == false {
134 // spin //wait for actual start 134 // spin //wait for actual start
135 } 135 }
136 while T::regs().sr().read().eoc() == false { 136 while self.sr().read().eoc() == false {
137 // spin //wait for finish 137 // spin //wait for finish
138 } 138 }
139
140 T::regs().dr().read().0 as u16
141 } 139 }
142 140
143 fn configure_dma(conversion_mode: ConversionMode) { 141 fn configure_dma(&self, conversion_mode: ConversionMode) {
144 match conversion_mode { 142 match conversion_mode {
145 ConversionMode::Repeated(_) => { 143 ConversionMode::Repeated(_) => {
146 let r = T::regs(); 144 let r = self;
147 145
148 // Clear all interrupts 146 // Clear all interrupts
149 r.sr().modify(|regs| { 147 r.sr().modify(|regs| {
@@ -177,25 +175,25 @@ impl<T: Instance> super::SealedAnyInstance for T {
177 } 175 }
178 } 176 }
179 177
180 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { 178 fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {
181 T::regs().cr2().modify(|reg| { 179 self.cr2().modify(|reg| {
182 reg.set_adon(true); 180 reg.set_adon(true);
183 }); 181 });
184 182
185 // Check the sequence is long enough 183 // Check the sequence is long enough
186 T::regs().sqr1().modify(|r| { 184 self.sqr1().modify(|r| {
187 r.set_l((sequence.len() - 1).try_into().unwrap()); 185 r.set_l((sequence.len() - 1).try_into().unwrap());
188 }); 186 });
189 187
190 for (i, ((ch, _), sample_time)) in sequence.enumerate() { 188 for (i, ((ch, _), sample_time)) in sequence.enumerate() {
191 // Set the channel in the right sequence field. 189 // Set the channel in the right sequence field.
192 T::regs().sqr3().modify(|w| w.set_sq(i, ch)); 190 self.sqr3().modify(|w| w.set_sq(i, ch));
193 191
194 let sample_time = sample_time.into(); 192 let sample_time = sample_time.into();
195 if ch <= 9 { 193 if ch <= 9 {
196 T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); 194 self.smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
197 } else { 195 } else {
198 T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); 196 self.smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
199 } 197 }
200 } 198 }
201 } 199 }
@@ -203,7 +201,7 @@ impl<T: Instance> super::SealedAnyInstance for T {
203 201
204impl<'d, T> Adc<'d, T> 202impl<'d, T> Adc<'d, T>
205where 203where
206 T: Instance + super::AnyInstance, 204 T: Instance<Regs = crate::pac::adc::Adc>,
207{ 205{
208 pub fn new(adc: Peri<'d, T>) -> Self { 206 pub fn new(adc: Peri<'d, T>) -> Self {
209 Self::new_with_config(adc, Default::default()) 207 Self::new_with_config(adc, Default::default())
@@ -214,7 +212,7 @@ where
214 212
215 let presc = from_pclk2(T::frequency()); 213 let presc = from_pclk2(T::frequency());
216 T::common_regs().ccr().modify(|w| w.set_adcpre(presc)); 214 T::common_regs().ccr().modify(|w| w.set_adcpre(presc));
217 T::enable(); 215 T::regs().enable();
218 216
219 if let Some(resolution) = config.resolution { 217 if let Some(resolution) = config.resolution {
220 T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); 218 T::regs().cr1().modify(|reg| reg.set_res(resolution.into()));
@@ -259,9 +257,7 @@ where
259 257
260impl<'d, T: Instance> Drop for Adc<'d, T> { 258impl<'d, T: Instance> Drop for Adc<'d, T> {
261 fn drop(&mut self) { 259 fn drop(&mut self) {
262 T::regs().cr2().modify(|reg| { 260 T::regs().stop();
263 reg.set_adon(false);
264 });
265 261
266 rcc::disable::<T>(); 262 rcc::disable::<T>();
267 } 263 }
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index b270588c4..9cc44aa9a 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -146,63 +146,63 @@ pub struct AdcConfig {
146 pub averaging: Option<Averaging>, 146 pub averaging: Option<Averaging>,
147} 147}
148 148
149impl<T: Instance> super::SealedAnyInstance for T { 149impl super::AdcRegs for crate::pac::adc::Adc {
150 fn dr() -> *mut u16 { 150 fn data(&self) -> *mut u16 {
151 T::regs().dr().as_ptr() as *mut u16 151 crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16
152 } 152 }
153 153
154 // Enable ADC only when it is not already running. 154 // Enable ADC only when it is not already running.
155 fn enable() { 155 fn enable(&self) {
156 // Make sure bits are off 156 // Make sure bits are off
157 while T::regs().cr().read().addis() { 157 while self.cr().read().addis() {
158 // spin 158 // spin
159 } 159 }
160 160
161 if !T::regs().cr().read().aden() { 161 if !self.cr().read().aden() {
162 // Enable ADC 162 // Enable ADC
163 T::regs().isr().modify(|reg| { 163 self.isr().modify(|reg| {
164 reg.set_adrdy(true); 164 reg.set_adrdy(true);
165 }); 165 });
166 T::regs().cr().modify(|reg| { 166 self.cr().modify(|reg| {
167 reg.set_aden(true); 167 reg.set_aden(true);
168 }); 168 });
169 169
170 while !T::regs().isr().read().adrdy() { 170 while !self.isr().read().adrdy() {
171 // spin 171 // spin
172 } 172 }
173 } 173 }
174 } 174 }
175 175
176 fn start() { 176 fn start(&self) {
177 T::regs().cr().modify(|reg| { 177 self.cr().modify(|reg| {
178 reg.set_adstart(true); 178 reg.set_adstart(true);
179 }); 179 });
180 } 180 }
181 181
182 fn stop() { 182 fn stop(&self) {
183 // Ensure conversions are finished. 183 // Ensure conversions are finished.
184 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { 184 if self.cr().read().adstart() && !self.cr().read().addis() {
185 T::regs().cr().modify(|reg| { 185 self.cr().modify(|reg| {
186 reg.set_adstp(true); 186 reg.set_adstp(true);
187 }); 187 });
188 while T::regs().cr().read().adstart() {} 188 while self.cr().read().adstart() {}
189 } 189 }
190 190
191 // Reset configuration. 191 // Reset configuration.
192 #[cfg(not(any(adc_g0, adc_u0)))] 192 #[cfg(not(any(adc_g0, adc_u0)))]
193 T::regs().cfgr().modify(|reg| { 193 self.cfgr().modify(|reg| {
194 reg.set_cont(false); 194 reg.set_cont(false);
195 reg.set_dmaen(false); 195 reg.set_dmaen(false);
196 }); 196 });
197 #[cfg(any(adc_g0, adc_u0))] 197 #[cfg(any(adc_g0, adc_u0))]
198 T::regs().cfgr1().modify(|reg| { 198 self.cfgr1().modify(|reg| {
199 reg.set_cont(false); 199 reg.set_cont(false);
200 reg.set_dmaen(false); 200 reg.set_dmaen(false);
201 }); 201 });
202 } 202 }
203 203
204 /// Perform a single conversion. 204 /// Perform a single conversion.
205 fn convert() -> u16 { 205 fn convert(&self) {
206 // Some models are affected by an erratum: 206 // Some models are affected by an erratum:
207 // If we perform conversions slower than 1 kHz, the first read ADC value can be 207 // If we perform conversions slower than 1 kHz, the first read ADC value can be
208 // corrupted, so we discard it and measure again. 208 // corrupted, so we discard it and measure again.
@@ -216,36 +216,34 @@ impl<T: Instance> super::SealedAnyInstance for T {
216 let len = 1; 216 let len = 1;
217 217
218 for _ in 0..len { 218 for _ in 0..len {
219 T::regs().isr().modify(|reg| { 219 self.isr().modify(|reg| {
220 reg.set_eos(true); 220 reg.set_eos(true);
221 reg.set_eoc(true); 221 reg.set_eoc(true);
222 }); 222 });
223 223
224 // Start conversion 224 // Start conversion
225 T::regs().cr().modify(|reg| { 225 self.cr().modify(|reg| {
226 reg.set_adstart(true); 226 reg.set_adstart(true);
227 }); 227 });
228 228
229 while !T::regs().isr().read().eos() { 229 while !self.isr().read().eos() {
230 // spin 230 // spin
231 } 231 }
232 } 232 }
233
234 T::regs().dr().read().0 as u16
235 } 233 }
236 234
237 fn configure_dma(conversion_mode: ConversionMode) { 235 fn configure_dma(&self, conversion_mode: ConversionMode) {
238 // Set continuous mode with oneshot dma. 236 // Set continuous mode with oneshot dma.
239 // Clear overrun flag before starting transfer. 237 // Clear overrun flag before starting transfer.
240 T::regs().isr().modify(|reg| { 238 self.isr().modify(|reg| {
241 reg.set_ovr(true); 239 reg.set_ovr(true);
242 }); 240 });
243 241
244 #[cfg(not(any(adc_g0, adc_u0)))] 242 #[cfg(not(any(adc_g0, adc_u0)))]
245 let regs = T::regs().cfgr(); 243 let regs = self.cfgr();
246 244
247 #[cfg(any(adc_g0, adc_u0))] 245 #[cfg(any(adc_g0, adc_u0))]
248 let regs = T::regs().cfgr1(); 246 let regs = self.cfgr1();
249 247
250 regs.modify(|reg| { 248 regs.modify(|reg| {
251 reg.set_discen(false); 249 reg.set_discen(false);
@@ -259,13 +257,13 @@ impl<T: Instance> super::SealedAnyInstance for T {
259 }); 257 });
260 } 258 }
261 259
262 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { 260 fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {
263 #[cfg(adc_h5)] 261 #[cfg(adc_h5)]
264 T::regs().cr().modify(|w| w.set_aden(false)); 262 self.cr().modify(|w| w.set_aden(false));
265 263
266 // Set sequence length 264 // Set sequence length
267 #[cfg(not(any(adc_g0, adc_u0)))] 265 #[cfg(not(any(adc_g0, adc_u0)))]
268 T::regs().sqr1().modify(|w| { 266 self.sqr1().modify(|w| {
269 w.set_l(sequence.len() as u8 - 1); 267 w.set_l(sequence.len() as u8 - 1);
270 }); 268 });
271 269
@@ -273,8 +271,8 @@ impl<T: Instance> super::SealedAnyInstance for T {
273 { 271 {
274 let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); 272 let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new();
275 273
276 T::regs().chselr().write(|chselr| { 274 self.chselr().write(|chselr| {
277 T::regs().smpr().write(|smpr| { 275 self.smpr().write(|smpr| {
278 for ((channel, _), sample_time) in sequence { 276 for ((channel, _), sample_time) in sequence {
279 chselr.set_chsel(channel.into(), true); 277 chselr.set_chsel(channel.into(), true);
280 if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { 278 if let Some(i) = sample_times.iter().position(|&t| t == sample_time) {
@@ -306,22 +304,22 @@ impl<T: Instance> super::SealedAnyInstance for T {
306 // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." 304 // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected."
307 #[cfg(any(adc_h5, adc_h7rs))] 305 #[cfg(any(adc_h5, adc_h7rs))]
308 if channel == 0 { 306 if channel == 0 {
309 T::regs().or().modify(|reg| reg.set_op0(true)); 307 self.or().modify(|reg| reg.set_op0(true));
310 } 308 }
311 309
312 // Configure channel 310 // Configure channel
313 cfg_if! { 311 cfg_if! {
314 if #[cfg(adc_u0)] { 312 if #[cfg(adc_u0)] {
315 // On G0 and U6 all channels use the same sampling time. 313 // On G0 and U6 all channels use the same sampling time.
316 T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); 314 self.smpr().modify(|reg| reg.set_smp1(sample_time.into()));
317 } else if #[cfg(any(adc_h5, adc_h7rs))] { 315 } else if #[cfg(any(adc_h5, adc_h7rs))] {
318 match channel { 316 match channel {
319 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), 317 0..=9 => self.smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())),
320 _ => T::regs().smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), 318 _ => self.smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())),
321 } 319 }
322 } else { 320 } else {
323 let sample_time = sample_time.into(); 321 let sample_time = sample_time.into();
324 T::regs() 322 self
325 .smpr(channel as usize / 10) 323 .smpr(channel as usize / 10)
326 .modify(|reg| reg.set_smp(channel as usize % 10, sample_time)); 324 .modify(|reg| reg.set_smp(channel as usize % 10, sample_time));
327 } 325 }
@@ -331,9 +329,8 @@ impl<T: Instance> super::SealedAnyInstance for T {
331 { 329 {
332 use crate::pac::adc::vals::Pcsel; 330 use crate::pac::adc::vals::Pcsel;
333 331
334 T::regs().cfgr2().modify(|w| w.set_lshift(0)); 332 self.cfgr2().modify(|w| w.set_lshift(0));
335 T::regs() 333 self.pcsel()
336 .pcsel()
337 .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); 334 .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED));
338 } 335 }
339 336
@@ -341,22 +338,22 @@ impl<T: Instance> super::SealedAnyInstance for T {
341 #[cfg(not(any(adc_g0, adc_u0)))] 338 #[cfg(not(any(adc_g0, adc_u0)))]
342 match _i { 339 match _i {
343 0..=3 => { 340 0..=3 => {
344 T::regs().sqr1().modify(|w| { 341 self.sqr1().modify(|w| {
345 w.set_sq(_i, channel); 342 w.set_sq(_i, channel);
346 }); 343 });
347 } 344 }
348 4..=8 => { 345 4..=8 => {
349 T::regs().sqr2().modify(|w| { 346 self.sqr2().modify(|w| {
350 w.set_sq(_i - 4, channel); 347 w.set_sq(_i - 4, channel);
351 }); 348 });
352 } 349 }
353 9..=13 => { 350 9..=13 => {
354 T::regs().sqr3().modify(|w| { 351 self.sqr3().modify(|w| {
355 w.set_sq(_i - 9, channel); 352 w.set_sq(_i - 9, channel);
356 }); 353 });
357 } 354 }
358 14..=15 => { 355 14..=15 => {
359 T::regs().sqr4().modify(|w| { 356 self.sqr4().modify(|w| {
360 w.set_sq(_i - 14, channel); 357 w.set_sq(_i - 14, channel);
361 }); 358 });
362 } 359 }
@@ -375,20 +372,20 @@ impl<T: Instance> super::SealedAnyInstance for T {
375 } 372 }
376 373
377 #[cfg(adc_h5)] 374 #[cfg(adc_h5)]
378 T::regs().difsel().write(|w| w.set_difsel(difsel)); 375 self.difsel().write(|w| w.set_difsel(difsel));
379 376
380 // On G0 and U0 enabled channels are sampled from 0 to last channel. 377 // On G0 and U0 enabled channels are sampled from 0 to last channel.
381 // It is possible to add up to 8 sequences if CHSELRMOD = 1. 378 // It is possible to add up to 8 sequences if CHSELRMOD = 1.
382 // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. 379 // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used.
383 #[cfg(adc_u0)] 380 #[cfg(adc_u0)]
384 T::regs().chselr().modify(|reg| { 381 self.chselr().modify(|reg| {
385 reg.set_chsel(channel_mask); 382 reg.set_chsel(channel_mask);
386 }); 383 });
387 } 384 }
388 } 385 }
389} 386}
390 387
391impl<'d, T: Instance> Adc<'d, T> { 388impl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> {
392 /// Enable the voltage regulator 389 /// Enable the voltage regulator
393 fn init_regulator() { 390 fn init_regulator() {
394 rcc::enable_and_reset::<T>(); 391 rcc::enable_and_reset::<T>();
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs
index a3d9e6176..09fc2ab22 100644
--- a/embassy-stm32/src/adc/v4.rs
+++ b/embassy-stm32/src/adc/v4.rs
@@ -5,7 +5,7 @@ use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel};
5use pac::adccommon::vals::Presc; 5use pac::adccommon::vals::Presc;
6 6
7use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; 7use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us};
8use crate::adc::ConversionMode; 8use crate::adc::{AdcRegs, ConversionMode};
9use crate::time::Hertz; 9use crate::time::Hertz;
10use crate::{Peri, pac, rcc}; 10use crate::{Peri, pac, rcc};
11 11
@@ -80,65 +80,63 @@ pub struct AdcConfig {
80 pub averaging: Option<Averaging>, 80 pub averaging: Option<Averaging>,
81} 81}
82 82
83impl<T: Instance> super::SealedAnyInstance for T { 83impl AdcRegs for crate::pac::adc::Adc {
84 fn dr() -> *mut u16 { 84 fn data(&self) -> *mut u16 {
85 T::regs().dr().as_ptr() as *mut u16 85 crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16
86 } 86 }
87 87
88 fn enable() { 88 fn enable(&self) {
89 T::regs().isr().write(|w| w.set_adrdy(true)); 89 self.isr().write(|w| w.set_adrdy(true));
90 T::regs().cr().modify(|w| w.set_aden(true)); 90 self.cr().modify(|w| w.set_aden(true));
91 while !T::regs().isr().read().adrdy() {} 91 while !self.isr().read().adrdy() {}
92 T::regs().isr().write(|w| w.set_adrdy(true)); 92 self.isr().write(|w| w.set_adrdy(true));
93 } 93 }
94 94
95 fn start() { 95 fn start(&self) {
96 // Start conversion 96 // Start conversion
97 T::regs().cr().modify(|reg| { 97 self.cr().modify(|reg| {
98 reg.set_adstart(true); 98 reg.set_adstart(true);
99 }); 99 });
100 } 100 }
101 101
102 fn stop() { 102 fn stop(&self) {
103 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { 103 if self.cr().read().adstart() && !self.cr().read().addis() {
104 T::regs().cr().modify(|reg| { 104 self.cr().modify(|reg| {
105 reg.set_adstp(Adstp::STOP); 105 reg.set_adstp(Adstp::STOP);
106 }); 106 });
107 while T::regs().cr().read().adstart() {} 107 while self.cr().read().adstart() {}
108 } 108 }
109 109
110 // Reset configuration. 110 // Reset configuration.
111 T::regs().cfgr().modify(|reg| { 111 self.cfgr().modify(|reg| {
112 reg.set_cont(false); 112 reg.set_cont(false);
113 reg.set_dmngt(Dmngt::from_bits(0)); 113 reg.set_dmngt(Dmngt::from_bits(0));
114 }); 114 });
115 } 115 }
116 116
117 fn convert() -> u16 { 117 fn convert(&self) {
118 T::regs().isr().modify(|reg| { 118 self.isr().modify(|reg| {
119 reg.set_eos(true); 119 reg.set_eos(true);
120 reg.set_eoc(true); 120 reg.set_eoc(true);
121 }); 121 });
122 122
123 // Start conversion 123 // Start conversion
124 T::regs().cr().modify(|reg| { 124 self.cr().modify(|reg| {
125 reg.set_adstart(true); 125 reg.set_adstart(true);
126 }); 126 });
127 127
128 while !T::regs().isr().read().eos() { 128 while !self.isr().read().eos() {
129 // spin 129 // spin
130 } 130 }
131
132 T::regs().dr().read().0 as u16
133 } 131 }
134 132
135 fn configure_dma(conversion_mode: ConversionMode) { 133 fn configure_dma(&self, conversion_mode: ConversionMode) {
136 match conversion_mode { 134 match conversion_mode {
137 ConversionMode::Singular => { 135 ConversionMode::Singular => {
138 T::regs().isr().modify(|reg| { 136 self.isr().modify(|reg| {
139 reg.set_ovr(true); 137 reg.set_ovr(true);
140 }); 138 });
141 T::regs().cfgr().modify(|reg| { 139 self.cfgr().modify(|reg| {
142 reg.set_cont(true); 140 reg.set_cont(true);
143 reg.set_dmngt(Dmngt::DMA_ONE_SHOT); 141 reg.set_dmngt(Dmngt::DMA_ONE_SHOT);
144 }); 142 });
@@ -148,9 +146,9 @@ impl<T: Instance> super::SealedAnyInstance for T {
148 } 146 }
149 } 147 }
150 148
151 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { 149 fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {
152 // Set sequence length 150 // Set sequence length
153 T::regs().sqr1().modify(|w| { 151 self.sqr1().modify(|w| {
154 w.set_l(sequence.len() as u8 - 1); 152 w.set_l(sequence.len() as u8 - 1);
155 }); 153 });
156 154
@@ -158,39 +156,35 @@ impl<T: Instance> super::SealedAnyInstance for T {
158 for (i, ((channel, _), sample_time)) in sequence.enumerate() { 156 for (i, ((channel, _), sample_time)) in sequence.enumerate() {
159 let sample_time = sample_time.into(); 157 let sample_time = sample_time.into();
160 if channel <= 9 { 158 if channel <= 9 {
161 T::regs().smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); 159 self.smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time));
162 } else { 160 } else {
163 T::regs() 161 self.smpr(1).modify(|reg| reg.set_smp((channel - 10) as _, sample_time));
164 .smpr(1)
165 .modify(|reg| reg.set_smp((channel - 10) as _, sample_time));
166 } 162 }
167 163
168 #[cfg(any(stm32h7, stm32u5))] 164 #[cfg(any(stm32h7, stm32u5))]
169 { 165 {
170 T::regs().cfgr2().modify(|w| w.set_lshift(0)); 166 self.cfgr2().modify(|w| w.set_lshift(0));
171 T::regs() 167 self.pcsel().modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
172 .pcsel()
173 .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
174 } 168 }
175 169
176 match i { 170 match i {
177 0..=3 => { 171 0..=3 => {
178 T::regs().sqr1().modify(|w| { 172 self.sqr1().modify(|w| {
179 w.set_sq(i, channel); 173 w.set_sq(i, channel);
180 }); 174 });
181 } 175 }
182 4..=8 => { 176 4..=8 => {
183 T::regs().sqr2().modify(|w| { 177 self.sqr2().modify(|w| {
184 w.set_sq(i - 4, channel); 178 w.set_sq(i - 4, channel);
185 }); 179 });
186 } 180 }
187 9..=13 => { 181 9..=13 => {
188 T::regs().sqr3().modify(|w| { 182 self.sqr3().modify(|w| {
189 w.set_sq(i - 9, channel); 183 w.set_sq(i - 9, channel);
190 }); 184 });
191 } 185 }
192 14..=15 => { 186 14..=15 => {
193 T::regs().sqr4().modify(|w| { 187 self.sqr4().modify(|w| {
194 w.set_sq(i - 14, channel); 188 w.set_sq(i - 14, channel);
195 }); 189 });
196 } 190 }
@@ -200,7 +194,7 @@ impl<T: Instance> super::SealedAnyInstance for T {
200 } 194 }
201} 195}
202 196
203impl<'d, T: Instance + super::AnyInstance> Adc<'d, T> { 197impl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> {
204 pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { 198 pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self {
205 let s = Self::new(adc); 199 let s = Self::new(adc);
206 200
@@ -292,7 +286,7 @@ impl<'d, T: Instance + super::AnyInstance> Adc<'d, T> {
292 286
293 blocking_delay_us(1); 287 blocking_delay_us(1);
294 288
295 T::enable(); 289 T::regs().enable();
296 290
297 // single conversion mode, software trigger 291 // single conversion mode, software trigger
298 T::regs().cfgr().modify(|w| { 292 T::regs().cfgr().modify(|w| {
diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs
index bfd0570f8..afb18ec1a 100644
--- a/embassy-stm32/src/dma/gpdma/mod.rs
+++ b/embassy-stm32/src/dma/gpdma/mod.rs
@@ -137,7 +137,6 @@ pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: c
137 137
138impl AnyChannel { 138impl AnyChannel {
139 /// Safety: Must be called with a matching set of parameters for a valid dma channel 139 /// Safety: Must be called with a matching set of parameters for a valid dma channel
140 #[cfg(not(stm32n6))]
141 pub(crate) unsafe fn on_irq(&self) { 140 pub(crate) unsafe fn on_irq(&self) {
142 let info = self.info(); 141 let info = self.info();
143 #[cfg(feature = "_dual-core")] 142 #[cfg(feature = "_dual-core")]
@@ -238,6 +237,11 @@ impl AnyChannel {
238 // "Preceding reads and writes cannot be moved past subsequent writes." 237 // "Preceding reads and writes cannot be moved past subsequent writes."
239 fence(Ordering::SeqCst); 238 fence(Ordering::SeqCst);
240 239
240 if ch.cr().read().en() {
241 ch.cr().modify(|w| w.set_susp(true));
242 while !ch.sr().read().suspf() {}
243 }
244
241 ch.cr().write(|w| w.set_reset(true)); 245 ch.cr().write(|w| w.set_reset(true));
242 ch.fcr().write(|w| { 246 ch.fcr().write(|w| {
243 // Clear all irqs 247 // Clear all irqs
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs
index efb324fa6..05d9c2e51 100644
--- a/embassy-stm32/src/dma/mod.rs
+++ b/embassy-stm32/src/dma/mod.rs
@@ -25,7 +25,7 @@ pub(crate) use util::*;
25pub(crate) mod ringbuffer; 25pub(crate) mod ringbuffer;
26pub mod word; 26pub mod word;
27 27
28use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; 28use embassy_hal_internal::{PeripheralType, impl_peripheral};
29 29
30use crate::interrupt; 30use crate::interrupt;
31use crate::rcc::StoppablePeripheral; 31use crate::rcc::StoppablePeripheral;
@@ -47,21 +47,10 @@ pub type Request = u8;
47#[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))] 47#[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))]
48pub type Request = (); 48pub type Request = ();
49 49
50impl<'a> StoppablePeripheral for Peri<'a, AnyChannel> { 50pub(crate) trait SealedChannel: StoppablePeripheral {
51 #[cfg(feature = "low-power")]
52 fn stop_mode(&self) -> crate::rcc::StopMode {
53 self.stop_mode
54 }
55}
56
57pub(crate) trait SealedChannel {
58 #[cfg(not(stm32n6))]
59 fn id(&self) -> u8; 51 fn id(&self) -> u8;
60 #[cfg(feature = "low-power")]
61 fn stop_mode(&self) -> crate::rcc::StopMode;
62} 52}
63 53
64#[cfg(not(stm32n6))]
65pub(crate) trait ChannelInterrupt { 54pub(crate) trait ChannelInterrupt {
66 #[cfg_attr(not(feature = "rt"), allow(unused))] 55 #[cfg_attr(not(feature = "rt"), allow(unused))]
67 unsafe fn on_irq(); 56 unsafe fn on_irq();
@@ -71,19 +60,21 @@ pub(crate) trait ChannelInterrupt {
71#[allow(private_bounds)] 60#[allow(private_bounds)]
72pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} 61pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {}
73 62
74#[cfg(not(stm32n6))]
75macro_rules! dma_channel_impl { 63macro_rules! dma_channel_impl {
76 ($channel_peri:ident, $index:expr, $stop_mode:ident) => { 64 ($channel_peri:ident, $index:expr, $stop_mode:ident) => {
77 impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { 65 impl crate::rcc::StoppablePeripheral for crate::peripherals::$channel_peri {
78 fn id(&self) -> u8 {
79 $index
80 }
81
82 #[cfg(feature = "low-power")] 66 #[cfg(feature = "low-power")]
83 fn stop_mode(&self) -> crate::rcc::StopMode { 67 fn stop_mode(&self) -> crate::rcc::StopMode {
84 crate::rcc::StopMode::$stop_mode 68 crate::rcc::StopMode::$stop_mode
85 } 69 }
86 } 70 }
71
72 impl crate::dma::SealedChannel for crate::peripherals::$channel_peri {
73 fn id(&self) -> u8 {
74 $index
75 }
76 }
77
87 impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { 78 impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri {
88 unsafe fn on_irq() { 79 unsafe fn on_irq() {
89 crate::dma::AnyChannel { 80 crate::dma::AnyChannel {
@@ -102,7 +93,7 @@ macro_rules! dma_channel_impl {
102 Self { 93 Self {
103 id: crate::dma::SealedChannel::id(&val), 94 id: crate::dma::SealedChannel::id(&val),
104 #[cfg(feature = "low-power")] 95 #[cfg(feature = "low-power")]
105 stop_mode: crate::dma::SealedChannel::stop_mode(&val), 96 stop_mode: crate::rcc::StoppablePeripheral::stop_mode(&val),
106 } 97 }
107 } 98 }
108 } 99 }
@@ -123,17 +114,18 @@ impl AnyChannel {
123 } 114 }
124} 115}
125 116
126impl SealedChannel for AnyChannel { 117impl StoppablePeripheral for AnyChannel {
127 #[cfg(not(stm32n6))]
128 fn id(&self) -> u8 {
129 self.id
130 }
131
132 #[cfg(feature = "low-power")] 118 #[cfg(feature = "low-power")]
133 fn stop_mode(&self) -> crate::rcc::StopMode { 119 fn stop_mode(&self) -> crate::rcc::StopMode {
134 self.stop_mode 120 self.stop_mode
135 } 121 }
136} 122}
123
124impl SealedChannel for AnyChannel {
125 fn id(&self) -> u8 {
126 self.id
127 }
128}
137impl Channel for AnyChannel {} 129impl Channel for AnyChannel {}
138 130
139const CHANNEL_COUNT: usize = crate::_generated::DMA_CHANNELS.len(); 131const CHANNEL_COUNT: usize = crate::_generated::DMA_CHANNELS.len();
diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs
index 3245887c1..304268963 100644
--- a/embassy-stm32/src/dma/util.rs
+++ b/embassy-stm32/src/dma/util.rs
@@ -20,6 +20,16 @@ impl<'d> ChannelAndRequest<'d> {
20 Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options) 20 Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options)
21 } 21 }
22 22
23 #[allow(dead_code)]
24 pub unsafe fn read_unchecked<'a, W: Word>(
25 &'a self,
26 peri_addr: *mut W,
27 buf: &'a mut [W],
28 options: TransferOptions,
29 ) -> Transfer<'a> {
30 Transfer::new_read(self.channel.clone_unchecked(), self.request, peri_addr, buf, options)
31 }
32
23 pub unsafe fn read_raw<'a, MW: Word, PW: Word>( 33 pub unsafe fn read_raw<'a, MW: Word, PW: Word>(
24 &'a mut self, 34 &'a mut self,
25 peri_addr: *mut PW, 35 peri_addr: *mut PW,
@@ -29,6 +39,16 @@ impl<'d> ChannelAndRequest<'d> {
29 Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options) 39 Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options)
30 } 40 }
31 41
42 #[allow(dead_code)]
43 pub unsafe fn read_raw_unchecked<'a, MW: Word, PW: Word>(
44 &'a self,
45 peri_addr: *mut PW,
46 buf: *mut [MW],
47 options: TransferOptions,
48 ) -> Transfer<'a> {
49 Transfer::new_read_raw(self.channel.clone_unchecked(), self.request, peri_addr, buf, options)
50 }
51
32 pub unsafe fn write<'a, W: Word>( 52 pub unsafe fn write<'a, W: Word>(
33 &'a mut self, 53 &'a mut self,
34 buf: &'a [W], 54 buf: &'a [W],
@@ -38,6 +58,16 @@ impl<'d> ChannelAndRequest<'d> {
38 Transfer::new_write(self.channel.reborrow(), self.request, buf, peri_addr, options) 58 Transfer::new_write(self.channel.reborrow(), self.request, buf, peri_addr, options)
39 } 59 }
40 60
61 #[allow(dead_code)]
62 pub unsafe fn write_unchecked<'a, W: Word>(
63 &'a self,
64 buf: &'a [W],
65 peri_addr: *mut W,
66 options: TransferOptions,
67 ) -> Transfer<'a> {
68 Transfer::new_write(self.channel.clone_unchecked(), self.request, buf, peri_addr, options)
69 }
70
41 pub unsafe fn write_raw<'a, MW: Word, PW: Word>( 71 pub unsafe fn write_raw<'a, MW: Word, PW: Word>(
42 &'a mut self, 72 &'a mut self,
43 buf: *const [MW], 73 buf: *const [MW],
@@ -48,6 +78,16 @@ impl<'d> ChannelAndRequest<'d> {
48 } 78 }
49 79
50 #[allow(dead_code)] 80 #[allow(dead_code)]
81 pub unsafe fn write_raw_unchecked<'a, MW: Word, PW: Word>(
82 &'a self,
83 buf: *const [MW],
84 peri_addr: *mut PW,
85 options: TransferOptions,
86 ) -> Transfer<'a> {
87 Transfer::new_write_raw(self.channel.clone_unchecked(), self.request, buf, peri_addr, options)
88 }
89
90 #[allow(dead_code)]
51 pub unsafe fn write_repeated<'a, W: Word>( 91 pub unsafe fn write_repeated<'a, W: Word>(
52 &'a mut self, 92 &'a mut self,
53 repeated: &'a W, 93 repeated: &'a W,
@@ -64,4 +104,22 @@ impl<'d> ChannelAndRequest<'d> {
64 options, 104 options,
65 ) 105 )
66 } 106 }
107
108 #[allow(dead_code)]
109 pub unsafe fn write_repeated_unchecked<'a, W: Word>(
110 &'a self,
111 repeated: &'a W,
112 count: usize,
113 peri_addr: *mut W,
114 options: TransferOptions,
115 ) -> Transfer<'a> {
116 Transfer::new_write_repeated(
117 self.channel.clone_unchecked(),
118 self.request,
119 repeated,
120 count,
121 peri_addr,
122 options,
123 )
124 }
67} 125}
diff --git a/embassy-stm32/src/dma/word.rs b/embassy-stm32/src/dma/word.rs
index fb1bde860..5c3bb8f7f 100644
--- a/embassy-stm32/src/dma/word.rs
+++ b/embassy-stm32/src/dma/word.rs
@@ -31,6 +31,10 @@ pub trait Word: SealedWord + Default + Copy + 'static {
31 fn size() -> WordSize; 31 fn size() -> WordSize;
32 /// Amount of bits of this word size. 32 /// Amount of bits of this word size.
33 fn bits() -> usize; 33 fn bits() -> usize;
34 /// Maximum value of this type.
35 fn max() -> usize {
36 (1 << Self::bits()) - 1
37 }
34} 38}
35 39
36macro_rules! impl_word { 40macro_rules! impl_word {
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs
index 7b7896d46..458174b5d 100644
--- a/embassy-stm32/src/exti.rs
+++ b/embassy-stm32/src/exti.rs
@@ -74,7 +74,7 @@ unsafe fn on_irq() {
74 } 74 }
75 75
76 #[cfg(feature = "low-power")] 76 #[cfg(feature = "low-power")]
77 crate::low_power::Executor::on_wakeup_irq(); 77 crate::low_power::Executor::on_wakeup_irq_or_event();
78} 78}
79 79
80struct BitIter(u32); 80struct BitIter(u32);
diff --git a/embassy-stm32/src/fmt.rs b/embassy-stm32/src/fmt.rs
index b6ae24ee8..b731796f0 100644
--- a/embassy-stm32/src/fmt.rs
+++ b/embassy-stm32/src/fmt.rs
@@ -207,6 +207,16 @@ macro_rules! error {
207} 207}
208 208
209#[cfg(feature = "defmt")] 209#[cfg(feature = "defmt")]
210trait_set::trait_set! {
211 pub trait Debuggable = Debug + defmt::Format;
212}
213
214#[cfg(not(feature = "defmt"))]
215trait_set::trait_set! {
216 pub trait Debuggable = Debug;
217}
218
219#[cfg(feature = "defmt")]
210#[collapse_debuginfo(yes)] 220#[collapse_debuginfo(yes)]
211macro_rules! unwrap { 221macro_rules! unwrap {
212 ($($x:tt)*) => { 222 ($($x:tt)*) => {
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs
index e7d4e9ad3..5de8bad2c 100644
--- a/embassy-stm32/src/gpio.rs
+++ b/embassy-stm32/src/gpio.rs
@@ -684,7 +684,11 @@ fn set_as_analog(pin_port: PinNumber) {
684 }); 684 });
685 685
686 #[cfg(gpio_v2)] 686 #[cfg(gpio_v2)]
687 r.moder().modify(|w| w.set_moder(n, vals::Moder::ANALOG)); 687 {
688 #[cfg(any(stm32l47x, stm32l48x))]
689 r.ascr().modify(|w| w.set_asc(n, true));
690 r.moder().modify(|w| w.set_moder(n, vals::Moder::ANALOG));
691 }
688} 692}
689 693
690#[inline(never)] 694#[inline(never)]
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs
index ee60c3f44..0bf430ffc 100644
--- a/embassy-stm32/src/i2c/mod.rs
+++ b/embassy-stm32/src/i2c/mod.rs
@@ -226,7 +226,7 @@ impl<'d, M: Mode> I2c<'d, M, Master> {
226 } 226 }
227 227
228 fn enable_and_init(&mut self, config: Config) { 228 fn enable_and_init(&mut self, config: Config) {
229 self.info.rcc.enable_and_reset(); 229 self.info.rcc.enable_and_reset_without_stop();
230 self.init(config); 230 self.init(config);
231 } 231 }
232} 232}
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs
index 128a58db7..81a6d74c1 100644
--- a/embassy-stm32/src/i2c/v1.rs
+++ b/embassy-stm32/src/i2c/v1.rs
@@ -529,6 +529,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
529 529
530 /// Write. 530 /// Write.
531 pub async fn write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> { 531 pub async fn write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> {
532 let _scoped_block_stop = self.info.rcc.block_stop();
532 self.write_frame(address, write_buffer, FrameOptions::FirstAndLastFrame) 533 self.write_frame(address, write_buffer, FrameOptions::FirstAndLastFrame)
533 .await?; 534 .await?;
534 535
@@ -537,6 +538,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
537 538
538 /// Read. 539 /// Read.
539 pub async fn read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { 540 pub async fn read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> {
541 let _scoped_block_stop = self.info.rcc.block_stop();
540 self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) 542 self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame)
541 .await?; 543 .await?;
542 544
@@ -701,6 +703,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
701 703
702 /// Write, restart, read. 704 /// Write, restart, read.
703 pub async fn write_read(&mut self, address: u8, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<(), Error> { 705 pub async fn write_read(&mut self, address: u8, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<(), Error> {
706 let _scoped_block_stop = self.info.rcc.block_stop();
704 // Check empty read buffer before starting transaction. Otherwise, we would not generate the 707 // Check empty read buffer before starting transaction. Otherwise, we would not generate the
705 // stop condition below. 708 // stop condition below.
706 if read_buffer.is_empty() { 709 if read_buffer.is_empty() {
@@ -719,6 +722,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
719 /// 722 ///
720 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction 723 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
721 pub async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { 724 pub async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
725 let _scoped_block_stop = self.info.rcc.block_stop();
722 for (op, frame) in operation_frames(operations)? { 726 for (op, frame) in operation_frames(operations)? {
723 match op { 727 match op {
724 Operation::Read(read_buffer) => self.read_frame(address, read_buffer, frame).await?, 728 Operation::Read(read_buffer) => self.read_frame(address, read_buffer, frame).await?,
@@ -1357,6 +1361,7 @@ impl<'d> I2c<'d, Async, MultiMaster> {
1357 /// (Read/Write) and the matched address. This method will suspend until 1361 /// (Read/Write) and the matched address. This method will suspend until
1358 /// an address match occurs. 1362 /// an address match occurs.
1359 pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { 1363 pub async fn listen(&mut self) -> Result<SlaveCommand, Error> {
1364 let _scoped_block_stop = self.info.rcc.block_stop();
1360 trace!("I2C slave: starting async listen for address match"); 1365 trace!("I2C slave: starting async listen for address match");
1361 let state = self.state; 1366 let state = self.state;
1362 let info = self.info; 1367 let info = self.info;
@@ -1421,6 +1426,7 @@ impl<'d> I2c<'d, Async, MultiMaster> {
1421 /// 1426 ///
1422 /// Returns the number of bytes stored in the buffer (not total received). 1427 /// Returns the number of bytes stored in the buffer (not total received).
1423 pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { 1428 pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
1429 let _scoped_block_stop = self.info.rcc.block_stop();
1424 trace!("I2C slave: starting respond_to_write, buffer_len={}", buffer.len()); 1430 trace!("I2C slave: starting respond_to_write, buffer_len={}", buffer.len());
1425 1431
1426 if buffer.is_empty() { 1432 if buffer.is_empty() {
@@ -1454,6 +1460,7 @@ impl<'d> I2c<'d, Async, MultiMaster> {
1454 /// 1460 ///
1455 /// Returns the total number of bytes transmitted (data + padding). 1461 /// Returns the total number of bytes transmitted (data + padding).
1456 pub async fn respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { 1462 pub async fn respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> {
1463 let _scoped_block_stop = self.info.rcc.block_stop();
1457 trace!("I2C slave: starting respond_to_read, data_len={}", data.len()); 1464 trace!("I2C slave: starting respond_to_read, data_len={}", data.len());
1458 1465
1459 if data.is_empty() { 1466 if data.is_empty() {
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 933cca9cb..fe7782a7c 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -73,7 +73,7 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() {
73 // restore the clocks to their last configured state as 73 // restore the clocks to their last configured state as
74 // much is lost in STOP modes 74 // much is lost in STOP modes
75 #[cfg(all(feature = "low-power", stm32wlex))] 75 #[cfg(all(feature = "low-power", stm32wlex))]
76 crate::low_power::Executor::on_wakeup_irq(); 76 crate::low_power::Executor::on_wakeup_irq_or_event();
77 77
78 let regs = T::info().regs; 78 let regs = T::info().regs;
79 let isr = regs.isr().read(); 79 let isr = regs.isr().read();
@@ -1075,6 +1075,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1075 1075
1076 /// Write. 1076 /// Write.
1077 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { 1077 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
1078 let _scoped_block_stop = self.info.rcc.block_stop();
1078 let timeout = self.timeout(); 1079 let timeout = self.timeout();
1079 if write.is_empty() { 1080 if write.is_empty() {
1080 self.write_internal(address.into(), write, true, timeout) 1081 self.write_internal(address.into(), write, true, timeout)
@@ -1089,6 +1090,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1089 /// 1090 ///
1090 /// The buffers are concatenated in a single write transaction. 1091 /// The buffers are concatenated in a single write transaction.
1091 pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { 1092 pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> {
1093 let _scoped_block_stop = self.info.rcc.block_stop();
1092 let timeout = self.timeout(); 1094 let timeout = self.timeout();
1093 1095
1094 if write.is_empty() { 1096 if write.is_empty() {
@@ -1120,6 +1122,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1120 1122
1121 /// Read. 1123 /// Read.
1122 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { 1124 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
1125 let _scoped_block_stop = self.info.rcc.block_stop();
1123 let timeout = self.timeout(); 1126 let timeout = self.timeout();
1124 1127
1125 if buffer.is_empty() { 1128 if buffer.is_empty() {
@@ -1132,6 +1135,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1132 1135
1133 /// Write, restart, read. 1136 /// Write, restart, read.
1134 pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { 1137 pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
1138 let _scoped_block_stop = self.info.rcc.block_stop();
1135 let timeout = self.timeout(); 1139 let timeout = self.timeout();
1136 1140
1137 if write.is_empty() { 1141 if write.is_empty() {
@@ -1157,6 +1161,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1157 /// 1161 ///
1158 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction 1162 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
1159 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { 1163 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
1164 let _scoped_block_stop = self.info.rcc.block_stop();
1160 if operations.is_empty() { 1165 if operations.is_empty() {
1161 return Err(Error::ZeroLengthTransfer); 1166 return Err(Error::ZeroLengthTransfer);
1162 } 1167 }
@@ -1741,6 +1746,7 @@ impl<'d> I2c<'d, Async, MultiMaster> {
1741 /// 1746 ///
1742 /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. 1747 /// The listen method is an asynchronous method but it does not require DMA to be asynchronous.
1743 pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { 1748 pub async fn listen(&mut self) -> Result<SlaveCommand, Error> {
1749 let _scoped_block_stop = self.info.rcc.block_stop();
1744 let state = self.state; 1750 let state = self.state;
1745 self.info.regs.cr1().modify(|reg| { 1751 self.info.regs.cr1().modify(|reg| {
1746 reg.set_addrie(true); 1752 reg.set_addrie(true);
@@ -1766,12 +1772,14 @@ impl<'d> I2c<'d, Async, MultiMaster> {
1766 /// 1772 ///
1767 /// Returns the total number of bytes received. 1773 /// Returns the total number of bytes received.
1768 pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { 1774 pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
1775 let _scoped_block_stop = self.info.rcc.block_stop();
1769 let timeout = self.timeout(); 1776 let timeout = self.timeout();
1770 timeout.with(self.read_dma_internal_slave(buffer, timeout)).await 1777 timeout.with(self.read_dma_internal_slave(buffer, timeout)).await
1771 } 1778 }
1772 1779
1773 /// Respond to a read request from an I2C master. 1780 /// Respond to a read request from an I2C master.
1774 pub async fn respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> { 1781 pub async fn respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> {
1782 let _scoped_block_stop = self.info.rcc.block_stop();
1775 let timeout = self.timeout(); 1783 let timeout = self.timeout();
1776 timeout.with(self.write_dma_internal_slave(write, timeout)).await 1784 timeout.with(self.write_dma_internal_slave(write, timeout)).await
1777 } 1785 }
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index cdf3323fb..2388abe3c 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -88,7 +88,7 @@ foreach_interrupt! {
88 #[interrupt] 88 #[interrupt]
89 #[allow(non_snake_case)] 89 #[allow(non_snake_case)]
90 unsafe fn $irq() { 90 unsafe fn $irq() {
91 Executor::on_wakeup_irq(); 91 Executor::on_wakeup_irq_or_event();
92 } 92 }
93 }; 93 };
94} 94}
@@ -99,7 +99,7 @@ foreach_interrupt! {
99 #[interrupt] 99 #[interrupt]
100 #[allow(non_snake_case)] 100 #[allow(non_snake_case)]
101 unsafe fn $irq() { 101 unsafe fn $irq() {
102 Executor::on_wakeup_irq(); 102 Executor::on_wakeup_irq_or_event();
103 } 103 }
104 }; 104 };
105} 105}
@@ -164,22 +164,30 @@ impl Executor {
164 } 164 }
165 } 165 }
166 166
167 pub(crate) unsafe fn on_wakeup_irq() { 167 pub(crate) unsafe fn on_wakeup_irq_or_event() {
168 if !get_driver().is_stopped() {
169 return;
170 }
171
168 critical_section::with(|cs| { 172 critical_section::with(|cs| {
169 #[cfg(stm32wlex)] 173 #[cfg(stm32wlex)]
170 { 174 {
171 use crate::pac::rcc::vals::Sw; 175 let es = crate::pac::PWR.extscr().read();
172 use crate::pac::{PWR, RCC}; 176 match (es.c1stopf(), es.c1stop2f()) {
173 use crate::rcc::init as init_rcc; 177 (true, false) => debug!("low power: wake from STOP1"),
174 178 (false, true) => debug!("low power: wake from STOP2"),
175 let extscr = PWR.extscr().read(); 179 (true, true) => debug!("low power: wake from STOP1 and STOP2 ???"),
176 if extscr.c1stop2f() || extscr.c1stopf() { 180 (false, false) => trace!("low power: stop mode not entered"),
181 };
182 crate::pac::PWR.extscr().modify(|w| {
183 w.set_c1cssf(false);
184 });
185
186 if es.c1stop2f() || es.c1stopf() {
177 // when we wake from any stop mode we need to re-initialize the rcc 187 // when we wake from any stop mode we need to re-initialize the rcc
178 while RCC.cfgr().read().sws() != Sw::MSI {} 188 crate::rcc::init(RCC_CONFIG.unwrap());
179
180 init_rcc(RCC_CONFIG.unwrap());
181 189
182 if extscr.c1stop2f() { 190 if es.c1stop2f() {
183 // when we wake from STOP2, we need to re-initialize the time driver 191 // when we wake from STOP2, we need to re-initialize the time driver
184 get_driver().init_timer(cs); 192 get_driver().init_timer(cs);
185 // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) 193 // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer)
@@ -190,7 +198,8 @@ impl Executor {
190 } 198 }
191 } 199 }
192 get_driver().resume_time(cs); 200 get_driver().resume_time(cs);
193 trace!("low power: resume"); 201
202 trace!("low power: resume time");
194 }); 203 });
195 } 204 }
196 205
@@ -201,10 +210,8 @@ impl Executor {
201 fn stop_mode(_cs: CriticalSection) -> Option<StopMode> { 210 fn stop_mode(_cs: CriticalSection) -> Option<StopMode> {
202 // We cannot enter standby because we will lose program state. 211 // We cannot enter standby because we will lose program state.
203 if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } { 212 if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } {
204 trace!("low power: stop 2");
205 Some(StopMode::Stop2) 213 Some(StopMode::Stop2)
206 } else if unsafe { REFCOUNT_STOP1 == 0 } { 214 } else if unsafe { REFCOUNT_STOP1 == 0 } {
207 trace!("low power: stop 1");
208 Some(StopMode::Stop1) 215 Some(StopMode::Stop1)
209 } else { 216 } else {
210 trace!("low power: not ready to stop (refcount_stop1: {})", unsafe { 217 trace!("low power: not ready to stop (refcount_stop1: {})", unsafe {
@@ -305,9 +312,11 @@ impl Executor {
305 get_driver().pause_time(cs).ok()?; 312 get_driver().pause_time(cs).ok()?;
306 self.configure_stop(cs, stop_mode).ok()?; 313 self.configure_stop(cs, stop_mode).ok()?;
307 314
308 Some(()) 315 Some(stop_mode)
309 }) 316 })
310 .map(|_| { 317 .map(|stop_mode| {
318 trace!("low power: enter stop: {}", stop_mode);
319
311 #[cfg(not(feature = "low-power-debug-with-sleep"))] 320 #[cfg(not(feature = "low-power-debug-with-sleep"))]
312 Self::get_scb().set_sleepdeep(); 321 Self::get_scb().set_sleepdeep();
313 }); 322 });
@@ -338,20 +347,10 @@ impl Executor {
338 unsafe { 347 unsafe {
339 self.inner.poll(); 348 self.inner.poll();
340 self.configure_pwr(); 349 self.configure_pwr();
350 #[cfg(feature = "defmt")]
351 defmt::flush();
341 asm!("wfe"); 352 asm!("wfe");
342 #[cfg(stm32wlex)] 353 Self::on_wakeup_irq_or_event();
343 {
344 let es = crate::pac::PWR.extscr().read();
345 match (es.c1stopf(), es.c1stop2f()) {
346 (true, false) => debug!("low power: wake from STOP1"),
347 (false, true) => debug!("low power: wake from STOP2"),
348 (true, true) => debug!("low power: wake from STOP1 and STOP2 ???"),
349 (false, false) => trace!("low power: stop mode not entered"),
350 };
351 crate::pac::PWR.extscr().modify(|w| {
352 w.set_c1cssf(false);
353 });
354 }
355 }; 354 };
356 } 355 }
357 } 356 }
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs
index bb4f4f1d0..1f47f4845 100644
--- a/embassy-stm32/src/qspi/mod.rs
+++ b/embassy-stm32/src/qspi/mod.rs
@@ -111,7 +111,7 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> {
111 config: Config, 111 config: Config,
112 fsel: FlashSelection, 112 fsel: FlashSelection,
113 ) -> Self { 113 ) -> Self {
114 rcc::enable_and_reset::<T>(); 114 rcc::enable_and_reset_without_stop::<T>();
115 115
116 while T::REGS.sr().read().busy() {} 116 while T::REGS.sr().read().busy() {}
117 117
@@ -403,6 +403,7 @@ impl<'d, T: Instance> Qspi<'d, T, Async> {
403 403
404 /// Async read data, using DMA. 404 /// Async read data, using DMA.
405 pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { 405 pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) {
406 let _scoped_block_stop = T::RCC_INFO.block_stop();
406 let transfer = self.start_read_transfer(transaction, buf); 407 let transfer = self.start_read_transfer(transaction, buf);
407 transfer.await; 408 transfer.await;
408 } 409 }
@@ -443,6 +444,7 @@ impl<'d, T: Instance> Qspi<'d, T, Async> {
443 444
444 /// Async write data, using DMA. 445 /// Async write data, using DMA.
445 pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) { 446 pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) {
447 let _scoped_block_stop = T::RCC_INFO.block_stop();
446 let transfer = self.start_write_transfer(transaction, buf); 448 let transfer = self.start_write_transfer(transaction, buf);
447 transfer.await; 449 transfer.await;
448 } 450 }
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index 1dd634cfe..2a9a1595a 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -12,6 +12,7 @@ pub use bd::*;
12#[cfg(any(mco, mco1, mco2))] 12#[cfg(any(mco, mco1, mco2))]
13mod mco; 13mod mco;
14use critical_section::CriticalSection; 14use critical_section::CriticalSection;
15use embassy_hal_internal::{Peri, PeripheralType};
15#[cfg(any(mco, mco1, mco2))] 16#[cfg(any(mco, mco1, mco2))]
16pub use mco::*; 17pub use mco::*;
17 18
@@ -172,7 +173,7 @@ pub(crate) struct RccInfo {
172/// E.g. if `StopMode::Stop1` is selected, the peripheral prevents the chip from entering Stop1 mode. 173/// E.g. if `StopMode::Stop1` is selected, the peripheral prevents the chip from entering Stop1 mode.
173#[cfg(feature = "low-power")] 174#[cfg(feature = "low-power")]
174#[allow(dead_code)] 175#[allow(dead_code)]
175#[derive(Debug, Clone, Copy, PartialEq, Default)] 176#[derive(Debug, Clone, Copy, PartialEq, Default, defmt::Format)]
176pub enum StopMode { 177pub enum StopMode {
177 #[default] 178 #[default]
178 /// Peripheral prevents chip from entering Stop1 or executor will enter Stop1 179 /// Peripheral prevents chip from entering Stop1 or executor will enter Stop1
@@ -381,12 +382,19 @@ pub(crate) trait StoppablePeripheral {
381} 382}
382 383
383#[cfg(feature = "low-power")] 384#[cfg(feature = "low-power")]
384impl<'a> StoppablePeripheral for StopMode { 385impl StoppablePeripheral for StopMode {
385 fn stop_mode(&self) -> StopMode { 386 fn stop_mode(&self) -> StopMode {
386 *self 387 *self
387 } 388 }
388} 389}
389 390
391impl<'a, T: StoppablePeripheral + PeripheralType> StoppablePeripheral for Peri<'a, T> {
392 #[cfg(feature = "low-power")]
393 fn stop_mode(&self) -> StopMode {
394 T::stop_mode(&self)
395 }
396}
397
390pub(crate) struct BusyPeripheral<T: StoppablePeripheral> { 398pub(crate) struct BusyPeripheral<T: StoppablePeripheral> {
391 peripheral: T, 399 peripheral: T,
392} 400}
@@ -490,6 +498,16 @@ pub fn enable_and_reset<T: RccPeripheral>() {
490 T::RCC_INFO.enable_and_reset(); 498 T::RCC_INFO.enable_and_reset();
491} 499}
492 500
501/// Enables and resets peripheral `T` without incrementing the stop refcount.
502///
503/// # Safety
504///
505/// Peripheral must not be in use.
506// TODO: should this be `unsafe`?
507pub fn enable_and_reset_without_stop<T: RccPeripheral>() {
508 T::RCC_INFO.enable_and_reset_without_stop();
509}
510
493/// Disables peripheral `T`. 511/// Disables peripheral `T`.
494/// 512///
495/// # Safety 513/// # Safety
diff --git a/embassy-stm32/src/rcc/n6.rs b/embassy-stm32/src/rcc/n6.rs
index 866851bbd..178ec57d4 100644
--- a/embassy-stm32/src/rcc/n6.rs
+++ b/embassy-stm32/src/rcc/n6.rs
@@ -1003,6 +1003,24 @@ pub(crate) unsafe fn init(config: Config) {
1003 p.SCB.cpacr.modify(|w| w | (3 << 20) | (3 << 22)); 1003 p.SCB.cpacr.modify(|w| w | (3 << 20) | (3 << 22));
1004 } 1004 }
1005 1005
1006 // TODO: ugly workaround for DMA accesses until RIF is properly implemented
1007 debug!("deactivating RIF");
1008 const RISAF3_BASE_NS: *mut u32 = stm32_metapac::RNG.wrapping_byte_offset(0x8000) as _; // AHB3PERIPH_BASE_NS + 0x8000UL
1009 const RISAF3_REG0_CFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x40);
1010 const RISAF3_REG0_ENDR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x48);
1011 const RISAF3_REG0_CIDCFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x4C);
1012 const RISAF3_REG1_CFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x80);
1013 const RISAF3_REG1_ENDR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x88);
1014 const RISAF3_REG1_CIDCFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x8C);
1015 unsafe {
1016 *RISAF3_REG0_CIDCFGR = 0x000F000F; /* RW for everyone */
1017 *RISAF3_REG0_ENDR = 0xFFFFFFFF; /* all-encompassing */
1018 *RISAF3_REG0_CFGR = 0x00000101; /* enabled, secure, unprivileged for everyone */
1019 *RISAF3_REG1_CIDCFGR = 0x00FF00FF; /* RW for everyone */
1020 *RISAF3_REG1_ENDR = 0xFFFFFFFF; /* all-encompassing */
1021 *RISAF3_REG1_CFGR = 0x00000001; /* enabled, non-secure, unprivileged*/
1022 }
1023
1006 debug!("setting power supply config"); 1024 debug!("setting power supply config");
1007 1025
1008 power_supply_config(config.supply_config); 1026 power_supply_config(config.supply_config);
@@ -1039,7 +1057,9 @@ pub(crate) unsafe fn init(config: Config) {
1039 i2s_ckin: None, 1057 i2s_ckin: None,
1040 ic8: None, 1058 ic8: None,
1041 ic9: None, 1059 ic9: None,
1060 ic10: None,
1042 ic14: None, 1061 ic14: None,
1062 ic15: None,
1043 ic17: None, 1063 ic17: None,
1044 ic20: None, 1064 ic20: None,
1045 ); 1065 );
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs
index ce4bc43c3..579c34c13 100644
--- a/embassy-stm32/src/sai/mod.rs
+++ b/embassy-stm32/src/sai/mod.rs
@@ -394,7 +394,8 @@ pub struct Config {
394 pub frame_length: u16, 394 pub frame_length: u16,
395 pub clock_strobe: ClockStrobe, 395 pub clock_strobe: ClockStrobe,
396 pub output_drive: OutputDrive, 396 pub output_drive: OutputDrive,
397 pub master_clock_divider: Option<MasterClockDivider>, 397 pub master_clock_divider: MasterClockDivider,
398 pub nodiv: bool,
398 pub is_high_impedance_on_inactive_slot: bool, 399 pub is_high_impedance_on_inactive_slot: bool,
399 pub fifo_threshold: FifoThreshold, 400 pub fifo_threshold: FifoThreshold,
400 pub companding: Companding, 401 pub companding: Companding,
@@ -423,7 +424,8 @@ impl Default for Config {
423 frame_sync_active_level_length: word::U7(16), 424 frame_sync_active_level_length: word::U7(16),
424 frame_sync_definition: FrameSyncDefinition::ChannelIdentification, 425 frame_sync_definition: FrameSyncDefinition::ChannelIdentification,
425 frame_length: 32, 426 frame_length: 32,
426 master_clock_divider: None, 427 master_clock_divider: MasterClockDivider::DIV1,
428 nodiv: false,
427 clock_strobe: ClockStrobe::Rising, 429 clock_strobe: ClockStrobe::Rising,
428 output_drive: OutputDrive::Immediately, 430 output_drive: OutputDrive::Immediately,
429 is_high_impedance_on_inactive_slot: false, 431 is_high_impedance_on_inactive_slot: false,
@@ -677,8 +679,8 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
677 w.set_syncen(config.sync_input.syncen()); 679 w.set_syncen(config.sync_input.syncen());
678 w.set_mono(config.stereo_mono.mono()); 680 w.set_mono(config.stereo_mono.mono());
679 w.set_outdriv(config.output_drive.outdriv()); 681 w.set_outdriv(config.output_drive.outdriv());
680 w.set_mckdiv(config.master_clock_divider.unwrap_or(MasterClockDivider::DIV1)); 682 w.set_mckdiv(config.master_clock_divider);
681 w.set_nodiv(config.master_clock_divider.is_none()); 683 w.set_nodiv(config.nodiv);
682 w.set_dmaen(true); 684 w.set_dmaen(true);
683 }); 685 });
684 686
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs
index e05131040..12086cd3a 100644
--- a/embassy-stm32/src/sdmmc/mod.rs
+++ b/embassy-stm32/src/sdmmc/mod.rs
@@ -4,16 +4,15 @@
4use core::default::Default; 4use core::default::Default;
5use core::future::poll_fn; 5use core::future::poll_fn;
6use core::marker::PhantomData; 6use core::marker::PhantomData;
7use core::ops::{Deref, DerefMut}; 7use core::slice;
8use core::task::Poll; 8use core::task::Poll;
9 9
10use embassy_hal_internal::drop::OnDrop;
11use embassy_hal_internal::{Peri, PeripheralType}; 10use embassy_hal_internal::{Peri, PeripheralType};
12use embassy_sync::waitqueue::AtomicWaker; 11use embassy_sync::waitqueue::AtomicWaker;
13use sdio_host::common_cmd::{self, Resp, ResponseLen}; 12use sdio_host::Cmd;
14use sdio_host::emmc::{EMMC, ExtCSD}; 13use sdio_host::common_cmd::{self, R1, R2, R3, Resp, ResponseLen, Rz};
15use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; 14use sdio_host::sd::{BusWidth, CardStatus};
16use sdio_host::{Cmd, emmc_cmd, sd_cmd}; 15use sdio_host::sd_cmd::{R6, R7};
17 16
18#[cfg(sdmmc_v1)] 17#[cfg(sdmmc_v1)]
19use crate::dma::ChannelAndRequest; 18use crate::dma::ChannelAndRequest;
@@ -22,37 +21,27 @@ use crate::gpio::Pull;
22use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; 21use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
23use crate::interrupt::typelevel::Interrupt; 22use crate::interrupt::typelevel::Interrupt;
24use crate::pac::sdmmc::Sdmmc as RegBlock; 23use crate::pac::sdmmc::Sdmmc as RegBlock;
25use crate::rcc::{self, RccPeripheral}; 24use crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral};
25use crate::sdmmc::sd::Addressable;
26use crate::time::Hertz; 26use crate::time::Hertz;
27use crate::{interrupt, peripherals}; 27use crate::{interrupt, peripherals};
28 28
29/// Module for SD and EMMC cards
30pub mod sd;
31
32/// Module for SDIO interface
33pub mod sdio;
34
29/// Interrupt handler. 35/// Interrupt handler.
30pub struct InterruptHandler<T: Instance> { 36pub struct InterruptHandler<T: Instance> {
31 _phantom: PhantomData<T>, 37 _phantom: PhantomData<T>,
32} 38}
33 39
34impl<T: Instance> InterruptHandler<T> {
35 fn enable_interrupts() {
36 let regs = T::regs();
37 regs.maskr().write(|w| {
38 w.set_dcrcfailie(true);
39 w.set_dtimeoutie(true);
40 w.set_dataendie(true);
41 w.set_dbckendie(true);
42
43 #[cfg(sdmmc_v1)]
44 w.set_stbiterre(true);
45 #[cfg(sdmmc_v2)]
46 w.set_dabortie(true);
47 });
48 }
49}
50
51impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { 40impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
52 unsafe fn on_interrupt() { 41 unsafe fn on_interrupt() {
53 T::state().wake(); 42 T::state().waker.wake();
54 let status = T::regs().star().read(); 43 let status = T::info().regs.star().read();
55 T::regs().maskr().modify(|w| { 44 T::info().regs.maskr().modify(|w| {
56 if status.dcrcfail() { 45 if status.dcrcfail() {
57 w.set_dcrcfailie(false) 46 w.set_dcrcfailie(false)
58 } 47 }
@@ -77,6 +66,57 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
77 } 66 }
78} 67}
79 68
69struct U128(pub u128);
70
71trait TypedResp: Resp {
72 type Word: From<U128>;
73}
74
75impl From<U128> for () {
76 fn from(value: U128) -> Self {
77 match value.0 {
78 0 => (),
79 _ => unreachable!(),
80 }
81 }
82}
83
84impl From<U128> for u32 {
85 fn from(value: U128) -> Self {
86 unwrap!(value.0.try_into())
87 }
88}
89
90impl From<U128> for u128 {
91 fn from(value: U128) -> Self {
92 value.0
93 }
94}
95
96impl TypedResp for Rz {
97 type Word = ();
98}
99
100impl TypedResp for R1 {
101 type Word = u32;
102}
103
104impl TypedResp for R2 {
105 type Word = u128;
106}
107
108impl TypedResp for R3 {
109 type Word = u32;
110}
111
112impl TypedResp for R6 {
113 type Word = u32;
114}
115
116impl TypedResp for R7 {
117 type Word = u32;
118}
119
80/// Frequency used for SD Card initialization. Must be no higher than 400 kHz. 120/// Frequency used for SD Card initialization. Must be no higher than 400 kHz.
81const SD_INIT_FREQ: Hertz = Hertz(400_000); 121const SD_INIT_FREQ: Hertz = Hertz(400_000);
82 122
@@ -99,54 +139,14 @@ impl Default for Signalling {
99 } 139 }
100} 140}
101 141
102/// Aligned data block for SDMMC transfers. 142const fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
103/// 143 let len = x.len() * 4;
104/// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements. 144 unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }
105#[repr(align(4))]
106#[derive(Debug, Clone, PartialEq, Eq)]
107#[cfg_attr(feature = "defmt", derive(defmt::Format))]
108pub struct DataBlock(pub [u8; 512]);
109
110impl Deref for DataBlock {
111 type Target = [u8; 512];
112
113 fn deref(&self) -> &Self::Target {
114 &self.0
115 }
116} 145}
117 146
118impl DerefMut for DataBlock { 147const fn slice8_ref(x: &[u32]) -> &[u8] {
119 fn deref_mut(&mut self) -> &mut Self::Target { 148 let len = x.len() * 4;
120 &mut self.0 149 unsafe { slice::from_raw_parts(x.as_ptr() as _, len) }
121 }
122}
123
124/// Command Block buffer for SDMMC command transfers.
125///
126/// This is a 16-word array, exposed so that DMA commpatible memory can be used if required.
127#[derive(Debug, Clone, PartialEq, Eq)]
128#[cfg_attr(feature = "defmt", derive(defmt::Format))]
129pub struct CmdBlock(pub [u32; 16]);
130
131impl CmdBlock {
132 /// Creates a new instance of CmdBlock
133 pub const fn new() -> Self {
134 Self([0u32; 16])
135 }
136}
137
138impl Deref for CmdBlock {
139 type Target = [u32; 16];
140
141 fn deref(&self) -> &Self::Target {
142 &self.0
143 }
144}
145
146impl DerefMut for CmdBlock {
147 fn deref_mut(&mut self) -> &mut Self::Target {
148 &mut self.0
149 }
150} 150}
151 151
152/// Errors 152/// Errors
@@ -181,42 +181,6 @@ pub enum Error {
181 StBitErr, 181 StBitErr,
182} 182}
183 183
184#[derive(Clone, Copy, Debug, Default)]
185/// SD Card
186pub struct Card {
187 /// The type of this card
188 pub card_type: CardCapacity,
189 /// Operation Conditions Register
190 pub ocr: OCR<SD>,
191 /// Relative Card Address
192 pub rca: u16,
193 /// Card ID
194 pub cid: CID<SD>,
195 /// Card Specific Data
196 pub csd: CSD<SD>,
197 /// SD CARD Configuration Register
198 pub scr: SCR,
199 /// SD Status
200 pub status: SDStatus,
201}
202
203#[derive(Clone, Copy, Debug, Default)]
204/// eMMC storage
205pub struct Emmc {
206 /// The capacity of this card
207 pub capacity: CardCapacity,
208 /// Operation Conditions Register
209 pub ocr: OCR<EMMC>,
210 /// Relative Card Address
211 pub rca: u16,
212 /// Card ID
213 pub cid: CID<EMMC>,
214 /// Card Specific Data
215 pub csd: CSD<EMMC>,
216 /// Extended Card Specific Data
217 pub ext_csd: ExtCSD,
218}
219
220#[repr(u8)] 184#[repr(u8)]
221enum PowerCtrl { 185enum PowerCtrl {
222 Off = 0b00, 186 Off = 0b00,
@@ -259,6 +223,55 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> {
259 Ok((false, clk_div, clk_f)) 223 Ok((false, clk_div, clk_f))
260} 224}
261 225
226fn bus_width_vals(bus_width: BusWidth) -> (u8, u32) {
227 match bus_width {
228 BusWidth::One => (0, 1u32),
229 BusWidth::Four => (1, 4u32),
230 BusWidth::Eight => (2, 8u32),
231 _ => panic!("Invalid Bus Width"),
232 }
233}
234
235#[repr(u8)]
236enum BlockSize {
237 Size1 = 0b0000,
238 Size2 = 0b0001,
239 Size4 = 0b0010,
240 Size8 = 0b0011,
241 Size16 = 0b0100,
242 Size32 = 0b0101,
243 Size64 = 0b0110,
244 Size128 = 0b0111,
245 Size256 = 0b1000,
246 Size512 = 0b1001,
247 Size1024 = 0b1010,
248 Size2048 = 0b1011,
249 Size4096 = 0b1100,
250 Size8192 = 0b1101,
251 Size16384 = 0b1110,
252}
253
254const fn block_size(bytes: usize) -> BlockSize {
255 match bytes {
256 1 => BlockSize::Size1,
257 2 => BlockSize::Size2,
258 4 => BlockSize::Size4,
259 8 => BlockSize::Size8,
260 16 => BlockSize::Size16,
261 32 => BlockSize::Size32,
262 64 => BlockSize::Size64,
263 128 => BlockSize::Size128,
264 256 => BlockSize::Size256,
265 512 => BlockSize::Size512,
266 1024 => BlockSize::Size1024,
267 2048 => BlockSize::Size2048,
268 4096 => BlockSize::Size4096,
269 8192 => BlockSize::Size8192,
270 16384 => BlockSize::Size16384,
271 _ => core::unreachable!(),
272 }
273}
274
262/// Calculate clock divisor. Returns a SDMMC_CK less than or equal to 275/// Calculate clock divisor. Returns a SDMMC_CK less than or equal to
263/// `sdmmc_ck` in Hertz. 276/// `sdmmc_ck` in Hertz.
264/// 277///
@@ -286,6 +299,34 @@ struct Transfer<'a> {
286 _dummy: PhantomData<&'a ()>, 299 _dummy: PhantomData<&'a ()>,
287} 300}
288 301
302struct WrappedTransfer<'a> {
303 _transfer: Transfer<'a>,
304 sdmmc: &'a Sdmmc<'a>,
305 defused: bool,
306}
307
308impl<'a> WrappedTransfer<'a> {
309 pub const fn new(_transfer: Transfer<'a>, sdmmc: &'a Sdmmc) -> Self {
310 Self {
311 _transfer,
312 sdmmc,
313 defused: false,
314 }
315 }
316
317 pub fn defuse(&mut self) {
318 self.defused = true;
319 }
320}
321
322impl<'a> Drop for WrappedTransfer<'a> {
323 fn drop(&mut self) {
324 if !self.defused {
325 self.sdmmc.on_drop();
326 }
327 }
328}
329
289#[cfg(all(sdmmc_v1, dma))] 330#[cfg(all(sdmmc_v1, dma))]
290const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { 331const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {
291 pburst: crate::dma::Burst::Incr4, 332 pburst: crate::dma::Burst::Incr4,
@@ -323,64 +364,11 @@ impl Default for Config {
323 } 364 }
324} 365}
325 366
326/// Peripheral that can be operated over SDMMC
327#[derive(Clone, Copy, Debug)]
328pub enum SdmmcPeripheral {
329 /// SD Card
330 SdCard(Card),
331 /// eMMC memory
332 Emmc(Emmc),
333}
334
335impl SdmmcPeripheral {
336 /// Get this peripheral's address on the SDMMC bus
337 fn get_address(&self) -> u16 {
338 match self {
339 Self::SdCard(c) => c.rca,
340 Self::Emmc(e) => e.rca,
341 }
342 }
343 /// Is this a standard or high capacity peripheral?
344 fn get_capacity(&self) -> CardCapacity {
345 match self {
346 Self::SdCard(c) => c.card_type,
347 Self::Emmc(e) => e.capacity,
348 }
349 }
350 /// Size in bytes
351 fn size(&self) -> u64 {
352 match self {
353 // SDHC / SDXC / SDUC
354 Self::SdCard(c) => u64::from(c.csd.block_count()) * 512,
355 // capacity > 2GB
356 Self::Emmc(e) => u64::from(e.ext_csd.sector_count()) * 512,
357 }
358 }
359
360 /// Get a mutable reference to the SD Card.
361 ///
362 /// Panics if there is another peripheral instead.
363 fn get_sd_card(&mut self) -> &mut Card {
364 match *self {
365 Self::SdCard(ref mut c) => c,
366 _ => unreachable!("SD only"),
367 }
368 }
369
370 /// Get a mutable reference to the eMMC.
371 ///
372 /// Panics if there is another peripheral instead.
373 fn get_emmc(&mut self) -> &mut Emmc {
374 match *self {
375 Self::Emmc(ref mut e) => e,
376 _ => unreachable!("eMMC only"),
377 }
378 }
379}
380
381/// Sdmmc device 367/// Sdmmc device
382pub struct Sdmmc<'d, T: Instance> { 368pub struct Sdmmc<'d> {
383 _peri: Peri<'d, T>, 369 info: &'static Info,
370 state: &'static State,
371 ker_clk: Hertz,
384 #[cfg(sdmmc_v1)] 372 #[cfg(sdmmc_v1)]
385 dma: ChannelAndRequest<'d>, 373 dma: ChannelAndRequest<'d>,
386 374
@@ -400,12 +388,6 @@ pub struct Sdmmc<'d, T: Instance> {
400 clock: Hertz, 388 clock: Hertz,
401 /// Current signalling scheme to card 389 /// Current signalling scheme to card
402 signalling: Signalling, 390 signalling: Signalling,
403 /// Card
404 card: Option<SdmmcPeripheral>,
405
406 /// An optional buffer to be used for commands
407 /// This should be used if there are special memory location requirements for dma
408 cmd_block: Option<&'d mut CmdBlock>,
409} 391}
410 392
411const CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh); 393const CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh);
@@ -416,9 +398,9 @@ const CMD_AF: AfType = AfType::output_pull(OutputType::PushPull, Speed::VeryHigh
416const DATA_AF: AfType = CMD_AF; 398const DATA_AF: AfType = CMD_AF;
417 399
418#[cfg(sdmmc_v1)] 400#[cfg(sdmmc_v1)]
419impl<'d, T: Instance> Sdmmc<'d, T> { 401impl<'d> Sdmmc<'d> {
420 /// Create a new SDMMC driver, with 1 data lane. 402 /// Create a new SDMMC driver, with 1 data lane.
421 pub fn new_1bit( 403 pub fn new_1bit<T: Instance>(
422 sdmmc: Peri<'d, T>, 404 sdmmc: Peri<'d, T>,
423 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 405 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
424 dma: Peri<'d, impl SdmmcDma<T>>, 406 dma: Peri<'d, impl SdmmcDma<T>>,
@@ -451,7 +433,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
451 } 433 }
452 434
453 /// Create a new SDMMC driver, with 4 data lanes. 435 /// Create a new SDMMC driver, with 4 data lanes.
454 pub fn new_4bit( 436 pub fn new_4bit<T: Instance>(
455 sdmmc: Peri<'d, T>, 437 sdmmc: Peri<'d, T>,
456 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 438 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
457 dma: Peri<'d, impl SdmmcDma<T>>, 439 dma: Peri<'d, impl SdmmcDma<T>>,
@@ -491,9 +473,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
491} 473}
492 474
493#[cfg(sdmmc_v1)] 475#[cfg(sdmmc_v1)]
494impl<'d, T: Instance> Sdmmc<'d, T> { 476impl<'d> Sdmmc<'d> {
495 /// Create a new SDMMC driver, with 8 data lanes. 477 /// Create a new SDMMC driver, with 8 data lanes.
496 pub fn new_8bit( 478 pub fn new_8bit<T: Instance>(
497 sdmmc: Peri<'d, T>, 479 sdmmc: Peri<'d, T>,
498 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 480 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
499 dma: Peri<'d, impl SdmmcDma<T>>, 481 dma: Peri<'d, impl SdmmcDma<T>>,
@@ -541,9 +523,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
541} 523}
542 524
543#[cfg(sdmmc_v2)] 525#[cfg(sdmmc_v2)]
544impl<'d, T: Instance> Sdmmc<'d, T> { 526impl<'d> Sdmmc<'d> {
545 /// Create a new SDMMC driver, with 1 data lane. 527 /// Create a new SDMMC driver, with 1 data lane.
546 pub fn new_1bit( 528 pub fn new_1bit<T: Instance>(
547 sdmmc: Peri<'d, T>, 529 sdmmc: Peri<'d, T>,
548 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 530 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
549 clk: Peri<'d, impl CkPin<T>>, 531 clk: Peri<'d, impl CkPin<T>>,
@@ -574,7 +556,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
574 } 556 }
575 557
576 /// Create a new SDMMC driver, with 4 data lanes. 558 /// Create a new SDMMC driver, with 4 data lanes.
577 pub fn new_4bit( 559 pub fn new_4bit<T: Instance>(
578 sdmmc: Peri<'d, T>, 560 sdmmc: Peri<'d, T>,
579 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 561 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
580 clk: Peri<'d, impl CkPin<T>>, 562 clk: Peri<'d, impl CkPin<T>>,
@@ -612,9 +594,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
612} 594}
613 595
614#[cfg(sdmmc_v2)] 596#[cfg(sdmmc_v2)]
615impl<'d, T: Instance> Sdmmc<'d, T> { 597impl<'d> Sdmmc<'d> {
616 /// Create a new SDMMC driver, with 8 data lanes. 598 /// Create a new SDMMC driver, with 8 data lanes.
617 pub fn new_8bit( 599 pub fn new_8bit<T: Instance>(
618 sdmmc: Peri<'d, T>, 600 sdmmc: Peri<'d, T>,
619 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 601 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
620 clk: Peri<'d, impl CkPin<T>>, 602 clk: Peri<'d, impl CkPin<T>>,
@@ -659,9 +641,24 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
659 } 641 }
660} 642}
661 643
662impl<'d, T: Instance> Sdmmc<'d, T> { 644impl<'d> Sdmmc<'d> {
663 fn new_inner( 645 fn enable_interrupts(&self) {
664 sdmmc: Peri<'d, T>, 646 let regs = self.info.regs;
647 regs.maskr().write(|w| {
648 w.set_dcrcfailie(true);
649 w.set_dtimeoutie(true);
650 w.set_dataendie(true);
651 w.set_dbckendie(true);
652
653 #[cfg(sdmmc_v1)]
654 w.set_stbiterre(true);
655 #[cfg(sdmmc_v2)]
656 w.set_dabortie(true);
657 });
658 }
659
660 fn new_inner<T: Instance>(
661 _sdmmc: Peri<'d, T>,
665 #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>, 662 #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>,
666 clk: Peri<'d, AnyPin>, 663 clk: Peri<'d, AnyPin>,
667 cmd: Peri<'d, AnyPin>, 664 cmd: Peri<'d, AnyPin>,
@@ -675,13 +672,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
675 d7: Option<Peri<'d, AnyPin>>, 672 d7: Option<Peri<'d, AnyPin>>,
676 config: Config, 673 config: Config,
677 ) -> Self { 674 ) -> Self {
678 rcc::enable_and_reset::<T>(); 675 rcc::enable_and_reset_without_stop::<T>();
679 676
680 T::Interrupt::unpend(); 677 T::Interrupt::unpend();
681 unsafe { T::Interrupt::enable() }; 678 unsafe { T::Interrupt::enable() };
682 679
683 let regs = T::regs(); 680 let info = T::info();
684 regs.clkcr().write(|w| { 681 let state = T::state();
682 let ker_clk = T::frequency();
683
684 info.regs.clkcr().write(|w| {
685 w.set_pwrsav(false); 685 w.set_pwrsav(false);
686 w.set_negedge(false); 686 w.set_negedge(false);
687 687
@@ -698,10 +698,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
698 698
699 // Power off, writen 00: Clock to the card is stopped; 699 // Power off, writen 00: Clock to the card is stopped;
700 // D[7:0], CMD, and CK are driven high. 700 // D[7:0], CMD, and CK are driven high.
701 regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); 701 info.regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8));
702 702
703 Self { 703 Self {
704 _peri: sdmmc, 704 info,
705 state,
706 ker_clk,
705 #[cfg(sdmmc_v1)] 707 #[cfg(sdmmc_v1)]
706 dma, 708 dma,
707 709
@@ -719,15 +721,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
719 config, 721 config,
720 clock: SD_INIT_FREQ, 722 clock: SD_INIT_FREQ,
721 signalling: Default::default(), 723 signalling: Default::default(),
722 card: None,
723 cmd_block: None,
724 } 724 }
725 } 725 }
726 726
727 /// Data transfer is in progress 727 /// Data transfer is in progress
728 #[inline] 728 #[inline]
729 fn data_active() -> bool { 729 fn data_active(&self) -> bool {
730 let regs = T::regs(); 730 let regs = self.info.regs;
731 731
732 let status = regs.star().read(); 732 let status = regs.star().read();
733 #[cfg(sdmmc_v1)] 733 #[cfg(sdmmc_v1)]
@@ -738,8 +738,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
738 738
739 /// Coammand transfer is in progress 739 /// Coammand transfer is in progress
740 #[inline] 740 #[inline]
741 fn cmd_active() -> bool { 741 fn cmd_active(&self) -> bool {
742 let regs = T::regs(); 742 let regs = self.info.regs;
743 743
744 let status = regs.star().read(); 744 let status = regs.star().read();
745 #[cfg(sdmmc_v1)] 745 #[cfg(sdmmc_v1)]
@@ -750,8 +750,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
750 750
751 /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) 751 /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2)
752 #[inline] 752 #[inline]
753 fn wait_idle() { 753 fn wait_idle(&self) {
754 while Self::data_active() || Self::cmd_active() {} 754 while self.data_active() || self.cmd_active() {}
755 }
756
757 fn bus_width(&self) -> BusWidth {
758 match (self.d3.is_some(), self.d7.is_some()) {
759 (true, true) => BusWidth::Eight,
760 (true, false) => BusWidth::Four,
761 _ => BusWidth::One,
762 }
755 } 763 }
756 764
757 /// # Safety 765 /// # Safety
@@ -759,23 +767,25 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
759 /// `buffer` must be valid for the whole transfer and word aligned 767 /// `buffer` must be valid for the whole transfer and word aligned
760 #[allow(unused_variables)] 768 #[allow(unused_variables)]
761 fn prepare_datapath_read<'a>( 769 fn prepare_datapath_read<'a>(
762 config: &Config, 770 &'a self,
763 #[cfg(sdmmc_v1)] dma: &'a mut ChannelAndRequest<'d>,
764 buffer: &'a mut [u32], 771 buffer: &'a mut [u32],
765 length_bytes: u32, 772 block_size: BlockSize,
766 block_size: u8, 773 byte_mode: bool,
767 ) -> Transfer<'a> { 774 ) -> WrappedTransfer<'a> {
768 assert!(block_size <= 14, "Block size up to 2^14 bytes"); 775 let regs = self.info.regs;
769 let regs = T::regs();
770 776
771 // Command AND Data state machines must be idle 777 // Command AND Data state machines must be idle
772 Self::wait_idle(); 778 self.wait_idle();
773 Self::clear_interrupt_flags(); 779 self.clear_interrupt_flags();
774 780
775 regs.dlenr().write(|w| w.set_datalength(length_bytes)); 781 regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32));
776 782
783 // SAFETY: No other functions use the dma
777 #[cfg(sdmmc_v1)] 784 #[cfg(sdmmc_v1)]
778 let transfer = unsafe { dma.read(regs.fifor().as_ptr() as *mut u32, buffer, DMA_TRANSFER_OPTIONS) }; 785 let transfer = unsafe {
786 self.dma
787 .read_unchecked(regs.fifor().as_ptr() as *mut u32, buffer, DMA_TRANSFER_OPTIONS)
788 };
779 #[cfg(sdmmc_v2)] 789 #[cfg(sdmmc_v2)]
780 let transfer = { 790 let transfer = {
781 regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); 791 regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32));
@@ -785,8 +795,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
785 } 795 }
786 }; 796 };
787 797
798 #[cfg(sdmmc_v2)]
799 let byte_mode = byte_mode as u8;
800
788 regs.dctrl().modify(|w| { 801 regs.dctrl().modify(|w| {
789 w.set_dblocksize(block_size); 802 w.set_dtmode(byte_mode);
803 w.set_dblocksize(block_size as u8);
790 w.set_dtdir(true); 804 w.set_dtdir(true);
791 #[cfg(sdmmc_v1)] 805 #[cfg(sdmmc_v1)]
792 { 806 {
@@ -795,26 +809,33 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
795 } 809 }
796 }); 810 });
797 811
798 transfer 812 self.enable_interrupts();
813
814 WrappedTransfer::new(transfer, &self)
799 } 815 }
800 816
801 /// # Safety 817 /// # Safety
802 /// 818 ///
803 /// `buffer` must be valid for the whole transfer and word aligned 819 /// `buffer` must be valid for the whole transfer and word aligned
804 fn prepare_datapath_write<'a>(&'a mut self, buffer: &'a [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { 820 fn prepare_datapath_write<'a>(
805 assert!(block_size <= 14, "Block size up to 2^14 bytes"); 821 &'a self,
806 let regs = T::regs(); 822 buffer: &'a [u32],
823 block_size: BlockSize,
824 byte_mode: bool,
825 ) -> WrappedTransfer<'a> {
826 let regs = self.info.regs;
807 827
808 // Command AND Data state machines must be idle 828 // Command AND Data state machines must be idle
809 Self::wait_idle(); 829 self.wait_idle();
810 Self::clear_interrupt_flags(); 830 self.clear_interrupt_flags();
811 831
812 regs.dlenr().write(|w| w.set_datalength(length_bytes)); 832 regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32));
813 833
834 // SAFETY: No other functions use the dma
814 #[cfg(sdmmc_v1)] 835 #[cfg(sdmmc_v1)]
815 let transfer = unsafe { 836 let transfer = unsafe {
816 self.dma 837 self.dma
817 .write(buffer, regs.fifor().as_ptr() as *mut u32, DMA_TRANSFER_OPTIONS) 838 .write_unchecked(buffer, regs.fifor().as_ptr() as *mut u32, DMA_TRANSFER_OPTIONS)
818 }; 839 };
819 #[cfg(sdmmc_v2)] 840 #[cfg(sdmmc_v2)]
820 let transfer = { 841 let transfer = {
@@ -825,8 +846,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
825 } 846 }
826 }; 847 };
827 848
849 #[cfg(sdmmc_v2)]
850 let byte_mode = byte_mode as u8;
851
828 regs.dctrl().modify(|w| { 852 regs.dctrl().modify(|w| {
829 w.set_dblocksize(block_size); 853 w.set_dtmode(byte_mode);
854 w.set_dblocksize(block_size as u8);
830 w.set_dtdir(false); 855 w.set_dtdir(false);
831 #[cfg(sdmmc_v1)] 856 #[cfg(sdmmc_v1)]
832 { 857 {
@@ -835,12 +860,14 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
835 } 860 }
836 }); 861 });
837 862
838 transfer 863 self.enable_interrupts();
864
865 WrappedTransfer::new(transfer, &self)
839 } 866 }
840 867
841 /// Stops the DMA datapath 868 /// Stops the DMA datapath
842 fn stop_datapath() { 869 fn stop_datapath(&self) {
843 let regs = T::regs(); 870 let regs = self.info.regs;
844 871
845 #[cfg(sdmmc_v1)] 872 #[cfg(sdmmc_v1)]
846 regs.dctrl().modify(|w| { 873 regs.dctrl().modify(|w| {
@@ -851,49 +878,58 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
851 regs.idmactrlr().modify(|w| w.set_idmaen(false)); 878 regs.idmactrlr().modify(|w| w.set_idmaen(false));
852 } 879 }
853 880
881 fn init_idle(&mut self) -> Result<(), Error> {
882 let regs = self.info.regs;
883
884 self.clkcr_set_clkdiv(SD_INIT_FREQ, BusWidth::One)?;
885 regs.dtimer()
886 .write(|w| w.set_datatime(self.config.data_transfer_timeout));
887
888 regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8));
889 self.cmd(common_cmd::idle(), false)
890 }
891
854 /// Sets the CLKDIV field in CLKCR. Updates clock field in self 892 /// Sets the CLKDIV field in CLKCR. Updates clock field in self
855 fn clkcr_set_clkdiv(&mut self, freq: u32, width: BusWidth) -> Result<(), Error> { 893 fn clkcr_set_clkdiv(&mut self, freq: Hertz, width: BusWidth) -> Result<(), Error> {
856 let regs = T::regs(); 894 let regs = self.info.regs;
857
858 let width_u32 = match width {
859 BusWidth::One => 1u32,
860 BusWidth::Four => 4u32,
861 BusWidth::Eight => 8u32,
862 _ => panic!("Invalid Bus Width"),
863 };
864 895
865 let ker_ck = T::frequency(); 896 let (widbus, width_u32) = bus_width_vals(width);
866 let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; 897 let (_bypass, clkdiv, new_clock) = clk_div(self.ker_clk, freq.0)?;
867 898
868 // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 899 // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7
869 // Section 55.5.8 900 // Section 55.5.8
870 let sdmmc_bus_bandwidth = new_clock.0 * width_u32; 901 let sdmmc_bus_bandwidth = new_clock.0 * width_u32;
871 assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); 902 assert!(self.ker_clk.0 > 3 * sdmmc_bus_bandwidth / 32);
872 self.clock = new_clock; 903 self.clock = new_clock;
873 904
874 // CPSMACT and DPSMACT must be 0 to set CLKDIV 905 // CPSMACT and DPSMACT must be 0 to set CLKDIV or WIDBUS
875 Self::wait_idle(); 906 self.wait_idle();
876 regs.clkcr().modify(|w| { 907 regs.clkcr().modify(|w| {
877 w.set_clkdiv(clkdiv); 908 w.set_clkdiv(clkdiv);
878 #[cfg(sdmmc_v1)] 909 #[cfg(sdmmc_v1)]
879 w.set_bypass(_bypass); 910 w.set_bypass(_bypass);
911 w.set_widbus(widbus);
880 }); 912 });
881 913
882 Ok(()) 914 Ok(())
883 } 915 }
884 916
917 fn get_cid(&self) -> Result<u128, Error> {
918 self.cmd(common_cmd::all_send_cid(), false) // CMD2
919 }
920
921 fn get_csd(&self, address: u16) -> Result<u128, Error> {
922 self.cmd(common_cmd::send_csd(address), false)
923 }
924
885 /// Query the card status (CMD13, returns R1) 925 /// Query the card status (CMD13, returns R1)
886 fn read_status<Ext>(&self, card: &SdmmcPeripheral) -> Result<CardStatus<Ext>, Error> 926 fn read_status<A: Addressable>(&self, card: &A) -> Result<CardStatus<A::Ext>, Error>
887 where 927 where
888 CardStatus<Ext>: From<u32>, 928 CardStatus<A::Ext>: From<u32>,
889 { 929 {
890 let regs = T::regs();
891 let rca = card.get_address(); 930 let rca = card.get_address();
892 931
893 Self::cmd(common_cmd::card_status(rca, false), false)?; // CMD13 932 Ok(self.cmd(common_cmd::card_status(rca, false), false)?.into()) // CMD13
894
895 let r1 = regs.respr(0).read().cardstatus();
896 Ok(r1.into())
897 } 933 }
898 934
899 /// Select one card and place it into the _Tranfer State_ 935 /// Select one card and place it into the _Tranfer State_
@@ -904,17 +940,23 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
904 // Determine Relative Card Address (RCA) of given card 940 // Determine Relative Card Address (RCA) of given card
905 let rca = rca.unwrap_or(0); 941 let rca = rca.unwrap_or(0);
906 942
907 let r = Self::cmd(common_cmd::select_card(rca), false); 943 let resp = self.cmd(common_cmd::select_card(rca), false);
908 match (r, rca) { 944
909 (Err(Error::Timeout), 0) => Ok(()), 945 if let Err(Error::Timeout) = resp
910 _ => r, 946 && rca == 0
947 {
948 return Ok(());
911 } 949 }
950
951 resp?;
952
953 Ok(())
912 } 954 }
913 955
914 /// Clear flags in interrupt clear register 956 /// Clear flags in interrupt clear register
915 #[inline] 957 #[inline]
916 fn clear_interrupt_flags() { 958 fn clear_interrupt_flags(&self) {
917 let regs = T::regs(); 959 let regs = self.info.regs;
918 regs.icr().write(|w| { 960 regs.icr().write(|w| {
919 w.set_ccrcfailc(true); 961 w.set_ccrcfailc(true);
920 w.set_dcrcfailc(true); 962 w.set_dcrcfailc(true);
@@ -947,12 +989,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
947 989
948 /// Send command to card 990 /// Send command to card
949 #[allow(unused_variables)] 991 #[allow(unused_variables)]
950 fn cmd<R: Resp>(cmd: Cmd<R>, data: bool) -> Result<(), Error> { 992 fn cmd<R: TypedResp>(&self, cmd: Cmd<R>, data: bool) -> Result<R::Word, Error> {
951 let regs = T::regs(); 993 let regs = self.info.regs;
952 994
953 Self::clear_interrupt_flags(); 995 self.clear_interrupt_flags();
954 // CP state machine must be idle 996 // CP state machine must be idle
955 while Self::cmd_active() {} 997 while self.cmd_active() {}
956 998
957 // Command arg 999 // Command arg
958 regs.argr().write(|w| w.set_cmdarg(cmd.arg)); 1000 regs.argr().write(|w| w.set_cmdarg(cmd.arg));
@@ -994,16 +1036,29 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
994 } else if status.ccrcfail() { 1036 } else if status.ccrcfail() {
995 return Err(Error::Crc); 1037 return Err(Error::Crc);
996 } 1038 }
997 Ok(()) 1039
1040 Ok(match R::LENGTH {
1041 ResponseLen::Zero => U128(0u128),
1042 ResponseLen::R48 => U128(self.info.regs.respr(0).read().cardstatus() as u128),
1043 ResponseLen::R136 => {
1044 let cid0 = self.info.regs.respr(0).read().cardstatus() as u128;
1045 let cid1 = self.info.regs.respr(1).read().cardstatus() as u128;
1046 let cid2 = self.info.regs.respr(2).read().cardstatus() as u128;
1047 let cid3 = self.info.regs.respr(3).read().cardstatus() as u128;
1048
1049 U128((cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3))
1050 }
1051 }
1052 .into())
998 } 1053 }
999 1054
1000 fn on_drop() { 1055 fn on_drop(&self) {
1001 let regs = T::regs(); 1056 let regs = self.info.regs;
1002 if Self::data_active() { 1057 if self.data_active() {
1003 Self::clear_interrupt_flags(); 1058 self.clear_interrupt_flags();
1004 // Send abort 1059 // Send abort
1005 // CP state machine must be idle 1060 // CP state machine must be idle
1006 while Self::cmd_active() {} 1061 while self.cmd_active() {}
1007 1062
1008 // Command arg 1063 // Command arg
1009 regs.argr().write(|w| w.set_cmdarg(0)); 1064 regs.argr().write(|w| w.set_cmdarg(0));
@@ -1023,22 +1078,22 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1023 }); 1078 });
1024 1079
1025 // Wait for the abort 1080 // Wait for the abort
1026 while Self::data_active() {} 1081 while self.data_active() {}
1027 } 1082 }
1028 regs.maskr().write(|_| ()); // disable irqs 1083 regs.maskr().write(|_| ()); // disable irqs
1029 Self::clear_interrupt_flags(); 1084 self.clear_interrupt_flags();
1030 Self::stop_datapath(); 1085 self.stop_datapath();
1031 } 1086 }
1032 1087
1033 /// Wait for a previously started datapath transfer to complete from an interrupt. 1088 /// Wait for a previously started datapath transfer to complete from an interrupt.
1034 #[inline] 1089 #[inline]
1035 #[allow(unused)] 1090 #[allow(unused)]
1036 async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { 1091 async fn complete_datapath_transfer(&self, mut transfer: WrappedTransfer<'_>, block: bool) -> Result<(), Error> {
1037 let res = poll_fn(|cx| { 1092 let res = poll_fn(|cx| {
1038 // Compiler might not be sufficiently constrained here 1093 // Compiler might not be sufficiently constrained here
1039 // https://github.com/embassy-rs/embassy/issues/4723 1094 // https://github.com/embassy-rs/embassy/issues/4723
1040 T::state().register(cx.waker()); 1095 self.state.waker.register(cx.waker());
1041 let status = T::regs().star().read(); 1096 let status = self.info.regs.star().read();
1042 1097
1043 if status.dcrcfail() { 1098 if status.dcrcfail() {
1044 return Poll::Ready(Err(Error::Crc)); 1099 return Poll::Ready(Err(Error::Crc));
@@ -1067,698 +1122,25 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1067 }) 1122 })
1068 .await; 1123 .await;
1069 1124
1070 Self::clear_interrupt_flags(); 1125 self.clear_interrupt_flags();
1071 1126 self.stop_datapath();
1072 res
1073 }
1074
1075 /// Read a data block.
1076 #[inline]
1077 pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> {
1078 let card_capacity = self.card()?.get_capacity();
1079
1080 // NOTE(unsafe) DataBlock uses align 4
1081 let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) };
1082 1127
1083 // Always read 1 block of 512 bytes 1128 transfer.defuse();
1084 // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes 1129 drop(transfer);
1085 let address = match card_capacity {
1086 CardCapacity::StandardCapacity => block_idx * 512,
1087 _ => block_idx,
1088 };
1089 Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16
1090 1130
1091 let on_drop = OnDrop::new(|| Self::on_drop());
1092
1093 let transfer = Self::prepare_datapath_read(
1094 &self.config,
1095 #[cfg(sdmmc_v1)]
1096 &mut self.dma,
1097 buffer,
1098 512,
1099 9,
1100 );
1101 InterruptHandler::<T>::enable_interrupts();
1102 Self::cmd(common_cmd::read_single_block(address), true)?;
1103
1104 let res = Self::complete_datapath_transfer(true).await;
1105
1106 if res.is_ok() {
1107 on_drop.defuse();
1108 Self::stop_datapath();
1109 drop(transfer);
1110 }
1111 res 1131 res
1112 } 1132 }
1113 1133
1114 /// Read multiple data blocks.
1115 #[inline]
1116 pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> {
1117 let card_capacity = self.card()?.get_capacity();
1118
1119 // NOTE(unsafe) reinterpret buffer as &mut [u32]
1120 let buffer = unsafe {
1121 let ptr = blocks.as_mut_ptr() as *mut u32;
1122 let len = blocks.len() * 128;
1123 core::slice::from_raw_parts_mut(ptr, len)
1124 };
1125
1126 // Always read 1 block of 512 bytes
1127 // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
1128 let address = match card_capacity {
1129 CardCapacity::StandardCapacity => block_idx * 512,
1130 _ => block_idx,
1131 };
1132 Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16
1133
1134 let on_drop = OnDrop::new(|| Self::on_drop());
1135
1136 let transfer = Self::prepare_datapath_read(
1137 &self.config,
1138 #[cfg(sdmmc_v1)]
1139 &mut self.dma,
1140 buffer,
1141 512 * blocks.len() as u32,
1142 9,
1143 );
1144 InterruptHandler::<T>::enable_interrupts();
1145
1146 Self::cmd(common_cmd::read_multiple_blocks(address), true)?;
1147
1148 let res = Self::complete_datapath_transfer(false).await;
1149
1150 Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12
1151 Self::clear_interrupt_flags();
1152
1153 if res.is_ok() {
1154 on_drop.defuse();
1155 Self::stop_datapath();
1156 drop(transfer);
1157 }
1158 res
1159 }
1160
1161 /// Write a data block.
1162 pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> {
1163 let card = self.card.as_mut().ok_or(Error::NoCard)?;
1164
1165 // NOTE(unsafe) DataBlock uses align 4
1166 let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) };
1167
1168 // Always read 1 block of 512 bytes
1169 // cards are byte addressed hence the blockaddress is in multiples of 512 bytes
1170 let address = match card.get_capacity() {
1171 CardCapacity::StandardCapacity => block_idx * 512,
1172 _ => block_idx,
1173 };
1174 Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16
1175
1176 let on_drop = OnDrop::new(|| Self::on_drop());
1177
1178 // sdmmc_v1 uses different cmd/dma order than v2, but only for writes
1179 #[cfg(sdmmc_v1)]
1180 Self::cmd(common_cmd::write_single_block(address), true)?;
1181
1182 let transfer = self.prepare_datapath_write(buffer, 512, 9);
1183 InterruptHandler::<T>::enable_interrupts();
1184
1185 #[cfg(sdmmc_v2)]
1186 Self::cmd(common_cmd::write_single_block(address), true)?;
1187
1188 let res = Self::complete_datapath_transfer(true).await;
1189
1190 match res {
1191 Ok(_) => {
1192 on_drop.defuse();
1193 Self::stop_datapath();
1194 drop(transfer);
1195
1196 // TODO: Make this configurable
1197 let mut timeout: u32 = 0x00FF_FFFF;
1198
1199 let card = self.card.as_ref().unwrap();
1200 while timeout > 0 {
1201 let ready_for_data = match card {
1202 SdmmcPeripheral::Emmc(_) => self.read_status::<EMMC>(card)?.ready_for_data(),
1203 SdmmcPeripheral::SdCard(_) => self.read_status::<SD>(card)?.ready_for_data(),
1204 };
1205
1206 if ready_for_data {
1207 return Ok(());
1208 }
1209 timeout -= 1;
1210 }
1211 Err(Error::SoftwareTimeout)
1212 }
1213 Err(e) => Err(e),
1214 }
1215 }
1216
1217 /// Write multiple data blocks.
1218 pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> {
1219 let card = self.card.as_mut().ok_or(Error::NoCard)?;
1220
1221 // NOTE(unsafe) reinterpret buffer as &[u32]
1222 let buffer = unsafe {
1223 let ptr = blocks.as_ptr() as *const u32;
1224 let len = blocks.len() * 128;
1225 core::slice::from_raw_parts(ptr, len)
1226 };
1227
1228 // Always read 1 block of 512 bytes
1229 // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
1230 let address = match card.get_capacity() {
1231 CardCapacity::StandardCapacity => block_idx * 512,
1232 _ => block_idx,
1233 };
1234
1235 Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16
1236
1237 let block_count = blocks.len();
1238
1239 let on_drop = OnDrop::new(|| Self::on_drop());
1240
1241 #[cfg(sdmmc_v1)]
1242 Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25
1243
1244 // Setup write command
1245 let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9);
1246 InterruptHandler::<T>::enable_interrupts();
1247
1248 #[cfg(sdmmc_v2)]
1249 Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25
1250
1251 let res = Self::complete_datapath_transfer(false).await;
1252
1253 Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12
1254 Self::clear_interrupt_flags();
1255
1256 match res {
1257 Ok(_) => {
1258 on_drop.defuse();
1259 Self::stop_datapath();
1260 drop(transfer);
1261
1262 // TODO: Make this configurable
1263 let mut timeout: u32 = 0x00FF_FFFF;
1264
1265 // Try to read card status (ACMD13)
1266 while timeout > 0 {
1267 match self.read_sd_status().await {
1268 Ok(_) => return Ok(()),
1269 Err(Error::Timeout) => (), // Try again
1270 Err(e) => return Err(e),
1271 }
1272 timeout -= 1;
1273 }
1274 Err(Error::SoftwareTimeout)
1275 }
1276 Err(e) => Err(e),
1277 }
1278 }
1279
1280 /// Get a reference to the initialized card
1281 ///
1282 /// # Errors
1283 ///
1284 /// Returns Error::NoCard if [`init_sd_card`](#method.init_sd_card) or
1285 /// [`init_emmc`](#method.init_emmc) has not previously succeeded
1286 #[inline]
1287 pub fn card(&self) -> Result<&SdmmcPeripheral, Error> {
1288 self.card.as_ref().ok_or(Error::NoCard)
1289 }
1290
1291 /// Get the current SDMMC bus clock 1134 /// Get the current SDMMC bus clock
1292 pub fn clock(&self) -> Hertz { 1135 pub fn clock(&self) -> Hertz {
1293 self.clock 1136 self.clock
1294 } 1137 }
1295
1296 /// Set a specific cmd buffer rather than using the default stack allocated one.
1297 /// This is required if stack RAM cannot be used with DMA and usually manifests
1298 /// itself as an indefinite wait on a dma transfer because the dma peripheral
1299 /// cannot access the memory.
1300 pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) {
1301 self.cmd_block = Some(cmd_block)
1302 }
1303
1304 async fn init_internal(&mut self, freq: Hertz, mut card: SdmmcPeripheral) -> Result<(), Error> {
1305 let regs = T::regs();
1306 let ker_ck = T::frequency();
1307
1308 let bus_width = match (self.d3.is_some(), self.d7.is_some()) {
1309 (true, true) => {
1310 if matches!(card, SdmmcPeripheral::SdCard(_)) {
1311 return Err(Error::BusWidth);
1312 }
1313 BusWidth::Eight
1314 }
1315 (true, false) => BusWidth::Four,
1316 _ => BusWidth::One,
1317 };
1318
1319 // While the SD/SDIO card or eMMC is in identification mode,
1320 // the SDMMC_CK frequency must be no more than 400 kHz.
1321 let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0));
1322 self.clock = init_clock;
1323
1324 // CPSMACT and DPSMACT must be 0 to set WIDBUS
1325 Self::wait_idle();
1326
1327 regs.clkcr().modify(|w| {
1328 w.set_widbus(0);
1329 w.set_clkdiv(clkdiv);
1330 #[cfg(sdmmc_v1)]
1331 w.set_bypass(_bypass);
1332 });
1333 regs.dtimer()
1334 .write(|w| w.set_datatime(self.config.data_transfer_timeout));
1335
1336 regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8));
1337 Self::cmd(common_cmd::idle(), false)?;
1338
1339 match card {
1340 SdmmcPeripheral::SdCard(ref mut card) => {
1341 // Check if cards supports CMD8 (with pattern)
1342 Self::cmd(sd_cmd::send_if_cond(1, 0xAA), false)?;
1343 let cic = CIC::from(regs.respr(0).read().cardstatus());
1344
1345 if cic.pattern() != 0xAA {
1346 return Err(Error::UnsupportedCardVersion);
1347 }
1348
1349 if cic.voltage_accepted() & 1 == 0 {
1350 return Err(Error::UnsupportedVoltage);
1351 }
1352
1353 let ocr = loop {
1354 // Signal that next command is a app command
1355 Self::cmd(common_cmd::app_cmd(0), false)?; // CMD55
1356
1357 // 3.2-3.3V
1358 let voltage_window = 1 << 5;
1359 // Initialize card
1360 match Self::cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) {
1361 // ACMD41
1362 Ok(_) => (),
1363 Err(Error::Crc) => (),
1364 Err(err) => return Err(err),
1365 }
1366 let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into();
1367 if !ocr.is_busy() {
1368 // Power up done
1369 break ocr;
1370 }
1371 };
1372
1373 if ocr.high_capacity() {
1374 // Card is SDHC or SDXC or SDUC
1375 card.card_type = CardCapacity::HighCapacity;
1376 } else {
1377 card.card_type = CardCapacity::StandardCapacity;
1378 }
1379 card.ocr = ocr;
1380 }
1381 SdmmcPeripheral::Emmc(ref mut emmc) => {
1382 let ocr = loop {
1383 let high_voltage = 0b0 << 7;
1384 let access_mode = 0b10 << 29;
1385 let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15;
1386 // Initialize card
1387 match Self::cmd(emmc_cmd::send_op_cond(op_cond), false) {
1388 Ok(_) => (),
1389 Err(Error::Crc) => (),
1390 Err(err) => return Err(err),
1391 }
1392 let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into();
1393 if !ocr.is_busy() {
1394 // Power up done
1395 break ocr;
1396 }
1397 };
1398
1399 emmc.capacity = if ocr.access_mode() == 0b10 {
1400 // Card is SDHC or SDXC or SDUC
1401 CardCapacity::HighCapacity
1402 } else {
1403 CardCapacity::StandardCapacity
1404 };
1405 emmc.ocr = ocr;
1406 }
1407 }
1408
1409 Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2
1410 let cid0 = regs.respr(0).read().cardstatus() as u128;
1411 let cid1 = regs.respr(1).read().cardstatus() as u128;
1412 let cid2 = regs.respr(2).read().cardstatus() as u128;
1413 let cid3 = regs.respr(3).read().cardstatus() as u128;
1414 let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3);
1415
1416 match card {
1417 SdmmcPeripheral::SdCard(ref mut card) => {
1418 card.cid = cid.into();
1419
1420 Self::cmd(sd_cmd::send_relative_address(), false)?;
1421 let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus());
1422 card.rca = rca.address();
1423 }
1424 SdmmcPeripheral::Emmc(ref mut emmc) => {
1425 emmc.cid = cid.into();
1426
1427 emmc.rca = 1u16.into();
1428 Self::cmd(emmc_cmd::assign_relative_address(emmc.rca), false)?;
1429 }
1430 }
1431
1432 Self::cmd(common_cmd::send_csd(card.get_address()), false)?;
1433 let csd0 = regs.respr(0).read().cardstatus() as u128;
1434 let csd1 = regs.respr(1).read().cardstatus() as u128;
1435 let csd2 = regs.respr(2).read().cardstatus() as u128;
1436 let csd3 = regs.respr(3).read().cardstatus() as u128;
1437 let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3);
1438
1439 self.select_card(Some(card.get_address()))?;
1440
1441 let bus_width = match card {
1442 SdmmcPeripheral::SdCard(ref mut card) => {
1443 card.csd = csd.into();
1444
1445 self.get_scr(card).await?;
1446
1447 if !card.scr.bus_width_four() {
1448 BusWidth::One
1449 } else {
1450 BusWidth::Four
1451 }
1452 }
1453 SdmmcPeripheral::Emmc(ref mut emmc) => {
1454 emmc.csd = csd.into();
1455
1456 bus_width
1457 }
1458 };
1459
1460 // Set bus width
1461 let widbus = match bus_width {
1462 BusWidth::Eight => 2,
1463 BusWidth::Four => 1,
1464 BusWidth::One => 0,
1465 _ => unreachable!(),
1466 };
1467
1468 match card {
1469 SdmmcPeripheral::SdCard(ref mut card) => {
1470 let acmd_arg = match bus_width {
1471 BusWidth::Four if card.scr.bus_width_four() => 2,
1472 _ => 0,
1473 };
1474 Self::cmd(common_cmd::app_cmd(card.rca), false)?;
1475 Self::cmd(sd_cmd::cmd6(acmd_arg), false)?;
1476 }
1477 SdmmcPeripheral::Emmc(_) => {
1478 // Write bus width to ExtCSD byte 183
1479 Self::cmd(
1480 emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus),
1481 false,
1482 )?;
1483
1484 // Wait for ready after R1b response
1485 loop {
1486 let status = self.read_status::<EMMC>(&card)?;
1487
1488 if status.ready_for_data() {
1489 break;
1490 }
1491 }
1492 }
1493 }
1494
1495 // CPSMACT and DPSMACT must be 0 to set WIDBUS
1496 Self::wait_idle();
1497
1498 regs.clkcr().modify(|w| w.set_widbus(widbus));
1499
1500 // Set Clock
1501 if freq.0 <= 25_000_000 {
1502 // Final clock frequency
1503 self.clkcr_set_clkdiv(freq.0, bus_width)?;
1504 } else {
1505 // Switch to max clock for SDR12
1506 self.clkcr_set_clkdiv(25_000_000, bus_width)?;
1507 }
1508
1509 self.card = Some(card);
1510
1511 match card {
1512 SdmmcPeripheral::SdCard(_) => {
1513 // Read status
1514 self.read_sd_status().await?;
1515
1516 if freq.0 > 25_000_000 {
1517 // Switch to SDR25
1518 self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?;
1519
1520 if self.signalling == Signalling::SDR25 {
1521 // Set final clock frequency
1522 self.clkcr_set_clkdiv(freq.0, bus_width)?;
1523
1524 if self.read_status::<SD>(self.card.as_ref().unwrap())?.state() != CurrentState::Transfer {
1525 return Err(Error::SignalingSwitchFailed);
1526 }
1527 }
1528 }
1529
1530 // Read status after signalling change
1531 self.read_sd_status().await?;
1532 }
1533 SdmmcPeripheral::Emmc(_) => {
1534 self.read_ext_csd().await?;
1535 }
1536 }
1537
1538 Ok(())
1539 }
1540
1541 /// Initializes card (if present) and sets the bus at the specified frequency.
1542 ///
1543 /// SD only.
1544 pub async fn init_sd_card(&mut self, freq: Hertz) -> Result<(), Error> {
1545 self.init_internal(freq, SdmmcPeripheral::SdCard(Card::default())).await
1546 }
1547
1548 /// Switch mode using CMD6.
1549 ///
1550 /// Attempt to set a new signalling mode. The selected
1551 /// signalling mode is returned. Expects the current clock
1552 /// frequency to be > 12.5MHz.
1553 ///
1554 /// SD only.
1555 async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result<Signalling, Error> {
1556 let _ = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card();
1557 // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not
1558 // necessary"
1559
1560 let set_function = 0x8000_0000
1561 | match signalling {
1562 // See PLSS v7_10 Table 4-11
1563 Signalling::DDR50 => 0xFF_FF04,
1564 Signalling::SDR104 => 0xFF_1F03,
1565 Signalling::SDR50 => 0xFF_1F02,
1566 Signalling::SDR25 => 0xFF_FF01,
1567 Signalling::SDR12 => 0xFF_FF00,
1568 };
1569
1570 let status = match self.cmd_block.as_deref_mut() {
1571 Some(x) => x,
1572 None => &mut CmdBlock::new(),
1573 };
1574
1575 // Arm `OnDrop` after the buffer, so it will be dropped first
1576 let on_drop = OnDrop::new(|| Self::on_drop());
1577
1578 let transfer = Self::prepare_datapath_read(
1579 &self.config,
1580 #[cfg(sdmmc_v1)]
1581 &mut self.dma,
1582 status.as_mut(),
1583 64,
1584 6,
1585 );
1586 InterruptHandler::<T>::enable_interrupts();
1587 Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6
1588
1589 let res = Self::complete_datapath_transfer(true).await;
1590
1591 // Host is allowed to use the new functions at least 8
1592 // clocks after the end of the switch command
1593 // transaction. We know the current clock period is < 80ns,
1594 // so a total delay of 640ns is required here
1595 for _ in 0..300 {
1596 cortex_m::asm::nop();
1597 }
1598
1599 match res {
1600 Ok(_) => {
1601 on_drop.defuse();
1602 Self::stop_datapath();
1603 drop(transfer);
1604
1605 // Function Selection of Function Group 1
1606 let selection = (u32::from_be(status[4]) >> 24) & 0xF;
1607
1608 match selection {
1609 0 => Ok(Signalling::SDR12),
1610 1 => Ok(Signalling::SDR25),
1611 2 => Ok(Signalling::SDR50),
1612 3 => Ok(Signalling::SDR104),
1613 4 => Ok(Signalling::DDR50),
1614 _ => Err(Error::UnsupportedCardType),
1615 }
1616 }
1617 Err(e) => Err(e),
1618 }
1619 }
1620
1621 /// Reads the SCR register.
1622 ///
1623 /// SD only.
1624 async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> {
1625 // Read the 64-bit SCR register
1626 Self::cmd(common_cmd::set_block_length(8), false)?; // CMD16
1627 Self::cmd(common_cmd::app_cmd(card.rca), false)?;
1628
1629 let cmd_block = match self.cmd_block.as_deref_mut() {
1630 Some(x) => x,
1631 None => &mut CmdBlock::new(),
1632 };
1633 let scr = &mut cmd_block.0[..2];
1634
1635 // Arm `OnDrop` after the buffer, so it will be dropped first
1636 let on_drop = OnDrop::new(|| Self::on_drop());
1637
1638 let transfer = Self::prepare_datapath_read(
1639 &self.config,
1640 #[cfg(sdmmc_v1)]
1641 &mut self.dma,
1642 scr,
1643 8,
1644 3,
1645 );
1646 InterruptHandler::<T>::enable_interrupts();
1647 Self::cmd(sd_cmd::send_scr(), true)?;
1648
1649 let res = Self::complete_datapath_transfer(true).await;
1650
1651 if res.is_ok() {
1652 on_drop.defuse();
1653 Self::stop_datapath();
1654 drop(transfer);
1655
1656 unsafe {
1657 let scr_bytes = &*(&scr as *const _ as *const [u8; 8]);
1658 card.scr = SCR(u64::from_be_bytes(*scr_bytes));
1659 }
1660 }
1661 res
1662 }
1663
1664 /// Reads the SD Status (ACMD13)
1665 ///
1666 /// SD only.
1667 async fn read_sd_status(&mut self) -> Result<(), Error> {
1668 let card = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card();
1669 let rca = card.rca;
1670
1671 let cmd_block = match self.cmd_block.as_deref_mut() {
1672 Some(x) => x,
1673 None => &mut CmdBlock::new(),
1674 };
1675
1676 Self::cmd(common_cmd::set_block_length(64), false)?; // CMD16
1677 Self::cmd(common_cmd::app_cmd(rca), false)?; // APP
1678
1679 let status = cmd_block;
1680
1681 // Arm `OnDrop` after the buffer, so it will be dropped first
1682 let on_drop = OnDrop::new(|| Self::on_drop());
1683
1684 let transfer = Self::prepare_datapath_read(
1685 &self.config,
1686 #[cfg(sdmmc_v1)]
1687 &mut self.dma,
1688 status.as_mut(),
1689 64,
1690 6,
1691 );
1692 InterruptHandler::<T>::enable_interrupts();
1693 Self::cmd(sd_cmd::sd_status(), true)?;
1694
1695 let res = Self::complete_datapath_transfer(true).await;
1696
1697 if res.is_ok() {
1698 on_drop.defuse();
1699 Self::stop_datapath();
1700 drop(transfer);
1701
1702 for byte in status.iter_mut() {
1703 *byte = u32::from_be(*byte);
1704 }
1705 card.status = status.0.into();
1706 }
1707 res
1708 }
1709
1710 /// Initializes eMMC and sets the bus at the specified frequency.
1711 ///
1712 /// eMMC only.
1713 pub async fn init_emmc(&mut self, freq: Hertz) -> Result<(), Error> {
1714 self.init_internal(freq, SdmmcPeripheral::Emmc(Emmc::default())).await
1715 }
1716
1717 /// Gets the EXT_CSD register.
1718 ///
1719 /// eMMC only.
1720 async fn read_ext_csd(&mut self) -> Result<(), Error> {
1721 let card = self.card.as_mut().ok_or(Error::NoCard)?.get_emmc();
1722
1723 // Note: cmd_block can't be used because ExtCSD is too long to fit.
1724 let mut data_block = DataBlock([0u8; 512]);
1725
1726 // NOTE(unsafe) DataBlock uses align 4
1727 let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) };
1728
1729 Self::cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16
1730
1731 // Arm `OnDrop` after the buffer, so it will be dropped first
1732 let on_drop = OnDrop::new(|| Self::on_drop());
1733
1734 let transfer = Self::prepare_datapath_read(
1735 &self.config,
1736 #[cfg(sdmmc_v1)]
1737 &mut self.dma,
1738 buffer,
1739 512,
1740 9,
1741 );
1742 InterruptHandler::<T>::enable_interrupts();
1743 Self::cmd(emmc_cmd::send_ext_csd(), true)?;
1744
1745 let res = Self::complete_datapath_transfer(true).await;
1746
1747 if res.is_ok() {
1748 on_drop.defuse();
1749 Self::stop_datapath();
1750 drop(transfer);
1751
1752 card.ext_csd = unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into();
1753 }
1754 res
1755 }
1756} 1138}
1757 1139
1758impl<'d, T: Instance> Drop for Sdmmc<'d, T> { 1140impl<'d> Drop for Sdmmc<'d> {
1759 fn drop(&mut self) { 1141 fn drop(&mut self) {
1760 T::Interrupt::disable(); 1142 // T::Interrupt::disable();
1761 Self::on_drop(); 1143 self.on_drop();
1762 1144
1763 critical_section::with(|_| { 1145 critical_section::with(|_| {
1764 self.clk.set_as_disconnected(); 1146 self.clk.set_as_disconnected();
@@ -1791,9 +1173,28 @@ impl<'d, T: Instance> Drop for Sdmmc<'d, T> {
1791 1173
1792////////////////////////////////////////////////////// 1174//////////////////////////////////////////////////////
1793 1175
1176type Regs = RegBlock;
1177
1178struct Info {
1179 regs: Regs,
1180 rcc: RccInfo,
1181}
1182
1183struct State {
1184 waker: AtomicWaker,
1185}
1186
1187impl State {
1188 const fn new() -> Self {
1189 Self {
1190 waker: AtomicWaker::new(),
1191 }
1192 }
1193}
1194
1794trait SealedInstance { 1195trait SealedInstance {
1795 fn regs() -> RegBlock; 1196 fn info() -> &'static Info;
1796 fn state() -> &'static AtomicWaker; 1197 fn state() -> &'static State;
1797} 1198}
1798 1199
1799/// SDMMC instance trait. 1200/// SDMMC instance trait.
@@ -1820,13 +1221,17 @@ dma_trait!(SdmmcDma, Instance);
1820foreach_peripheral!( 1221foreach_peripheral!(
1821 (sdmmc, $inst:ident) => { 1222 (sdmmc, $inst:ident) => {
1822 impl SealedInstance for peripherals::$inst { 1223 impl SealedInstance for peripherals::$inst {
1823 fn regs() -> RegBlock { 1224 fn info() -> &'static Info {
1824 crate::pac::$inst 1225 static INFO: Info = Info {
1226 regs: unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) },
1227 rcc: crate::peripherals::$inst::RCC_INFO,
1228 };
1229 &INFO
1825 } 1230 }
1826 1231
1827 fn state() -> &'static ::embassy_sync::waitqueue::AtomicWaker { 1232 fn state() -> &'static State {
1828 static WAKER: ::embassy_sync::waitqueue::AtomicWaker = ::embassy_sync::waitqueue::AtomicWaker::new(); 1233 static STATE: State = State::new();
1829 &WAKER 1234 &STATE
1830 } 1235 }
1831 } 1236 }
1832 1237
@@ -1835,46 +1240,3 @@ foreach_peripheral!(
1835 } 1240 }
1836 }; 1241 };
1837); 1242);
1838
1839impl<'d, T: Instance> block_device_driver::BlockDevice<512> for Sdmmc<'d, T> {
1840 type Error = Error;
1841 type Align = aligned::A4;
1842
1843 async fn read(
1844 &mut self,
1845 block_address: u32,
1846 buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>],
1847 ) -> Result<(), Self::Error> {
1848 // TODO: I think block_address needs to be adjusted by the partition start offset
1849 if buf.len() == 1 {
1850 let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) };
1851 self.read_block(block_address, block).await?;
1852 } else {
1853 let blocks: &mut [DataBlock] =
1854 unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) };
1855 self.read_blocks(block_address, blocks).await?;
1856 }
1857 Ok(())
1858 }
1859
1860 async fn write(
1861 &mut self,
1862 block_address: u32,
1863 buf: &[aligned::Aligned<Self::Align, [u8; 512]>],
1864 ) -> Result<(), Self::Error> {
1865 // TODO: I think block_address needs to be adjusted by the partition start offset
1866 if buf.len() == 1 {
1867 let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) };
1868 self.write_block(block_address, block).await?;
1869 } else {
1870 let blocks: &[DataBlock] =
1871 unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) };
1872 self.write_blocks(block_address, blocks).await?;
1873 }
1874 Ok(())
1875 }
1876
1877 async fn size(&mut self) -> Result<u64, Self::Error> {
1878 Ok(self.card()?.size())
1879 }
1880}
diff --git a/embassy-stm32/src/sdmmc/sd.rs b/embassy-stm32/src/sdmmc/sd.rs
new file mode 100644
index 000000000..6190226b8
--- /dev/null
+++ b/embassy-stm32/src/sdmmc/sd.rs
@@ -0,0 +1,693 @@
1use core::default::Default;
2use core::ops::{Deref, DerefMut};
3
4use sdio_host::emmc::{EMMC, ExtCSD};
5use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus};
6use sdio_host::{common_cmd, emmc_cmd, sd_cmd};
7
8use crate::sdmmc::{BlockSize, Error, Sdmmc, Signalling, block_size, bus_width_vals, slice8_mut, slice8_ref};
9use crate::time::{Hertz, mhz};
10
11/// Aligned data block for SDMMC transfers.
12///
13/// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements.
14#[repr(align(4))]
15#[derive(Debug, Clone, PartialEq, Eq)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub struct DataBlock(pub [u32; 128]);
18
19impl DataBlock {
20 /// Create a new DataBlock
21 pub const fn new() -> Self {
22 DataBlock([0u32; 128])
23 }
24}
25
26impl Deref for DataBlock {
27 type Target = [u8; 512];
28
29 fn deref(&self) -> &Self::Target {
30 unwrap!(slice8_ref(&self.0[..]).try_into())
31 }
32}
33
34impl DerefMut for DataBlock {
35 fn deref_mut(&mut self) -> &mut Self::Target {
36 unwrap!(slice8_mut(&mut self.0[..]).try_into())
37 }
38}
39
40/// Command Block buffer for SDMMC command transfers.
41///
42/// This is a 16-word array, exposed so that DMA commpatible memory can be used if required.
43#[derive(Debug, Clone, PartialEq, Eq)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45pub struct CmdBlock(pub [u32; 16]);
46
47impl CmdBlock {
48 /// Creates a new instance of CmdBlock
49 pub const fn new() -> Self {
50 Self([0u32; 16])
51 }
52}
53
54impl Deref for CmdBlock {
55 type Target = [u32; 16];
56
57 fn deref(&self) -> &Self::Target {
58 &self.0
59 }
60}
61
62impl DerefMut for CmdBlock {
63 fn deref_mut(&mut self) -> &mut Self::Target {
64 &mut self.0
65 }
66}
67
68/// Represents either an SD or EMMC card
69pub trait Addressable: Sized + Clone {
70 /// Associated type
71 type Ext;
72
73 /// Get this peripheral's address on the SDMMC bus
74 fn get_address(&self) -> u16;
75
76 /// Is this a standard or high capacity peripheral?
77 fn get_capacity(&self) -> CardCapacity;
78
79 /// Size in bytes
80 fn size(&self) -> u64;
81}
82
83/// Storage Device
84pub struct StorageDevice<'a, 'b, T: Addressable> {
85 info: T,
86 /// Inner member
87 pub sdmmc: &'a mut Sdmmc<'b>,
88}
89
90/// Card Storage Device
91impl<'a, 'b> StorageDevice<'a, 'b, Card> {
92 /// Create a new SD card
93 pub async fn new_sd_card(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> {
94 let mut s = Self {
95 info: Card::default(),
96 sdmmc,
97 };
98
99 s.acquire(cmd_block, freq).await?;
100
101 Ok(s)
102 }
103
104 /// Initializes the card into a known state (or at least tries to).
105 async fn acquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> {
106 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
107 let regs = self.sdmmc.info.regs;
108
109 let _bus_width = match self.sdmmc.bus_width() {
110 BusWidth::Eight => return Err(Error::BusWidth),
111 bus_width => bus_width,
112 };
113
114 // While the SD/SDIO card or eMMC is in identification mode,
115 // the SDMMC_CK frequency must be no more than 400 kHz.
116 self.sdmmc.init_idle()?;
117
118 // Check if cards supports CMD8 (with pattern)
119 self.sdmmc.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?;
120 let cic = CIC::from(regs.respr(0).read().cardstatus());
121
122 if cic.pattern() != 0xAA {
123 return Err(Error::UnsupportedCardVersion);
124 }
125
126 if cic.voltage_accepted() & 1 == 0 {
127 return Err(Error::UnsupportedVoltage);
128 }
129
130 let ocr = loop {
131 // Signal that next command is a app command
132 self.sdmmc.cmd(common_cmd::app_cmd(0), false)?; // CMD55
133
134 // 3.2-3.3V
135 let voltage_window = 1 << 5;
136 // Initialize card
137 match self
138 .sdmmc
139 .cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false)
140 {
141 // ACMD41
142 Ok(_) => (),
143 Err(Error::Crc) => (),
144 Err(err) => return Err(err),
145 }
146
147 let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into();
148 if !ocr.is_busy() {
149 // Power up done
150 break ocr;
151 }
152 };
153
154 if ocr.high_capacity() {
155 // Card is SDHC or SDXC or SDUC
156 self.info.card_type = CardCapacity::HighCapacity;
157 } else {
158 self.info.card_type = CardCapacity::StandardCapacity;
159 }
160 self.info.ocr = ocr;
161
162 self.info.cid = self.sdmmc.get_cid()?.into();
163
164 self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?;
165 let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus());
166 self.info.rca = rca.address();
167
168 self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into();
169 self.sdmmc.select_card(Some(self.info.get_address()))?;
170
171 self.info.scr = self.get_scr(cmd_block).await?;
172
173 let (bus_width, acmd_arg) = if !self.info.scr.bus_width_four() {
174 (BusWidth::One, 0)
175 } else {
176 (BusWidth::Four, 2)
177 };
178
179 self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?;
180 self.sdmmc.cmd(sd_cmd::cmd6(acmd_arg), false)?;
181
182 self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?;
183
184 // Read status
185 self.info.status = self.read_sd_status(cmd_block).await?;
186
187 if freq > mhz(25) {
188 // Switch to SDR25
189 self.sdmmc.signalling = self.switch_signalling_mode(cmd_block, Signalling::SDR25).await?;
190
191 if self.sdmmc.signalling == Signalling::SDR25 {
192 // Set final clock frequency
193 self.sdmmc.clkcr_set_clkdiv(freq, bus_width)?;
194
195 if self.sdmmc.read_status(&self.info)?.state() != CurrentState::Transfer {
196 return Err(Error::SignalingSwitchFailed);
197 }
198 }
199
200 // Read status after signalling change
201 self.read_sd_status(cmd_block).await?;
202 }
203
204 Ok(())
205 }
206
207 /// Switch mode using CMD6.
208 ///
209 /// Attempt to set a new signalling mode. The selected
210 /// signalling mode is returned. Expects the current clock
211 /// frequency to be > 12.5MHz.
212 ///
213 /// SD only.
214 async fn switch_signalling_mode(
215 &self,
216 cmd_block: &mut CmdBlock,
217 signalling: Signalling,
218 ) -> Result<Signalling, Error> {
219 // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not
220 // necessary"
221
222 let set_function = 0x8000_0000
223 | match signalling {
224 // See PLSS v7_10 Table 4-11
225 Signalling::DDR50 => 0xFF_FF04,
226 Signalling::SDR104 => 0xFF_1F03,
227 Signalling::SDR50 => 0xFF_1F02,
228 Signalling::SDR25 => 0xFF_FF01,
229 Signalling::SDR12 => 0xFF_FF00,
230 };
231
232 let buffer = &mut cmd_block.0[..64 / 4];
233
234 let transfer = self
235 .sdmmc
236 .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false);
237
238 self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6
239
240 self.sdmmc.complete_datapath_transfer(transfer, true).await?;
241
242 // Host is allowed to use the new functions at least 8
243 // clocks after the end of the switch command
244 // transaction. We know the current clock period is < 80ns,
245 // so a total delay of 640ns is required here
246 for _ in 0..300 {
247 cortex_m::asm::nop();
248 }
249
250 // Function Selection of Function Group 1
251 let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF;
252
253 match selection {
254 0 => Ok(Signalling::SDR12),
255 1 => Ok(Signalling::SDR25),
256 2 => Ok(Signalling::SDR50),
257 3 => Ok(Signalling::SDR104),
258 4 => Ok(Signalling::DDR50),
259 _ => Err(Error::UnsupportedCardType),
260 }
261 }
262
263 /// Reads the SCR register.
264 ///
265 /// SD only.
266 async fn get_scr(&self, cmd_block: &mut CmdBlock) -> Result<SCR, Error> {
267 // Read the 64-bit SCR register
268 self.sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16
269 self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?;
270
271 let scr = &mut cmd_block.0[..2];
272
273 // Arm `OnDrop` after the buffer, so it will be dropped first
274
275 let transfer = self.sdmmc.prepare_datapath_read(scr, BlockSize::Size8, false);
276 self.sdmmc.cmd(sd_cmd::send_scr(), true)?;
277
278 self.sdmmc.complete_datapath_transfer(transfer, true).await?;
279
280 Ok(SCR(u64::from_be_bytes(unwrap!(slice8_mut(scr).try_into()))))
281 }
282
283 /// Reads the SD Status (ACMD13)
284 ///
285 /// SD only.
286 async fn read_sd_status(&self, cmd_block: &mut CmdBlock) -> Result<SDStatus, Error> {
287 let rca = self.info.rca;
288
289 self.sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16
290 self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP
291
292 let buffer = &mut cmd_block.as_mut()[..64 / 4];
293
294 let transfer = self
295 .sdmmc
296 .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false);
297 self.sdmmc.cmd(sd_cmd::sd_status(), true)?;
298
299 self.sdmmc.complete_datapath_transfer(transfer, true).await?;
300
301 for byte in cmd_block.iter_mut() {
302 *byte = u32::from_be(*byte);
303 }
304
305 Ok(cmd_block.0.into())
306 }
307}
308
309/// Emmc storage device
310impl<'a, 'b> StorageDevice<'a, 'b, Emmc> {
311 /// Create a new EMMC card
312 pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> {
313 let mut s = Self {
314 info: Emmc::default(),
315 sdmmc,
316 };
317
318 s.acquire(cmd_block, freq).await?;
319
320 Ok(s)
321 }
322
323 async fn acquire(&mut self, _cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> {
324 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
325 let regs = self.sdmmc.info.regs;
326
327 let bus_width = self.sdmmc.bus_width();
328
329 // While the SD/SDIO card or eMMC is in identification mode,
330 // the SDMMC_CK frequency must be no more than 400 kHz.
331 self.sdmmc.init_idle()?;
332
333 let ocr = loop {
334 let high_voltage = 0b0 << 7;
335 let access_mode = 0b10 << 29;
336 let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15;
337 // Initialize card
338 match self.sdmmc.cmd(emmc_cmd::send_op_cond(op_cond), false) {
339 Ok(_) => (),
340 Err(Error::Crc) => (),
341 Err(err) => return Err(err),
342 }
343 let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into();
344 if !ocr.is_busy() {
345 // Power up done
346 break ocr;
347 }
348 };
349
350 self.info.capacity = if ocr.access_mode() == 0b10 {
351 // Card is SDHC or SDXC or SDUC
352 CardCapacity::HighCapacity
353 } else {
354 CardCapacity::StandardCapacity
355 };
356 self.info.ocr = ocr;
357
358 self.info.cid = self.sdmmc.get_cid()?.into();
359
360 self.info.rca = 1u16.into();
361 self.sdmmc
362 .cmd(emmc_cmd::assign_relative_address(self.info.rca), false)?;
363
364 self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into();
365 self.sdmmc.select_card(Some(self.info.get_address()))?;
366
367 let (widbus, _) = bus_width_vals(bus_width);
368
369 // Write bus width to ExtCSD byte 183
370 self.sdmmc.cmd(
371 emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus),
372 false,
373 )?;
374
375 // Wait for ready after R1b response
376 loop {
377 let status = self.sdmmc.read_status(&self.info)?;
378
379 if status.ready_for_data() {
380 break;
381 }
382 }
383
384 self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?;
385 self.info.ext_csd = self.read_ext_csd().await?;
386
387 Ok(())
388 }
389
390 /// Gets the EXT_CSD register.
391 ///
392 /// eMMC only.
393 async fn read_ext_csd(&self) -> Result<ExtCSD, Error> {
394 // Note: cmd_block can't be used because ExtCSD is too long to fit.
395 let mut data_block = DataBlock::new();
396
397 self.sdmmc
398 .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)
399 .unwrap(); // CMD16
400
401 let transfer = self
402 .sdmmc
403 .prepare_datapath_read(&mut data_block.0, block_size(size_of::<DataBlock>()), false);
404 self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?;
405
406 self.sdmmc.complete_datapath_transfer(transfer, true).await?;
407
408 Ok(data_block.0.into())
409 }
410}
411
412/// Card or Emmc storage device
413impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> {
414 /// Write a block
415 pub fn card(&self) -> A {
416 self.info.clone()
417 }
418
419 /// Read a data block.
420 #[inline]
421 pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> {
422 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
423 let card_capacity = self.info.get_capacity();
424
425 // Always read 1 block of 512 bytes
426 // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
427 let address = match card_capacity {
428 CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32,
429 _ => block_idx,
430 };
431 self.sdmmc
432 .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16
433
434 let transfer = self
435 .sdmmc
436 .prepare_datapath_read(&mut buffer.0, block_size(size_of::<DataBlock>()), false);
437 self.sdmmc.cmd(common_cmd::read_single_block(address), true)?;
438
439 self.sdmmc.complete_datapath_transfer(transfer, true).await?;
440
441 Ok(())
442 }
443
444 /// Read multiple data blocks.
445 #[inline]
446 pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> {
447 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
448 let card_capacity = self.info.get_capacity();
449
450 // NOTE(unsafe) reinterpret buffer as &mut [u32]
451 let buffer = unsafe {
452 core::slice::from_raw_parts_mut(
453 blocks.as_mut_ptr() as *mut u32,
454 blocks.len() * size_of::<DataBlock>() / size_of::<u32>(),
455 )
456 };
457
458 // Always read 1 block of 512 bytes
459 // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
460 let address = match card_capacity {
461 CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32,
462 _ => block_idx,
463 };
464 self.sdmmc
465 .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16
466
467 let transfer = self
468 .sdmmc
469 .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false);
470 self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?;
471
472 self.sdmmc.complete_datapath_transfer(transfer, false).await?;
473
474 self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12
475 self.sdmmc.clear_interrupt_flags();
476
477 Ok(())
478 }
479
480 /// Write a data block.
481 pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error>
482 where
483 CardStatus<A::Ext>: From<u32>,
484 {
485 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
486
487 // Always read 1 block of 512 bytes
488 // cards are byte addressed hence the blockaddress is in multiples of 512 bytes
489 let address = match self.info.get_capacity() {
490 CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32,
491 _ => block_idx,
492 };
493 self.sdmmc
494 .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16
495
496 // sdmmc_v1 uses different cmd/dma order than v2, but only for writes
497 #[cfg(sdmmc_v1)]
498 self.sdmmc.cmd(common_cmd::write_single_block(address), true)?;
499
500 let transfer = self
501 .sdmmc
502 .prepare_datapath_write(&buffer.0, block_size(size_of::<DataBlock>()), false);
503
504 #[cfg(sdmmc_v2)]
505 self.sdmmc.cmd(common_cmd::write_single_block(address), true)?;
506
507 self.sdmmc.complete_datapath_transfer(transfer, true).await?;
508
509 // TODO: Make this configurable
510 let mut timeout: u32 = 0x00FF_FFFF;
511
512 while timeout > 0 {
513 let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data();
514 if ready_for_data {
515 return Ok(());
516 }
517 timeout -= 1;
518 }
519
520 Err(Error::SoftwareTimeout)
521 }
522
523 /// Write multiple data blocks.
524 pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error>
525 where
526 CardStatus<A::Ext>: From<u32>,
527 {
528 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
529
530 // NOTE(unsafe) reinterpret buffer as &[u32]
531 let buffer = unsafe {
532 core::slice::from_raw_parts(
533 blocks.as_ptr() as *const u32,
534 blocks.len() * size_of::<DataBlock>() / size_of::<u32>(),
535 )
536 };
537 // Always read 1 block of 512 bytes
538 // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
539 let address = match self.info.get_capacity() {
540 CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32,
541 _ => block_idx,
542 };
543
544 self.sdmmc
545 .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16
546
547 #[cfg(sdmmc_v1)]
548 self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25
549
550 // Setup write command
551 let transfer = self
552 .sdmmc
553 .prepare_datapath_write(buffer, block_size(size_of::<DataBlock>()), false);
554
555 #[cfg(sdmmc_v2)]
556 self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25
557
558 self.sdmmc.complete_datapath_transfer(transfer, false).await?;
559
560 self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12
561 self.sdmmc.clear_interrupt_flags();
562
563 // TODO: Make this configurable
564 let mut timeout: u32 = 0x00FF_FFFF;
565
566 while timeout > 0 {
567 let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data();
568
569 if ready_for_data {
570 return Ok(());
571 }
572 timeout -= 1;
573 }
574 Err(Error::SoftwareTimeout)
575 }
576}
577
578#[derive(Clone, Copy, Debug, Default)]
579/// SD Card
580pub struct Card {
581 /// The type of this card
582 pub card_type: CardCapacity,
583 /// Operation Conditions Register
584 pub ocr: OCR<SD>,
585 /// Relative Card Address
586 pub rca: u16,
587 /// Card ID
588 pub cid: CID<SD>,
589 /// Card Specific Data
590 pub csd: CSD<SD>,
591 /// SD CARD Configuration Register
592 pub scr: SCR,
593 /// SD Status
594 pub status: SDStatus,
595}
596
597impl Addressable for Card {
598 type Ext = SD;
599
600 /// Get this peripheral's address on the SDMMC bus
601 fn get_address(&self) -> u16 {
602 self.rca
603 }
604
605 /// Is this a standard or high capacity peripheral?
606 fn get_capacity(&self) -> CardCapacity {
607 self.card_type
608 }
609
610 /// Size in bytes
611 fn size(&self) -> u64 {
612 u64::from(self.csd.block_count()) * 512
613 }
614}
615
616#[derive(Clone, Copy, Debug, Default)]
617/// eMMC storage
618pub struct Emmc {
619 /// The capacity of this card
620 pub capacity: CardCapacity,
621 /// Operation Conditions Register
622 pub ocr: OCR<EMMC>,
623 /// Relative Card Address
624 pub rca: u16,
625 /// Card ID
626 pub cid: CID<EMMC>,
627 /// Card Specific Data
628 pub csd: CSD<EMMC>,
629 /// Extended Card Specific Data
630 pub ext_csd: ExtCSD,
631}
632
633impl Addressable for Emmc {
634 type Ext = EMMC;
635
636 /// Get this peripheral's address on the SDMMC bus
637 fn get_address(&self) -> u16 {
638 self.rca
639 }
640
641 /// Is this a standard or high capacity peripheral?
642 fn get_capacity(&self) -> CardCapacity {
643 self.capacity
644 }
645
646 /// Size in bytes
647 fn size(&self) -> u64 {
648 u64::from(self.ext_csd.sector_count()) * 512
649 }
650}
651
652impl<'d, 'e, A: Addressable> block_device_driver::BlockDevice<512> for StorageDevice<'d, 'e, A> {
653 type Error = Error;
654 type Align = aligned::A4;
655
656 async fn read(
657 &mut self,
658 block_address: u32,
659 buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>],
660 ) -> Result<(), Self::Error> {
661 // TODO: I think block_address needs to be adjusted by the partition start offset
662 if buf.len() == 1 {
663 let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut DataBlock) };
664 self.read_block(block_address, block).await?;
665 } else {
666 let blocks: &mut [DataBlock] =
667 unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) };
668 self.read_blocks(block_address, blocks).await?;
669 }
670 Ok(())
671 }
672
673 async fn write(
674 &mut self,
675 block_address: u32,
676 buf: &[aligned::Aligned<Self::Align, [u8; 512]>],
677 ) -> Result<(), Self::Error> {
678 // TODO: I think block_address needs to be adjusted by the partition start offset
679 if buf.len() == 1 {
680 let block = unsafe { &*(&buf[0] as *const _ as *const DataBlock) };
681 self.write_block(block_address, block).await?;
682 } else {
683 let blocks: &[DataBlock] =
684 unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) };
685 self.write_blocks(block_address, blocks).await?;
686 }
687 Ok(())
688 }
689
690 async fn size(&mut self) -> Result<u64, Self::Error> {
691 Ok(self.info.size())
692 }
693}
diff --git a/embassy-stm32/src/sdmmc/sdio.rs b/embassy-stm32/src/sdmmc/sdio.rs
new file mode 100644
index 000000000..1412b21fc
--- /dev/null
+++ b/embassy-stm32/src/sdmmc/sdio.rs
@@ -0,0 +1,177 @@
1use core::ops::{Deref, DerefMut};
2
3use sdio_host::common_cmd::{R1, Rz, cmd};
4use sdio_host::sd::BusWidth;
5use sdio_host::sd_cmd;
6
7use crate::sdmmc::{Error, Sdmmc, block_size, slice8_mut, slice8_ref};
8use crate::time::Hertz;
9
10/// Aligned data block for SDMMC transfers.
11///
12/// This is a 64-byte array, aligned to 4 bytes to satisfy DMA requirements.
13#[repr(align(4))]
14#[derive(Debug, Clone, PartialEq, Eq)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16pub struct DataBlock(pub [u32; 16]);
17
18impl DataBlock {
19 /// Create a new DataBlock
20 pub const fn new() -> Self {
21 DataBlock([0u32; 16])
22 }
23}
24
25impl Deref for DataBlock {
26 type Target = [u8; 64];
27
28 fn deref(&self) -> &Self::Target {
29 unwrap!(slice8_ref(&self.0[..]).try_into())
30 }
31}
32
33impl DerefMut for DataBlock {
34 fn deref_mut(&mut self) -> &mut Self::Target {
35 unwrap!(slice8_mut(&mut self.0[..]).try_into())
36 }
37}
38
39/// Storage Device
40pub struct SerialDataInterface<'a, 'b> {
41 /// Inner member
42 pub sdmmc: &'a mut Sdmmc<'b>,
43}
44
45/// Card Storage Device
46impl<'a, 'b> SerialDataInterface<'a, 'b> {
47 /// Create a new SD card
48 pub async fn new(sdmmc: &'a mut Sdmmc<'b>, freq: Hertz) -> Result<Self, Error> {
49 let mut s = Self { sdmmc };
50
51 s.acquire(freq).await?;
52
53 Ok(s)
54 }
55
56 /// Initializes the card into a known state (or at least tries to).
57 async fn acquire(&mut self, _freq: Hertz) -> Result<(), Error> {
58 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
59
60 let _bus_width = match self.sdmmc.bus_width() {
61 BusWidth::Eight => return Err(Error::BusWidth),
62 bus_width => bus_width,
63 };
64
65 // While the SD/SDIO card or eMMC is in identification mode,
66 // the SDMMC_CK frequency must be no more than 400 kHz.
67 self.sdmmc.init_idle()?;
68
69 self.sdmmc.cmd(cmd::<Rz>(5, 0), false)?;
70
71 // Get RCA
72 let rca = self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?;
73
74 // Select the card with RCA
75 self.sdmmc.select_card(Some(rca.try_into().unwrap()))?;
76
77 Ok(())
78 }
79
80 /// Set the bus to the 4-bit high-speed frequency
81 pub fn set_bus_to_high_speed(&mut self, frequency: Hertz) -> Result<(), Error> {
82 self.sdmmc.clkcr_set_clkdiv(frequency, BusWidth::Four)?;
83
84 Ok(())
85 }
86
87 /// Run cmd52
88 pub async fn cmd52(&mut self, arg: u32) -> Result<u32, Error> {
89 self.sdmmc.cmd(cmd::<R1>(52, arg), false)
90 }
91
92 /// Read in block mode using cmd53
93 pub async fn cmd53_block_read(&mut self, arg: u32, blocks: &mut [DataBlock]) -> Result<(), Error> {
94 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
95
96 // NOTE(unsafe) reinterpret buffer as &mut [u32]
97 let buffer = unsafe {
98 core::slice::from_raw_parts_mut(
99 blocks.as_mut_ptr() as *mut u32,
100 blocks.len() * size_of::<DataBlock>() / size_of::<u32>(),
101 )
102 };
103
104 let transfer = self
105 .sdmmc
106 .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false);
107 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?;
108
109 self.sdmmc.complete_datapath_transfer(transfer, false).await?;
110 self.sdmmc.clear_interrupt_flags();
111
112 Ok(())
113 }
114
115 /// Read in multibyte mode using cmd53
116 pub async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut [u32]) -> Result<(), Error> {
117 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
118
119 let transfer = self
120 .sdmmc
121 .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), true);
122 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?;
123
124 self.sdmmc.complete_datapath_transfer(transfer, false).await?;
125 self.sdmmc.clear_interrupt_flags();
126
127 Ok(())
128 }
129
130 /// Write in block mode using cmd53
131 pub async fn cmd53_block_write(&mut self, arg: u32, blocks: &[DataBlock]) -> Result<(), Error> {
132 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
133
134 // NOTE(unsafe) reinterpret buffer as &mut [u32]
135 let buffer = unsafe {
136 core::slice::from_raw_parts_mut(
137 blocks.as_ptr() as *mut u32,
138 blocks.len() * size_of::<DataBlock>() / size_of::<u32>(),
139 )
140 };
141
142 #[cfg(sdmmc_v1)]
143 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?;
144
145 let transfer = self
146 .sdmmc
147 .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false);
148
149 #[cfg(sdmmc_v2)]
150 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?;
151
152 self.sdmmc.complete_datapath_transfer(transfer, false).await?;
153 self.sdmmc.clear_interrupt_flags();
154
155 Ok(())
156 }
157
158 /// Write in multibyte mode using cmd53
159 pub async fn cmd53_byte_write(&mut self, arg: u32, buffer: &[u32]) -> Result<(), Error> {
160 let _scoped_block_stop = self.sdmmc.info.rcc.block_stop();
161
162 #[cfg(sdmmc_v1)]
163 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?;
164
165 let transfer = self
166 .sdmmc
167 .prepare_datapath_write(buffer, block_size(size_of::<DataBlock>()), true);
168
169 #[cfg(sdmmc_v2)]
170 self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?;
171
172 self.sdmmc.complete_datapath_transfer(transfer, false).await?;
173 self.sdmmc.clear_interrupt_flags();
174
175 Ok(())
176 }
177}
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index abb80ed26..c90e0cef4 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -54,6 +54,16 @@ pub enum BitOrder {
54 MsbFirst, 54 MsbFirst,
55} 55}
56 56
57/// SPI Direction.
58#[derive(Debug, PartialEq, Eq, Clone, Copy)]
59#[cfg_attr(feature = "defmt", derive(defmt::Format))]
60pub enum Direction {
61 /// Transmit
62 Transmit,
63 /// Receive
64 Receive,
65}
66
57/// SPI configuration. 67/// SPI configuration.
58#[non_exhaustive] 68#[non_exhaustive]
59#[derive(Copy, Clone)] 69#[derive(Copy, Clone)]
@@ -72,6 +82,10 @@ pub struct Config {
72 /// signal rise/fall speed (slew rate) - defaults to `Medium`. 82 /// signal rise/fall speed (slew rate) - defaults to `Medium`.
73 /// Increase for high SPI speeds. Change to `Low` to reduce ringing. 83 /// Increase for high SPI speeds. Change to `Low` to reduce ringing.
74 pub gpio_speed: Speed, 84 pub gpio_speed: Speed,
85 /// If True sets SSOE to zero even if SPI is in Master Mode.
86 /// NSS output enabled (SSM = 0, SSOE = 1): The NSS signal is driven low when the master starts the communication and is kept low until the SPI is disabled.
87 /// NSS output disabled (SSM = 0, SSOE = 0): For devices set as slave, the NSS pin acts as a classical NSS input: the slave is selected when NSS is low and deselected when NSS high.
88 pub nss_output_disable: bool,
75} 89}
76 90
77impl Default for Config { 91impl Default for Config {
@@ -82,6 +96,7 @@ impl Default for Config {
82 frequency: Hertz(1_000_000), 96 frequency: Hertz(1_000_000),
83 miso_pull: Pull::None, 97 miso_pull: Pull::None,
84 gpio_speed: Speed::VeryHigh, 98 gpio_speed: Speed::VeryHigh,
99 nss_output_disable: false,
85 } 100 }
86 } 101 }
87} 102}
@@ -215,13 +230,35 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
215 let cpol = config.raw_polarity(); 230 let cpol = config.raw_polarity();
216 let lsbfirst = config.raw_byte_order(); 231 let lsbfirst = config.raw_byte_order();
217 232
218 self.info.rcc.enable_and_reset(); 233 self.info.rcc.enable_and_reset_without_stop();
234
235 /*
236 - Software NSS management (SSM = 1)
237 The slave select information is driven internally by the value of the SSI bit in the
238 SPI_CR1 register. The external NSS pin remains free for other application uses.
239
240 - Hardware NSS management (SSM = 0)
241 Two configurations are possible depending on the NSS output configuration (SSOE bit
242 in register SPI_CR1).
243
244 -- NSS output enabled (SSM = 0, SSOE = 1)
245 This configuration is used only when the device operates in master mode. The
246 NSS signal is driven low when the master starts the communication and is kept
247 low until the SPI is disabled.
248
249 -- NSS output disabled (SSM = 0, SSOE = 0)
250 This configuration allows multimaster capability for devices operating in master
251 mode. For devices set as slave, the NSS pin acts as a classical NSS input: the
252 slave is selected when NSS is low and deselected when NSS high
253 */
254 let ssm = self.nss.is_none();
219 255
220 let regs = self.info.regs; 256 let regs = self.info.regs;
221 #[cfg(any(spi_v1, spi_v2))] 257 #[cfg(any(spi_v1, spi_v2))]
222 { 258 {
259 let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable;
223 regs.cr2().modify(|w| { 260 regs.cr2().modify(|w| {
224 w.set_ssoe(false); 261 w.set_ssoe(ssoe);
225 }); 262 });
226 regs.cr1().modify(|w| { 263 regs.cr1().modify(|w| {
227 w.set_cpha(cpha); 264 w.set_cpha(cpha);
@@ -232,7 +269,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
232 w.set_spe(true); 269 w.set_spe(true);
233 w.set_lsbfirst(lsbfirst); 270 w.set_lsbfirst(lsbfirst);
234 w.set_ssi(CM::MASTER == vals::Mstr::MASTER); 271 w.set_ssi(CM::MASTER == vals::Mstr::MASTER);
235 w.set_ssm(CM::MASTER == vals::Mstr::MASTER); 272 w.set_ssm(ssm);
236 w.set_crcen(false); 273 w.set_crcen(false);
237 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); 274 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL);
238 // we're doing "fake rxonly", by actually writing one 275 // we're doing "fake rxonly", by actually writing one
@@ -244,11 +281,12 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
244 } 281 }
245 #[cfg(spi_v3)] 282 #[cfg(spi_v3)]
246 { 283 {
284 let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable;
247 regs.cr2().modify(|w| { 285 regs.cr2().modify(|w| {
248 let (ds, frxth) = <u8 as SealedWord>::CONFIG; 286 let (ds, frxth) = <u8 as SealedWord>::CONFIG;
249 w.set_frxth(frxth); 287 w.set_frxth(frxth);
250 w.set_ds(ds); 288 w.set_ds(ds);
251 w.set_ssoe(false); 289 w.set_ssoe(ssoe);
252 }); 290 });
253 regs.cr1().modify(|w| { 291 regs.cr1().modify(|w| {
254 w.set_cpha(cpha); 292 w.set_cpha(cpha);
@@ -258,7 +296,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
258 w.set_br(br); 296 w.set_br(br);
259 w.set_lsbfirst(lsbfirst); 297 w.set_lsbfirst(lsbfirst);
260 w.set_ssi(CM::MASTER == vals::Mstr::MASTER); 298 w.set_ssi(CM::MASTER == vals::Mstr::MASTER);
261 w.set_ssm(CM::MASTER == vals::Mstr::MASTER); 299 w.set_ssm(ssm);
262 w.set_crcen(false); 300 w.set_crcen(false);
263 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); 301 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL);
264 w.set_spe(true); 302 w.set_spe(true);
@@ -266,14 +304,14 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
266 } 304 }
267 #[cfg(any(spi_v4, spi_v5, spi_v6))] 305 #[cfg(any(spi_v4, spi_v5, spi_v6))]
268 { 306 {
307 let ssoe = CM::MASTER == vals::Master::MASTER && !config.nss_output_disable;
269 regs.ifcr().write(|w| w.0 = 0xffff_ffff); 308 regs.ifcr().write(|w| w.0 = 0xffff_ffff);
270 regs.cfg2().modify(|w| { 309 regs.cfg2().modify(|w| {
271 //w.set_ssoe(true); 310 w.set_ssoe(ssoe);
272 w.set_ssoe(false);
273 w.set_cpha(cpha); 311 w.set_cpha(cpha);
274 w.set_cpol(cpol); 312 w.set_cpol(cpol);
275 w.set_lsbfirst(lsbfirst); 313 w.set_lsbfirst(lsbfirst);
276 w.set_ssm(CM::MASTER == vals::Master::MASTER); 314 w.set_ssm(ssm);
277 w.set_master(CM::MASTER); 315 w.set_master(CM::MASTER);
278 w.set_comm(vals::Comm::FULL_DUPLEX); 316 w.set_comm(vals::Comm::FULL_DUPLEX);
279 w.set_ssom(vals::Ssom::ASSERTED); 317 w.set_ssom(vals::Ssom::ASSERTED);
@@ -348,6 +386,20 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
348 Ok(()) 386 Ok(())
349 } 387 }
350 388
389 /// Set SPI direction
390 #[cfg(any(spi_v1, spi_v2, spi_v3))]
391 pub fn set_direction(&mut self, dir: Option<Direction>) {
392 let (bidimode, bidioe) = match dir {
393 Some(Direction::Transmit) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::TRANSMIT),
394 Some(Direction::Receive) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::RECEIVE),
395 None => (vals::Bidimode::UNIDIRECTIONAL, vals::Bidioe::TRANSMIT),
396 };
397 self.info.regs.cr1().modify(|w| {
398 w.set_bidimode(bidimode);
399 w.set_bidioe(bidioe);
400 });
401 }
402
351 /// Get current SPI configuration. 403 /// Get current SPI configuration.
352 pub fn get_current_config(&self) -> Config { 404 pub fn get_current_config(&self) -> Config {
353 #[cfg(any(spi_v1, spi_v2, spi_v3))] 405 #[cfg(any(spi_v1, spi_v2, spi_v3))]
@@ -357,6 +409,11 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
357 #[cfg(any(spi_v4, spi_v5, spi_v6))] 409 #[cfg(any(spi_v4, spi_v5, spi_v6))]
358 let cfg1 = self.info.regs.cfg1().read(); 410 let cfg1 = self.info.regs.cfg1().read();
359 411
412 #[cfg(any(spi_v1, spi_v2, spi_v3))]
413 let ssoe = self.info.regs.cr2().read().ssoe();
414 #[cfg(any(spi_v4, spi_v5, spi_v6))]
415 let ssoe = cfg.ssoe();
416
360 let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { 417 let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW {
361 Polarity::IdleLow 418 Polarity::IdleLow
362 } else { 419 } else {
@@ -386,12 +443,16 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
386 443
387 let frequency = compute_frequency(self.kernel_clock, br); 444 let frequency = compute_frequency(self.kernel_clock, br);
388 445
446 // NSS output disabled if SSOE=0 or if SSM=1 software slave management enabled
447 let nss_output_disable = !ssoe || cfg.ssm();
448
389 Config { 449 Config {
390 mode: Mode { polarity, phase }, 450 mode: Mode { polarity, phase },
391 bit_order, 451 bit_order,
392 frequency, 452 frequency,
393 miso_pull, 453 miso_pull,
394 gpio_speed: self.gpio_speed, 454 gpio_speed: self.gpio_speed,
455 nss_output_disable,
395 } 456 }
396 } 457 }
397 458
@@ -708,6 +769,30 @@ impl<'d> Spi<'d, Async, Master> {
708 ) 769 )
709 } 770 }
710 771
772 /// Create a new SPI driver, in bidirectional mode, specifically in tranmit mode
773 #[cfg(any(spi_v1, spi_v2, spi_v3))]
774 pub fn new_bidi<T: Instance, #[cfg(afio)] A>(
775 peri: Peri<'d, T>,
776 sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,
777 sdio: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
778 tx_dma: Peri<'d, impl TxDma<T>>,
779 rx_dma: Peri<'d, impl RxDma<T>>,
780 config: Config,
781 ) -> Self {
782 let mut this = Self::new_inner(
783 peri,
784 new_pin!(sck, config.sck_af()),
785 new_pin!(sdio, AfType::output(OutputType::PushPull, config.gpio_speed)),
786 None,
787 None,
788 new_dma!(tx_dma),
789 new_dma!(rx_dma),
790 config,
791 );
792 this.set_direction(Some(Direction::Transmit));
793 this
794 }
795
711 /// Create a new SPI driver, in TX-only mode, without SCK pin. 796 /// Create a new SPI driver, in TX-only mode, without SCK pin.
712 /// 797 ///
713 /// This can be useful for bit-banging non-SPI protocols. 798 /// This can be useful for bit-banging non-SPI protocols.
@@ -763,6 +848,7 @@ impl<'d> Spi<'d, Async, Master> {
763impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { 848impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
764 /// SPI write, using DMA. 849 /// SPI write, using DMA.
765 pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { 850 pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> {
851 let _scoped_block_stop = self.info.rcc.block_stop();
766 if data.is_empty() { 852 if data.is_empty() {
767 return Ok(()); 853 return Ok(());
768 } 854 }
@@ -794,6 +880,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
794 /// SPI read, using DMA. 880 /// SPI read, using DMA.
795 #[cfg(any(spi_v4, spi_v5, spi_v6))] 881 #[cfg(any(spi_v4, spi_v5, spi_v6))]
796 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 882 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
883 let _scoped_block_stop = self.info.rcc.block_stop();
797 if data.is_empty() { 884 if data.is_empty() {
798 return Ok(()); 885 return Ok(());
799 } 886 }
@@ -881,6 +968,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
881 /// SPI read, using DMA. 968 /// SPI read, using DMA.
882 #[cfg(any(spi_v1, spi_v2, spi_v3))] 969 #[cfg(any(spi_v1, spi_v2, spi_v3))]
883 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 970 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
971 let _scoped_block_stop = self.info.rcc.block_stop();
884 if data.is_empty() { 972 if data.is_empty() {
885 return Ok(()); 973 return Ok(());
886 } 974 }
@@ -928,6 +1016,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
928 } 1016 }
929 1017
930 async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { 1018 async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> {
1019 let _scoped_block_stop = self.info.rcc.block_stop();
931 assert_eq!(read.len(), write.len()); 1020 assert_eq!(read.len(), write.len());
932 if read.len() == 0 { 1021 if read.len() == 0 {
933 return Ok(()); 1022 return Ok(());
@@ -979,6 +1068,8 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
979 /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. 1068 /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored.
980 /// If `write` is shorter it is padded with zero bytes. 1069 /// If `write` is shorter it is padded with zero bytes.
981 pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { 1070 pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
1071 let _scoped_block_stop = self.info.rcc.block_stop();
1072
982 self.transfer_inner(read, write).await 1073 self.transfer_inner(read, write).await
983 } 1074 }
984 1075
@@ -986,6 +1077,8 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
986 /// 1077 ///
987 /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. 1078 /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.
988 pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 1079 pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
1080 let _scoped_block_stop = self.info.rcc.block_stop();
1081
989 self.transfer_inner(data, data).await 1082 self.transfer_inner(data, data).await
990 } 1083 }
991} 1084}
diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs
index 532877f70..88a28ee3d 100644
--- a/embassy-stm32/src/time.rs
+++ b/embassy-stm32/src/time.rs
@@ -4,7 +4,7 @@ use core::fmt::Display;
4use core::ops::{Div, Mul}; 4use core::ops::{Div, Mul};
5 5
6/// Hertz 6/// Hertz
7#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] 7#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Default)]
8pub struct Hertz(pub u32); 8pub struct Hertz(pub u32);
9 9
10impl Display for Hertz { 10impl Display for Hertz {
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index cfcf5f3fd..ed5d902bd 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -329,6 +329,12 @@ impl RtcDriver {
329 regs_gp16().cr1().modify(|w| w.set_cen(true)); 329 regs_gp16().cr1().modify(|w| w.set_cen(true));
330 } 330 }
331 331
332 #[cfg(feature = "low-power")]
333 /// Returns whether time is currently stopped
334 pub(crate) fn is_stopped(&self) -> bool {
335 !regs_gp16().cr1().read().cen()
336 }
337
332 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { 338 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
333 let r = regs_gp16(); 339 let r = regs_gp16();
334 340
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 77f19a37b..620d7858e 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -2,16 +2,17 @@
2 2
3use core::marker::PhantomData; 3use core::marker::PhantomData;
4 4
5pub use stm32_metapac::timer::vals::{Ckd, Mms2, Ossi, Ossr};
6
7use super::low_level::{CountingMode, OutputPolarity, Timer}; 5use super::low_level::{CountingMode, OutputPolarity, Timer};
8use super::simple_pwm::PwmPin; 6use super::simple_pwm::PwmPin;
9use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; 7use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin};
10use crate::Peri; 8use crate::Peri;
11use crate::gpio::{AnyPin, OutputType}; 9use crate::dma::word::Word;
10use crate::gpio::{AfType, AnyPin, OutputType};
11pub use crate::pac::timer::vals::{Ccds, Ckd, Mms2, Ossi, Ossr};
12use crate::time::Hertz; 12use crate::time::Hertz;
13use crate::timer::TimerChannel; 13use crate::timer::TimerChannel;
14use crate::timer::low_level::OutputCompareMode; 14use crate::timer::low_level::OutputCompareMode;
15use crate::timer::simple_pwm::PwmPinConfig;
15 16
16/// Complementary PWM pin wrapper. 17/// Complementary PWM pin wrapper.
17/// 18///
@@ -27,9 +28,27 @@ impl<'d, T: AdvancedInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(
27 pub fn new(pin: Peri<'d, if_afio!(impl TimerComplementaryPin<T, C, A>)>, output_type: OutputType) -> Self { 28 pub fn new(pin: Peri<'d, if_afio!(impl TimerComplementaryPin<T, C, A>)>, output_type: OutputType) -> Self {
28 critical_section::with(|_| { 29 critical_section::with(|_| {
29 pin.set_low(); 30 pin.set_low();
30 set_as_af!( 31 set_as_af!(pin, AfType::output(output_type, crate::gpio::Speed::VeryHigh));
31 pin, 32 });
32 crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh) 33 ComplementaryPwmPin {
34 pin: pin.into(),
35 phantom: PhantomData,
36 }
37 }
38
39 /// Create a new PWM pin instance with config.
40 pub fn new_with_config(
41 pin: Peri<'d, if_afio!(impl TimerComplementaryPin<T, C, A>)>,
42 pin_config: PwmPinConfig,
43 ) -> Self {
44 critical_section::with(|_| {
45 pin.set_low();
46 #[cfg(gpio_v1)]
47 set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed));
48 #[cfg(gpio_v2)]
49 pin.set_as_af(
50 pin.af_num(),
51 AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull),
33 ); 52 );
34 }); 53 });
35 ComplementaryPwmPin { 54 ComplementaryPwmPin {
@@ -176,20 +195,20 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
176 /// Get max duty value. 195 /// Get max duty value.
177 /// 196 ///
178 /// This value depends on the configured frequency and the timer's clock rate from RCC. 197 /// This value depends on the configured frequency and the timer's clock rate from RCC.
179 pub fn get_max_duty(&self) -> u16 { 198 pub fn get_max_duty(&self) -> u32 {
180 if self.inner.get_counting_mode().is_center_aligned() { 199 if self.inner.get_counting_mode().is_center_aligned() {
181 self.inner.get_max_compare_value() as u16 200 self.inner.get_max_compare_value().into()
182 } else { 201 } else {
183 self.inner.get_max_compare_value() as u16 + 1 202 self.inner.get_max_compare_value().into() + 1
184 } 203 }
185 } 204 }
186 205
187 /// Set the duty for a given channel. 206 /// Set the duty for a given channel.
188 /// 207 ///
189 /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. 208 /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
190 pub fn set_duty(&mut self, channel: Channel, duty: u16) { 209 pub fn set_duty(&mut self, channel: Channel, duty: u32) {
191 assert!(duty <= self.get_max_duty()); 210 assert!(duty <= self.get_max_duty());
192 self.inner.set_compare_value(channel, duty as _) 211 self.inner.set_compare_value(channel, unwrap!(duty.try_into()))
193 } 212 }
194 213
195 /// Set the output polarity for a given channel. 214 /// Set the output polarity for a given channel.
@@ -219,9 +238,34 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
219 /// Generate a sequence of PWM waveform 238 /// Generate a sequence of PWM waveform
220 /// 239 ///
221 /// Note: 240 /// Note:
241 /// The DMA channel provided does not need to correspond to the requested channel.
242 pub async fn waveform<C: TimerChannel, W: Word + Into<T::Word>>(
243 &mut self,
244 dma: Peri<'_, impl super::Dma<T, C>>,
245 channel: Channel,
246 duty: &[W],
247 ) {
248 self.inner.enable_channel(channel, true);
249 self.inner.enable_channel(C::CHANNEL, true);
250 self.inner.clamp_compare_value::<W>(channel);
251 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE);
252 self.inner.set_cc_dma_enable_state(C::CHANNEL, true);
253 self.inner.setup_channel_update_dma(dma, channel, duty).await;
254 self.inner.set_cc_dma_enable_state(C::CHANNEL, false);
255 }
256
257 /// Generate a sequence of PWM waveform
258 ///
259 /// Note:
222 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 260 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
223 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 261 pub async fn waveform_up<W: Word + Into<T::Word>>(
262 &mut self,
263 dma: Peri<'_, impl super::UpDma<T>>,
264 channel: Channel,
265 duty: &[W],
266 ) {
224 self.inner.enable_channel(channel, true); 267 self.inner.enable_channel(channel, true);
268 self.inner.clamp_compare_value::<W>(channel);
225 self.inner.enable_update_dma(true); 269 self.inner.enable_update_dma(true);
226 self.inner.setup_update_dma(dma, channel, duty).await; 270 self.inner.setup_update_dma(dma, channel, duty).await;
227 self.inner.enable_update_dma(false); 271 self.inner.enable_update_dma(false);
@@ -256,13 +300,21 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
256 /// Also be aware that embassy timers use one of timers internally. It is possible to 300 /// Also be aware that embassy timers use one of timers internally. It is possible to
257 /// switch this timer by using `time-driver-timX` feature. 301 /// switch this timer by using `time-driver-timX` feature.
258 /// 302 ///
259 pub async fn waveform_up_multi_channel( 303 pub async fn waveform_up_multi_channel<W: Word + Into<T::Word>>(
260 &mut self, 304 &mut self,
261 dma: Peri<'_, impl super::UpDma<T>>, 305 dma: Peri<'_, impl super::UpDma<T>>,
262 starting_channel: Channel, 306 starting_channel: Channel,
263 ending_channel: Channel, 307 ending_channel: Channel,
264 duty: &[u16], 308 duty: &[W],
265 ) { 309 ) {
310 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
311 .iter()
312 .filter(|ch| ch.index() >= starting_channel.index())
313 .filter(|ch| ch.index() <= ending_channel.index())
314 .for_each(|ch| {
315 self.inner.enable_channel(*ch, true);
316 self.inner.clamp_compare_value::<W>(*ch);
317 });
266 self.inner.enable_update_dma(true); 318 self.inner.enable_update_dma(true);
267 self.inner 319 self.inner
268 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) 320 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
@@ -291,20 +343,20 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<
291 } 343 }
292 344
293 fn get_duty(&self, channel: Self::Channel) -> Self::Duty { 345 fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
294 self.inner.get_compare_value(channel) as u16 346 unwrap!(self.inner.get_compare_value(channel).try_into())
295 } 347 }
296 348
297 fn get_max_duty(&self) -> Self::Duty { 349 fn get_max_duty(&self) -> Self::Duty {
298 if self.inner.get_counting_mode().is_center_aligned() { 350 if self.inner.get_counting_mode().is_center_aligned() {
299 self.inner.get_max_compare_value() as u16 351 unwrap!(self.inner.get_max_compare_value().try_into())
300 } else { 352 } else {
301 self.inner.get_max_compare_value() as u16 + 1 353 unwrap!(self.inner.get_max_compare_value().try_into()) + 1
302 } 354 }
303 } 355 }
304 356
305 fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { 357 fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
306 assert!(duty <= self.get_max_duty()); 358 assert!(duty <= unwrap!(self.get_max_duty().try_into()));
307 self.inner.set_compare_value(channel, duty as u32) 359 self.inner.set_compare_value(channel, unwrap!(duty.try_into()))
308 } 360 }
309 361
310 fn set_period<P>(&mut self, period: P) 362 fn set_period<P>(&mut self, period: P)
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs
index 9cf0f8c34..3e1482b67 100644
--- a/embassy-stm32/src/timer/input_capture.rs
+++ b/embassy-stm32/src/timer/input_capture.rs
@@ -97,7 +97,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {
97 97
98 /// Get capture value for a channel. 98 /// Get capture value for a channel.
99 pub fn get_capture_value(&self, channel: Channel) -> u32 { 99 pub fn get_capture_value(&self, channel: Channel) -> u32 {
100 self.inner.get_capture_value(channel) 100 self.inner.get_capture_value(channel).into()
101 } 101 }
102 102
103 /// Get input interrupt. 103 /// Get input interrupt.
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index aba08081f..82e936f3a 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -13,7 +13,7 @@ use embassy_hal_internal::Peri;
13pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; 13pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource};
14 14
15use super::*; 15use super::*;
16use crate::dma::{Transfer, WritableRingBuffer}; 16use crate::dma::{self, Transfer, WritableRingBuffer};
17use crate::pac::timer::vals; 17use crate::pac::timer::vals;
18use crate::rcc; 18use crate::rcc;
19use crate::time::Hertz; 19use crate::time::Hertz;
@@ -268,6 +268,11 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
268 unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } 268 unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) }
269 } 269 }
270 270
271 #[cfg(stm32l0)]
272 fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp16 {
273 unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }
274 }
275
271 /// Start the timer. 276 /// Start the timer.
272 pub fn start(&self) { 277 pub fn start(&self) {
273 self.regs_core().cr1().modify(|r| r.set_cen(true)); 278 self.regs_core().cr1().modify(|r| r.set_cen(true));
@@ -296,7 +301,12 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
296 301
297 /// get the capability of the timer 302 /// get the capability of the timer
298 pub fn bits(&self) -> TimerBits { 303 pub fn bits(&self) -> TimerBits {
299 T::BITS 304 match T::Word::bits() {
305 16 => TimerBits::Bits16,
306 #[cfg(not(stm32l0))]
307 32 => TimerBits::Bits32,
308 _ => unreachable!(),
309 }
300 } 310 }
301 311
302 /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. 312 /// Set the frequency of how many times per second the timer counts up to the max value or down to 0.
@@ -306,18 +316,10 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
306 /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved 316 /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved
307 /// because it needs to count up and down. 317 /// because it needs to count up and down.
308 pub fn set_frequency(&self, frequency: Hertz) { 318 pub fn set_frequency(&self, frequency: Hertz) {
309 match T::BITS { 319 self.set_frequency_internal(frequency, T::Word::bits());
310 TimerBits::Bits16 => {
311 self.set_frequency_internal(frequency, 16);
312 }
313 #[cfg(not(stm32l0))]
314 TimerBits::Bits32 => {
315 self.set_frequency_internal(frequency, 32);
316 }
317 }
318 } 320 }
319 321
320 pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) { 322 pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: usize) {
321 let f = frequency.0; 323 let f = frequency.0;
322 assert!(f > 0); 324 assert!(f > 0);
323 let timer_f = T::frequency().0; 325 let timer_f = T::frequency().0;
@@ -326,25 +328,15 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
326 let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); 328 let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into());
327 let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); 329 let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1);
328 330
329 match T::BITS { 331 // the timer counts `0..=arr`, we want it to count `0..divide_by`
330 TimerBits::Bits16 => { 332 let arr: T::Word = unwrap!(T::Word::try_from(divide_by - 1));
331 // the timer counts `0..=arr`, we want it to count `0..divide_by`
332 let arr = unwrap!(u16::try_from(divide_by - 1));
333
334 let regs = self.regs_core();
335 regs.psc().write_value(psc);
336 regs.arr().write(|r| r.set_arr(arr));
337 }
338 #[cfg(not(stm32l0))]
339 TimerBits::Bits32 => {
340 // the timer counts `0..=arr`, we want it to count `0..divide_by`
341 let arr: u32 = unwrap!(u32::try_from(divide_by - 1));
342 333
343 let regs = self.regs_gp32_unchecked(); 334 let regs = self.regs_gp32_unchecked();
344 regs.psc().write_value(psc); 335 regs.psc().write_value(psc);
345 regs.arr().write_value(arr); 336 #[cfg(stm32l0)]
346 } 337 regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into())));
347 } 338 #[cfg(not(stm32l0))]
339 regs.arr().write_value(arr.into());
348 } 340 }
349 341
350 /// Set tick frequency. 342 /// Set tick frequency.
@@ -393,23 +385,14 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
393 pub fn get_frequency(&self) -> Hertz { 385 pub fn get_frequency(&self) -> Hertz {
394 let timer_f = T::frequency(); 386 let timer_f = T::frequency();
395 387
396 match T::BITS { 388 let regs = self.regs_gp32_unchecked();
397 TimerBits::Bits16 => { 389 #[cfg(not(stm32l0))]
398 let regs = self.regs_core(); 390 let arr = regs.arr().read();
399 let arr = regs.arr().read().arr(); 391 #[cfg(stm32l0)]
400 let psc = regs.psc().read(); 392 let arr = regs.arr().read().arr();
393 let psc = regs.psc().read();
401 394
402 timer_f / arr / (psc + 1) 395 timer_f / arr / (psc + 1)
403 }
404 #[cfg(not(stm32l0))]
405 TimerBits::Bits32 => {
406 let regs = self.regs_gp32_unchecked();
407 let arr = regs.arr().read();
408 let psc = regs.psc().read();
409
410 timer_f / arr / (psc + 1)
411 }
412 }
413 } 396 }
414 397
415 /// Get the clock frequency of the timer (before prescaler is applied). 398 /// Get the clock frequency of the timer (before prescaler is applied).
@@ -469,42 +452,29 @@ impl<'d, T: GeneralInstance1Channel> Timer<'d, T> {
469 } 452 }
470 453
471 /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. 454 /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
472 pub fn get_max_compare_value(&self) -> u32 { 455 pub fn get_max_compare_value(&self) -> T::Word {
473 match T::BITS { 456 #[cfg(not(stm32l0))]
474 TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32, 457 return unwrap!(self.regs_gp32_unchecked().arr().read().try_into());
475 #[cfg(not(stm32l0))] 458 #[cfg(stm32l0)]
476 TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), 459 return unwrap!(self.regs_gp32_unchecked().arr().read().arr().try_into());
477 }
478 } 460 }
479 461
480 /// Set the max compare value. 462 /// Set the max compare value.
481 /// 463 ///
482 /// An update event is generated to load the new value. The update event is 464 /// An update event is generated to load the new value. The update event is
483 /// generated such that it will not cause an interrupt or DMA request. 465 /// generated such that it will not cause an interrupt or DMA request.
484 pub fn set_max_compare_value(&self, ticks: u32) { 466 pub fn set_max_compare_value(&self, ticks: T::Word) {
485 match T::BITS { 467 let arr = ticks;
486 TimerBits::Bits16 => {
487 let arr = unwrap!(u16::try_from(ticks));
488 468
489 let regs = self.regs_1ch(); 469 let regs = self.regs_gp32_unchecked();
490 regs.arr().write(|r| r.set_arr(arr)); 470 #[cfg(not(stm32l0))]
471 regs.arr().write_value(arr.into());
472 #[cfg(stm32l0)]
473 regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into())));
491 474
492 regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); 475 regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
493 regs.egr().write(|r| r.set_ug(true)); 476 regs.egr().write(|r| r.set_ug(true));
494 regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); 477 regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
495 }
496 #[cfg(not(stm32l0))]
497 TimerBits::Bits32 => {
498 let arr = ticks;
499
500 let regs = self.regs_gp32_unchecked();
501 regs.arr().write_value(arr);
502
503 regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
504 regs.egr().write(|r| r.set_ug(true));
505 regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
506 }
507 }
508 } 478 }
509} 479}
510 480
@@ -638,35 +608,44 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
638 } 608 }
639 609
640 /// Set compare value for a channel. 610 /// Set compare value for a channel.
641 pub fn set_compare_value(&self, channel: Channel, value: u32) { 611 pub fn set_compare_value(&self, channel: Channel, value: T::Word) {
642 match T::BITS { 612 #[cfg(not(stm32l0))]
643 TimerBits::Bits16 => { 613 self.regs_gp32_unchecked()
644 let value = unwrap!(u16::try_from(value)); 614 .ccr(channel.index())
645 self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); 615 .write_value(value.into());
646 } 616 #[cfg(stm32l0)]
647 #[cfg(not(stm32l0))] 617 self.regs_gp16()
648 TimerBits::Bits32 => { 618 .ccr(channel.index())
649 self.regs_gp32_unchecked().ccr(channel.index()).write_value(value); 619 .modify(|w| w.set_ccr(unwrap!(value.try_into())));
650 }
651 }
652 } 620 }
653 621
654 /// Get compare value for a channel. 622 /// Get compare value for a channel.
655 pub fn get_compare_value(&self, channel: Channel) -> u32 { 623 pub fn get_compare_value(&self, channel: Channel) -> T::Word {
656 match T::BITS { 624 #[cfg(not(stm32l0))]
657 TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32, 625 return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().try_into());
658 #[cfg(not(stm32l0))] 626 #[cfg(stm32l0)]
659 TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(), 627 return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().ccr().try_into());
660 } 628 }
629
630 pub(crate) fn clamp_compare_value<W: Word>(&mut self, channel: Channel) {
631 self.set_compare_value(
632 channel,
633 unwrap!(
634 self.get_compare_value(channel)
635 .into()
636 .clamp(0, W::max() as u32)
637 .try_into()
638 ),
639 );
661 } 640 }
662 641
663 /// Setup a ring buffer for the channel 642 /// Setup a ring buffer for the channel
664 pub fn setup_ring_buffer<'a>( 643 pub fn setup_ring_buffer<'a, W: Word + Into<T::Word>>(
665 &mut self, 644 &mut self,
666 dma: Peri<'a, impl super::UpDma<T>>, 645 dma: Peri<'a, impl super::UpDma<T>>,
667 channel: Channel, 646 channel: Channel,
668 dma_buf: &'a mut [u16], 647 dma_buf: &'a mut [W],
669 ) -> WritableRingBuffer<'a, u16> { 648 ) -> WritableRingBuffer<'a, W> {
670 #[allow(clippy::let_unit_value)] // eg. stm32f334 649 #[allow(clippy::let_unit_value)] // eg. stm32f334
671 let req = dma.request(); 650 let req = dma.request();
672 651
@@ -686,7 +665,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
686 WritableRingBuffer::new( 665 WritableRingBuffer::new(
687 dma, 666 dma,
688 req, 667 req,
689 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, 668 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut W,
690 dma_buf, 669 dma_buf,
691 dma_transfer_option, 670 dma_transfer_option,
692 ) 671 )
@@ -697,15 +676,35 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
697 /// 676 ///
698 /// Note: 677 /// Note:
699 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 678 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
700 pub fn setup_update_dma<'a>( 679 pub fn setup_update_dma<'a, W: Word + Into<T::Word>>(
701 &mut self, 680 &mut self,
702 dma: Peri<'a, impl super::UpDma<T>>, 681 dma: Peri<'a, impl super::UpDma<T>>,
703 channel: Channel, 682 channel: Channel,
704 duty: &'a [u16], 683 duty: &'a [W],
705 ) -> Transfer<'a> { 684 ) -> Transfer<'a> {
706 #[allow(clippy::let_unit_value)] // eg. stm32f334 685 self.setup_update_dma_inner(dma.request(), dma, channel, duty)
707 let req = dma.request(); 686 }
708 687
688 /// Generate a sequence of PWM waveform
689 ///
690 /// Note:
691 /// The DMA channel provided does not need to correspond to the requested channel.
692 pub fn setup_channel_update_dma<'a, C: TimerChannel, W: Word + Into<T::Word>>(
693 &mut self,
694 dma: Peri<'a, impl super::Dma<T, C>>,
695 channel: Channel,
696 duty: &'a [W],
697 ) -> Transfer<'a> {
698 self.setup_update_dma_inner(dma.request(), dma, channel, duty)
699 }
700
701 fn setup_update_dma_inner<'a, W: Word + Into<T::Word>>(
702 &mut self,
703 request: dma::Request,
704 dma: Peri<'a, impl dma::Channel>,
705 channel: Channel,
706 duty: &'a [W],
707 ) -> Transfer<'a> {
709 unsafe { 708 unsafe {
710 #[cfg(not(any(bdma, gpdma)))] 709 #[cfg(not(any(bdma, gpdma)))]
711 use crate::dma::{Burst, FifoThreshold}; 710 use crate::dma::{Burst, FifoThreshold};
@@ -719,29 +718,13 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
719 ..Default::default() 718 ..Default::default()
720 }; 719 };
721 720
722 match self.bits() { 721 Transfer::new_write(
723 TimerBits::Bits16 => Transfer::new_write( 722 dma,
724 dma, 723 request,
725 req, 724 duty,
726 duty, 725 self.regs_gp16().ccr(channel.index()).as_ptr() as *mut W,
727 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, 726 dma_transfer_option,
728 dma_transfer_option, 727 )
729 ),
730 #[cfg(not(any(stm32l0)))]
731 TimerBits::Bits32 => {
732 #[cfg(not(any(bdma, gpdma)))]
733 panic!("unsupported timer bits");
734
735 #[cfg(any(bdma, gpdma))]
736 Transfer::new_write(
737 dma,
738 req,
739 duty,
740 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32,
741 dma_transfer_option,
742 )
743 }
744 }
745 } 728 }
746 } 729 }
747 730
@@ -774,12 +757,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
774 /// Also be aware that embassy timers use one of timers internally. It is possible to 757 /// Also be aware that embassy timers use one of timers internally. It is possible to
775 /// switch this timer by using `time-driver-timX` feature. 758 /// switch this timer by using `time-driver-timX` feature.
776 /// 759 ///
777 pub fn setup_update_dma_burst<'a>( 760 pub fn setup_update_dma_burst<'a, W: Word + Into<T::Word>>(
778 &mut self, 761 &mut self,
779 dma: Peri<'a, impl super::UpDma<T>>, 762 dma: Peri<'a, impl super::UpDma<T>>,
780 starting_channel: Channel, 763 starting_channel: Channel,
781 ending_channel: Channel, 764 ending_channel: Channel,
782 duty: &'a [u16], 765 duty: &'a [W],
783 ) -> Transfer<'a> { 766 ) -> Transfer<'a> {
784 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; 767 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32;
785 let start_ch_index = starting_channel.index(); 768 let start_ch_index = starting_channel.index();
@@ -815,14 +798,14 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
815 dma, 798 dma,
816 req, 799 req,
817 duty, 800 duty,
818 self.regs_gp16().dmar().as_ptr() as *mut u16, 801 self.regs_gp16().dmar().as_ptr() as *mut W,
819 dma_transfer_option, 802 dma_transfer_option,
820 ) 803 )
821 } 804 }
822 } 805 }
823 806
824 /// Get capture value for a channel. 807 /// Get capture value for a channel.
825 pub fn get_capture_value(&self, channel: Channel) -> u32 { 808 pub fn get_capture_value(&self, channel: Channel) -> T::Word {
826 self.get_compare_value(channel) 809 self.get_compare_value(channel)
827 } 810 }
828 811
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index 3fa363881..998e3a6f4 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -15,6 +15,8 @@ pub mod qei;
15pub mod ringbuffered; 15pub mod ringbuffered;
16pub mod simple_pwm; 16pub mod simple_pwm;
17 17
18use crate::dma::word::Word;
19use crate::fmt::Debuggable;
18use crate::interrupt; 20use crate::interrupt;
19use crate::rcc::RccPeripheral; 21use crate::rcc::RccPeripheral;
20 22
@@ -163,7 +165,12 @@ pub trait CoreInstance: SealedInstance + 'static {
163 type UpdateInterrupt: interrupt::typelevel::Interrupt; 165 type UpdateInterrupt: interrupt::typelevel::Interrupt;
164 166
165 /// Amount of bits this timer has. 167 /// Amount of bits this timer has.
166 const BITS: TimerBits; 168 type Word: Word
169 + TryInto<u16, Error: Debuggable>
170 + From<u16>
171 + TryFrom<u32, Error: Debuggable>
172 + Into<u32>
173 + TryFrom<u64, Error: Debuggable>;
167 174
168 /// Registers for this timer. 175 /// Registers for this timer.
169 /// 176 ///
@@ -241,7 +248,7 @@ dma_trait!(Dma, GeneralInstance4Channel, TimerChannel);
241 248
242#[allow(unused)] 249#[allow(unused)]
243macro_rules! impl_core_timer { 250macro_rules! impl_core_timer {
244 ($inst:ident, $bits:expr) => { 251 ($inst:ident, $bits:ident) => {
245 impl SealedInstance for crate::peripherals::$inst { 252 impl SealedInstance for crate::peripherals::$inst {
246 fn state() -> &'static State { 253 fn state() -> &'static State {
247 static STATE: State = State::new(); 254 static STATE: State = State::new();
@@ -251,8 +258,7 @@ macro_rules! impl_core_timer {
251 258
252 impl CoreInstance for crate::peripherals::$inst { 259 impl CoreInstance for crate::peripherals::$inst {
253 type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; 260 type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP;
254 261 type Word = $bits;
255 const BITS: TimerBits = $bits;
256 262
257 fn regs() -> *mut () { 263 fn regs() -> *mut () {
258 crate::pac::$inst.as_ptr() 264 crate::pac::$inst.as_ptr()
@@ -306,13 +312,13 @@ macro_rules! impl_general_4ch_blank_sealed {
306 312
307foreach_interrupt! { 313foreach_interrupt! {
308 ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { 314 ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => {
309 impl_core_timer!($inst, TimerBits::Bits16); 315 impl_core_timer!($inst, u16);
310 impl BasicNoCr2Instance for crate::peripherals::$inst {} 316 impl BasicNoCr2Instance for crate::peripherals::$inst {}
311 impl BasicInstance for crate::peripherals::$inst {} 317 impl BasicInstance for crate::peripherals::$inst {}
312 }; 318 };
313 319
314 ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => { 320 ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => {
315 impl_core_timer!($inst, TimerBits::Bits16); 321 impl_core_timer!($inst, u16);
316 impl BasicNoCr2Instance for crate::peripherals::$inst {} 322 impl BasicNoCr2Instance for crate::peripherals::$inst {}
317 impl BasicInstance for crate::peripherals::$inst {} 323 impl BasicInstance for crate::peripherals::$inst {}
318 impl_general_1ch!($inst); 324 impl_general_1ch!($inst);
@@ -322,7 +328,7 @@ foreach_interrupt! {
322 }; 328 };
323 329
324 ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => { 330 ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => {
325 impl_core_timer!($inst, TimerBits::Bits16); 331 impl_core_timer!($inst, u16);
326 impl BasicNoCr2Instance for crate::peripherals::$inst {} 332 impl BasicNoCr2Instance for crate::peripherals::$inst {}
327 impl BasicInstance for crate::peripherals::$inst {} 333 impl BasicInstance for crate::peripherals::$inst {}
328 impl_general_1ch!($inst); 334 impl_general_1ch!($inst);
@@ -332,7 +338,7 @@ foreach_interrupt! {
332 }; 338 };
333 339
334 ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { 340 ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => {
335 impl_core_timer!($inst, TimerBits::Bits16); 341 impl_core_timer!($inst, u16);
336 impl BasicNoCr2Instance for crate::peripherals::$inst {} 342 impl BasicNoCr2Instance for crate::peripherals::$inst {}
337 impl BasicInstance for crate::peripherals::$inst {} 343 impl BasicInstance for crate::peripherals::$inst {}
338 impl_general_1ch!($inst); 344 impl_general_1ch!($inst);
@@ -342,7 +348,7 @@ foreach_interrupt! {
342 }; 348 };
343 349
344 ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { 350 ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => {
345 impl_core_timer!($inst, TimerBits::Bits32); 351 impl_core_timer!($inst, u32);
346 impl BasicNoCr2Instance for crate::peripherals::$inst {} 352 impl BasicNoCr2Instance for crate::peripherals::$inst {}
347 impl BasicInstance for crate::peripherals::$inst {} 353 impl BasicInstance for crate::peripherals::$inst {}
348 impl_general_1ch!($inst); 354 impl_general_1ch!($inst);
@@ -353,7 +359,7 @@ foreach_interrupt! {
353 }; 359 };
354 360
355 ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => { 361 ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => {
356 impl_core_timer!($inst, TimerBits::Bits16); 362 impl_core_timer!($inst, u16);
357 impl BasicNoCr2Instance for crate::peripherals::$inst {} 363 impl BasicNoCr2Instance for crate::peripherals::$inst {}
358 impl BasicInstance for crate::peripherals::$inst {} 364 impl BasicInstance for crate::peripherals::$inst {}
359 impl_general_1ch!($inst); 365 impl_general_1ch!($inst);
@@ -366,7 +372,7 @@ foreach_interrupt! {
366 }; 372 };
367 373
368 ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { 374 ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => {
369 impl_core_timer!($inst, TimerBits::Bits16); 375 impl_core_timer!($inst, u16);
370 impl BasicNoCr2Instance for crate::peripherals::$inst {} 376 impl BasicNoCr2Instance for crate::peripherals::$inst {}
371 impl BasicInstance for crate::peripherals::$inst {} 377 impl BasicInstance for crate::peripherals::$inst {}
372 impl_general_1ch!($inst); 378 impl_general_1ch!($inst);
@@ -379,7 +385,7 @@ foreach_interrupt! {
379 }; 385 };
380 386
381 ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { 387 ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => {
382 impl_core_timer!($inst, TimerBits::Bits16); 388 impl_core_timer!($inst, u16);
383 impl BasicNoCr2Instance for crate::peripherals::$inst {} 389 impl BasicNoCr2Instance for crate::peripherals::$inst {}
384 impl BasicInstance for crate::peripherals::$inst {} 390 impl BasicInstance for crate::peripherals::$inst {}
385 impl_general_1ch!($inst); 391 impl_general_1ch!($inst);
@@ -400,7 +406,7 @@ pub struct UpdateInterruptHandler<T: CoreInstance> {
400impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { 406impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> {
401 unsafe fn on_interrupt() { 407 unsafe fn on_interrupt() {
402 #[cfg(feature = "low-power")] 408 #[cfg(feature = "low-power")]
403 crate::low_power::Executor::on_wakeup_irq(); 409 crate::low_power::Executor::on_wakeup_irq_or_event();
404 410
405 let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); 411 let regs = crate::pac::timer::TimCore::from_ptr(T::regs());
406 412
@@ -430,7 +436,7 @@ impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompare
430{ 436{
431 unsafe fn on_interrupt() { 437 unsafe fn on_interrupt() {
432 #[cfg(feature = "low-power")] 438 #[cfg(feature = "low-power")]
433 crate::low_power::Executor::on_wakeup_irq(); 439 crate::low_power::Executor::on_wakeup_irq_or_event();
434 440
435 let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); 441 let regs = crate::pac::timer::TimGp16::from_ptr(T::regs());
436 442
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs
index fe8681356..989e1d630 100644
--- a/embassy-stm32/src/timer/one_pulse.rs
+++ b/embassy-stm32/src/timer/one_pulse.rs
@@ -199,7 +199,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
199 fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) { 199 fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) {
200 self.inner.set_counting_mode(counting_mode); 200 self.inner.set_counting_mode(counting_mode);
201 self.inner.set_tick_freq(freq); 201 self.inner.set_tick_freq(freq);
202 self.inner.set_max_compare_value(pulse_end); 202 self.inner.set_max_compare_value(unwrap!(pulse_end.try_into()));
203 self.inner.regs_core().cr1().modify(|r| r.set_opm(true)); 203 self.inner.regs_core().cr1().modify(|r| r.set_opm(true));
204 // Required for advanced timers, see GeneralInstance4Channel for details 204 // Required for advanced timers, see GeneralInstance4Channel for details
205 self.inner.enable_outputs(); 205 self.inner.enable_outputs();
@@ -211,14 +211,14 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
211 211
212 /// Get the end of the pulse in ticks from the trigger. 212 /// Get the end of the pulse in ticks from the trigger.
213 pub fn pulse_end(&self) -> u32 { 213 pub fn pulse_end(&self) -> u32 {
214 let max = self.inner.get_max_compare_value(); 214 let max: u32 = self.inner.get_max_compare_value().into();
215 assert!(max < u32::MAX); 215 assert!(max < u32::MAX);
216 max + 1 216 max + 1
217 } 217 }
218 218
219 /// Set the end of the pulse in ticks from the trigger. 219 /// Set the end of the pulse in ticks from the trigger.
220 pub fn set_pulse_end(&mut self, ticks: u32) { 220 pub fn set_pulse_end(&mut self, ticks: u32) {
221 self.inner.set_max_compare_value(ticks) 221 self.inner.set_max_compare_value(unwrap!(ticks.try_into()))
222 } 222 }
223 223
224 /// Reset the timer on each trigger 224 /// Reset the timer on each trigger
@@ -327,7 +327,7 @@ pub struct OnePulseChannel<'d, T: GeneralInstance4Channel> {
327impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { 327impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> {
328 /// Get the end of the pulse in ticks from the trigger. 328 /// Get the end of the pulse in ticks from the trigger.
329 pub fn pulse_end(&self) -> u32 { 329 pub fn pulse_end(&self) -> u32 {
330 let max = self.inner.get_max_compare_value(); 330 let max: u32 = self.inner.get_max_compare_value().into();
331 assert!(max < u32::MAX); 331 assert!(max < u32::MAX);
332 max + 1 332 max + 1
333 } 333 }
@@ -339,13 +339,13 @@ impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> {
339 339
340 /// Get the start of the pulse in ticks from the trigger. 340 /// Get the start of the pulse in ticks from the trigger.
341 pub fn pulse_delay(&mut self) -> u32 { 341 pub fn pulse_delay(&mut self) -> u32 {
342 self.inner.get_compare_value(self.channel) 342 self.inner.get_compare_value(self.channel).into()
343 } 343 }
344 344
345 /// Set the start of the pulse in ticks from the trigger. 345 /// Set the start of the pulse in ticks from the trigger.
346 pub fn set_pulse_delay(&mut self, delay: u32) { 346 pub fn set_pulse_delay(&mut self, delay: u32) {
347 assert!(delay <= self.pulse_end()); 347 assert!(delay <= self.pulse_end());
348 self.inner.set_compare_value(self.channel, delay); 348 self.inner.set_compare_value(self.channel, unwrap!(delay.try_into()));
349 } 349 }
350 350
351 /// Set the pulse width in ticks. 351 /// Set the pulse width in ticks.
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs
index 057ab011a..f2f00927d 100644
--- a/embassy-stm32/src/timer/pwm_input.rs
+++ b/embassy-stm32/src/timer/pwm_input.rs
@@ -91,16 +91,18 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
91 91
92 /// Get the period tick count 92 /// Get the period tick count
93 pub fn get_period_ticks(&self) -> u32 { 93 pub fn get_period_ticks(&self) -> u32 {
94 self.inner.get_capture_value(self.channel) 94 self.inner.get_capture_value(self.channel).into()
95 } 95 }
96 96
97 /// Get the pulse width tick count 97 /// Get the pulse width tick count
98 pub fn get_width_ticks(&self) -> u32 { 98 pub fn get_width_ticks(&self) -> u32 {
99 self.inner.get_capture_value(match self.channel { 99 self.inner
100 Channel::Ch1 => Channel::Ch2, 100 .get_capture_value(match self.channel {
101 Channel::Ch2 => Channel::Ch1, 101 Channel::Ch1 => Channel::Ch2,
102 _ => panic!("Invalid channel for PWM input"), 102 Channel::Ch2 => Channel::Ch1,
103 }) 103 _ => panic!("Invalid channel for PWM input"),
104 })
105 .into()
104 } 106 }
105 107
106 /// Get the duty cycle in 100% 108 /// Get the duty cycle in 100%
diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs
index e8f97bf59..fbb6b19ea 100644
--- a/embassy-stm32/src/timer/ringbuffered.rs
+++ b/embassy-stm32/src/timer/ringbuffered.rs
@@ -7,6 +7,7 @@ use super::low_level::Timer;
7use super::{Channel, GeneralInstance4Channel}; 7use super::{Channel, GeneralInstance4Channel};
8use crate::dma::WritableRingBuffer; 8use crate::dma::WritableRingBuffer;
9use crate::dma::ringbuffer::Error; 9use crate::dma::ringbuffer::Error;
10use crate::dma::word::Word;
10 11
11/// A PWM channel that uses a DMA ring buffer for continuous waveform generation. 12/// A PWM channel that uses a DMA ring buffer for continuous waveform generation.
12/// 13///
@@ -23,17 +24,17 @@ use crate::dma::ringbuffer::Error;
23/// channel.start(); // Start DMA transfer 24/// channel.start(); // Start DMA transfer
24/// channel.write(&[100, 200, 300]).ok(); // Update duty cycles 25/// channel.write(&[100, 200, 300]).ok(); // Update duty cycles
25/// ``` 26/// ```
26pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { 27pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel, W: Word + Into<T::Word>> {
27 timer: ManuallyDrop<Timer<'d, T>>, 28 timer: ManuallyDrop<Timer<'d, T>>,
28 ring_buf: WritableRingBuffer<'d, u16>, 29 ring_buf: WritableRingBuffer<'d, W>,
29 channel: Channel, 30 channel: Channel,
30} 31}
31 32
32impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { 33impl<'d, T: GeneralInstance4Channel, W: Word + Into<T::Word>> RingBufferedPwmChannel<'d, T, W> {
33 pub(crate) fn new( 34 pub(crate) fn new(
34 timer: ManuallyDrop<Timer<'d, T>>, 35 timer: ManuallyDrop<Timer<'d, T>>,
35 channel: Channel, 36 channel: Channel,
36 ring_buf: WritableRingBuffer<'d, u16>, 37 ring_buf: WritableRingBuffer<'d, W>,
37 ) -> Self { 38 ) -> Self {
38 Self { 39 Self {
39 timer, 40 timer,
@@ -55,18 +56,18 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> {
55 } 56 }
56 57
57 /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. 58 /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer.
58 pub fn write_immediate(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { 59 pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {
59 self.ring_buf.write_immediate(buf) 60 self.ring_buf.write_immediate(buf)
60 } 61 }
61 62
62 /// Write elements from the ring buffer 63 /// Write elements from the ring buffer
63 /// Return a tuple of the length written and the length remaining in the buffer 64 /// Return a tuple of the length written and the length remaining in the buffer
64 pub fn write(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { 65 pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {
65 self.ring_buf.write(buf) 66 self.ring_buf.write(buf)
66 } 67 }
67 68
68 /// Write an exact number of elements to the ringbuffer. 69 /// Write an exact number of elements to the ringbuffer.
69 pub async fn write_exact(&mut self, buffer: &[u16]) -> Result<usize, Error> { 70 pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, Error> {
70 self.ring_buf.write_exact(buffer).await 71 self.ring_buf.write_exact(buffer).await
71 } 72 }
72 73
@@ -140,7 +141,7 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> {
140 /// 141 ///
141 /// This value depends on the configured frequency and the timer's clock rate from RCC. 142 /// This value depends on the configured frequency and the timer's clock rate from RCC.
142 pub fn max_duty_cycle(&self) -> u16 { 143 pub fn max_duty_cycle(&self) -> u16 {
143 let max = self.timer.get_max_compare_value(); 144 let max: u32 = self.timer.get_max_compare_value().into();
144 assert!(max < u16::MAX as u32); 145 assert!(max < u16::MAX as u32);
145 max as u16 + 1 146 max as u16 + 1
146 } 147 }
@@ -155,15 +156,3 @@ impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> {
155 self.timer.set_output_compare_mode(self.channel, mode); 156 self.timer.set_output_compare_mode(self.channel, mode);
156 } 157 }
157} 158}
158
159/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
160pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> {
161 /// Channel 1
162 pub ch1: RingBufferedPwmChannel<'d, T>,
163 /// Channel 2
164 pub ch2: RingBufferedPwmChannel<'d, T>,
165 /// Channel 3
166 pub ch3: RingBufferedPwmChannel<'d, T>,
167 /// Channel 4
168 pub ch4: RingBufferedPwmChannel<'d, T>,
169}
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index 484e9fd81..9a5f0fd1d 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -7,9 +7,11 @@ use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
7use super::ringbuffered::RingBufferedPwmChannel; 7use super::ringbuffered::RingBufferedPwmChannel;
8use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; 8use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin};
9use crate::Peri; 9use crate::Peri;
10use crate::dma::word::Word;
10#[cfg(gpio_v2)] 11#[cfg(gpio_v2)]
11use crate::gpio::Pull; 12use crate::gpio::Pull;
12use crate::gpio::{AfType, AnyPin, OutputType, Speed}; 13use crate::gpio::{AfType, AnyPin, OutputType, Speed};
14use crate::pac::timer::vals::Ccds;
13use crate::time::Hertz; 15use crate::time::Hertz;
14 16
15/// PWM pin wrapper. 17/// PWM pin wrapper.
@@ -98,18 +100,16 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
98 /// Get max duty value. 100 /// Get max duty value.
99 /// 101 ///
100 /// This value depends on the configured frequency and the timer's clock rate from RCC. 102 /// This value depends on the configured frequency and the timer's clock rate from RCC.
101 pub fn max_duty_cycle(&self) -> u16 { 103 pub fn max_duty_cycle(&self) -> u32 {
102 let max = self.timer.get_max_compare_value(); 104 self.timer.get_max_compare_value().into() + 1
103 assert!(max < u16::MAX as u32);
104 max as u16 + 1
105 } 105 }
106 106
107 /// Set the duty for a given channel. 107 /// Set the duty for a given channel.
108 /// 108 ///
109 /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. 109 /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.
110 pub fn set_duty_cycle(&mut self, duty: u16) { 110 pub fn set_duty_cycle(&mut self, duty: u32) {
111 assert!(duty <= (*self).max_duty_cycle()); 111 assert!(duty <= (*self).max_duty_cycle());
112 self.timer.set_compare_value(self.channel, duty.into()) 112 self.timer.set_compare_value(self.channel, unwrap!(duty.try_into()))
113 } 113 }
114 114
115 /// Set the duty cycle to 0%, or always inactive. 115 /// Set the duty cycle to 0%, or always inactive.
@@ -126,21 +126,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
126 /// 126 ///
127 /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, 127 /// The caller is responsible for ensuring that `num` is less than or equal to `denom`,
128 /// and that `denom` is not zero. 128 /// and that `denom` is not zero.
129 pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { 129 pub fn set_duty_cycle_fraction(&mut self, num: u32, denom: u32) {
130 assert!(denom != 0); 130 assert!(denom != 0);
131 assert!(num <= denom); 131 assert!(num <= denom);
132 let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); 132 let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom);
133 133
134 // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) 134 // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16)
135 #[allow(clippy::cast_possible_truncation)] 135 #[allow(clippy::cast_possible_truncation)]
136 self.set_duty_cycle(duty as u16); 136 self.set_duty_cycle(unwrap!(duty.try_into()));
137 } 137 }
138 138
139 /// Set the duty cycle to `percent / 100` 139 /// Set the duty cycle to `percent / 100`
140 /// 140 ///
141 /// The caller is responsible for ensuring that `percent` is less than or equal to 100. 141 /// The caller is responsible for ensuring that `percent` is less than or equal to 100.
142 pub fn set_duty_cycle_percent(&mut self, percent: u8) { 142 pub fn set_duty_cycle_percent(&mut self, percent: u8) {
143 self.set_duty_cycle_fraction(u16::from(percent), 100) 143 self.set_duty_cycle_fraction(percent as u32, 100)
144 } 144 }
145 145
146 /// Get the duty for a given channel. 146 /// Get the duty for a given channel.
@@ -171,13 +171,14 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
171 /// 171 ///
172 /// # Panics 172 /// # Panics
173 /// Panics if `dma_buf` is empty or longer than 65535 elements. 173 /// Panics if `dma_buf` is empty or longer than 65535 elements.
174 pub fn into_ring_buffered_channel( 174 pub fn into_ring_buffered_channel<W: Word + Into<T::Word>>(
175 mut self, 175 mut self,
176 tx_dma: Peri<'d, impl super::UpDma<T>>, 176 tx_dma: Peri<'d, impl super::UpDma<T>>,
177 dma_buf: &'d mut [u16], 177 dma_buf: &'d mut [W],
178 ) -> RingBufferedPwmChannel<'d, T> { 178 ) -> RingBufferedPwmChannel<'d, T, W> {
179 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); 179 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
180 180
181 self.timer.clamp_compare_value::<W>(self.channel);
181 self.timer.enable_update_dma(true); 182 self.timer.enable_update_dma(true);
182 183
183 RingBufferedPwmChannel::new( 184 RingBufferedPwmChannel::new(
@@ -332,10 +333,27 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
332 /// Get max duty value. 333 /// Get max duty value.
333 /// 334 ///
334 /// This value depends on the configured frequency and the timer's clock rate from RCC. 335 /// This value depends on the configured frequency and the timer's clock rate from RCC.
335 pub fn max_duty_cycle(&self) -> u16 { 336 pub fn max_duty_cycle(&self) -> u32 {
336 let max = self.inner.get_max_compare_value(); 337 self.inner.get_max_compare_value().into() + 1
337 assert!(max < u16::MAX as u32); 338 }
338 max as u16 + 1 339
340 /// Generate a sequence of PWM waveform
341 ///
342 /// Note:
343 /// The DMA channel provided does not need to correspond to the requested channel.
344 pub async fn waveform<C: TimerChannel, W: Word + Into<T::Word>>(
345 &mut self,
346 dma: Peri<'_, impl super::Dma<T, C>>,
347 channel: Channel,
348 duty: &[W],
349 ) {
350 self.inner.enable_channel(channel, true);
351 self.inner.enable_channel(C::CHANNEL, true);
352 self.inner.clamp_compare_value::<W>(channel);
353 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE);
354 self.inner.set_cc_dma_enable_state(C::CHANNEL, true);
355 self.inner.setup_channel_update_dma(dma, channel, duty).await;
356 self.inner.set_cc_dma_enable_state(C::CHANNEL, false);
339 } 357 }
340 358
341 /// Generate a sequence of PWM waveform 359 /// Generate a sequence of PWM waveform
@@ -344,8 +362,14 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
344 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. 362 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
345 /// Also be aware that embassy timers use one of timers internally. It is possible to 363 /// Also be aware that embassy timers use one of timers internally. It is possible to
346 /// switch this timer by using `time-driver-timX` feature. 364 /// switch this timer by using `time-driver-timX` feature.
347 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 365 pub async fn waveform_up<W: Word + Into<T::Word>>(
366 &mut self,
367 dma: Peri<'_, impl super::UpDma<T>>,
368 channel: Channel,
369 duty: &[W],
370 ) {
348 self.inner.enable_channel(channel, true); 371 self.inner.enable_channel(channel, true);
372 self.inner.clamp_compare_value::<W>(channel);
349 self.inner.enable_update_dma(true); 373 self.inner.enable_update_dma(true);
350 self.inner.setup_update_dma(dma, channel, duty).await; 374 self.inner.setup_update_dma(dma, channel, duty).await;
351 self.inner.enable_update_dma(false); 375 self.inner.enable_update_dma(false);
@@ -380,13 +404,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
380 /// Also be aware that embassy timers use one of timers internally. It is possible to 404 /// Also be aware that embassy timers use one of timers internally. It is possible to
381 /// switch this timer by using `time-driver-timX` feature. 405 /// switch this timer by using `time-driver-timX` feature.
382 /// 406 ///
383 pub async fn waveform_up_multi_channel( 407 pub async fn waveform_up_multi_channel<W: Word + Into<T::Word>>(
384 &mut self, 408 &mut self,
385 dma: Peri<'_, impl super::UpDma<T>>, 409 dma: Peri<'_, impl super::UpDma<T>>,
386 starting_channel: Channel, 410 starting_channel: Channel,
387 ending_channel: Channel, 411 ending_channel: Channel,
388 duty: &[u16], 412 duty: &[W],
389 ) { 413 ) {
414 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
415 .iter()
416 .filter(|ch| ch.index() >= starting_channel.index())
417 .filter(|ch| ch.index() <= ending_channel.index())
418 .for_each(|ch| {
419 self.inner.enable_channel(*ch, true);
420 self.inner.clamp_compare_value::<W>(*ch);
421 });
390 self.inner.enable_update_dma(true); 422 self.inner.enable_update_dma(true);
391 self.inner 423 self.inner
392 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) 424 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
@@ -401,11 +433,11 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePw
401 433
402impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { 434impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> {
403 fn max_duty_cycle(&self) -> u16 { 435 fn max_duty_cycle(&self) -> u16 {
404 self.max_duty_cycle() 436 unwrap!(self.max_duty_cycle().try_into())
405 } 437 }
406 438
407 fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { 439 fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
408 self.set_duty_cycle(duty); 440 self.set_duty_cycle(duty.into());
409 Ok(()) 441 Ok(())
410 } 442 }
411 443
@@ -420,7 +452,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for Simpl
420 } 452 }
421 453
422 fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { 454 fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> {
423 self.set_duty_cycle_fraction(num, denom); 455 self.set_duty_cycle_fraction(num.into(), denom.into());
424 Ok(()) 456 Ok(())
425 } 457 }
426 458
@@ -448,16 +480,16 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {
448 } 480 }
449 481
450 fn get_duty(&self, channel: Self::Channel) -> Self::Duty { 482 fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
451 self.inner.get_compare_value(channel) 483 self.inner.get_compare_value(channel).into()
452 } 484 }
453 485
454 fn get_max_duty(&self) -> Self::Duty { 486 fn get_max_duty(&self) -> Self::Duty {
455 self.inner.get_max_compare_value() + 1 487 self.inner.get_max_compare_value().into() + 1
456 } 488 }
457 489
458 fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { 490 fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
459 assert!(duty <= self.max_duty_cycle() as u32); 491 assert!(duty <= self.max_duty_cycle() as u32);
460 self.inner.set_compare_value(channel, duty) 492 self.inner.set_compare_value(channel, unwrap!(duty.try_into()))
461 } 493 }
462 494
463 fn set_period<P>(&mut self, period: P) 495 fn set_period<P>(&mut self, period: P)
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index 8047d6005..d2c361c61 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -491,7 +491,7 @@ impl<'d> UartTx<'d, Async> {
491 491
492 /// Initiate an asynchronous UART write 492 /// Initiate an asynchronous UART write
493 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { 493 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
494 let _ = self.info.rcc.block_stop(); 494 let _scoped_block_stop = self.info.rcc.block_stop();
495 495
496 let r = self.info.regs; 496 let r = self.info.regs;
497 497
@@ -510,7 +510,7 @@ impl<'d> UartTx<'d, Async> {
510 510
511 /// Wait until transmission complete 511 /// Wait until transmission complete
512 pub async fn flush(&mut self) -> Result<(), Error> { 512 pub async fn flush(&mut self) -> Result<(), Error> {
513 let _ = self.info.rcc.block_stop(); 513 let _scoped_block_stop = self.info.rcc.block_stop();
514 514
515 flush(&self.info, &self.state).await 515 flush(&self.info, &self.state).await
516 } 516 }
@@ -730,7 +730,7 @@ impl<'d> UartRx<'d, Async> {
730 730
731 /// Initiate an asynchronous UART read 731 /// Initiate an asynchronous UART read
732 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { 732 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
733 let _ = self.info.rcc.block_stop(); 733 let _scoped_block_stop = self.info.rcc.block_stop();
734 734
735 self.inner_read(buffer, false).await?; 735 self.inner_read(buffer, false).await?;
736 736
@@ -739,7 +739,7 @@ impl<'d> UartRx<'d, Async> {
739 739
740 /// Initiate an asynchronous read with idle line detection enabled 740 /// Initiate an asynchronous read with idle line detection enabled
741 pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { 741 pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
742 let _ = self.info.rcc.block_stop(); 742 let _scoped_block_stop = self.info.rcc.block_stop();
743 743
744 self.inner_read(buffer, true).await 744 self.inner_read(buffer, true).await
745 } 745 }
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs
index 2f5967c63..e6d1aed84 100644
--- a/embassy-time/src/timer.rs
+++ b/embassy-time/src/timer.rs
@@ -253,7 +253,7 @@ impl Ticker {
253 } 253 }
254 254
255 /// Reset the ticker at the deadline. 255 /// Reset the ticker at the deadline.
256 /// If the deadline is in the past, the ticker will fire instantly. 256 /// If the deadline is in the past, the ticker will fire before the next scheduled tick.
257 pub fn reset_at(&mut self, deadline: Instant) { 257 pub fn reset_at(&mut self, deadline: Instant) {
258 self.expires_at = deadline + self.duration; 258 self.expires_at = deadline + self.duration;
259 } 259 }
diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md
index 3dd71ffbc..431f4ddde 100644
--- a/embassy-usb/CHANGELOG.md
+++ b/embassy-usb/CHANGELOG.md
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10 10
11- Add support for USB HID Boot Protocol Mode 11- Add support for USB HID Boot Protocol Mode
12- Bump usbd-hid from 0.8.1 to 0.9.0 12- Bump usbd-hid from 0.8.1 to 0.9.0
13- Fix a bug where CDC ACM BufferedReceiver repeats data when its future is dropped
14- Expose `dtr()` and `rts()` on `cdc_acm::ControlChanged`
13 15
14## 0.5.1 - 2025-08-26 16## 0.5.1 - 2025-08-26
15 17
diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs
index 388e21fbd..ab2311f4e 100644
--- a/embassy-usb/src/class/cdc_acm.rs
+++ b/embassy-usb/src/class/cdc_acm.rs
@@ -366,6 +366,16 @@ impl<'d> ControlChanged<'d> {
366 pub async fn control_changed(&self) { 366 pub async fn control_changed(&self) {
367 self.control.changed().await; 367 self.control.changed().await;
368 } 368 }
369
370 /// Gets the DTR (data terminal ready) state
371 pub fn dtr(&self) -> bool {
372 self.control.dtr.load(Ordering::Relaxed)
373 }
374
375 /// Gets the RTS (request to send) state
376 pub fn rts(&self) -> bool {
377 self.control.rts.load(Ordering::Relaxed)
378 }
369} 379}
370 380
371/// CDC ACM class packet sender. 381/// CDC ACM class packet sender.
@@ -545,9 +555,12 @@ impl<'d, D: Driver<'d>> embedded_io_async::Read for BufferedReceiver<'d, D> {
545 return self.receiver.read_packet(buf).await; 555 return self.receiver.read_packet(buf).await;
546 } 556 }
547 557
548 // Otherwise read a packet into the internal buffer, and return some of it to the caller 558 // Otherwise read a packet into the internal buffer, and return some of it to the caller.
549 self.start = 0; 559 //
560 // It's important that `start` and `end` be updated in this order so they're left in a
561 // consistent state if the `read` future is dropped mid-execution, e.g. from a timeout.
550 self.end = self.receiver.read_packet(&mut self.buffer).await?; 562 self.end = self.receiver.read_packet(&mut self.buffer).await?;
563 self.start = 0;
551 return Ok(self.read_from_buffer(buf)); 564 return Ok(self.read_from_buffer(buf));
552 } 565 }
553} 566}
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml
index 55053bc33..79286e295 100644
--- a/examples/boot/application/nrf/Cargo.toml
+++ b/examples/boot/application/nrf/Cargo.toml
@@ -9,7 +9,7 @@ publish = false
9embassy-sync = { version = "0.7.2", path = "../../../../embassy-sync" } 9embassy-sync = { version = "0.7.2", path = "../../../../embassy-sync" }
10embassy-executor = { version = "0.9.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } 10embassy-executor = { version = "0.9.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] }
11embassy-time = { version = "0.5.0", path = "../../../../embassy-time", features = [] } 11embassy-time = { version = "0.5.0", path = "../../../../embassy-time", features = [] }
12embassy-nrf = { version = "0.8.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } 12embassy-nrf = { version = "0.8.0", path = "../../../../embassy-nrf", features = ["gpiote", ] }
13embassy-boot = { version = "0.6.1", path = "../../../../embassy-boot", features = [] } 13embassy-boot = { version = "0.6.1", path = "../../../../embassy-boot", features = [] }
14embassy-boot-nrf = { version = "0.9.0", path = "../../../../embassy-boot-nrf", features = [] } 14embassy-boot-nrf = { version = "0.9.0", path = "../../../../embassy-boot-nrf", features = [] }
15embassy-embedded-hal = { version = "0.5.0", path = "../../../../embassy-embedded-hal" } 15embassy-embedded-hal = { version = "0.5.0", path = "../../../../embassy-embedded-hal" }
@@ -33,12 +33,14 @@ defmt = [
33 "embassy-boot-nrf/defmt", 33 "embassy-boot-nrf/defmt",
34 "embassy-sync/defmt", 34 "embassy-sync/defmt",
35] 35]
36nrf54 = ["embassy-nrf/time-driver-grtc"]
36 37
37[package.metadata.embassy] 38[package.metadata.embassy]
38build = [ 39build = [
39 { target = "thumbv7em-none-eabi", features = ["embassy-nrf/nrf52840", "skip-include"], artifact-dir = "out/examples/boot/nrf52840" }, 40 { target = "thumbv7em-none-eabi", features = ["embassy-nrf/nrf52840", "embassy-nrf/time-driver-rtc1", "skip-include"], artifact-dir = "out/examples/boot/nrf52840" },
40 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9160-ns", "skip-include"], artifact-dir = "out/examples/boot/nrf9160" }, 41 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9160-ns", "embassy-nrf/time-driver-rtc1", "skip-include"], artifact-dir = "out/examples/boot/nrf9160" },
41 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9120-ns", "skip-include"], artifact-dir = "out/examples/boot/nrf9120" }, 42 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9120-ns", "embassy-nrf/time-driver-rtc1", "skip-include"], artifact-dir = "out/examples/boot/nrf9120" },
42 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9151-ns", "skip-include"], artifact-dir = "out/examples/boot/nrf9151" }, 43 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9151-ns", "embassy-nrf/time-driver-rtc1", "skip-include"], artifact-dir = "out/examples/boot/nrf9151" },
43 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9161-ns", "skip-include"], artifact-dir = "out/examples/boot/nrf9161" } 44 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9161-ns", "embassy-nrf/time-driver-rtc1", "skip-include"], artifact-dir = "out/examples/boot/nrf9161" },
45 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf54l15-app-s", "nrf54", "skip-include"], artifact-dir = "out/examples/boot/nrf54l15" }
44] 46]
diff --git a/examples/boot/application/nrf/README.md b/examples/boot/application/nrf/README.md
index 9d6d20336..c92ccb358 100644
--- a/examples/boot/application/nrf/README.md
+++ b/examples/boot/application/nrf/README.md
@@ -22,7 +22,7 @@ cp memory-bl.x ../../bootloader/nrf/memory.x
22# Flash bootloader 22# Flash bootloader
23cargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --release --chip nRF52840_xxAA 23cargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --release --chip nRF52840_xxAA
24# Build 'b' 24# Build 'b'
25cargo build --release --bin b --features embassy-nrf/nrf52840 25cargo build --release --bin b --features embassy-nrf/nrf52840,time-driver-rtc1
26# Generate binary for 'b' 26# Generate binary for 'b'
27cargo objcopy --release --bin b --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi -- -O binary b.bin 27cargo objcopy --release --bin b --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi -- -O binary b.bin
28``` 28```
diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs
index 2c1d1a7bb..035ffe214 100644
--- a/examples/boot/application/nrf/src/bin/a.rs
+++ b/examples/boot/application/nrf/src/bin/a.rs
@@ -23,10 +23,21 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
23async fn main(_spawner: Spawner) { 23async fn main(_spawner: Spawner) {
24 let p = embassy_nrf::init(Default::default()); 24 let p = embassy_nrf::init(Default::default());
25 25
26 #[cfg(not(feature = "nrf54"))]
26 let mut button = Input::new(p.P0_11, Pull::Up); 27 let mut button = Input::new(p.P0_11, Pull::Up);
28 #[cfg(not(feature = "nrf54"))]
27 let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); 29 let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard);
30 #[cfg(not(feature = "nrf54"))]
28 let mut led_reverted = Output::new(p.P0_14, Level::High, OutputDrive::Standard); 31 let mut led_reverted = Output::new(p.P0_14, Level::High, OutputDrive::Standard);
29 32
33 // nRF54 DK
34 #[cfg(feature = "nrf54")]
35 let mut button = Input::new(p.P1_13, Pull::Up);
36 #[cfg(feature = "nrf54")]
37 let mut led = Output::new(p.P1_14, Level::Low, OutputDrive::Standard);
38 #[cfg(feature = "nrf54")]
39 let mut led_reverted = Output::new(p.P2_09, Level::High, OutputDrive::Standard);
40
30 //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); 41 //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard);
31 //let mut button = Input::new(p.P1_02, Pull::Up); 42 //let mut button = Input::new(p.P1_02, Pull::Up);
32 43
@@ -40,8 +51,12 @@ async fn main(_spawner: Spawner) {
40 // the watchdog will cause the device to reset as per its configured timeout in the bootloader. 51 // the watchdog will cause the device to reset as per its configured timeout in the bootloader.
41 // This helps is avoid a situation where new firmware might be bad and block our executor. 52 // This helps is avoid a situation where new firmware might be bad and block our executor.
42 // If firmware is bad in this way then the bootloader will revert to any previous version. 53 // If firmware is bad in this way then the bootloader will revert to any previous version.
43 let wdt_config = wdt::Config::try_new(&p.WDT).unwrap(); 54 #[cfg(feature = "nrf54")]
44 let (_wdt, [_wdt_handle]) = match Watchdog::try_new(p.WDT, wdt_config) { 55 let wdt = p.WDT0;
56 #[cfg(not(feature = "nrf54"))]
57 let wdt = p.WDT;
58 let wdt_config = wdt::Config::try_new(&wdt).unwrap();
59 let (_wdt, [_wdt_handle]) = match Watchdog::try_new(wdt, wdt_config) {
45 Ok(x) => x, 60 Ok(x) => x,
46 Err(_) => { 61 Err(_) => {
47 // Watchdog already active with the wrong number of handles, waiting for it to timeout... 62 // Watchdog already active with the wrong number of handles, waiting for it to timeout...
@@ -51,11 +66,15 @@ async fn main(_spawner: Spawner) {
51 } 66 }
52 }; 67 };
53 68
69 // RRAMC for nRF54
70 #[cfg(feature = "nrf54")]
71 let nvmc = Nvmc::new(p.RRAMC);
72 #[cfg(not(feature = "nrf54"))]
54 let nvmc = Nvmc::new(p.NVMC); 73 let nvmc = Nvmc::new(p.NVMC);
55 let nvmc = Mutex::new(BlockingAsync::new(nvmc)); 74 let nvmc = Mutex::new(BlockingAsync::new(nvmc));
56 75
57 let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc); 76 let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc);
58 let mut magic = [0; 4]; 77 let mut magic = [0; 16];
59 let mut updater = FirmwareUpdater::new(config, &mut magic); 78 let mut updater = FirmwareUpdater::new(config, &mut magic);
60 let state = updater.get_state().await.unwrap(); 79 let state = updater.get_state().await.unwrap();
61 if state == State::Revert { 80 if state == State::Revert {
diff --git a/examples/boot/application/nrf/src/bin/b.rs b/examples/boot/application/nrf/src/bin/b.rs
index de97b6a22..6718df5a1 100644
--- a/examples/boot/application/nrf/src/bin/b.rs
+++ b/examples/boot/application/nrf/src/bin/b.rs
@@ -10,11 +10,15 @@ use panic_reset as _;
10#[embassy_executor::main] 10#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 11async fn main(_spawner: Spawner) {
12 let p = embassy_nrf::init(Default::default()); 12 let p = embassy_nrf::init(Default::default());
13 #[cfg(not(feature = "nrf54"))]
13 let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); 14 let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard);
14 // let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); 15 // let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard);
15 16
16 // nRF91 DK 17 // nRF91 DK
17 // let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard); 18 // let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard);
19 // nrf54l15 dk
20 #[cfg(feature = "nrf54")]
21 let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard);
18 22
19 loop { 23 loop {
20 led.set_high(); 24 led.set_high();
diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml
index 1fea2b7d7..59fc6e4ed 100644
--- a/examples/boot/bootloader/nrf/Cargo.toml
+++ b/examples/boot/bootloader/nrf/Cargo.toml
@@ -27,6 +27,7 @@ defmt = [
27softdevice = [ 27softdevice = [
28 "embassy-boot-nrf/softdevice", 28 "embassy-boot-nrf/softdevice",
29] 29]
30nrf54 = []
30 31
31[profile.dev] 32[profile.dev]
32debug = 2 33debug = 2
@@ -65,5 +66,6 @@ build = [
65 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9160-ns"] }, 66 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9160-ns"] },
66 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9120-ns"] }, 67 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9120-ns"] },
67 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9151-ns"] }, 68 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9151-ns"] },
68 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9161-ns"] } 69 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf9161-ns"] },
70 { target = "thumbv8m.main-none-eabihf", features = ["embassy-nrf/nrf54l15-app-s", "nrf54"] }
69] 71]
diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs
index 76c4c1048..9ba57e81b 100644
--- a/examples/boot/bootloader/nrf/src/main.rs
+++ b/examples/boot/bootloader/nrf/src/main.rs
@@ -28,7 +28,10 @@ fn main() -> ! {
28 wdt_config.action_during_sleep = SleepConfig::RUN; 28 wdt_config.action_during_sleep = SleepConfig::RUN;
29 wdt_config.action_during_debug_halt = HaltConfig::PAUSE; 29 wdt_config.action_during_debug_halt = HaltConfig::PAUSE;
30 30
31 #[cfg(not(feature = "nrf54"))]
31 let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config); 32 let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config);
33 #[cfg(feature = "nrf54")]
34 let flash = WatchdogFlash::start(Nvmc::new(p.RRAMC), p.WDT0, wdt_config);
32 let flash = Mutex::new(RefCell::new(flash)); 35 let flash = Mutex::new(RefCell::new(flash));
33 36
34 let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash); 37 let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);
diff --git a/examples/mcxa/.cargo/config.toml b/examples/mcxa/.cargo/config.toml
new file mode 100644
index 000000000..aedc55b06
--- /dev/null
+++ b/examples/mcxa/.cargo/config.toml
@@ -0,0 +1,17 @@
1[target.thumbv8m.main-none-eabihf]
2runner = 'probe-rs run --chip MCXA276 --preverify --verify --protocol swd --speed 12000'
3
4rustflags = [
5 "-C", "linker=flip-link",
6 "-C", "link-arg=-Tlink.x",
7 "-C", "link-arg=-Tdefmt.x",
8 # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
9 # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
10 "-C", "link-arg=--nmagic",
11]
12
13[build]
14target = "thumbv8m.main-none-eabihf" # Cortex-M33
15
16[env]
17DEFMT_LOG = "trace"
diff --git a/examples/mcxa/.gitignore b/examples/mcxa/.gitignore
new file mode 100644
index 000000000..2f7896d1d
--- /dev/null
+++ b/examples/mcxa/.gitignore
@@ -0,0 +1 @@
target/
diff --git a/examples/mcxa/Cargo.toml b/examples/mcxa/Cargo.toml
new file mode 100644
index 000000000..d07cc4272
--- /dev/null
+++ b/examples/mcxa/Cargo.toml
@@ -0,0 +1,34 @@
1[package]
2name = "embassy-mcxa-examples"
3version = "0.1.0"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6publish = false
7
8[dependencies]
9cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
10cortex-m-rt = { version = "0.7", features = ["set-sp", "set-vtor"] }
11crc = "3.4.0"
12critical-section = "1.2.0"
13defmt = "1.0"
14defmt-rtt = "1.0"
15embassy-embedded-hal = "0.5.0"
16embassy-executor = { version = "0.9.0", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"], default-features = false }
17embassy-mcxa = { path = "../../embassy-mcxa", features = ["defmt", "unstable-pac", "time"] }
18embassy-sync = "0.7.2"
19embassy-time = "0.5.0"
20embassy-time-driver = "0.2.1"
21embedded-io-async = "0.6.1"
22heapless = "0.9.2"
23panic-probe = { version = "1.0", features = ["print-defmt"] }
24static_cell = "2.1.1"
25tmp108 = "0.4.0"
26
27[profile.release]
28lto = true # better optimizations
29debug = 2 # enough information for defmt/rtt locations
30
31[package.metadata.embassy]
32build = [
33 { target = "thumbv8m.main-none-eabihf", artifact-dir = "out/examples/mcxa" }
34]
diff --git a/examples/mcxa/build.rs b/examples/mcxa/build.rs
new file mode 100644
index 000000000..f076bba9f
--- /dev/null
+++ b/examples/mcxa/build.rs
@@ -0,0 +1,20 @@
1use std::env;
2use std::fs::File;
3use std::io::Write;
4use std::path::PathBuf;
5
6fn main() {
7 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
8
9 // Generate memory.x - put "FLASH" at start of RAM, RAM after "FLASH"
10 // cortex-m-rt expects FLASH for code, RAM for data/bss/stack
11 // Both are in RAM, but separated to satisfy cortex-m-rt's expectations
12 // MCXA256 has 128KB RAM total
13 File::create(out.join("memory.x"))
14 .unwrap()
15 .write_all(include_bytes!("memory.x"))
16 .unwrap();
17
18 println!("cargo:rustc-link-search={}", out.display());
19 println!("cargo:rerun-if-changed=memory.x");
20}
diff --git a/examples/mcxa/memory.x b/examples/mcxa/memory.x
new file mode 100644
index 000000000..315ced58a
--- /dev/null
+++ b/examples/mcxa/memory.x
@@ -0,0 +1,5 @@
1MEMORY
2{
3 FLASH : ORIGIN = 0x00000000, LENGTH = 1M
4 RAM : ORIGIN = 0x20000000, LENGTH = 128K
5}
diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs
new file mode 100644
index 000000000..5876923a1
--- /dev/null
+++ b/examples/mcxa/src/bin/adc_interrupt.rs
@@ -0,0 +1,73 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use hal::adc::{Adc, InterruptHandler, LpadcConfig, TriggerPriorityPolicy};
6use hal::bind_interrupts;
7use hal::clocks::PoweredClock;
8use hal::clocks::config::Div8;
9use hal::clocks::periph_helpers::{AdcClockSel, Div4};
10use hal::config::Config;
11use hal::pac::adc1::cfg::{Pwrsel, Refsel};
12use hal::pac::adc1::cmdl1::{Adch, Mode};
13use hal::pac::adc1::ctrl::CalAvgs;
14use hal::pac::adc1::tctrl::Tcmd;
15use hal::peripherals::ADC1;
16use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
17
18bind_interrupts!(struct Irqs {
19 ADC1 => InterruptHandler<ADC1>;
20});
21
22#[embassy_executor::main]
23async fn main(_spawner: Spawner) {
24 let mut config = Config::default();
25 config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);
26
27 let p = hal::init(config);
28
29 defmt::info!("ADC interrupt Example");
30
31 let adc_config = LpadcConfig {
32 enable_in_doze_mode: true,
33 conversion_average_mode: CalAvgs::Average128,
34 enable_analog_preliminary: true,
35 power_up_delay: 0x80,
36 reference_voltage_source: Refsel::Option3,
37 power_level_mode: Pwrsel::Lowest,
38 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
39 enable_conv_pause: false,
40 conv_pause_delay: 0,
41 fifo_watermark: 0,
42 power: PoweredClock::NormalEnabledDeepSleepDisabled,
43 source: AdcClockSel::FroLfDiv,
44 div: Div4::no_div(),
45 };
46 let mut adc = Adc::new_async(p.ADC1, p.P1_10, Irqs, adc_config).unwrap();
47
48 adc.do_offset_calibration();
49 adc.do_auto_calibration();
50
51 let mut conv_command_config = adc.get_default_conv_command_config();
52 conv_command_config.channel_number = Adch::SelectCorrespondingChannel8;
53 conv_command_config.conversion_resolution_mode = Mode::Data16Bits;
54 adc.set_conv_command_config(1, &conv_command_config).unwrap();
55
56 let mut conv_trigger_config = adc.get_default_conv_trigger_config();
57 conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1;
58 conv_trigger_config.enable_hardware_trigger = false;
59 adc.set_conv_trigger_config(0, &conv_trigger_config);
60
61 defmt::info!("ADC configuration done...");
62
63 loop {
64 match adc.read().await {
65 Ok(value) => {
66 defmt::info!("*** ADC interrupt TRIGGERED! *** -- value: {}", value);
67 }
68 Err(e) => {
69 defmt::error!("ADC read error: {:?}", e);
70 }
71 }
72 }
73}
diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs
new file mode 100644
index 000000000..d048bb56f
--- /dev/null
+++ b/examples/mcxa/src/bin/adc_polling.rs
@@ -0,0 +1,72 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy};
6use hal::clocks::PoweredClock;
7use hal::clocks::config::Div8;
8use hal::clocks::periph_helpers::{AdcClockSel, Div4};
9use hal::config::Config;
10use hal::pac::adc1::cfg::{Pwrsel, Refsel};
11use hal::pac::adc1::cmdl1::{Adch, Mode};
12use hal::pac::adc1::ctrl::CalAvgs;
13use hal::pac::adc1::tctrl::Tcmd;
14use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
15
16const G_LPADC_RESULT_SHIFT: u32 = 0;
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 let mut config = Config::default();
21 config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);
22
23 let p = hal::init(config);
24
25 defmt::info!("=== ADC polling Example ===");
26
27 let adc_config = LpadcConfig {
28 enable_in_doze_mode: true,
29 conversion_average_mode: CalAvgs::Average128,
30 enable_analog_preliminary: true,
31 power_up_delay: 0x80,
32 reference_voltage_source: Refsel::Option3,
33 power_level_mode: Pwrsel::Lowest,
34 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
35 enable_conv_pause: false,
36 conv_pause_delay: 0,
37 fifo_watermark: 0,
38 power: PoweredClock::NormalEnabledDeepSleepDisabled,
39 source: AdcClockSel::FroLfDiv,
40 div: Div4::no_div(),
41 };
42 let adc = Adc::new_blocking(p.ADC1, p.P1_10, adc_config).unwrap();
43
44 adc.do_offset_calibration();
45 adc.do_auto_calibration();
46
47 let mut conv_command_config = adc.get_default_conv_command_config();
48 conv_command_config.channel_number = Adch::SelectCorrespondingChannel8;
49 conv_command_config.conversion_resolution_mode = Mode::Data16Bits;
50 adc.set_conv_command_config(1, &conv_command_config).unwrap();
51
52 let mut conv_trigger_config = adc.get_default_conv_trigger_config();
53 conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1;
54 conv_trigger_config.enable_hardware_trigger = false;
55 adc.set_conv_trigger_config(0, &conv_trigger_config);
56
57 defmt::info!("=== ADC configuration done... ===");
58
59 loop {
60 adc.do_software_trigger(1);
61 let result = loop {
62 match adc.get_conv_result() {
63 Ok(res) => break res,
64 Err(_) => {
65 // Conversion not ready, continue polling
66 }
67 }
68 };
69 let value = result.conv_value >> G_LPADC_RESULT_SHIFT;
70 defmt::info!("ADC value: {=u16}", value);
71 }
72}
diff --git a/examples/mcxa/src/bin/blinky.rs b/examples/mcxa/src/bin/blinky.rs
new file mode 100644
index 000000000..dd08ec0d9
--- /dev/null
+++ b/examples/mcxa/src/bin/blinky.rs
@@ -0,0 +1,36 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::gpio::{DriveStrength, Level, Output, SlewRate};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 let p = hal::init(hal::config::Config::default());
12
13 defmt::info!("Blink example");
14
15 let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast);
16 let mut green = Output::new(p.P3_19, Level::High, DriveStrength::Normal, SlewRate::Fast);
17 let mut blue = Output::new(p.P3_21, Level::High, DriveStrength::Normal, SlewRate::Fast);
18
19 loop {
20 defmt::info!("Toggle LEDs");
21
22 red.toggle();
23 Timer::after_millis(250).await;
24
25 red.toggle();
26 green.toggle();
27 Timer::after_millis(250).await;
28
29 green.toggle();
30 blue.toggle();
31 Timer::after_millis(250).await;
32 blue.toggle();
33
34 Timer::after_millis(250).await;
35 }
36}
diff --git a/examples/mcxa/src/bin/button.rs b/examples/mcxa/src/bin/button.rs
new file mode 100644
index 000000000..943edbb15
--- /dev/null
+++ b/examples/mcxa/src/bin/button.rs
@@ -0,0 +1,23 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::gpio::{Input, Pull};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 let p = hal::init(hal::config::Config::default());
12
13 defmt::info!("Button example");
14
15 // This button is labeled "WAKEUP" on the FRDM-MCXA276
16 // The board already has a 10K pullup
17 let monitor = Input::new(p.P1_7, Pull::Disabled);
18
19 loop {
20 defmt::info!("Pin level is {:?}", monitor.get_level());
21 Timer::after_millis(1000).await;
22 }
23}
diff --git a/examples/mcxa/src/bin/button_async.rs b/examples/mcxa/src/bin/button_async.rs
new file mode 100644
index 000000000..6cc7b62cd
--- /dev/null
+++ b/examples/mcxa/src/bin/button_async.rs
@@ -0,0 +1,29 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::gpio::{Input, Pull};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 let p = hal::init(hal::config::Config::default());
12
13 defmt::info!("GPIO interrupt example");
14
15 // This button is labeled "WAKEUP" on the FRDM-MCXA276
16 // The board already has a 10K pullup
17 let mut pin = Input::new(p.P1_7, Pull::Disabled);
18
19 let mut press_count = 0u32;
20
21 loop {
22 pin.wait_for_falling_edge().await;
23
24 press_count += 1;
25
26 defmt::info!("Button pressed! Count: {}", press_count);
27 Timer::after_millis(50).await;
28 }
29}
diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs
new file mode 100644
index 000000000..bfd963540
--- /dev/null
+++ b/examples/mcxa/src/bin/clkout.rs
@@ -0,0 +1,69 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4};
6use embassy_mcxa::clocks::PoweredClock;
7use embassy_mcxa::gpio::{DriveStrength, SlewRate};
8use embassy_mcxa::{Level, Output};
9use embassy_time::Timer;
10use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
11
12/// Demonstrate CLKOUT, using Pin P4.2
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let p = hal::init(hal::config::Config::default());
16 let mut pin = p.P4_2;
17 let mut clkout = p.CLKOUT;
18
19 loop {
20 defmt::info!("Set Low...");
21 let mut output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow);
22 Timer::after_millis(500).await;
23
24 defmt::info!("Set High...");
25 output.set_high();
26 Timer::after_millis(400).await;
27
28 defmt::info!("Set Low...");
29 output.set_low();
30 Timer::after_millis(500).await;
31
32 defmt::info!("16k...");
33 // Run Clock Out with the 16K clock
34 let _clock_out = ClockOut::new(
35 clkout.reborrow(),
36 pin.reborrow(),
37 Config {
38 sel: ClockOutSel::Clk16K,
39 div: Div4::no_div(),
40 level: PoweredClock::NormalEnabledDeepSleepDisabled,
41 },
42 )
43 .unwrap();
44
45 Timer::after_millis(3000).await;
46
47 defmt::info!("Set Low...");
48 drop(_clock_out);
49
50 let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow);
51 Timer::after_millis(500).await;
52
53 // Run Clock Out with the 12M clock, divided by 3
54 defmt::info!("4M...");
55 let _clock_out = ClockOut::new(
56 clkout.reborrow(),
57 pin.reborrow(),
58 Config {
59 sel: ClockOutSel::Fro12M,
60 div: const { Div4::from_divisor(3).unwrap() },
61 level: PoweredClock::NormalEnabledDeepSleepDisabled,
62 },
63 )
64 .unwrap();
65
66 // Let it run for 3 seconds...
67 Timer::after_millis(3000).await;
68 }
69}
diff --git a/examples/mcxa/src/bin/crc.rs b/examples/mcxa/src/bin/crc.rs
new file mode 100644
index 000000000..0125e625c
--- /dev/null
+++ b/examples/mcxa/src/bin/crc.rs
@@ -0,0 +1,154 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use hal::config::Config;
6use hal::crc::Crc;
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9const CCITT_FALSE: crc::Algorithm<u16> = crc::Algorithm {
10 width: 16,
11 poly: 0x1021,
12 init: 0xffff,
13 refin: false,
14 refout: false,
15 xorout: 0,
16 check: 0x29b1,
17 residue: 0x0000,
18};
19
20const POSIX: crc::Algorithm<u32> = crc::Algorithm {
21 width: 32,
22 poly: 0x04c1_1db7,
23 init: 0,
24 refin: false,
25 refout: false,
26 xorout: 0xffff_ffff,
27 check: 0x765e_7680,
28 residue: 0x0000,
29};
30
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let config = Config::default();
34 let mut p = hal::init(config);
35
36 defmt::info!("CRC example");
37
38 let buf_u8 = [0x00u8, 0x11, 0x22, 0x33];
39 let buf_u16 = [0x0000u16, 0x1111, 0x2222, 0x3333];
40 let buf_u32 = [0x0000_0000u32, 0x1111_1111, 0x2222_2222, 0x3333_3333];
41
42 // CCITT False
43
44 let sw_crc = crc::Crc::<u16>::new(&CCITT_FALSE);
45 let mut digest = sw_crc.digest();
46 digest.update(&buf_u8);
47 let sw_sum = digest.finalize();
48
49 let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());
50 crc.feed(&buf_u8);
51 let sum = crc.finalize();
52 assert_eq!(sum, sw_sum);
53
54 let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());
55 crc.feed(&buf_u16);
56 let sum = crc.finalize();
57 assert_eq!(sum, 0xa467);
58
59 let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());
60 crc.feed(&buf_u32);
61 let sum = crc.finalize();
62 assert_eq!(sum, 0xe5c7);
63
64 // Maxim
65
66 let sw_crc = crc::Crc::<u16>::new(&crc::CRC_16_MAXIM_DOW);
67 let mut digest = sw_crc.digest();
68 digest.update(&buf_u8);
69 let sw_sum = digest.finalize();
70
71 let mut crc = Crc::new_maxim(p.CRC0.reborrow());
72 crc.feed(&buf_u8);
73 let sum = crc.finalize();
74 assert_eq!(sum, sw_sum);
75
76 let mut crc = Crc::new_maxim(p.CRC0.reborrow());
77 crc.feed(&buf_u16);
78 let sum = crc.finalize();
79 assert_eq!(sum, 0x2afe);
80
81 let mut crc = Crc::new_maxim(p.CRC0.reborrow());
82 crc.feed(&buf_u32);
83 let sum = crc.finalize();
84 assert_eq!(sum, 0x17d7);
85
86 // Kermit
87
88 let sw_crc = crc::Crc::<u16>::new(&crc::CRC_16_KERMIT);
89 let mut digest = sw_crc.digest();
90 digest.update(&buf_u8);
91 let sw_sum = digest.finalize();
92
93 let mut crc = Crc::new_kermit(p.CRC0.reborrow());
94 crc.feed(&buf_u8);
95 let sum = crc.finalize();
96 assert_eq!(sum, sw_sum);
97
98 let mut crc = Crc::new_kermit(p.CRC0.reborrow());
99 crc.feed(&buf_u16);
100 let sum = crc.finalize();
101 assert_eq!(sum, 0x66eb);
102
103 let mut crc = Crc::new_kermit(p.CRC0.reborrow());
104 crc.feed(&buf_u32);
105 let sum = crc.finalize();
106 assert_eq!(sum, 0x75ea);
107
108 // ISO HDLC
109
110 let sw_crc = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
111 let mut digest = sw_crc.digest();
112 digest.update(&buf_u8);
113 let sw_sum = digest.finalize();
114
115 let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());
116 crc.feed(&buf_u8);
117 let sum = crc.finalize();
118 assert_eq!(sum, sw_sum);
119
120 let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());
121 crc.feed(&buf_u16);
122 let sum = crc.finalize();
123 assert_eq!(sum, 0x8a61_4178);
124
125 let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());
126 crc.feed(&buf_u32);
127 let sum = crc.finalize();
128 assert_eq!(sum, 0xfab5_d04e);
129
130 // POSIX
131
132 let sw_crc = crc::Crc::<u32>::new(&POSIX);
133 let mut digest = sw_crc.digest();
134 digest.update(&buf_u8);
135 let sw_sum = digest.finalize();
136
137 let mut crc = Crc::new_posix(p.CRC0.reborrow());
138 crc.feed(&buf_u8);
139 let sum = crc.finalize();
140
141 assert_eq!(sum, sw_sum);
142
143 let mut crc = Crc::new_posix(p.CRC0.reborrow());
144 crc.feed(&buf_u16);
145 let sum = crc.finalize();
146 assert_eq!(sum, 0x6d76_4f58);
147
148 let mut crc = Crc::new_posix(p.CRC0.reborrow());
149 crc.feed(&buf_u32);
150 let sum = crc.finalize();
151 assert_eq!(sum, 0x2a5b_cb90);
152
153 defmt::info!("CRC successful");
154}
diff --git a/examples/mcxa/src/bin/dma_mem_to_mem.rs b/examples/mcxa/src/bin/dma_mem_to_mem.rs
new file mode 100644
index 000000000..b38baccb5
--- /dev/null
+++ b/examples/mcxa/src/bin/dma_mem_to_mem.rs
@@ -0,0 +1,118 @@
1//! DMA memory-to-memory transfer example for MCXA276.
2//!
3//! This example demonstrates using DMA to copy data between memory buffers
4//! using the Embassy-style async API with type-safe transfers.
5//!
6//! # Embassy-style features demonstrated:
7//! - `TransferOptions` for configuration
8//! - Type-safe `mem_to_mem<u32>()` method with async `.await`
9//! - `Transfer` Future that can be `.await`ed
10//! - `Word` trait for automatic transfer width detection
11//! - `memset()` method for filling memory with a pattern
12
13#![no_std]
14#![no_main]
15
16use embassy_executor::Spawner;
17use embassy_mcxa::clocks::config::Div8;
18use embassy_mcxa::dma::{DmaChannel, TransferOptions};
19use static_cell::ConstStaticCell;
20use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
21
22const BUFFER_LENGTH: usize = 4;
23
24// Buffers in RAM (static mut is automatically placed in .bss/.data)
25static SRC_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([1, 2, 3, 4]);
26static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);
27static MEMSET_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 // Small delay to allow probe-rs to attach after reset
32 for _ in 0..100_000 {
33 cortex_m::asm::nop();
34 }
35
36 let mut cfg = hal::config::Config::default();
37 cfg.clock_cfg.sirc.fro_12m_enabled = true;
38 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
39 let p = hal::init(cfg);
40
41 defmt::info!("DMA memory-to-memory example starting...");
42
43 defmt::info!("EDMA memory to memory example begin.");
44
45 let src = SRC_BUFFER.take();
46 let dst = DEST_BUFFER.take();
47 let mst = MEMSET_BUFFER.take();
48
49 defmt::info!("Source Buffer: {=[?]}", src.as_slice());
50 defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice());
51 defmt::info!("Configuring DMA with Embassy-style API...");
52
53 // Create DMA channel
54 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
55
56 // Configure transfer options (Embassy-style)
57 // TransferOptions defaults to: complete_transfer_interrupt = true
58 let options = TransferOptions::default();
59
60 // =========================================================================
61 // Part 1: Embassy-style async API demonstration (mem_to_mem)
62 // =========================================================================
63 //
64 // Use the new type-safe `mem_to_mem<u32>()` method:
65 // - Automatically determines transfer width from buffer element type (u32)
66 // - Returns a `Transfer` future that can be `.await`ed
67 // - Uses TransferOptions for consistent configuration
68 //
69 // Using async `.await` - the executor can run other tasks while waiting!
70
71 // Perform type-safe memory-to-memory transfer using Embassy-style async API
72 // Using async `.await` - the executor can run other tasks while waiting!
73 let transfer = dma_ch0.mem_to_mem(src, dst, options).unwrap();
74 transfer.await.unwrap();
75
76 defmt::info!("DMA mem-to-mem transfer complete!");
77 defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice());
78
79 // Verify data
80 if src != dst {
81 defmt::error!("FAIL: mem_to_mem mismatch!");
82 } else {
83 defmt::info!("PASS: mem_to_mem verified.");
84 }
85
86 // =========================================================================
87 // Part 2: memset() demonstration
88 // =========================================================================
89 //
90 // The `memset()` method fills a buffer with a pattern value:
91 // - Fixed source address (pattern is read repeatedly)
92 // - Incrementing destination address
93 // - Uses the same Transfer future pattern
94
95 defmt::info!("--- Demonstrating memset() feature ---");
96
97 defmt::info!("Memset Buffer (before): {=[?]}", mst.as_slice());
98
99 // Fill buffer with a pattern value using DMA memset
100 let pattern: u32 = 0xDEADBEEF;
101 defmt::info!("Filling with pattern 0xDEADBEEF...");
102
103 // Using blocking_wait() for demonstration - also shows non-async usage
104 let transfer = dma_ch0.memset(&pattern, mst, options);
105 transfer.blocking_wait();
106
107 defmt::info!("DMA memset complete!");
108 defmt::info!("Memset Buffer (after): {=[?]}", mst.as_slice());
109
110 // Verify memset result
111 if !mst.iter().all(|&v| v == pattern) {
112 defmt::error!("FAIL: memset mismatch!");
113 } else {
114 defmt::info!("PASS: memset verified.");
115 }
116
117 defmt::info!("=== All DMA tests complete ===");
118}
diff --git a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs
new file mode 100644
index 000000000..30ce20c96
--- /dev/null
+++ b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs
@@ -0,0 +1,130 @@
1//! DMA Scatter-Gather Builder example for MCXA276.
2//!
3//! This example demonstrates using the new `ScatterGatherBuilder` API for
4//! chaining multiple DMA transfers with a type-safe builder pattern.
5//!
6//! # Features demonstrated:
7//! - `ScatterGatherBuilder::new()` for creating a builder
8//! - `add_transfer()` for adding memory-to-memory segments
9//! - `build()` to start the chained transfer
10//! - Automatic TCD linking and ESG bit management
11//!
12//! # Comparison with manual scatter-gather:
13//! The manual approach (see `dma_scatter_gather.rs`) requires:
14//! - Manual TCD pool allocation and alignment
15//! - Manual CSR/ESG/INTMAJOR bit manipulation
16//! - Manual dlast_sga address calculations
17//!
18//! The builder approach handles all of this automatically!
19
20#![no_std]
21#![no_main]
22
23use embassy_executor::Spawner;
24use embassy_mcxa::clocks::config::Div8;
25use embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder};
26use static_cell::ConstStaticCell;
27use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
28
29// Source buffers (multiple segments)
30static SRC1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x11111111, 0x22222222, 0x33333333, 0x44444444]);
31static SRC2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD]);
32static SRC3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210]);
33
34// Destination buffers (one per segment)
35static DST1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);
36static DST2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);
37static DST3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);
38
39#[embassy_executor::main]
40async fn main(_spawner: Spawner) {
41 // Small delay to allow probe-rs to attach after reset
42 for _ in 0..100_000 {
43 cortex_m::asm::nop();
44 }
45
46 let mut cfg = hal::config::Config::default();
47 cfg.clock_cfg.sirc.fro_12m_enabled = true;
48 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
49 let p = hal::init(cfg);
50
51 defmt::info!("DMA Scatter-Gather Builder example starting...");
52
53 defmt::info!("DMA Scatter-Gather Builder Example");
54 defmt::info!("===================================");
55 let src1 = SRC1.take();
56 let src2 = SRC2.take();
57 let src3 = SRC3.take();
58 let dst1 = DST1.take();
59 let dst2 = DST2.take();
60 let dst3 = DST3.take();
61
62 // Show source buffers
63 defmt::info!("Source buffers:");
64 defmt::info!(" SRC1: {=[?]}", src1.as_slice());
65 defmt::info!(" SRC2: {=[?]}", src2.as_slice());
66 defmt::info!(" SRC3: {=[?]}", src3.as_slice());
67
68 defmt::info!("Destination buffers (before):");
69 defmt::info!(" DST1: {=[?]}", dst1.as_slice());
70 defmt::info!(" DST2: {=[?]}", dst2.as_slice());
71 defmt::info!(" DST3: {=[?]}", dst3.as_slice());
72
73 // Create DMA channel
74 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
75
76 defmt::info!("Building scatter-gather chain with builder API...");
77
78 // =========================================================================
79 // ScatterGatherBuilder API demonstration
80 // =========================================================================
81 //
82 // The builder pattern makes scatter-gather transfers much easier:
83 // 1. Create a builder
84 // 2. Add transfer segments with add_transfer()
85 // 3. Call build() to start the entire chain
86 // No manual TCD manipulation required!
87
88 let mut builder = ScatterGatherBuilder::<u32>::new();
89
90 // Add three transfer segments - the builder handles TCD linking automatically
91 builder.add_transfer(src1, dst1);
92 builder.add_transfer(src2, dst2);
93 builder.add_transfer(src3, dst3);
94
95 defmt::info!("Added 3 transfer segments to chain.");
96 defmt::info!("Starting scatter-gather transfer with .await...");
97
98 // Build and execute the scatter-gather chain
99 // The build() method:
100 // - Links all TCDs together with ESG bit
101 // - Sets INTMAJOR on all TCDs
102 // - Loads the first TCD into hardware
103 // - Returns a Transfer future
104 let transfer = builder.build(&dma_ch0).expect("Failed to build scatter-gather");
105 transfer.blocking_wait();
106
107 defmt::info!("Scatter-gather transfer complete!");
108
109 // Show results
110 defmt::info!("Destination buffers (after):");
111 defmt::info!(" DST1: {=[?]}", dst1.as_slice());
112 defmt::info!(" DST2: {=[?]}", dst2.as_slice());
113 defmt::info!(" DST3: {=[?]}", dst3.as_slice());
114
115 let comps = [(src1, dst1), (src2, dst2), (src3, dst3)];
116
117 // Verify all three segments
118 let mut all_ok = true;
119 for (src, dst) in comps {
120 all_ok &= src == dst;
121 }
122
123 if all_ok {
124 defmt::info!("PASS: All segments verified!");
125 } else {
126 defmt::error!("FAIL: Mismatch detected!");
127 }
128
129 defmt::info!("=== Scatter-Gather Builder example complete ===");
130}
diff --git a/examples/mcxa/src/bin/dma_wrap_transfer.rs b/examples/mcxa/src/bin/dma_wrap_transfer.rs
new file mode 100644
index 000000000..acfd29f08
--- /dev/null
+++ b/examples/mcxa/src/bin/dma_wrap_transfer.rs
@@ -0,0 +1,184 @@
1//! DMA wrap transfer example for MCXA276.
2//!
3//! This example demonstrates using DMA with modulo addressing to wrap around
4//! a source buffer, effectively repeating the source data in the destination.
5//!
6//! # Embassy-style features demonstrated:
7//! - `DmaChannel::is_done()` and `clear_done()` helper methods
8//! - No need to pass register block around
9
10#![no_std]
11#![no_main]
12
13use core::fmt::Write as _;
14
15use embassy_executor::Spawner;
16use embassy_mcxa::clocks::config::Div8;
17use embassy_mcxa::dma::DmaChannel;
18use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
19use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
20
21// Source buffer: 4 words (16 bytes), aligned to 16 bytes for modulo
22#[repr(align(16))]
23struct AlignedSrc([u32; 4]);
24
25static mut SRC: AlignedSrc = AlignedSrc([0; 4]);
26static mut DST: [u32; 8] = [0; 8];
27
28/// Helper to print a buffer to UART
29fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) {
30 write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok();
31}
32
33#[embassy_executor::main]
34async fn main(_spawner: Spawner) {
35 // Small delay to allow probe-rs to attach after reset
36 for _ in 0..100_000 {
37 cortex_m::asm::nop();
38 }
39
40 let mut cfg = hal::config::Config::default();
41 cfg.clock_cfg.sirc.fro_12m_enabled = true;
42 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
43 let p = hal::init(cfg);
44
45 defmt::info!("DMA wrap transfer example starting...");
46
47 let config = Config {
48 baudrate_bps: 115_200,
49 ..Default::default()
50 };
51
52 let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap();
53 let (mut tx, _rx) = lpuart.split();
54
55 tx.blocking_write(b"EDMA wrap transfer example begin.\r\n\r\n").unwrap();
56
57 // Initialize buffers
58 unsafe {
59 SRC.0 = [1, 2, 3, 4];
60 DST = [0; 8];
61 }
62
63 tx.blocking_write(b"Source Buffer: ").unwrap();
64 print_buffer(&mut tx, unsafe { core::ptr::addr_of!(SRC.0) } as *const u32, 4);
65 tx.blocking_write(b"\r\n").unwrap();
66
67 tx.blocking_write(b"Destination Buffer (before): ").unwrap();
68 print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8);
69 tx.blocking_write(b"\r\n").unwrap();
70
71 tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n")
72 .unwrap();
73
74 // Create DMA channel using Embassy-style API
75 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
76
77 // Configure wrap transfer using direct TCD access:
78 // SRC is 16 bytes (4 * u32). We want to transfer 32 bytes (8 * u32).
79 // SRC modulo is 16 bytes (2^4 = 16) - wraps source address.
80 // DST modulo is 0 (disabled).
81 // This causes the source address to wrap around after 16 bytes,
82 // effectively repeating the source data.
83 unsafe {
84 let t = dma_ch0.tcd();
85
86 // Reset channel state
87 t.ch_csr().write(|w| {
88 w.erq()
89 .disable()
90 .earq()
91 .disable()
92 .eei()
93 .no_error()
94 .ebw()
95 .disable()
96 .done()
97 .clear_bit_by_one()
98 });
99 t.ch_es().write(|w| w.bits(0));
100 t.ch_int().write(|w| w.int().clear_bit_by_one());
101
102 // Source/destination addresses
103 t.tcd_saddr()
104 .write(|w| w.saddr().bits(core::ptr::addr_of!(SRC.0) as u32));
105 t.tcd_daddr()
106 .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DST) as u32));
107
108 // Offsets: both increment by 4 bytes
109 t.tcd_soff().write(|w| w.soff().bits(4));
110 t.tcd_doff().write(|w| w.doff().bits(4));
111
112 // Attributes: 32-bit transfers (size = 2)
113 // SMOD = 4 (2^4 = 16 byte modulo for source), DMOD = 0 (disabled)
114 t.tcd_attr().write(|w| {
115 w.ssize()
116 .bits(2)
117 .dsize()
118 .bits(2)
119 .smod()
120 .bits(4) // Source modulo: 2^4 = 16 bytes
121 .dmod()
122 .bits(0) // Dest modulo: disabled
123 });
124
125 // Transfer 32 bytes total in one minor loop
126 let nbytes = 32u32;
127 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes));
128
129 // Source wraps via modulo, no adjustment needed
130 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0));
131 // Reset dest address after major loop
132 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32));
133
134 // Major loop count = 1
135 t.tcd_biter_elinkno().write(|w| w.biter().bits(1));
136 t.tcd_citer_elinkno().write(|w| w.citer().bits(1));
137
138 // Enable interrupt on major loop completion
139 t.tcd_csr().write(|w| w.intmajor().set_bit());
140
141 cortex_m::asm::dsb();
142
143 tx.blocking_write(b"Triggering transfer...\r\n").unwrap();
144 dma_ch0.trigger_start();
145 }
146
147 // Wait for completion using channel helper method
148 while !dma_ch0.is_done() {
149 cortex_m::asm::nop();
150 }
151 unsafe {
152 dma_ch0.clear_done();
153 }
154
155 tx.blocking_write(b"\r\nEDMA wrap transfer example finish.\r\n\r\n")
156 .unwrap();
157 tx.blocking_write(b"Destination Buffer (after): ").unwrap();
158 print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8);
159 tx.blocking_write(b"\r\n\r\n").unwrap();
160
161 // Verify: DST should be [1, 2, 3, 4, 1, 2, 3, 4]
162 let expected = [1u32, 2, 3, 4, 1, 2, 3, 4];
163 let mut mismatch = false;
164 unsafe {
165 for i in 0..8 {
166 if DST[i] != expected[i] {
167 mismatch = true;
168 break;
169 }
170 }
171 }
172
173 if mismatch {
174 tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap();
175 defmt::error!("FAIL: Mismatch detected!");
176 } else {
177 tx.blocking_write(b"PASS: Data verified.\r\n").unwrap();
178 defmt::info!("PASS: Data verified.");
179 }
180
181 loop {
182 cortex_m::asm::wfe();
183 }
184}
diff --git a/examples/mcxa/src/bin/hello.rs b/examples/mcxa/src/bin/hello.rs
new file mode 100644
index 000000000..e371d9413
--- /dev/null
+++ b/examples/mcxa/src/bin/hello.rs
@@ -0,0 +1,119 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clocks::config::Div8;
6use hal::lpuart::{Blocking, Config, Lpuart};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9/// Simple helper to write a byte as hex to UART
10fn write_hex_byte(uart: &mut Lpuart<'_, Blocking>, byte: u8) {
11 const HEX_DIGITS: &[u8] = b"0123456789ABCDEF";
12 let _ = uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]);
13 let _ = uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]);
14}
15
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) {
18 let mut cfg = hal::config::Config::default();
19 cfg.clock_cfg.sirc.fro_12m_enabled = true;
20 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
21 let p = hal::init(cfg);
22
23 defmt::info!("boot");
24
25 // Create UART configuration
26 let config = Config {
27 baudrate_bps: 115_200,
28 ..Default::default()
29 };
30
31 // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX
32 let mut uart = Lpuart::new_blocking(
33 p.LPUART2, // Peripheral
34 p.P2_2, // TX pin
35 p.P2_3, // RX pin
36 config,
37 )
38 .unwrap();
39
40 // Print welcome message before any async delays to guarantee early console output
41 uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n");
42 uart.write_str_blocking("Available commands:\r\n");
43 uart.write_str_blocking(" help - Show this help\r\n");
44 uart.write_str_blocking(" echo <text> - Echo back the text\r\n");
45 uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n");
46 uart.write_str_blocking("Type a command: ");
47
48 let mut buffer = [0u8; 64];
49 let mut buf_idx = 0;
50
51 loop {
52 // Read a byte from UART
53 let byte = uart.read_byte_blocking();
54
55 // Echo the character back
56 if byte == b'\r' || byte == b'\n' {
57 // Enter pressed - process command
58 uart.write_str_blocking("\r\n");
59
60 if buf_idx > 0 {
61 let command = &buffer[0..buf_idx];
62
63 if command == b"help" {
64 uart.write_str_blocking("Available commands:\r\n");
65 uart.write_str_blocking(" help - Show this help\r\n");
66 uart.write_str_blocking(" echo <text> - Echo back the text\r\n");
67 uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n");
68 } else if command.starts_with(b"echo ") && command.len() > 5 {
69 uart.write_str_blocking("Echo: ");
70 uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or(""));
71 uart.write_str_blocking("\r\n");
72 } else if command.starts_with(b"hex ") && command.len() > 4 {
73 // Parse the byte value
74 let num_str = &command[4..];
75 if let Ok(num) = parse_u8(num_str) {
76 uart.write_str_blocking("Hex: 0x");
77 write_hex_byte(&mut uart, num);
78 uart.write_str_blocking("\r\n");
79 } else {
80 uart.write_str_blocking("Invalid number for hex command\r\n");
81 }
82 } else if !command.is_empty() {
83 uart.write_str_blocking("Unknown command: ");
84 uart.write_str_blocking(core::str::from_utf8(command).unwrap_or(""));
85 uart.write_str_blocking("\r\n");
86 }
87 }
88
89 // Reset buffer and prompt
90 buf_idx = 0;
91 uart.write_str_blocking("Type a command: ");
92 } else if byte == 8 || byte == 127 {
93 // Backspace
94 if buf_idx > 0 {
95 buf_idx -= 1;
96 uart.write_str_blocking("\x08 \x08"); // Erase character
97 }
98 } else if buf_idx < buffer.len() - 1 {
99 // Regular character
100 buffer[buf_idx] = byte;
101 buf_idx += 1;
102 let _ = uart.write_byte(byte);
103 }
104 }
105}
106
107/// Simple parser for u8 from ASCII bytes
108fn parse_u8(bytes: &[u8]) -> Result<u8, ()> {
109 let mut result = 0u8;
110 for &b in bytes {
111 if b.is_ascii_digit() {
112 result = result.checked_mul(10).ok_or(())?;
113 result = result.checked_add(b - b'0').ok_or(())?;
114 } else {
115 return Err(());
116 }
117 }
118 Ok(result)
119}
diff --git a/examples/mcxa/src/bin/i2c-async.rs b/examples/mcxa/src/bin/i2c-async.rs
new file mode 100644
index 000000000..edcfd5f22
--- /dev/null
+++ b/examples/mcxa/src/bin/i2c-async.rs
@@ -0,0 +1,39 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::bind_interrupts;
7use hal::clocks::config::Div8;
8use hal::config::Config;
9use hal::i2c::InterruptHandler;
10use hal::i2c::controller::{self, I2c, Speed};
11use hal::peripherals::LPI2C3;
12use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
13
14bind_interrupts!(
15 struct Irqs {
16 LPI2C3 => InterruptHandler<LPI2C3>;
17 }
18);
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 let mut config = Config::default();
23 config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);
24
25 let p = hal::init(config);
26
27 defmt::info!("I2C example");
28
29 let mut config = controller::Config::default();
30 config.speed = Speed::Standard;
31 let mut i2c = I2c::new_async(p.LPI2C3, p.P3_27, p.P3_28, Irqs, config).unwrap();
32 let mut buf = [0u8; 2];
33
34 loop {
35 i2c.async_write_read(0x48, &[0x00], &mut buf).await.unwrap();
36 defmt::info!("Buffer: {:02x}", buf);
37 Timer::after_secs(1).await;
38 }
39}
diff --git a/examples/mcxa/src/bin/i2c-blocking.rs b/examples/mcxa/src/bin/i2c-blocking.rs
new file mode 100644
index 000000000..0f6c8cbae
--- /dev/null
+++ b/examples/mcxa/src/bin/i2c-blocking.rs
@@ -0,0 +1,31 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::clocks::config::Div8;
7use hal::config::Config;
8use hal::i2c::controller::{self, I2c, Speed};
9use tmp108::Tmp108;
10use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let mut config = Config::default();
15 config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);
16
17 let p = hal::init(config);
18
19 defmt::info!("I2C example");
20
21 let mut config = controller::Config::default();
22 config.speed = Speed::Standard;
23 let i2c = I2c::new_blocking(p.LPI2C3, p.P3_27, p.P3_28, config).unwrap();
24 let mut tmp = Tmp108::new_with_a0_gnd(i2c);
25
26 loop {
27 let temperature = tmp.temperature().unwrap();
28 defmt::info!("Temperature: {}C", temperature);
29 Timer::after_secs(1).await;
30 }
31}
diff --git a/examples/mcxa/src/bin/i2c-scan-blocking.rs b/examples/mcxa/src/bin/i2c-scan-blocking.rs
new file mode 100644
index 000000000..0197f9b1d
--- /dev/null
+++ b/examples/mcxa/src/bin/i2c-scan-blocking.rs
@@ -0,0 +1,41 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::Input;
6use embassy_mcxa::gpio::Pull;
7use embassy_time::Timer;
8use hal::clocks::config::Div8;
9use hal::config::Config;
10use hal::i2c::controller::{self, I2c, Speed};
11use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let mut config = Config::default();
16 config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);
17
18 let p = hal::init(config);
19
20 defmt::info!("I2C example");
21
22 let mut config = controller::Config::default();
23 config.speed = Speed::Standard;
24
25 // Note: P0_2 is connected to P1_8 on the FRDM_MCXA276 via a resistor, and
26 // defaults to SWO on the debug peripheral. Explicitly make it a high-z
27 // input.
28 let _pin = Input::new(p.P0_2, Pull::Disabled);
29 let mut i2c = I2c::new_blocking(p.LPI2C2, p.P1_9, p.P1_8, config).unwrap();
30
31 for addr in 0x01..=0x7f {
32 let result = i2c.blocking_write(addr, &[]);
33 if result.is_ok() {
34 defmt::info!("Device found at addr {:02x}", addr);
35 }
36 }
37
38 loop {
39 Timer::after_secs(10).await;
40 }
41}
diff --git a/examples/mcxa/src/bin/lpuart_buffered.rs b/examples/mcxa/src/bin/lpuart_buffered.rs
new file mode 100644
index 000000000..47b56b7c7
--- /dev/null
+++ b/examples/mcxa/src/bin/lpuart_buffered.rs
@@ -0,0 +1,62 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clocks::config::Div8;
6use embassy_mcxa::lpuart::Config;
7use embassy_mcxa::lpuart::buffered::BufferedLpuart;
8use embassy_mcxa::{bind_interrupts, lpuart};
9use embedded_io_async::Write;
10use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
11
12// Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver
13bind_interrupts!(struct Irqs {
14 LPUART2 => lpuart::buffered::BufferedInterruptHandler::<hal::peripherals::LPUART2>;
15});
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 let mut cfg = hal::config::Config::default();
20 cfg.clock_cfg.sirc.fro_12m_enabled = true;
21 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
22 let p = hal::init(cfg);
23
24 // Configure NVIC for LPUART2
25 hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3);
26
27 // UART configuration (enable both TX and RX)
28 let config = Config {
29 baudrate_bps: 115_200,
30 rx_fifo_watermark: 0,
31 tx_fifo_watermark: 0,
32 ..Default::default()
33 };
34
35 let mut tx_buf = [0u8; 256];
36 let mut rx_buf = [0u8; 256];
37
38 // Create a buffered LPUART2 instance with both TX and RX
39 let mut uart = BufferedLpuart::new(
40 p.LPUART2,
41 p.P2_2, // TX pin
42 p.P2_3, // RX pin
43 Irqs,
44 &mut tx_buf,
45 &mut rx_buf,
46 config,
47 )
48 .unwrap();
49
50 // Split into TX and RX parts
51 let (tx, rx) = uart.split_ref();
52
53 tx.write(b"Hello buffered LPUART.\r\n").await.unwrap();
54 tx.write(b"Type characters to echo them back.\r\n").await.unwrap();
55
56 // Echo loop
57 let mut buf = [0u8; 4];
58 loop {
59 let used = rx.read(&mut buf).await.unwrap();
60 tx.write_all(&buf[..used]).await.unwrap();
61 }
62}
diff --git a/examples/mcxa/src/bin/lpuart_dma.rs b/examples/mcxa/src/bin/lpuart_dma.rs
new file mode 100644
index 000000000..cc86f6a40
--- /dev/null
+++ b/examples/mcxa/src/bin/lpuart_dma.rs
@@ -0,0 +1,68 @@
1//! LPUART DMA example for MCXA276.
2//!
3//! This example demonstrates using DMA for UART TX and RX operations.
4//! It sends a message using DMA, then waits for 16 characters to be received
5//! via DMA and echoes them back.
6//!
7//! The DMA request sources are automatically derived from the LPUART instance type.
8//! DMA clock/reset/init is handled automatically by the HAL.
9
10#![no_std]
11#![no_main]
12
13use embassy_executor::Spawner;
14use embassy_mcxa::clocks::config::Div8;
15use embassy_mcxa::lpuart::{Config, LpuartDma};
16use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 let mut cfg = hal::config::Config::default();
21 cfg.clock_cfg.sirc.fro_12m_enabled = true;
22 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
23 let p = hal::init(cfg);
24
25 defmt::info!("LPUART DMA example starting...");
26
27 // Create UART configuration
28 let config = Config {
29 baudrate_bps: 115_200,
30 ..Default::default()
31 };
32
33 // Create UART instance with DMA channels
34 let mut lpuart = LpuartDma::new(
35 p.LPUART2, // Instance
36 p.P2_2, // TX pin
37 p.P2_3, // RX pin
38 p.DMA_CH0, // TX DMA channel
39 p.DMA_CH1, // RX DMA channel
40 config,
41 )
42 .unwrap();
43
44 // Send a message using DMA (DMA request source is automatically derived from LPUART2)
45 let tx_msg = b"Hello from LPUART2 DMA TX!\r\n";
46 lpuart.write_dma(tx_msg).await.unwrap();
47
48 defmt::info!("TX DMA complete");
49
50 // Send prompt
51 let prompt = b"Type 16 characters to echo via DMA:\r\n";
52 lpuart.write_dma(prompt).await.unwrap();
53
54 // Receive 16 characters using DMA
55 let mut rx_buf = [0u8; 16];
56 lpuart.read_dma(&mut rx_buf).await.unwrap();
57
58 defmt::info!("RX DMA complete");
59
60 // Echo back the received data
61 let echo_prefix = b"\r\nReceived: ";
62 lpuart.write_dma(echo_prefix).await.unwrap();
63 lpuart.write_dma(&rx_buf).await.unwrap();
64 let done_msg = b"\r\nDone!\r\n";
65 lpuart.write_dma(done_msg).await.unwrap();
66
67 defmt::info!("Example complete");
68}
diff --git a/examples/mcxa/src/bin/lpuart_polling.rs b/examples/mcxa/src/bin/lpuart_polling.rs
new file mode 100644
index 000000000..b80668834
--- /dev/null
+++ b/examples/mcxa/src/bin/lpuart_polling.rs
@@ -0,0 +1,47 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clocks::config::Div8;
6use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
7
8use crate::hal::lpuart::{Config, Lpuart};
9
10#[embassy_executor::main]
11async fn main(_spawner: Spawner) {
12 let mut cfg = hal::config::Config::default();
13 cfg.clock_cfg.sirc.fro_12m_enabled = true;
14 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
15 let p = hal::init(cfg);
16
17 defmt::info!("boot");
18
19 // Create UART configuration
20 let config = Config {
21 baudrate_bps: 115_200,
22 ..Default::default()
23 };
24
25 // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX
26 let lpuart = Lpuart::new_blocking(
27 p.LPUART2, // Peripheral
28 p.P2_2, // TX pin
29 p.P2_3, // RX pin
30 config,
31 )
32 .unwrap();
33
34 // Split into separate TX and RX parts
35 let (mut tx, mut rx) = lpuart.split();
36
37 // Write hello messages
38 tx.blocking_write(b"Hello world.\r\n").unwrap();
39 tx.blocking_write(b"Echoing. Type characters...\r\n").unwrap();
40
41 // Echo loop
42 loop {
43 let mut buf = [0u8; 1];
44 rx.blocking_read(&mut buf).unwrap();
45 tx.blocking_write(&buf).unwrap();
46 }
47}
diff --git a/examples/mcxa/src/bin/lpuart_ring_buffer.rs b/examples/mcxa/src/bin/lpuart_ring_buffer.rs
new file mode 100644
index 000000000..be7fd4534
--- /dev/null
+++ b/examples/mcxa/src/bin/lpuart_ring_buffer.rs
@@ -0,0 +1,115 @@
1//! LPUART Ring Buffer DMA example for MCXA276.
2//!
3//! This example demonstrates using the high-level `LpuartRxDma::setup_ring_buffer()`
4//! API for continuous circular DMA reception from a UART peripheral.
5//!
6//! # Features demonstrated:
7//! - `LpuartRxDma::setup_ring_buffer()` for continuous peripheral-to-memory DMA
8//! - `RingBuffer` for async reading of received data
9//! - Handling of potential overrun conditions
10//! - Half-transfer and complete-transfer interrupts for timely wakeups
11//!
12//! # How it works:
13//! 1. Create an `LpuartRxDma` driver with a DMA channel
14//! 2. Call `setup_ring_buffer()` which handles all low-level DMA configuration
15//! 3. Application asynchronously reads data as it arrives via `ring_buf.read()`
16//! 4. Both half-transfer and complete-transfer interrupts wake the reader
17
18#![no_std]
19#![no_main]
20
21use embassy_executor::Spawner;
22use embassy_mcxa::clocks::config::Div8;
23use embassy_mcxa::lpuart::{Config, LpuartDma, LpuartTxDma};
24use static_cell::ConstStaticCell;
25use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
26
27// Ring buffer for RX - power of 2 is ideal for modulo efficiency
28static RX_RING_BUFFER: ConstStaticCell<[u8; 64]> = ConstStaticCell::new([0; 64]);
29
30/// Helper to write a byte as hex to UART
31fn write_hex<T: embassy_mcxa::lpuart::Instance, C: embassy_mcxa::dma::Channel>(
32 tx: &mut LpuartTxDma<'_, T, C>,
33 byte: u8,
34) {
35 const HEX: &[u8; 16] = b"0123456789ABCDEF";
36 let buf = [HEX[(byte >> 4) as usize], HEX[(byte & 0x0F) as usize]];
37 tx.blocking_write(&buf).ok();
38}
39
40#[embassy_executor::main]
41async fn main(_spawner: Spawner) {
42 // Small delay to allow probe-rs to attach after reset
43 for _ in 0..100_000 {
44 cortex_m::asm::nop();
45 }
46
47 let mut cfg = hal::config::Config::default();
48 cfg.clock_cfg.sirc.fro_12m_enabled = true;
49 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
50 let p = hal::init(cfg);
51
52 defmt::info!("LPUART Ring Buffer DMA example starting...");
53
54 // Create UART configuration
55 let config = Config {
56 baudrate_bps: 115_200,
57 ..Default::default()
58 };
59
60 // Create LPUART with DMA support for both TX and RX, then split
61 // This is the proper Embassy pattern - create once, split into TX and RX
62 let lpuart = LpuartDma::new(p.LPUART2, p.P2_2, p.P2_3, p.DMA_CH1, p.DMA_CH0, config).unwrap();
63 let (mut tx, rx) = lpuart.split();
64
65 tx.blocking_write(b"LPUART Ring Buffer DMA Example\r\n").unwrap();
66 tx.blocking_write(b"==============================\r\n\r\n").unwrap();
67
68 tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n")
69 .unwrap();
70
71 let buf = RX_RING_BUFFER.take();
72 // Set up the ring buffer with circular DMA
73 let mut ring_buf = rx.into_ring_dma_rx(buf);
74
75 tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n")
76 .unwrap();
77 tx.blocking_write(b"The DMA continuously receives in the background.\r\n\r\n")
78 .unwrap();
79
80 // Main loop: read from ring buffer and echo back
81 let mut read_buf = [0u8; 16];
82 let mut total_received: usize = 0;
83
84 loop {
85 // Async read - waits until data is available
86 match ring_buf.read(&mut read_buf).await {
87 Ok(n) if n > 0 => {
88 total_received += n;
89
90 // Echo back what we received
91 tx.blocking_write(b"RX[").unwrap();
92 for (i, &byte) in read_buf.iter().enumerate().take(n) {
93 write_hex(&mut tx, byte);
94 if i < n - 1 {
95 tx.blocking_write(b" ").unwrap();
96 }
97 }
98 tx.blocking_write(b"]: ").unwrap();
99 tx.blocking_write(&read_buf[..n]).unwrap();
100 tx.blocking_write(b"\r\n").unwrap();
101
102 defmt::info!("Received {} bytes, total: {}", n, total_received);
103 }
104 Ok(_) => {
105 // No data, shouldn't happen with async read
106 }
107 Err(_) => {
108 // Overrun detected
109 tx.blocking_write(b"ERROR: Ring buffer overrun!\r\n").unwrap();
110 defmt::error!("Ring buffer overrun!");
111 ring_buf.clear();
112 }
113 }
114 }
115}
diff --git a/examples/mcxa/src/bin/raw_dma_channel_link.rs b/examples/mcxa/src/bin/raw_dma_channel_link.rs
new file mode 100644
index 000000000..74785e4f3
--- /dev/null
+++ b/examples/mcxa/src/bin/raw_dma_channel_link.rs
@@ -0,0 +1,278 @@
1//! DMA channel linking example for MCXA276.
2//!
3//! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have
4//! a high-level and safe API for. It should not be taken as typical, recommended, or
5//! stable usage!
6//!
7//! This example demonstrates DMA channel linking (minor and major loop linking):
8//! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with:
9//! - Minor Link to Channel 1 (triggers CH1 after each minor loop)
10//! - Major Link to Channel 2 (triggers CH2 after major loop completes)
11//! - Channel 1: Transfers SRC_BUFFER to DEST_BUFFER1 (triggered by CH0 minor link)
12//! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link)
13//!
14//! # Embassy-style features demonstrated:
15//! - `DmaChannel::new()` for channel creation
16//! - `DmaChannel::is_done()` and `clear_done()` helper methods
17//! - Channel linking with `set_minor_link()` and `set_major_link()`
18
19#![no_std]
20#![no_main]
21
22use embassy_executor::Spawner;
23use embassy_mcxa::clocks::config::Div8;
24use embassy_mcxa::dma::DmaChannel;
25use embassy_mcxa::pac;
26use static_cell::ConstStaticCell;
27use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
28
29// Buffers
30static SRC_BUFFER: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([1, 2, 3, 4]);
31static DEST_BUFFER0: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);
32static DEST_BUFFER1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);
33static DEST_BUFFER2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);
34
35#[embassy_executor::main]
36async fn main(_spawner: Spawner) {
37 // Small delay to allow probe-rs to attach after reset
38 for _ in 0..100_000 {
39 cortex_m::asm::nop();
40 }
41
42 let mut cfg = hal::config::Config::default();
43 cfg.clock_cfg.sirc.fro_12m_enabled = true;
44 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
45 let p = hal::init(cfg);
46
47 defmt::info!("DMA channel link example starting...");
48
49 // DMA is initialized during hal::init() - no need to call ensure_init()
50
51 let pac_periphs = unsafe { pac::Peripherals::steal() };
52 let dma0 = &pac_periphs.dma0;
53 let edma = unsafe { &*pac::Edma0Tcd0::ptr() };
54
55 // Clear any residual state
56 for i in 0..3 {
57 let t = edma.tcd(i);
58 t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one());
59 t.ch_int().write(|w| w.int().clear_bit_by_one());
60 t.ch_es().write(|w| w.err().clear_bit_by_one());
61 t.ch_mux().write(|w| unsafe { w.bits(0) });
62 }
63
64 // Clear Global Halt/Error state
65 dma0.mp_csr().modify(|_, w| {
66 w.halt()
67 .normal_operation()
68 .hae()
69 .normal_operation()
70 .ecx()
71 .normal_operation()
72 .cx()
73 .normal_operation()
74 });
75
76 defmt::info!("EDMA channel link example begin.");
77
78 // Initialize buffers
79 let src = SRC_BUFFER.take();
80 let dst0 = DEST_BUFFER0.take();
81 let dst1 = DEST_BUFFER1.take();
82 let dst2 = DEST_BUFFER2.take();
83
84 defmt::info!("Source Buffer: {=[?]}", src.as_slice());
85 defmt::info!("DEST0 (before): {=[?]}", dst0.as_slice());
86 defmt::info!("DEST1 (before): {=[?]}", dst1.as_slice());
87 defmt::info!("DEST2 (before): {=[?]}", dst2.as_slice());
88
89 defmt::info!("Configuring DMA channels with Embassy-style API...");
90
91 let ch0 = DmaChannel::new(p.DMA_CH0);
92 let ch1 = DmaChannel::new(p.DMA_CH1);
93 let ch2 = DmaChannel::new(p.DMA_CH2);
94
95 // Configure channels using direct TCD access (advanced feature demo)
96 // This example demonstrates channel linking which requires direct TCD manipulation
97
98 // Helper to configure TCD for memory-to-memory transfer
99 // Parameters: channel, src, dst, width, nbytes (minor loop), count (major loop), interrupt
100 #[allow(clippy::too_many_arguments)]
101 unsafe fn configure_tcd(
102 edma: &embassy_mcxa::pac::edma_0_tcd0::RegisterBlock,
103 ch: usize,
104 src: u32,
105 dst: u32,
106 width: u8,
107 nbytes: u32,
108 count: u16,
109 enable_int: bool,
110 ) {
111 let t = edma.tcd(ch);
112
113 // Reset channel state
114 t.ch_csr().write(|w| {
115 w.erq()
116 .disable()
117 .earq()
118 .disable()
119 .eei()
120 .no_error()
121 .ebw()
122 .disable()
123 .done()
124 .clear_bit_by_one()
125 });
126 t.ch_es().write(|w| w.bits(0));
127 t.ch_int().write(|w| w.int().clear_bit_by_one());
128
129 // Source/destination addresses
130 t.tcd_saddr().write(|w| w.saddr().bits(src));
131 t.tcd_daddr().write(|w| w.daddr().bits(dst));
132
133 // Offsets: increment by width
134 t.tcd_soff().write(|w| w.soff().bits(width as u16));
135 t.tcd_doff().write(|w| w.doff().bits(width as u16));
136
137 // Attributes: size = log2(width)
138 let size = match width {
139 1 => 0,
140 2 => 1,
141 4 => 2,
142 _ => 0,
143 };
144 t.tcd_attr().write(|w| w.ssize().bits(size).dsize().bits(size));
145
146 // Number of bytes per minor loop
147 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes));
148
149 // Major loop: reset source address after major loop
150 let total_bytes = nbytes * count as u32;
151 t.tcd_slast_sda()
152 .write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32));
153 t.tcd_dlast_sga()
154 .write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32));
155
156 // Major loop count
157 t.tcd_biter_elinkno().write(|w| w.biter().bits(count));
158 t.tcd_citer_elinkno().write(|w| w.citer().bits(count));
159
160 // Control/status: enable interrupt if requested
161 if enable_int {
162 t.tcd_csr().write(|w| w.intmajor().set_bit());
163 } else {
164 t.tcd_csr().write(|w| w.intmajor().clear_bit());
165 }
166
167 cortex_m::asm::dsb();
168 }
169
170 unsafe {
171 // Channel 0: Transfer 16 bytes total (8 bytes per minor loop, 2 major iterations)
172 // Minor Link -> Channel 1
173 // Major Link -> Channel 2
174 configure_tcd(
175 edma,
176 0,
177 src.as_ptr() as u32,
178 dst0.as_mut_ptr() as u32,
179 4, // src width
180 8, // nbytes (minor loop = 2 words)
181 2, // count (major loop = 2 iterations)
182 false, // no interrupt
183 );
184 ch0.set_minor_link(1); // Link to CH1 after each minor loop
185 ch0.set_major_link(2); // Link to CH2 after major loop
186
187 // Channel 1: Transfer 16 bytes (triggered by CH0 minor link)
188 configure_tcd(
189 edma,
190 1,
191 src.as_ptr() as u32,
192 dst1.as_mut_ptr() as u32,
193 4,
194 16, // full buffer in one minor loop
195 1, // 1 major iteration
196 false,
197 );
198
199 // Channel 2: Transfer 16 bytes (triggered by CH0 major link)
200 configure_tcd(
201 edma,
202 2,
203 src.as_ptr() as u32,
204 dst2.as_mut_ptr() as u32,
205 4,
206 16, // full buffer in one minor loop
207 1, // 1 major iteration
208 true, // enable interrupt
209 );
210 }
211
212 defmt::info!("Triggering Channel 0 (1st minor loop)...");
213
214 // Trigger first minor loop of CH0
215 unsafe {
216 ch0.trigger_start();
217 }
218
219 // Wait for CH1 to complete (triggered by CH0 minor link)
220 while !ch1.is_done() {
221 cortex_m::asm::nop();
222 }
223 unsafe {
224 ch1.clear_done();
225 }
226
227 defmt::info!("CH1 done (via minor link).");
228 defmt::info!("Triggering Channel 0 (2nd minor loop)...");
229
230 // Trigger second minor loop of CH0
231 unsafe {
232 ch0.trigger_start();
233 }
234
235 // Wait for CH0 major loop to complete
236 while !ch0.is_done() {
237 cortex_m::asm::nop();
238 }
239 unsafe {
240 ch0.clear_done();
241 }
242
243 defmt::info!("CH0 major loop done.");
244
245 // Wait for CH2 to complete (triggered by CH0 major link)
246 // Using is_done() instead of AtomicBool - the standard interrupt handler
247 // clears the interrupt flag and wakes wakers, but DONE bit remains set
248 while !ch2.is_done() {
249 cortex_m::asm::nop();
250 }
251 unsafe {
252 ch2.clear_done();
253 }
254
255 defmt::info!("CH2 done (via major link).");
256
257 defmt::info!("EDMA channel link example finish.");
258
259 defmt::info!("DEST0 (after): {=[?]}", dst0.as_slice());
260 defmt::info!("DEST1 (after): {=[?]}", dst1.as_slice());
261 defmt::info!("DEST2 (after): {=[?]}", dst2.as_slice());
262
263 // Verify all buffers match source
264 let mut success = true;
265 for sli in [dst0, dst1, dst2] {
266 success &= sli == src;
267 }
268
269 if success {
270 defmt::info!("PASS: Data verified.");
271 } else {
272 defmt::error!("FAIL: Mismatch detected!");
273 }
274
275 loop {
276 cortex_m::asm::wfe();
277 }
278}
diff --git a/examples/mcxa/src/bin/raw_dma_interleave_transfer.rs b/examples/mcxa/src/bin/raw_dma_interleave_transfer.rs
new file mode 100644
index 000000000..a383b6cf4
--- /dev/null
+++ b/examples/mcxa/src/bin/raw_dma_interleave_transfer.rs
@@ -0,0 +1,141 @@
1//! DMA interleaved transfer example for MCXA276.
2//!
3//! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have
4//! a high-level and safe API for. It should not be taken as typical, recommended, or
5//! stable usage!
6//!
7//! This example demonstrates using DMA with custom source/destination offsets
8//! to interleave data during transfer.
9//!
10//! # Embassy-style features demonstrated:
11//! - `TransferOptions::default()` for configuration (used internally)
12//! - DMA channel with `DmaChannel::new()`
13
14#![no_std]
15#![no_main]
16
17use embassy_executor::Spawner;
18use embassy_mcxa::clocks::config::Div8;
19use embassy_mcxa::dma::DmaChannel;
20use static_cell::ConstStaticCell;
21use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
22
23const BUFFER_LENGTH: usize = 16;
24const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2;
25
26// Buffers in RAM
27static SRC_BUFFER: ConstStaticCell<[u32; HALF_BUFF_LENGTH]> = ConstStaticCell::new([0; HALF_BUFF_LENGTH]);
28static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);
29
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 // Small delay to allow probe-rs to attach after reset
33 for _ in 0..100_000 {
34 cortex_m::asm::nop();
35 }
36
37 let mut cfg = hal::config::Config::default();
38 cfg.clock_cfg.sirc.fro_12m_enabled = true;
39 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
40 let p = hal::init(cfg);
41
42 defmt::info!("DMA interleave transfer example starting...");
43
44 defmt::info!("EDMA interleave transfer example begin.");
45
46 // Initialize buffers
47 let src = SRC_BUFFER.take();
48 *src = [1, 2, 3, 4, 5, 6, 7, 8];
49 let dst = DEST_BUFFER.take();
50
51 defmt::info!("Source Buffer: {=[?]}", src.as_slice());
52 defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice());
53
54 defmt::info!("Configuring DMA with Embassy-style API...");
55
56 // Create DMA channel using Embassy-style API
57 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
58
59 // Configure interleaved transfer using direct TCD access:
60 // - src_offset = 4: advance source by 4 bytes after each read
61 // - dst_offset = 8: advance dest by 8 bytes after each write
62 // This spreads source data across every other word in destination
63 unsafe {
64 let t = dma_ch0.tcd();
65
66 // Reset channel state
67 t.ch_csr().write(|w| {
68 w.erq()
69 .disable()
70 .earq()
71 .disable()
72 .eei()
73 .no_error()
74 .ebw()
75 .disable()
76 .done()
77 .clear_bit_by_one()
78 });
79 t.ch_es().write(|w| w.bits(0));
80 t.ch_int().write(|w| w.int().clear_bit_by_one());
81
82 // Source/destination addresses
83 t.tcd_saddr().write(|w| w.saddr().bits(src.as_ptr() as u32));
84 t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32));
85
86 // Custom offsets for interleaving
87 t.tcd_soff().write(|w| w.soff().bits(4)); // src: +4 bytes per read
88 t.tcd_doff().write(|w| w.doff().bits(8)); // dst: +8 bytes per write
89
90 // Attributes: 32-bit transfers (size = 2)
91 t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2));
92
93 // Transfer entire source buffer in one minor loop
94 let nbytes = (HALF_BUFF_LENGTH * 4) as u32;
95 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes));
96
97 // Reset source address after major loop
98 t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(nbytes as i32) as u32));
99 // Destination uses 2x offset, so adjust accordingly
100 let dst_total = (HALF_BUFF_LENGTH * 8) as u32;
101 t.tcd_dlast_sga()
102 .write(|w| w.dlast_sga().bits(-(dst_total as i32) as u32));
103
104 // Major loop count = 1
105 t.tcd_biter_elinkno().write(|w| w.biter().bits(1));
106 t.tcd_citer_elinkno().write(|w| w.citer().bits(1));
107
108 // Enable interrupt on major loop completion
109 t.tcd_csr().write(|w| w.intmajor().set_bit());
110
111 cortex_m::asm::dsb();
112
113 defmt::info!("Triggering transfer...");
114 dma_ch0.trigger_start();
115 }
116
117 // Wait for completion using channel helper method
118 while !dma_ch0.is_done() {
119 cortex_m::asm::nop();
120 }
121 unsafe {
122 dma_ch0.clear_done();
123 }
124
125 defmt::info!("EDMA interleave transfer example finish.");
126 defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice());
127
128 // Verify: Even indices should match SRC_BUFFER[i/2], odd indices should be 0
129 let mut mismatch = false;
130 let diter = dst.chunks_exact(2);
131 let siter = src.iter();
132 for (ch, src) in diter.zip(siter) {
133 mismatch |= !matches!(ch, [a, 0] if a == src);
134 }
135
136 if mismatch {
137 defmt::error!("FAIL: Mismatch detected!");
138 } else {
139 defmt::info!("PASS: Data verified.");
140 }
141}
diff --git a/examples/mcxa/src/bin/raw_dma_memset.rs b/examples/mcxa/src/bin/raw_dma_memset.rs
new file mode 100644
index 000000000..7b3c06ffa
--- /dev/null
+++ b/examples/mcxa/src/bin/raw_dma_memset.rs
@@ -0,0 +1,129 @@
1//! DMA memset example for MCXA276.
2//!
3//! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have
4//! a high-level and safe API for. It should not be taken as typical, recommended, or
5//! stable usage!
6//!
7//! This example demonstrates using DMA to fill a buffer with a repeated pattern.
8//! The source address stays fixed while the destination increments.
9//!
10//! # Embassy-style features demonstrated:
11//! - `DmaChannel::is_done()` and `clear_done()` helper methods
12//! - No need to pass register block around
13
14#![no_std]
15#![no_main]
16
17use embassy_executor::Spawner;
18use embassy_mcxa::clocks::config::Div8;
19use embassy_mcxa::dma::DmaChannel;
20use static_cell::ConstStaticCell;
21use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
22
23const BUFFER_LENGTH: usize = 4;
24
25// Buffers in RAM
26static PATTERN: u32 = 0xDEADBEEF;
27static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 // Small delay to allow probe-rs to attach after reset
32 for _ in 0..100_000 {
33 cortex_m::asm::nop();
34 }
35
36 let mut cfg = hal::config::Config::default();
37 cfg.clock_cfg.sirc.fro_12m_enabled = true;
38 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
39 let p = hal::init(cfg);
40
41 defmt::info!("DMA memset example starting...");
42 defmt::info!("EDMA memset example begin.");
43
44 // Initialize buffers
45 let pat = &PATTERN;
46 let dst = DEST_BUFFER.take();
47 defmt::info!("Pattern Value: {=u32}", pat);
48 defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice());
49 defmt::info!("Configuring DMA with Embassy-style API...");
50
51 // Create DMA channel using Embassy-style API
52 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
53
54 // Configure memset transfer using direct TCD access:
55 // Source stays fixed (soff = 0, reads same pattern repeatedly)
56 // Destination increments (doff = 4)
57 unsafe {
58 let t = dma_ch0.tcd();
59
60 // Reset channel state
61 t.ch_csr().write(|w| {
62 w.erq()
63 .disable()
64 .earq()
65 .disable()
66 .eei()
67 .no_error()
68 .ebw()
69 .disable()
70 .done()
71 .clear_bit_by_one()
72 });
73 t.ch_es().write(|w| w.bits(0));
74 t.ch_int().write(|w| w.int().clear_bit_by_one());
75
76 // Source address (pattern) - fixed
77 t.tcd_saddr().write(|w| w.saddr().bits(pat as *const _ as u32));
78 // Destination address - increments
79 t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32));
80
81 // Source offset = 0 (stays fixed), Dest offset = 4 (increments)
82 t.tcd_soff().write(|w| w.soff().bits(0));
83 t.tcd_doff().write(|w| w.doff().bits(4));
84
85 // Attributes: 32-bit transfers (size = 2)
86 t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2));
87
88 // Transfer entire buffer in one minor loop
89 let nbytes = (BUFFER_LENGTH * 4) as u32;
90 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes));
91
92 // Source doesn't need adjustment (stays fixed)
93 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0));
94 // Reset dest address after major loop
95 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32));
96
97 // Major loop count = 1
98 t.tcd_biter_elinkno().write(|w| w.biter().bits(1));
99 t.tcd_citer_elinkno().write(|w| w.citer().bits(1));
100
101 // Enable interrupt on major loop completion
102 t.tcd_csr().write(|w| w.intmajor().set_bit());
103
104 cortex_m::asm::dsb();
105
106 defmt::info!("Triggering transfer...");
107 dma_ch0.trigger_start();
108 }
109
110 // Wait for completion using channel helper method
111 while !dma_ch0.is_done() {
112 cortex_m::asm::nop();
113 }
114 unsafe {
115 dma_ch0.clear_done();
116 }
117
118 defmt::info!("EDMA memset example finish.");
119 defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice());
120
121 // Verify: All elements should equal PATTERN
122 let mismatch = dst.iter().any(|i| *i != *pat);
123
124 if mismatch {
125 defmt::error!("FAIL: Mismatch detected!");
126 } else {
127 defmt::info!("PASS: Data verified.");
128 }
129}
diff --git a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs
new file mode 100644
index 000000000..80df40449
--- /dev/null
+++ b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs
@@ -0,0 +1,244 @@
1//! DMA ping-pong/double-buffer transfer example for MCXA276.
2//!
3//! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have
4//! a high-level and safe API for. It should not be taken as typical, recommended, or
5//! stable usage!
6//!
7//! This example demonstrates two approaches for ping-pong/double-buffering:
8//!
9//! ## Approach 1: Scatter/Gather with linked TCDs (manual)
10//! - Two TCDs link to each other for alternating transfers
11//! - Uses custom handler that delegates to on_interrupt() then signals completion
12//! - Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads,
13//! so we need an AtomicBool to track completion
14//!
15//! ## Approach 2: Half-transfer interrupt with wait_half() (NEW!)
16//! - Single continuous transfer over entire buffer
17//! - Uses half-transfer interrupt to know when first half is ready
18//! - Application can process first half while second half is being filled
19//!
20//! # Embassy-style features demonstrated:
21//! - `DmaChannel::new()` for channel creation
22//! - Scatter/gather with linked TCDs
23//! - Custom handler that delegates to HAL's `on_interrupt()` (best practice)
24//! - Standard `DmaCh1InterruptHandler` with `bind_interrupts!` macro
25//! - NEW: `wait_half()` for half-transfer interrupt handling
26
27#![no_std]
28#![no_main]
29
30use embassy_executor::Spawner;
31use embassy_mcxa::clocks::config::Div8;
32use embassy_mcxa::dma::{DmaChannel, Tcd, TransferOptions};
33use embassy_mcxa::pac;
34use static_cell::ConstStaticCell;
35use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
36
37// Source and destination buffers for Approach 1 (scatter/gather)
38static SRC: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8]);
39static DST: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]);
40
41// Source and destination buffers for Approach 2 (wait_half)
42static SRC2: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]);
43static DST2: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]);
44
45// TCD pool for scatter/gather - must be 32-byte aligned
46#[repr(C, align(32))]
47struct TcdPool([Tcd; 2]);
48
49static TCD_POOL: ConstStaticCell<TcdPool> = ConstStaticCell::new(TcdPool(
50 [Tcd {
51 saddr: 0,
52 soff: 0,
53 attr: 0,
54 nbytes: 0,
55 slast: 0,
56 daddr: 0,
57 doff: 0,
58 citer: 0,
59 dlast_sga: 0,
60 csr: 0,
61 biter: 0,
62 }; 2],
63));
64
65#[embassy_executor::main]
66async fn main(_spawner: Spawner) {
67 // Small delay to allow probe-rs to attach after reset
68 for _ in 0..100_000 {
69 cortex_m::asm::nop();
70 }
71
72 let mut cfg = hal::config::Config::default();
73 cfg.clock_cfg.sirc.fro_12m_enabled = true;
74 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
75 let p = hal::init(cfg);
76
77 defmt::info!("DMA ping-pong transfer example starting...");
78
79 defmt::info!("EDMA ping-pong transfer example begin.");
80
81 // Initialize buffers
82 let src = SRC.take();
83 let dst = DST.take();
84
85 defmt::info!("Source Buffer: {=[?]}", src.as_slice());
86 defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice());
87
88 defmt::info!("Configuring ping-pong DMA with Embassy-style API...");
89
90 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
91
92 // Configure ping-pong transfer using direct TCD access:
93 // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel.
94 // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1.
95 // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0.
96 let tcds = &mut TCD_POOL.take().0;
97
98 let half_len = 4usize;
99 let half_bytes = (half_len * 4) as u32;
100
101 unsafe {
102 let tcd0_addr = &tcds[0] as *const _ as u32;
103 let tcd1_addr = &tcds[1] as *const _ as u32;
104
105 // TCD0: First half -> Links to TCD1
106 tcds[0] = Tcd {
107 saddr: src.as_ptr() as u32,
108 soff: 4,
109 attr: 0x0202, // 32-bit src/dst
110 nbytes: half_bytes,
111 slast: 0,
112 daddr: dst.as_mut_ptr() as u32,
113 doff: 4,
114 citer: 1,
115 dlast_sga: tcd1_addr as i32,
116 csr: 0x0012, // ESG | INTMAJOR
117 biter: 1,
118 };
119
120 // TCD1: Second half -> Links to TCD0
121 tcds[1] = Tcd {
122 saddr: src.as_ptr().add(half_len) as u32,
123 soff: 4,
124 attr: 0x0202,
125 nbytes: half_bytes,
126 slast: 0,
127 daddr: dst.as_mut_ptr().add(half_len) as u32,
128 doff: 4,
129 citer: 1,
130 dlast_sga: tcd0_addr as i32,
131 csr: 0x0012,
132 biter: 1,
133 };
134
135 // Load TCD0 into hardware registers
136 dma_ch0.load_tcd(&tcds[0]);
137 }
138
139 defmt::info!("Triggering first half transfer...");
140
141 // Trigger first transfer (first half: SRC[0..4] -> DST[0..4])
142 unsafe {
143 dma_ch0.trigger_start();
144 }
145
146 let tcd = dma_ch0.tcd();
147 // Wait for first half
148 loop {
149 if tcd.tcd_saddr().read().bits() != src.as_ptr() as u32 {
150 break;
151 }
152 }
153
154 defmt::info!("First half transferred.");
155 defmt::info!("Triggering second half transfer...");
156
157 // Trigger second transfer (second half: SRC[4..8] -> DST[4..8])
158 unsafe {
159 dma_ch0.trigger_start();
160 }
161
162 // Wait for second half
163 loop {
164 if tcd.tcd_saddr().read().bits() != unsafe { src.as_ptr().add(half_len) } as u32 {
165 break;
166 }
167 }
168
169 defmt::info!("Second half transferred.");
170
171 defmt::info!("EDMA ping-pong transfer example finish.");
172 defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice());
173
174 // Verify: DST should match SRC
175 let mismatch = src != dst;
176
177 if mismatch {
178 defmt::error!("FAIL: Approach 1 mismatch detected!");
179 } else {
180 defmt::info!("PASS: Approach 1 data verified.");
181 }
182
183 // =========================================================================
184 // Approach 2: Half-Transfer Interrupt with wait_half() (NEW!)
185 // =========================================================================
186 //
187 // This approach uses a single continuous DMA transfer with half-transfer
188 // interrupt enabled. The wait_half() method allows you to be notified
189 // when the first half of the buffer is complete, so you can process it
190 // while the second half is still being filled.
191 //
192 // Benefits:
193 // - Simpler setup (no TCD pool needed)
194 // - True async/await support
195 // - Good for streaming data processing
196
197 defmt::info!("--- Approach 2: wait_half() demo ---");
198
199 // Enable DMA CH1 interrupt
200 unsafe {
201 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1);
202 }
203
204 // Initialize approach 2 buffers
205 let src2 = SRC2.take();
206 let dst2 = DST2.take();
207
208 defmt::info!("SRC2: {=[?]}", src2.as_slice());
209
210 let dma_ch1 = DmaChannel::new(p.DMA_CH1);
211
212 // Configure transfer with half-transfer interrupt enabled
213 let mut options = TransferOptions::default();
214 options.half_transfer_interrupt = true; // Enable half-transfer interrupt
215 options.complete_transfer_interrupt = true;
216
217 defmt::info!("Starting transfer with half_transfer_interrupt...");
218
219 // Create the transfer
220 let mut transfer = dma_ch1.mem_to_mem(src2, dst2, options).unwrap();
221
222 // Wait for half-transfer (first 4 elements)
223 defmt::info!("Waiting for first half...");
224 let _ok = transfer.wait_half().await.unwrap();
225
226 defmt::info!("Half-transfer complete!");
227
228 // Wait for complete transfer
229 defmt::info!("Waiting for second half...");
230 transfer.await.unwrap();
231
232 defmt::info!("Transfer complete! Full DST2: {=[?]}", dst2.as_slice());
233
234 // Verify approach 2
235 let mismatch2 = src2 != dst2;
236
237 if mismatch2 {
238 defmt::error!("FAIL: Approach 2 mismatch!");
239 } else {
240 defmt::info!("PASS: Approach 2 verified.");
241 }
242
243 defmt::info!("=== All ping-pong demos complete ===");
244}
diff --git a/examples/mcxa/src/bin/raw_dma_scatter_gather.rs b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs
new file mode 100644
index 000000000..eb9960764
--- /dev/null
+++ b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs
@@ -0,0 +1,165 @@
1//! DMA scatter-gather transfer example for MCXA276.
2//!
3//! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have
4//! a high-level and safe API for. It should not be taken as typical, recommended, or
5//! stable usage!
6//!
7//! This example demonstrates using DMA with scatter/gather to chain multiple
8//! transfer descriptors. The first TCD transfers the first half of the buffer,
9//! then automatically loads the second TCD to transfer the second half.
10//!
11//! # Embassy-style features demonstrated:
12//! - `DmaChannel::new()` for channel creation
13//! - Scatter/gather with chained TCDs
14//! - Custom handler that delegates to HAL's `on_interrupt()` (best practice)
15
16#![no_std]
17#![no_main]
18
19use embassy_executor::Spawner;
20use embassy_mcxa::clocks::config::Div8;
21use embassy_mcxa::dma::{DmaChannel, Tcd};
22use static_cell::ConstStaticCell;
23use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
24
25// Source and destination buffers
26static SRC: ConstStaticCell<[u32; 12]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
27static DST: ConstStaticCell<[u32; 12]> = ConstStaticCell::new([0; 12]);
28
29// TCD pool for scatter/gather - must be 32-byte aligned
30#[repr(C, align(32))]
31struct TcdPool([Tcd; 2]);
32
33static TCD_POOL: ConstStaticCell<TcdPool> = ConstStaticCell::new(TcdPool(
34 [Tcd {
35 saddr: 0,
36 soff: 0,
37 attr: 0,
38 nbytes: 0,
39 slast: 0,
40 daddr: 0,
41 doff: 0,
42 citer: 0,
43 dlast_sga: 0,
44 csr: 0,
45 biter: 0,
46 }; 2],
47));
48
49#[embassy_executor::main]
50async fn main(_spawner: Spawner) {
51 // Small delay to allow probe-rs to attach after reset
52 for _ in 0..100_000 {
53 cortex_m::asm::nop();
54 }
55
56 let mut cfg = hal::config::Config::default();
57 cfg.clock_cfg.sirc.fro_12m_enabled = true;
58 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
59 let p = hal::init(cfg);
60
61 defmt::info!("DMA scatter-gather transfer example starting...");
62
63 defmt::info!("EDMA scatter-gather transfer example begin.");
64
65 // Initialize buffers
66 let src = SRC.take();
67 let dst = DST.take();
68
69 defmt::info!("Source Buffer: {=[?]}", src.as_slice());
70 defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice());
71 defmt::info!("Configuring scatter-gather DMA with Embassy-style API...");
72
73 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
74 let src_ptr = src.as_ptr();
75 let dst_ptr = dst.as_mut_ptr();
76
77 // Configure scatter-gather transfer using direct TCD access:
78 // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel.
79 // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1.
80 // TCD1 transfers second half (SRC[4..12] -> DST[4..12]), last TCD.
81 unsafe {
82 let tcds = &mut TCD_POOL.take().0;
83
84 // In the first transfer, copy
85 tcds[0] = Tcd {
86 saddr: src_ptr as u32,
87 soff: 4,
88 attr: 0x0202, // 32-bit src/dst
89 nbytes: 4 * 4,
90 slast: 0,
91 daddr: dst_ptr as u32,
92 doff: 4,
93 citer: 1,
94 dlast_sga: tcds.as_ptr().add(1) as i32,
95 // ESG (scatter/gather) for non-last, INTMAJOR for all
96 csr: 0x0012,
97 biter: 1,
98 };
99
100 tcds[1] = Tcd {
101 saddr: src_ptr.add(4) as u32,
102 soff: 4,
103 attr: 0x0202, // 32-bit src/dst
104 nbytes: 8 * 4,
105 slast: 0,
106 daddr: dst_ptr.add(4) as u32,
107 doff: 4,
108 citer: 1,
109 dlast_sga: 0,
110 // ESG (scatter/gather) for non-last, INTMAJOR for all
111 csr: 0x0002,
112 biter: 1,
113 };
114
115 // Load TCD0 into hardware registers
116 dma_ch0.load_tcd(&tcds[0]);
117 }
118
119 defmt::info!("Triggering first half transfer...");
120
121 let tcd = dma_ch0.tcd();
122
123 // Trigger first transfer (first half: SRC[0..4] -> DST[0..4])
124 // TCD0 is currently loaded.
125 unsafe {
126 dma_ch0.trigger_start();
127 }
128
129 // Wait for first half
130 loop {
131 if tcd.tcd_saddr().read().bits() != src_ptr as u32 {
132 defmt::info!("saddr: {=u32}", tcd.tcd_saddr().read().bits());
133 defmt::info!("srptr: {=u32}", src_ptr as u32);
134 break;
135 }
136 }
137
138 defmt::info!("First half transferred.");
139 defmt::info!("Triggering second half transfer...");
140
141 // Trigger second transfer (second half: SRC[4..8] -> DST[4..8])
142 // TCD1 should have been loaded by the scatter/gather engine.
143 unsafe {
144 dma_ch0.trigger_start();
145 }
146
147 // Wait for second half
148 while !dma_ch0.is_done() {
149 cortex_m::asm::nop();
150 }
151
152 defmt::info!("Second half transferred.");
153
154 defmt::info!("EDMA scatter-gather transfer example finish.");
155 defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice());
156
157 // Verify: DST should match SRC
158 let mismatch = src != dst;
159
160 if mismatch {
161 defmt::error!("FAIL: Mismatch detected!");
162 } else {
163 defmt::info!("PASS: Data verified.");
164 }
165}
diff --git a/examples/mcxa/src/bin/rtc_alarm.rs b/examples/mcxa/src/bin/rtc_alarm.rs
new file mode 100644
index 000000000..fe355888b
--- /dev/null
+++ b/examples/mcxa/src/bin/rtc_alarm.rs
@@ -0,0 +1,47 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::bind_interrupts;
6use hal::rtc::{InterruptHandler, Rtc, RtcDateTime};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9bind_interrupts!(struct Irqs {
10 RTC => InterruptHandler<hal::rtc::Rtc0>;
11});
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let p = hal::init(hal::config::Config::default());
16
17 defmt::info!("=== RTC Alarm Example ===");
18
19 let rtc_config = hal::rtc::get_default_config();
20
21 let mut rtc = Rtc::new(p.RTC0, Irqs, rtc_config);
22
23 let now = RtcDateTime {
24 year: 2025,
25 month: 10,
26 day: 15,
27 hour: 14,
28 minute: 30,
29 second: 0,
30 };
31
32 rtc.stop();
33
34 defmt::info!("Time set to: 2025-10-15 14:30:00");
35 rtc.set_datetime(now);
36
37 let mut alarm = now;
38 alarm.second += 10;
39
40 defmt::info!("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)");
41 defmt::info!("RTC started, waiting for alarm...");
42
43 rtc.wait_for_alarm(alarm).await;
44 defmt::info!("*** ALARM TRIGGERED! ***");
45
46 defmt::info!("Example complete - Test PASSED!");
47}
diff --git a/examples/nrf54lm20/.cargo/config.toml b/examples/nrf54lm20/.cargo/config.toml
new file mode 100644
index 000000000..0ffebed14
--- /dev/null
+++ b/examples/nrf54lm20/.cargo/config.toml
@@ -0,0 +1,9 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# Note: it needs the latest git version of probe-rs
3runner = "probe-rs run --chip nrf54lm20a"
4
5[build]
6target = "thumbv8m.main-none-eabihf"
7
8[env]
9DEFMT_LOG = "trace"
diff --git a/examples/nrf54lm20/Cargo.toml b/examples/nrf54lm20/Cargo.toml
new file mode 100644
index 000000000..5482fc77a
--- /dev/null
+++ b/examples/nrf54lm20/Cargo.toml
@@ -0,0 +1,37 @@
1[package]
2edition = "2024"
3name = "embassy-nrf54lm20-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6publish = false
7
8[dependencies]
9embassy-futures = { version = "0.1.2", path = "../../embassy-futures" }
10embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
11embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
13embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf54lm20-app-s", "time-driver-grtc", "gpiote", "unstable-pac", "time"] }
14embedded-io = { version = "0.6.0", features = ["defmt-03"] }
15embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
16
17rand = { version = "0.9.0", default-features = false }
18
19defmt = "1.0.1"
20defmt-rtt = "1.0.0"
21panic-probe = { version = "1.0.0", features = ["print-defmt"] }
22
23cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
24cortex-m-rt = "0.7.0"
25
26embedded-storage = "0.3.1"
27portable-atomic = "1"
28
29static_cell = "2"
30
31[profile.release]
32debug = 2
33
34[package.metadata.embassy]
35build = [
36 { target = "thumbv8m.main-none-eabihf", artifact-dir = "out/examples/nrf54lm20" }
37]
diff --git a/examples/nrf54lm20/build.rs b/examples/nrf54lm20/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/nrf54lm20/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/nrf54lm20/memory.x b/examples/nrf54lm20/memory.x
new file mode 100644
index 000000000..564f488ac
--- /dev/null
+++ b/examples/nrf54lm20/memory.x
@@ -0,0 +1,5 @@
1MEMORY
2{
3 FLASH : ORIGIN = 0x00000000, LENGTH = 2036K
4 RAM : ORIGIN = 0x20000000, LENGTH = 512K
5}
diff --git a/examples/nrf54lm20/src/bin/blinky.rs b/examples/nrf54lm20/src/bin/blinky.rs
new file mode 100644
index 000000000..962e9f5a6
--- /dev/null
+++ b/examples/nrf54lm20/src/bin/blinky.rs
@@ -0,0 +1,23 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_nrf::gpio::{Level, Output, OutputDrive};
7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _};
9
10#[embassy_executor::main]
11async fn main(_spawner: Spawner) {
12 let p = embassy_nrf::init(Default::default());
13 let mut led = Output::new(p.P1_22, Level::Low, OutputDrive::HighDrive);
14
15 loop {
16 info!("high!");
17 led.set_high();
18 Timer::after_millis(300).await;
19 info!("low!");
20 led.set_low();
21 Timer::after_millis(300).await;
22 }
23}
diff --git a/examples/nrf54lm20/src/bin/gpio.rs b/examples/nrf54lm20/src/bin/gpio.rs
new file mode 100644
index 000000000..4b6b4630d
--- /dev/null
+++ b/examples/nrf54lm20/src/bin/gpio.rs
@@ -0,0 +1,23 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_nrf::gpio::{Input, Pull};
7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _};
9
10#[embassy_executor::main]
11async fn main(_spawner: Spawner) {
12 let p = embassy_nrf::init(Default::default());
13 let btn = Input::new(p.P1_26, Pull::Up);
14
15 loop {
16 if btn.is_high() {
17 info!("high");
18 } else {
19 info!("low");
20 }
21 Timer::after_millis(100).await;
22 }
23}
diff --git a/examples/nrf54lm20/src/bin/gpiote_channel.rs b/examples/nrf54lm20/src/bin/gpiote_channel.rs
new file mode 100644
index 000000000..0f30f06c7
--- /dev/null
+++ b/examples/nrf54lm20/src/bin/gpiote_channel.rs
@@ -0,0 +1,49 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_nrf::gpio::Pull;
7use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity};
8use {defmt_rtt as _, panic_probe as _};
9
10#[embassy_executor::main]
11async fn main(_spawner: Spawner) {
12 let p = embassy_nrf::init(Default::default());
13 info!("Starting!");
14
15 let mut ch1 = InputChannel::new(p.GPIOTE20_CH0, p.P1_26, Pull::Up, InputChannelPolarity::HiToLo);
16 let mut ch2 = InputChannel::new(p.GPIOTE20_CH1, p.P1_09, Pull::Up, InputChannelPolarity::LoToHi);
17 let mut ch3 = InputChannel::new(p.GPIOTE20_CH2, p.P1_08, Pull::Up, InputChannelPolarity::Toggle);
18 let mut ch4 = InputChannel::new(p.GPIOTE30_CH0, p.P0_05, Pull::Up, InputChannelPolarity::Toggle);
19
20 let button1 = async {
21 loop {
22 ch1.wait().await;
23 info!("Button 1 pressed")
24 }
25 };
26
27 let button2 = async {
28 loop {
29 ch2.wait().await;
30 info!("Button 2 released")
31 }
32 };
33
34 let button3 = async {
35 loop {
36 ch3.wait().await;
37 info!("Button 3 toggled")
38 }
39 };
40
41 let button4 = async {
42 loop {
43 ch4.wait().await;
44 info!("Button 4 toggled")
45 }
46 };
47
48 embassy_futures::join::join4(button1, button2, button3, button4).await;
49}
diff --git a/examples/nrf54lm20/src/bin/gpiote_port.rs b/examples/nrf54lm20/src/bin/gpiote_port.rs
new file mode 100644
index 000000000..6dbd63d92
--- /dev/null
+++ b/examples/nrf54lm20/src/bin/gpiote_port.rs
@@ -0,0 +1,33 @@
1#![no_std]
2#![no_main]
3
4use defmt::{info, unwrap};
5use embassy_executor::Spawner;
6use embassy_nrf::gpio::{Input, Pull};
7use {defmt_rtt as _, panic_probe as _};
8
9#[embassy_executor::task(pool_size = 4)]
10async fn button_task(n: usize, mut pin: Input<'static>) {
11 loop {
12 pin.wait_for_low().await;
13 info!("Button {:?} pressed!", n);
14 pin.wait_for_high().await;
15 info!("Button {:?} released!", n);
16 }
17}
18
19#[embassy_executor::main]
20async fn main(spawner: Spawner) {
21 let p = embassy_nrf::init(Default::default());
22 info!("Starting!");
23
24 let btn1 = Input::new(p.P1_26, Pull::Up);
25 let btn2 = Input::new(p.P1_09, Pull::Up);
26 let btn3 = Input::new(p.P1_08, Pull::Up);
27 let btn4 = Input::new(p.P0_05, Pull::Up);
28
29 spawner.spawn(unwrap!(button_task(1, btn1)));
30 spawner.spawn(unwrap!(button_task(2, btn2)));
31 spawner.spawn(unwrap!(button_task(3, btn3)));
32 spawner.spawn(unwrap!(button_task(4, btn4)));
33}
diff --git a/examples/nrf54lm20/src/bin/timer.rs b/examples/nrf54lm20/src/bin/timer.rs
new file mode 100644
index 000000000..68acc91c1
--- /dev/null
+++ b/examples/nrf54lm20/src/bin/timer.rs
@@ -0,0 +1,30 @@
1#![no_std]
2#![no_main]
3
4use defmt::{info, unwrap};
5use embassy_executor::Spawner;
6use embassy_time::Timer;
7use {defmt_rtt as _, panic_probe as _};
8
9#[embassy_executor::task]
10async fn run1() {
11 loop {
12 info!("BIG INFREQUENT TICK");
13 Timer::after_secs(10).await;
14 }
15}
16
17#[embassy_executor::task]
18async fn run2() {
19 loop {
20 info!("tick");
21 Timer::after_secs(1).await;
22 }
23}
24
25#[embassy_executor::main]
26async fn main(spawner: Spawner) {
27 let _p = embassy_nrf::init(Default::default());
28 spawner.spawn(unwrap!(run1()));
29 spawner.spawn(unwrap!(run2()));
30}
diff --git a/examples/rp235x/src/bin/pio_i2s.rs b/examples/rp235x/src/bin/pio_i2s.rs
index cfcb0221d..7ed952a40 100644
--- a/examples/rp235x/src/bin/pio_i2s.rs
+++ b/examples/rp235x/src/bin/pio_i2s.rs
@@ -5,7 +5,7 @@
5//! bclk : GPIO 18 5//! bclk : GPIO 18
6//! lrc : GPIO 19 6//! lrc : GPIO 19
7//! din : GPIO 20 7//! din : GPIO 20
8//! Then hold down the boot select button to trigger a rising triangle waveform. 8//! Then short GPIO 0 to GND to trigger a rising triangle waveform.
9 9
10#![no_std] 10#![no_std]
11#![no_main] 11#![no_main]
@@ -70,7 +70,7 @@ async fn main(_spawner: Spawner) {
70 // but don't await the returned future, yet 70 // but don't await the returned future, yet
71 let dma_future = i2s.write(front_buffer); 71 let dma_future = i2s.write(front_buffer);
72 72
73 // fade in audio when bootsel is pressed 73 // fade in audio when GPIO 0 pin is shorted to GND
74 let fade_target = if fade_input.is_low() { i32::MAX } else { 0 }; 74 let fade_target = if fade_input.is_low() { i32::MAX } else { 0 };
75 75
76 // fill back buffer with fresh audio samples before awaiting the dma future 76 // fill back buffer with fresh audio samples before awaiting the dma future
diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs
index 50008a37b..5e39a06f5 100644
--- a/examples/stm32f4/src/bin/pwm_complementary.rs
+++ b/examples/stm32f4/src/bin/pwm_complementary.rs
@@ -33,7 +33,7 @@ async fn main(_spawner: Spawner) {
33 ); 33 );
34 34
35 let max = pwm.get_max_duty(); 35 let max = pwm.get_max_duty();
36 pwm.set_dead_time(max / 1024); 36 pwm.set_dead_time((max / 1024) as u16);
37 37
38 pwm.enable(Channel::Ch1); 38 pwm.enable(Channel::Ch1);
39 39
diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs
index fe0f887bf..098fd6986 100644
--- a/examples/stm32f4/src/bin/sdmmc.rs
+++ b/examples/stm32f4/src/bin/sdmmc.rs
@@ -3,7 +3,8 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; 6use embassy_stm32::sdmmc::Sdmmc;
7use embassy_stm32::sdmmc::sd::{CmdBlock, DataBlock, StorageDevice};
7use embassy_stm32::time::{Hertz, mhz}; 8use embassy_stm32::time::{Hertz, mhz};
8use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; 9use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc};
9use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
@@ -57,31 +58,24 @@ async fn main(_spawner: Spawner) {
57 // Should print 400kHz for initialization 58 // Should print 400kHz for initialization
58 info!("Configured clock: {}", sdmmc.clock().0); 59 info!("Configured clock: {}", sdmmc.clock().0);
59 60
60 let mut err = None; 61 let mut cmd_block = CmdBlock::new();
61 loop { 62
62 match sdmmc.init_sd_card(mhz(24)).await { 63 let mut storage = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(24))
63 Ok(_) => break, 64 .await
64 Err(e) => { 65 .unwrap();
65 if err != Some(e) {
66 info!("waiting for card error, retrying: {:?}", e);
67 err = Some(e);
68 }
69 }
70 }
71 }
72 66
73 let card = unwrap!(sdmmc.card()); 67 let card = storage.card();
74 68
75 info!("Card: {:#?}", Debug2Format(card)); 69 info!("Card: {:#?}", Debug2Format(&card));
76 info!("Clock: {}", sdmmc.clock()); 70 info!("Clock: {}", storage.sdmmc.clock());
77 71
78 // Arbitrary block index 72 // Arbitrary block index
79 let block_idx = 16; 73 let block_idx = 16;
80 74
81 // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware. 75 // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware.
82 let mut block = DataBlock([0u8; 512]); 76 let mut block = DataBlock::new();
83 77
84 sdmmc.read_block(block_idx, &mut block).await.unwrap(); 78 storage.read_block(block_idx, &mut block).await.unwrap();
85 info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); 79 info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]);
86 80
87 if !ALLOW_WRITES { 81 if !ALLOW_WRITES {
@@ -91,17 +85,17 @@ async fn main(_spawner: Spawner) {
91 85
92 info!("Filling block with 0x55"); 86 info!("Filling block with 0x55");
93 block.fill(0x55); 87 block.fill(0x55);
94 sdmmc.write_block(block_idx, &block).await.unwrap(); 88 storage.write_block(block_idx, &block).await.unwrap();
95 info!("Write done"); 89 info!("Write done");
96 90
97 sdmmc.read_block(block_idx, &mut block).await.unwrap(); 91 storage.read_block(block_idx, &mut block).await.unwrap();
98 info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); 92 info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]);
99 93
100 info!("Filling block with 0xAA"); 94 info!("Filling block with 0xAA");
101 block.fill(0xAA); 95 block.fill(0xAA);
102 sdmmc.write_block(block_idx, &block).await.unwrap(); 96 storage.write_block(block_idx, &block).await.unwrap();
103 info!("Write done"); 97 info!("Write done");
104 98
105 sdmmc.read_block(block_idx, &mut block).await.unwrap(); 99 storage.read_block(block_idx, &mut block).await.unwrap();
106 info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); 100 info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]);
107} 101}
diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs
index ccfd0661e..4e556f0d4 100644
--- a/examples/stm32f4/src/bin/ws2812_pwm.rs
+++ b/examples/stm32f4/src/bin/ws2812_pwm.rs
@@ -61,7 +61,7 @@ async fn main(_spawner: Spawner) {
61 // construct ws2812 non-return-to-zero (NRZ) code bit by bit 61 // construct ws2812 non-return-to-zero (NRZ) code bit by bit
62 // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low 62 // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low
63 63
64 let max_duty = ws2812_pwm.max_duty_cycle(); 64 let max_duty = ws2812_pwm.max_duty_cycle() as u16;
65 let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing 65 let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing
66 let n1 = 2 * n0; // ws2812 Bit 1 high level timing 66 let n1 = 2 * n0; // ws2812 Bit 1 high level timing
67 67
diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs
index 8809b5d0c..e5d261d89 100644
--- a/examples/stm32f7/src/bin/sdmmc.rs
+++ b/examples/stm32f7/src/bin/sdmmc.rs
@@ -4,6 +4,7 @@
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::sdmmc::Sdmmc; 6use embassy_stm32::sdmmc::Sdmmc;
7use embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice};
7use embassy_stm32::time::{Hertz, mhz}; 8use embassy_stm32::time::{Hertz, mhz};
8use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; 9use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc};
9use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
@@ -54,9 +55,13 @@ async fn main(_spawner: Spawner) {
54 // Should print 400kHz for initialization 55 // Should print 400kHz for initialization
55 info!("Configured clock: {}", sdmmc.clock().0); 56 info!("Configured clock: {}", sdmmc.clock().0);
56 57
57 unwrap!(sdmmc.init_sd_card(mhz(25)).await); 58 let mut cmd_block = CmdBlock::new();
58 59
59 let card = unwrap!(sdmmc.card()); 60 let storage = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(25))
61 .await
62 .unwrap();
60 63
61 info!("Card: {:#?}", Debug2Format(card)); 64 let card = storage.card();
65
66 info!("Card: {:#?}", Debug2Format(&card));
62} 67}
diff --git a/examples/stm32h5/src/bin/adc_dma.rs b/examples/stm32h5/src/bin/adc_dma.rs
index 2138257f7..e625e3a34 100644
--- a/examples/stm32h5/src/bin/adc_dma.rs
+++ b/examples/stm32h5/src/bin/adc_dma.rs
@@ -66,7 +66,7 @@ async fn adc2_task(
66 adc_task(adc, dma, pin1, pin2).await; 66 adc_task(adc, dma, pin1, pin2).await;
67} 67}
68 68
69async fn adc_task<'a, T: adc::Instance>( 69async fn adc_task<'a, T: adc::DefaultInstance>(
70 adc: Peri<'a, T>, 70 adc: Peri<'a, T>,
71 mut dma: Peri<'a, impl RxDma<T>>, 71 mut dma: Peri<'a, impl RxDma<T>>,
72 pin1: impl AdcChannel<T>, 72 pin1: impl AdcChannel<T>,
diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs
index 847b70c85..0300f83bf 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 = Some(mclk_div); 66 tx_config.master_clock_divider = 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;
diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs
index 4977fec79..f2e5bedeb 100644
--- a/examples/stm32h7/src/bin/sdmmc.rs
+++ b/examples/stm32h7/src/bin/sdmmc.rs
@@ -4,6 +4,7 @@
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::sdmmc::Sdmmc; 6use embassy_stm32::sdmmc::Sdmmc;
7use embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice};
7use embassy_stm32::time::mhz; 8use embassy_stm32::time::mhz;
8use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; 9use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc};
9use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
@@ -13,7 +14,7 @@ bind_interrupts!(struct Irqs {
13}); 14});
14 15
15#[embassy_executor::main] 16#[embassy_executor::main]
16async fn main(_spawner: Spawner) -> ! { 17async fn main(_spawner: Spawner) {
17 let mut config = Config::default(); 18 let mut config = Config::default();
18 { 19 {
19 use embassy_stm32::rcc::*; 20 use embassy_stm32::rcc::*;
@@ -53,11 +54,13 @@ async fn main(_spawner: Spawner) -> ! {
53 // Should print 400kHz for initialization 54 // Should print 400kHz for initialization
54 info!("Configured clock: {}", sdmmc.clock().0); 55 info!("Configured clock: {}", sdmmc.clock().0);
55 56
56 unwrap!(sdmmc.init_sd_card(mhz(25)).await); 57 let mut cmd_block = CmdBlock::new();
57 58
58 let card = unwrap!(sdmmc.card()); 59 let storage = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(25))
60 .await
61 .unwrap();
59 62
60 info!("Card: {:#?}", Debug2Format(card)); 63 let card = storage.card();
61 64
62 loop {} 65 info!("Card: {:#?}", Debug2Format(&card));
63} 66}
diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs
index 5c29602c6..959e2aa18 100644
--- a/examples/stm32h723/src/bin/spdifrx.rs
+++ b/examples/stm32h723/src/bin/spdifrx.rs
@@ -168,7 +168,6 @@ 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 u16; 170 sai_config.frame_length = (CHANNEL_COUNT * 32) as u16;
171 sai_config.master_clock_divider = None;
172 171
173 let (sub_block_tx, _) = hal::sai::split_subblocks(sai); 172 let (sub_block_tx, _) = hal::sai::split_subblocks(sai);
174 Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config) 173 Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config)
diff --git a/examples/stm32n6/Cargo.toml b/examples/stm32n6/Cargo.toml
index 5ed28eed1..5ad5b97ce 100644
--- a/examples/stm32n6/Cargo.toml
+++ b/examples/stm32n6/Cargo.toml
@@ -32,6 +32,8 @@ micromath = "2.0.0"
32stm32-fmc = "0.3.0" 32stm32-fmc = "0.3.0"
33embedded-storage = "0.3.1" 33embedded-storage = "0.3.1"
34static_cell = "2" 34static_cell = "2"
35hmac = "0.12.1"
36sha2 = { version = "0.10.9", default-features = false }
35 37
36 38
37# cargo build/run 39# cargo build/run
diff --git a/examples/stm32n6/src/bin/crc.rs b/examples/stm32n6/src/bin/crc.rs
new file mode 100644
index 000000000..d1b545d5b
--- /dev/null
+++ b/examples/stm32n6/src/bin/crc.rs
@@ -0,0 +1,31 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::crc::{Config, Crc, InputReverseConfig, PolySize};
7use {defmt_rtt as _, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 let p = embassy_stm32::init(Default::default());
12 info!("Hello World!");
13
14 // Setup for: https://crccalc.com/?crc=Life, it never dieWomen are my favorite guy&method=crc32&datatype=ascii&outtype=0
15 let mut crc = Crc::new(
16 p.CRC,
17 unwrap!(Config::new(
18 InputReverseConfig::Byte,
19 true,
20 PolySize::Width32,
21 0xFFFFFFFF,
22 0x04C11DB7
23 )),
24 );
25
26 let output = crc.feed_bytes(b"Life, it never die\nWomen are my favorite guy") ^ 0xFFFFFFFF;
27
28 defmt::assert_eq!(output, 0x33F0E26B);
29
30 cortex_m::asm::bkpt();
31}
diff --git a/examples/stm32n6/src/bin/hash.rs b/examples/stm32n6/src/bin/hash.rs
new file mode 100644
index 000000000..9f248318f
--- /dev/null
+++ b/examples/stm32n6/src/bin/hash.rs
@@ -0,0 +1,78 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_stm32::hash::*;
7use embassy_stm32::{Config, bind_interrupts, hash, peripherals};
8use embassy_time::Instant;
9use hmac::{Hmac, Mac};
10use sha2::{Digest, Sha256};
11use {defmt_rtt as _, panic_probe as _};
12
13type HmacSha256 = Hmac<Sha256>;
14
15bind_interrupts!(struct Irqs {
16 HASH => hash::InterruptHandler<peripherals::HASH>;
17});
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) -> ! {
21 let config = Config::default();
22 let p = embassy_stm32::init(config);
23
24 let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh";
25 let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr";
26
27 let mut hw_hasher = Hash::new_blocking(p.HASH, Irqs);
28
29 let hw_start_time = Instant::now();
30
31 // Compute a digest in hardware.
32 let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None);
33 hw_hasher.update_blocking(&mut context, test_1);
34 hw_hasher.update_blocking(&mut context, test_2);
35 let mut hw_digest: [u8; 32] = [0; 32];
36 hw_hasher.finish_blocking(context, &mut hw_digest);
37
38 let hw_end_time = Instant::now();
39 let hw_execution_time = hw_end_time - hw_start_time;
40
41 let sw_start_time = Instant::now();
42
43 // Compute a digest in software.
44 let mut sw_hasher = Sha256::new();
45 sw_hasher.update(test_1);
46 sw_hasher.update(test_2);
47 let sw_digest = sw_hasher.finalize();
48
49 let sw_end_time = Instant::now();
50 let sw_execution_time = sw_end_time - sw_start_time;
51
52 info!("Hardware Digest: {:?}", hw_digest);
53 info!("Software Digest: {:?}", sw_digest[..]);
54 info!("Hardware Execution Time: {:?}", hw_execution_time);
55 info!("Software Execution Time: {:?}", sw_execution_time);
56 assert_eq!(hw_digest, sw_digest[..]);
57
58 let hmac_key: [u8; 64] = [0x55; 64];
59
60 // Compute HMAC in hardware.
61 let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key));
62 hw_hasher.update_blocking(&mut sha256hmac_context, test_1);
63 hw_hasher.update_blocking(&mut sha256hmac_context, test_2);
64 let mut hw_hmac: [u8; 32] = [0; 32];
65 hw_hasher.finish_blocking(sha256hmac_context, &mut hw_hmac);
66
67 // Compute HMAC in software.
68 let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap();
69 sw_mac.update(test_1);
70 sw_mac.update(test_2);
71 let sw_hmac = sw_mac.finalize().into_bytes();
72
73 info!("Hardware HMAC: {:?}", hw_hmac);
74 info!("Software HMAC: {:?}", sw_hmac[..]);
75 assert_eq!(hw_hmac, sw_hmac[..]);
76
77 loop {}
78}
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml
index 83119e3a0..83f7cb56b 100644
--- a/examples/stm32wb/Cargo.toml
+++ b/examples/stm32wb/Cargo.toml
@@ -26,8 +26,8 @@ static_cell = "2"
26 26
27[features] 27[features]
28default = ["ble", "mac"] 28default = ["ble", "mac"]
29mac = ["embassy-stm32-wpan/mac", "dep:embassy-net"] 29mac = ["embassy-stm32-wpan/wb55_mac", "dep:embassy-net"]
30ble = ["embassy-stm32-wpan/ble"] 30ble = ["embassy-stm32-wpan/wb55_ble"]
31 31
32[[bin]] 32[[bin]]
33name = "tl_mbox_ble" 33name = "tl_mbox_ble"
diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml
index 3496b41b0..b10114420 100644
--- a/examples/stm32wba/Cargo.toml
+++ b/examples/stm32wba/Cargo.toml
@@ -6,7 +6,8 @@ license = "MIT OR Apache-2.0"
6publish = false 6publish = false
7 7
8[dependencies] 8[dependencies]
9embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba55cg", "time-driver-any", "memory-x", "exti"] } 9embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] }
10embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wba52cg"] }
10embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 11embassy-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", "defmt"] } 12embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-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"] }
@@ -22,6 +23,11 @@ panic-probe = { version = "1.0.0", features = ["print-defmt"] }
22heapless = { version = "0.8", default-features = false } 23heapless = { version = "0.8", default-features = false }
23static_cell = "2" 24static_cell = "2"
24 25
26[features]
27default = ["ble", "mac"]
28mac = ["embassy-stm32-wpan/wba_mac", "dep:embassy-net"]
29ble = ["embassy-stm32-wpan/wba_ble"]
30
25[profile.release] 31[profile.release]
26debug = 2 32debug = 2
27 33
diff --git a/examples/stm32wba/src/bin/mac_ffd.rs b/examples/stm32wba/src/bin/mac_ffd.rs
new file mode 100644
index 000000000..b15fb3452
--- /dev/null
+++ b/examples/stm32wba/src/bin/mac_ffd.rs
@@ -0,0 +1,58 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_stm32::rcc::{Sysclk, mux};
8use embassy_stm32_wpan::bindings::mac::ST_MAC_callbacks_t;
9use {defmt_rtt as _, panic_probe as _};
10
11static _MAC_CALLBACKS: ST_MAC_callbacks_t = ST_MAC_callbacks_t {
12 mlmeAssociateCnfCb: None, // ST_MAC_MLMEAssociateCnfCbPtr,
13 mlmeAssociateIndCb: None, // ST_MAC_MLMEAssociateIndCbPtr,
14 mlmeBeaconNotifyIndCb: None, // ST_MAC_MLMEBeaconNotifyIndCbPtr,
15 mlmeCalibrateCnfCb: None, // ST_MAC_MLMECalibrateCnfCbPtr,
16 mlmeCommStatusIndCb: None, // ST_MAC_MLMECommStatusIndCbPtr,
17 mlmeDisassociateCnfCb: None, // ST_MAC_MLMEDisassociateCnfCbPtr,
18 mlmeDisassociateIndCb: None, // ST_MAC_MLMEDisassociateIndCbPtr,
19 mlmeDpsCnfCb: None, // ST_MAC_MLMEDpsCnfCbPtr,
20 mlmeDpsIndCb: None, // ST_MAC_MLMEDpsIndCbPtr,
21 mlmeGetCnfCb: None, // ST_MAC_MLMEGetCnfCbPtr,
22 mlmeGtsCnfCb: None, // ST_MAC_MLMEGtsCnfCbPtr,
23 mlmeGtsIndCb: None, // ST_MAC_MLMEGtsIndCbPtr,
24 mlmeOrphanIndCb: None, // ST_MAC_MLMEOrphanIndCbPtr,
25 mlmePollCnfCb: None, // ST_MAC_MLMEPollCnfCbPtr,
26 mlmeResetCnfCb: None, // ST_MAC_MLMEResetCnfCbPtr,
27 mlmeRxEnableCnfCb: None, // ST_MAC_MLMERxEnableCnfCbPtr,
28 mlmeScanCnfCb: None, // ST_MAC_MLMEScanCnfCbPtr,
29 mlmeSetCnfCb: None, // ST_MAC_MLMESetCnfCbPtr,
30 mlmeSoundingCnfCb: None, // ST_MAC_MLMESoundingCnfCbPtr,
31 mlmeStartCnfCb: None, // ST_MAC_MLMEStartCnfCbPtr,
32 mlmeSyncLossIndCb: None, // ST_MAC_MLMESyncLossIndCbPtr,
33 mcpsDataIndCb: None, // ST_MAC_MCPSDataIndCbPtr,
34 mcpsDataCnfCb: None, // ST_MAC_MCPSDataCnfCbPtr,
35 mcpsPurgeCnfCb: None, // ST_MAC_MCPSPurgeCnfCbPtr,
36 mlmePollIndCb: None, // ST_MAC_MLMEPollIndCbPtr,
37 mlmeBeaconReqIndCb: None, // ST_MAC_MLMEBeaconReqIndCbPtr,
38 mlmeBeaconCnfCb: None, // ST_MAC_MLMEBeaconCnfCbPtr,
39 mlmeGetPwrInfoTableCnfCb: None, // ST_MAC_MLMEGetPwrInfoTableCnfCbPtr,
40 mlmeSetPwrInfoTableCnfCb: None, // ST_MAC_MLMESetPwrInfoTableCnfCbPtr,
41};
42
43#[embassy_executor::main]
44async fn main(_spawner: Spawner) {
45 let mut config = Config::default();
46
47 config.rcc.sys = Sysclk::HSI;
48 config.rcc.mux.rngsel = mux::Rngsel::HSI;
49
50 let _p = embassy_stm32::init(config);
51 info!("Hello World!");
52
53 // let status = unsafe { ST_MAC_init(&_MAC_CALLBACKS as *const _ as *mut _) };
54 //
55 // info!("mac init: {}", status);
56
57 cortex_m::asm::bkpt();
58}
diff --git a/examples/stm32wba/src/bin/rtc.rs b/examples/stm32wba/src/bin/rtc.rs
new file mode 100644
index 000000000..cef8501e0
--- /dev/null
+++ b/examples/stm32wba/src/bin/rtc.rs
@@ -0,0 +1,62 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_stm32::rcc::*;
8use embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig};
9use embassy_time::Timer;
10use {defmt_rtt as _, panic_probe as _};
11
12pub fn pll_init(config: &mut Config) {
13 config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {
14 source: PllSource::HSI,
15 prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz
16 mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO
17 divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)
18 // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)
19 divq: None,
20 divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)
21 frac: Some(0), // Fractional part (enabled)
22 });
23
24 config.rcc.ahb_pre = AHBPrescaler::DIV1;
25 config.rcc.apb1_pre = APBPrescaler::DIV1;
26 config.rcc.apb2_pre = APBPrescaler::DIV1;
27 config.rcc.apb7_pre = APBPrescaler::DIV1;
28 config.rcc.ahb5_pre = AHB5Prescaler::DIV4;
29
30 // voltage scale for max performance
31 config.rcc.voltage_scale = VoltageScale::RANGE1;
32 // route PLL1_P into the USB‐OTG‐HS block
33 config.rcc.sys = Sysclk::PLL1_R;
34}
35
36#[embassy_executor::main]
37async fn main(_spawner: Spawner) {
38 let mut config = Config::default();
39
40 pll_init(&mut config);
41
42 let p = embassy_stm32::init(config);
43
44 let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
45
46 // Setting datetime
47 let initial_datetime = DateTime::from(1970, 1, 1, DayOfWeek::Thursday, 0, 00, 00, 0).unwrap();
48 match rtc.0.set_datetime(initial_datetime) {
49 Ok(()) => info!("RTC set successfully."),
50 Err(e) => error!("Failed to set RTC date/time: {:?}", e),
51 }
52
53 // Reading datetime every 1s
54 loop {
55 match rtc.1.now() {
56 Ok(result) => info!("{}", result),
57 Err(e) => error!("Failed to set RTC date/time: {:?}", e),
58 }
59
60 Timer::after_millis(1000).await;
61 }
62}
diff --git a/examples/stm32wba6/src/bin/blinky.rs b/examples/stm32wba6/src/bin/blinky.rs
index 0d803b257..95f3339b7 100644
--- a/examples/stm32wba6/src/bin/blinky.rs
+++ b/examples/stm32wba6/src/bin/blinky.rs
@@ -3,16 +3,45 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::Config;
6use embassy_stm32::gpio::{Level, Output, Speed}; 7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::rcc::{
9 AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,
10};
7use embassy_time::Timer; 11use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
9 13
10#[embassy_executor::main] 14#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 15async fn main(_spawner: Spawner) {
12 let p = embassy_stm32::init(Default::default()); 16 let mut config = Config::default();
17 // Fine-tune PLL1 dividers/multipliers
18 config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {
19 source: PllSource::HSI,
20 prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz
21 mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO
22 divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)
23 // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)
24 divq: None,
25 divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)
26 frac: Some(0), // Fractional part (enabled)
27 });
28
29 config.rcc.ahb_pre = AHBPrescaler::DIV1;
30 config.rcc.apb1_pre = APBPrescaler::DIV1;
31 config.rcc.apb2_pre = APBPrescaler::DIV1;
32 config.rcc.apb7_pre = APBPrescaler::DIV1;
33 config.rcc.ahb5_pre = AHB5Prescaler::DIV4;
34
35 // voltage scale for max performance
36 config.rcc.voltage_scale = VoltageScale::RANGE1;
37 // route PLL1_P into the USB‐OTG‐HS block
38 config.rcc.sys = Sysclk::PLL1_R;
39
40 let p = embassy_stm32::init(config);
13 info!("Hello World!"); 41 info!("Hello World!");
14 42
15 let mut led = Output::new(p.PB4, Level::High, Speed::Low); 43 // LD2 - PC4
44 let mut led = Output::new(p.PC4, Level::High, Speed::Low);
16 45
17 loop { 46 loop {
18 info!("high"); 47 info!("high");
diff --git a/examples/stm32wba6/src/bin/rtc.rs b/examples/stm32wba6/src/bin/rtc.rs
new file mode 100644
index 000000000..cef8501e0
--- /dev/null
+++ b/examples/stm32wba6/src/bin/rtc.rs
@@ -0,0 +1,62 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_stm32::rcc::*;
8use embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig};
9use embassy_time::Timer;
10use {defmt_rtt as _, panic_probe as _};
11
12pub fn pll_init(config: &mut Config) {
13 config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {
14 source: PllSource::HSI,
15 prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz
16 mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO
17 divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)
18 // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)
19 divq: None,
20 divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)
21 frac: Some(0), // Fractional part (enabled)
22 });
23
24 config.rcc.ahb_pre = AHBPrescaler::DIV1;
25 config.rcc.apb1_pre = APBPrescaler::DIV1;
26 config.rcc.apb2_pre = APBPrescaler::DIV1;
27 config.rcc.apb7_pre = APBPrescaler::DIV1;
28 config.rcc.ahb5_pre = AHB5Prescaler::DIV4;
29
30 // voltage scale for max performance
31 config.rcc.voltage_scale = VoltageScale::RANGE1;
32 // route PLL1_P into the USB‐OTG‐HS block
33 config.rcc.sys = Sysclk::PLL1_R;
34}
35
36#[embassy_executor::main]
37async fn main(_spawner: Spawner) {
38 let mut config = Config::default();
39
40 pll_init(&mut config);
41
42 let p = embassy_stm32::init(config);
43
44 let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
45
46 // Setting datetime
47 let initial_datetime = DateTime::from(1970, 1, 1, DayOfWeek::Thursday, 0, 00, 00, 0).unwrap();
48 match rtc.0.set_datetime(initial_datetime) {
49 Ok(()) => info!("RTC set successfully."),
50 Err(e) => error!("Failed to set RTC date/time: {:?}", e),
51 }
52
53 // Reading datetime every 1s
54 loop {
55 match rtc.1.now() {
56 Ok(result) => info!("{}", result),
57 Err(e) => error!("Failed to set RTC date/time: {:?}", e),
58 }
59
60 Timer::after_millis(1000).await;
61 }
62}
diff --git a/fmtall.sh b/fmtall.sh
new file mode 100755
index 000000000..9cadcdbe9
--- /dev/null
+++ b/fmtall.sh
@@ -0,0 +1,13 @@
1#!/bin/bash
2
3set -euo pipefail
4
5# We need the nightly toolchain for this
6mv rust-toolchain-nightly.toml rust-toolchain.toml
7
8# Similar to the CI workflow, but don't just CHECK, actualy DO the formatting
9find . -name '*.rs' -not -path '*target*' | xargs rustfmt --skip-children --unstable-features --edition 2024
10
11# Put the toolchains back, copy back to nightly and do a clean checkout of rust-toolchain
12mv rust-toolchain.toml rust-toolchain-nightly.toml
13git checkout -- rust-toolchain.toml
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index 496a9de18..6ee7f8e84 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -51,8 +51,8 @@ stop = ["embassy-stm32/low-power", "embassy-stm32/low-power-debug-with-sleep"]
51chrono = ["embassy-stm32/chrono", "dep:chrono"] 51chrono = ["embassy-stm32/chrono", "dep:chrono"]
52can = [] 52can = []
53fdcan = [] 53fdcan = []
54ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] 54ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/wb55_ble"]
55mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] 55mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/wb55_mac"]
56embassy-stm32-wpan = [] 56embassy-stm32-wpan = []
57not-gpdma = [] 57not-gpdma = []
58dac = [] 58dac = []
@@ -77,7 +77,7 @@ embassy-executor = { version = "0.9.0", path = "../../embassy-executor", feature
77embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "tick-hz-131_072", "defmt-timestamp-uptime"] } 77embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "tick-hz-131_072", "defmt-timestamp-uptime"] }
78embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "memory-x", "time-driver-any", "_allow-disable-rtc"] } 78embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "memory-x", "time-driver-any", "_allow-disable-rtc"] }
79embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } 79embassy-futures = { version = "0.1.2", path = "../../embassy-futures" }
80embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg", "ble"] } 80embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg", "wb55_ble"] }
81embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } 81embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
82perf-client = { path = "../perf-client" } 82perf-client = { path = "../perf-client" }
83 83
diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs
index 9f9c526e1..07422c42e 100644
--- a/tests/stm32/src/bin/sdmmc.rs
+++ b/tests/stm32/src/bin/sdmmc.rs
@@ -7,7 +7,8 @@ mod common;
7use common::*; 7use common::*;
8use defmt::assert_eq; 8use defmt::assert_eq;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; 10use embassy_stm32::sdmmc::Sdmmc;
11use embassy_stm32::sdmmc::sd::{CmdBlock, DataBlock, StorageDevice};
11use embassy_stm32::time::mhz; 12use embassy_stm32::time::mhz;
12use embassy_stm32::{bind_interrupts, peripherals, sdmmc}; 13use embassy_stm32::{bind_interrupts, peripherals, sdmmc};
13use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
@@ -28,16 +29,16 @@ async fn main(_spawner: Spawner) {
28 // Arbitrary block index 29 // Arbitrary block index
29 let block_idx = 16; 30 let block_idx = 16;
30 31
31 let mut pattern1 = DataBlock([0u8; 512]); 32 let mut pattern1 = DataBlock::new();
32 let mut pattern2 = DataBlock([0u8; 512]); 33 let mut pattern2 = DataBlock::new();
33 for i in 0..512 { 34 for i in 0..512 {
34 pattern1[i] = i as u8; 35 pattern1[i] = i as u8;
35 pattern2[i] = !i as u8; 36 pattern2[i] = !i as u8;
36 } 37 }
37 let patterns = [pattern1.clone(), pattern2.clone()]; 38 let patterns = [pattern1.clone(), pattern2.clone()];
38 39
39 let mut block = DataBlock([0u8; 512]); 40 let mut block = DataBlock::new();
40 let mut blocks = [DataBlock([0u8; 512]), DataBlock([0u8; 512])]; 41 let mut blocks = [DataBlock::new(), DataBlock::new()];
41 42
42 // ======== Try 4bit. ============== 43 // ======== Try 4bit. ==============
43 info!("initializing in 4-bit mode..."); 44 info!("initializing in 4-bit mode...");
@@ -54,43 +55,80 @@ async fn main(_spawner: Spawner) {
54 Default::default(), 55 Default::default(),
55 ); 56 );
56 57
57 let mut err = None; 58 let mut cmd_block = CmdBlock::new();
58 loop {
59 match s.init_sd_card(mhz(24)).await {
60 Ok(_) => break,
61 Err(e) => {
62 if err != Some(e) {
63 info!("waiting for card: {:?}", e);
64 err = Some(e);
65 }
66 }
67 }
68 }
69 59
70 let card = unwrap!(s.card()); 60 let mut storage = loop {
71 61 if let Ok(storage) = StorageDevice::new_sd_card(&mut s, &mut cmd_block, mhz(24)).await {
72 info!("Card: {:#?}", Debug2Format(card)); 62 break storage;
73 info!("Clock: {}", s.clock()); 63 }
64 };
65
66 let card = storage.card();
67
68 info!("Card: {:#?}", Debug2Format(&card));
69 info!("Clock: {}", storage.sdmmc.clock());
70
71 // card_type: HighCapacity,
72 // ocr: OCR: Operation Conditions Register {
73 // Voltage Window (mV): (2700, 3600),
74 // S18A (UHS-I only): true,
75 // Over 2TB flag (SDUC only): false,
76 // UHS-II Card: false,
77 // Card Capacity Status (CSS): \"SDHC/SDXC/SDUC\",
78 // Busy: false },
79 // rca: 43690,
80 // cid: CID: Card Identification { Manufacturer ID: 3,
81 // OEM ID: \"SD\",
82 // Product Name: \"SL08G\",
83 // Product Revision: 128,
84 // Product Serial Number: 701445767,
85 // Manufacturing Date: (9,
86 // 2015) },
87 // csd: CSD: Card Specific Data { Transfer Rate: 50,
88 // Block Count: 15523840,
89 // Card Size (bytes): 7948206080,
90 // Read I (@min VDD): 100 mA,
91 // Write I (@min VDD): 10 mA,
92 // Read I (@max VDD): 5 mA,
93 // Write I (@max VDD): 45 mA,
94 // Erase Size (Blocks): 1 },
95 // scr: SCR: SD CARD Configuration Register { Version: Unknown,
96 // 1-bit width: false,
97 // 4-bit width: true },
98 // status: SD Status { Bus Width: One,
99 // Secured Mode: false,
100 // SD Memory Card Type: 0,
101 // Protected Area Size (B): 0,
102 // Speed Class: 0,
103 // Video Speed Class: 0,
104 // Application Performance Class: 0,
105 // Move Performance (MB/s): 0,
106 // AU Size: 0,
107 // Erase Size (units of AU): 0,
108 // Erase Timeout (s): 0,
109 // Discard Support: false } }
110
111 defmt::assert!(card.scr.bus_width_four());
74 112
75 info!("writing pattern1..."); 113 info!("writing pattern1...");
76 s.write_block(block_idx, &pattern1).await.unwrap(); 114 storage.write_block(block_idx, &pattern1).await.unwrap();
77 115
78 info!("reading..."); 116 info!("reading...");
79 s.read_block(block_idx, &mut block).await.unwrap(); 117 storage.read_block(block_idx, &mut block).await.unwrap();
80 assert_eq!(block, pattern1); 118 assert_eq!(block, pattern1);
81 119
82 info!("writing pattern2..."); 120 info!("writing pattern2...");
83 s.write_block(block_idx, &pattern2).await.unwrap(); 121 storage.write_block(block_idx, &pattern2).await.unwrap();
84 122
85 info!("reading..."); 123 info!("reading...");
86 s.read_block(block_idx, &mut block).await.unwrap(); 124 storage.read_block(block_idx, &mut block).await.unwrap();
87 assert_eq!(block, pattern2); 125 assert_eq!(block, pattern2);
88 126
89 info!("writing blocks [pattern1, pattern2]..."); 127 info!("writing blocks [pattern1, pattern2]...");
90 s.write_blocks(block_idx, &patterns).await.unwrap(); 128 storage.write_blocks(block_idx, &patterns).await.unwrap();
91 129
92 info!("reading blocks..."); 130 info!("reading blocks...");
93 s.read_blocks(block_idx, &mut blocks).await.unwrap(); 131 storage.read_blocks(block_idx, &mut blocks).await.unwrap();
94 assert_eq!(&blocks, &patterns); 132 assert_eq!(&blocks, &patterns);
95 133
96 drop(s); 134 drop(s);
diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs
index 0b98d3eeb..ef7efe96a 100644
--- a/tests/stm32/src/bin/usart.rs
+++ b/tests/stm32/src/bin/usart.rs
@@ -6,6 +6,7 @@ mod common;
6use common::*; 6use common::*;
7use defmt::{assert, assert_eq, unreachable}; 7use defmt::{assert, assert_eq, unreachable};
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_stm32::mode::Blocking;
9use embassy_stm32::usart::{Config, ConfigError, Error, Uart}; 10use embassy_stm32::usart::{Config, ConfigError, Error, Uart};
10use embassy_time::{Duration, Instant, block_for}; 11use embassy_time::{Duration, Instant, block_for};
11 12
@@ -24,22 +25,41 @@ async fn main(_spawner: Spawner) {
24 let config = Config::default(); 25 let config = Config::default();
25 let mut usart = Uart::new_blocking(usart.reborrow(), rx.reborrow(), tx.reborrow(), config).unwrap(); 26 let mut usart = Uart::new_blocking(usart.reborrow(), rx.reborrow(), tx.reborrow(), config).unwrap();
26 27
27 // We can't send too many bytes, they have to fit in the FIFO. 28 let test_usart = async |usart: &mut Uart<'_, Blocking>| -> Result<(), Error> {
28 // This is because we aren't sending+receiving at the same time. 29 // We can't send too many bytes, they have to fit in the FIFO.
30 // This is because we aren't sending+receiving at the same time.
29 31
30 let data = [0xC0, 0xDE]; 32 let data = [0xC0, 0xDE];
31 usart.blocking_write(&data).unwrap(); 33 usart.blocking_write(&data)?;
32 34
33 let mut buf = [0; 2]; 35 let mut buf = [0; 2];
34 usart.blocking_read(&mut buf).unwrap(); 36 usart.blocking_read(&mut buf)?;
35 assert_eq!(buf, data); 37 assert_eq!(buf, data);
36 38
37 // Test flush doesn't hang. 39 // Test flush doesn't hang.
38 usart.blocking_write(&data).unwrap(); 40 usart.blocking_write(&data)?;
39 usart.blocking_flush().unwrap(); 41 usart.blocking_flush()?;
40 42
41 // Test flush doesn't hang if there's nothing to flush 43 // Test flush doesn't hang if there's nothing to flush
42 usart.blocking_flush().unwrap(); 44 usart.blocking_flush()?;
45
46 Ok(())
47 };
48
49 let mut is_ok = false;
50 for _ in 0..3 {
51 match test_usart(&mut usart).await {
52 Ok(()) => is_ok = true,
53 Err(Error::Noise) => is_ok = false,
54 Err(e) => defmt::panic!("{}", e),
55 }
56
57 if is_ok {
58 break;
59 }
60 }
61
62 assert!(is_ok);
43 } 63 }
44 64
45 // Test error handling with with an overflow error 65 // Test error handling with with an overflow error
diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs
index a34498376..9f610739d 100644
--- a/tests/stm32/src/bin/usart_dma.rs
+++ b/tests/stm32/src/bin/usart_dma.rs
@@ -7,7 +7,7 @@ use common::*;
7use defmt::assert_eq; 7use defmt::assert_eq;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_futures::join::join; 9use embassy_futures::join::join;
10use embassy_stm32::usart::{Config, Uart}; 10use embassy_stm32::usart::{Config, Error, Uart};
11 11
12#[embassy_executor::main] 12#[embassy_executor::main]
13async fn main(_spawner: Spawner) { 13async fn main(_spawner: Spawner) {
@@ -32,6 +32,7 @@ async fn main(_spawner: Spawner) {
32 32
33 let (mut tx, mut rx) = usart.split(); 33 let (mut tx, mut rx) = usart.split();
34 34
35 let mut noise_count = 0;
35 for n in 0..42 { 36 for n in 0..42 {
36 for i in 0..LEN { 37 for i in 0..LEN {
37 tx_buf[i] = (i ^ n) as u8; 38 tx_buf[i] = (i ^ n) as u8;
@@ -40,17 +41,30 @@ async fn main(_spawner: Spawner) {
40 let tx_fut = async { 41 let tx_fut = async {
41 tx.write(&tx_buf).await.unwrap(); 42 tx.write(&tx_buf).await.unwrap();
42 }; 43 };
44
45 let mut is_noisy = false;
43 let rx_fut = async { 46 let rx_fut = async {
44 rx.read(&mut rx_buf).await.unwrap(); 47 match rx.read(&mut rx_buf).await {
48 Ok(()) => {}
49 Err(Error::Noise) => is_noisy = true,
50 _ => defmt::panic!(),
51 }
45 }; 52 };
46 53
47 // note: rx needs to be polled first, to workaround this bug: 54 // note: rx needs to be polled first, to workaround this bug:
48 // https://github.com/embassy-rs/embassy/issues/1426 55 // https://github.com/embassy-rs/embassy/issues/1426
49 join(rx_fut, tx_fut).await; 56 join(rx_fut, tx_fut).await;
50 57
58 if is_noisy {
59 noise_count += 1;
60 continue;
61 }
62
51 assert_eq!(tx_buf, rx_buf); 63 assert_eq!(tx_buf, rx_buf);
52 } 64 }
53 65
66 defmt::assert!(noise_count < 3);
67
54 // Test flush doesn't hang. Check multiple combinations of async+blocking. 68 // Test flush doesn't hang. Check multiple combinations of async+blocking.
55 tx.write(&tx_buf).await.unwrap(); 69 tx.write(&tx_buf).await.unwrap();
56 tx.flush().await.unwrap(); 70 tx.flush().await.unwrap();