diff options
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 | |||
| 9 | make -C docs | 9 | make -C docs |
| 10 | 10 | ||
| 11 | export KUBECONFIG=/ci/secrets/kubeconfig.yml | 11 | export KUBECONFIG=/ci/secrets/kubeconfig.yml |
| 12 | POD=$(kubectl -n embassy get po -l app=website -o jsonpath={.items[0].metadata.name}) | 12 | POD=$(kubectl get po -l app=website -o jsonpath={.items[0].metadata.name}) |
| 13 | 13 | ||
| 14 | mkdir -p build | 14 | mkdir -p build |
| 15 | mv docs/book build/book | 15 | mv 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 | |||
| 23 | hashtime restore /ci/cache/filetime.json || true | 23 | hashtime restore /ci/cache/filetime.json || true |
| 24 | hashtime save /ci/cache/filetime.json | 24 | hashtime save /ci/cache/filetime.json |
| 25 | 25 | ||
| 26 | cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 | 26 | cargo 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 | |||
| 25 | hashtime restore /ci/cache/filetime.json || true | 25 | hashtime restore /ci/cache/filetime.json || true |
| 26 | hashtime save /ci/cache/filetime.json | 26 | hashtime save /ci/cache/filetime.json |
| 27 | 27 | ||
| 28 | cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 | 28 | cargo 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 | |||
| 28 | hashtime restore /ci/cache/filetime.json || true | 28 | hashtime restore /ci/cache/filetime.json || true |
| 29 | hashtime save /ci/cache/filetime.json | 29 | hashtime save /ci/cache/filetime.json |
| 30 | 30 | ||
| 31 | cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 | 31 | cargo 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 | |||
| 12 | export PATH=$CARGO_HOME/bin:$PATH | 12 | export PATH=$CARGO_HOME/bin:$PATH |
| 13 | mv rust-toolchain-nightly.toml rust-toolchain.toml | 13 | mv rust-toolchain-nightly.toml rust-toolchain.toml |
| 14 | 14 | ||
| 15 | cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 | 15 | cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev c60400e213f7eb0296581183140ec147dd7a848b |
| 16 | 16 | ||
| 17 | cargo embassy-devtool doc -o webroot | 17 | cargo embassy-devtool doc -o webroot |
| 18 | 18 | ||
| 19 | export KUBECONFIG=/ci/secrets/kubeconfig.yml | 19 | export KUBECONFIG=/ci/secrets/kubeconfig.yml |
| 20 | POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) | 20 | POD=$(kubectl get po -l app=docserver -o jsonpath={.items[0].metadata.name}) |
| 21 | kubectl cp webroot/crates $POD:/data | 21 | kubectl cp webroot/crates $POD:/data |
| 22 | kubectl cp webroot/static $POD:/data | 22 | kubectl 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 | |||
| 9 | export CARGO_TARGET_DIR=/ci/cache/target | 9 | export CARGO_TARGET_DIR=/ci/cache/target |
| 10 | export PATH=$CARGO_HOME/bin:$PATH | 10 | export PATH=$CARGO_HOME/bin:$PATH |
| 11 | 11 | ||
| 12 | cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 | 12 | cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev c60400e213f7eb0296581183140ec147dd7a848b |
| 13 | 13 | ||
| 14 | cargo embassy-devtool check-crlf | 14 | cargo embassy-devtool check-crlf |
| 15 | cargo embassy-devtool check-manifest | 15 | cargo 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 @@ | |||
| 1 | name: Changelog check | 1 | # name: Changelog check |
| 2 | 2 | # | |
| 3 | on: | 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 | # | |
| 17 | jobs: | 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." |
| @@ -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 | |||
| 3 | The 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 | |||
| 9 | The 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 | |||
| 27 | The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy. | 27 | The 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 | |||
| 42 | The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. Several drivers for WiFi and Ethernet chips can be found. | 42 | The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. Several drivers for WiFi and Ethernet chips can be found. |
| 43 | 43 | ||
| 44 | === Bluetooth | 44 | === Bluetooth |
| 45 | 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. | 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 |
| 48 | link: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. | 50 | link: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 | ||
| 10 | Thumbs.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] | ||
| 2 | name = "embassy-mcxa" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2021" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | description = "Embassy Hardware Abstraction Layer (HAL) for NXP MCXA series of MCUs" | ||
| 7 | repository = "https://github.com/embassy-rs/embassy" | ||
| 8 | keywords = ["embedded", "hal", "nxp", "mcxa", "embassy"] | ||
| 9 | categories = ["embedded", "hardware-support", "no-std"] | ||
| 10 | documentation = "https://docs.embassy.dev/embassy-mcxa" | ||
| 11 | |||
| 12 | [package.metadata.embassy] | ||
| 13 | build = [ | ||
| 14 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "time", "unstable-pac"]}, | ||
| 15 | ] | ||
| 16 | |||
| 17 | [package.metadata.embassy_docs] | ||
| 18 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-mcxa-v$VERSION/embassy-mcxa/src/" | ||
| 19 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-mcxa/src/" | ||
| 20 | |||
| 21 | features = ["defmt", "time", "unstable-pac"] | ||
| 22 | flavors = [ | ||
| 23 | { name = "mcx-a256", target = "thumbv8m.main-none-eabihf" }, | ||
| 24 | ] | ||
| 25 | |||
| 26 | [dependencies] | ||
| 27 | cortex-m = { version = "0.7", features = ["critical-section-single-core"] } | ||
| 28 | # If you would like "device" to be an optional feature, please open an issue. | ||
| 29 | cortex-m-rt = { version = "0.7", features = ["device"] } | ||
| 30 | critical-section = "1.2.0" | ||
| 31 | defmt = { version = "1.0", optional = true } | ||
| 32 | embassy-embedded-hal = "0.5.0" | ||
| 33 | embassy-hal-internal = { version = "0.3.0", features = ["cortex-m", "prio-bits-3"] } | ||
| 34 | embassy-sync = "0.7.2" | ||
| 35 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 36 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | ||
| 37 | embedded-hal-async = { version = "1.0" } | ||
| 38 | embedded-hal-nb = { version = "1.0" } | ||
| 39 | embedded-io = "0.6" | ||
| 40 | embedded-io-async = { version = "0.6.1" } | ||
| 41 | heapless = "0.8" | ||
| 42 | mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt", "critical-section"], version = "0.1.0", rev = "02bd04a21ef8f8f67f88239ff5df765bb7e60fd8" } | ||
| 43 | nb = "1.1.0" | ||
| 44 | paste = "1.0.15" | ||
| 45 | maitake-sync = { version = "0.2.2", default-features = false, features = ["critical-section", "no-cache-pad"] } | ||
| 46 | |||
| 47 | # `time` dependencies | ||
| 48 | embassy-time = { version = "0.5.0", optional = true } | ||
| 49 | embassy-time-driver = { version = "0.2.1", optional = true } | ||
| 50 | |||
| 51 | [features] | ||
| 52 | default = ["rt"] | ||
| 53 | |||
| 54 | # Base defmt feature enables core + panic handler | ||
| 55 | # Use with one logger feature: defmt-rtt (preferred) or defmt-uart (fallback) | ||
| 56 | defmt = ["dep:defmt", "mcxa-pac/defmt"] | ||
| 57 | |||
| 58 | unstable-pac = [] | ||
| 59 | |||
| 60 | # dummy feature to silence embassy-hal-internal lint | ||
| 61 | # | ||
| 62 | # This feature makes no change to embassy-mcxa's operation. | ||
| 63 | rt = [] | ||
| 64 | |||
| 65 | # Embassy time | ||
| 66 | time = [ | ||
| 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 | |||
| 3 | A Hardware Abstraction Layer (HAL) for the NXP MCXA256 microcontroller | ||
| 4 | using the Embassy async framework. This HAL provides safe, idiomatic | ||
| 5 | Rust 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 | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | |||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 5 | use maitake_sync::WaitCell; | ||
| 6 | use paste::paste; | ||
| 7 | |||
| 8 | use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; | ||
| 9 | use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; | ||
| 10 | use crate::gpio::{GpioPin, SealedPin}; | ||
| 11 | use crate::interrupt::typelevel::{Handler, Interrupt}; | ||
| 12 | use crate::pac; | ||
| 13 | use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; | ||
| 14 | use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; | ||
| 15 | use crate::pac::adc1::cmdl1::{Adch, Mode}; | ||
| 16 | use crate::pac::adc1::ctrl::CalAvgs; | ||
| 17 | use crate::pac::adc1::tctrl::{Tcmd, Tpri}; | ||
| 18 | |||
| 19 | const G_LPADC_RESULT_SHIFT: u32 = 0; | ||
| 20 | |||
| 21 | /// Trigger priority policy for ADC conversions. | ||
| 22 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 23 | #[repr(u8)] | ||
| 24 | pub 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)] | ||
| 39 | pub 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 | |||
| 74 | impl 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)] | ||
| 98 | pub 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)] | ||
| 116 | pub 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>`. | ||
| 124 | pub 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))] | ||
| 129 | pub 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)] | ||
| 142 | pub 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. | ||
| 150 | pub struct InterruptHandler<I: Instance> { | ||
| 151 | _phantom: PhantomData<I>, | ||
| 152 | } | ||
| 153 | |||
| 154 | /// ADC driver instance. | ||
| 155 | pub struct Adc<'a, I: Instance, M: ModeAdc> { | ||
| 156 | _inst: PhantomData<&'a mut I>, | ||
| 157 | _phantom: PhantomData<M>, | ||
| 158 | } | ||
| 159 | |||
| 160 | impl<'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 | |||
| 171 | impl<'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 | |||
| 218 | impl<'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 | |||
| 571 | impl<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 | |||
| 578 | mod sealed { | ||
| 579 | /// Seal a trait | ||
| 580 | pub trait Sealed {} | ||
| 581 | } | ||
| 582 | |||
| 583 | impl<I: GpioPin> sealed::Sealed for I {} | ||
| 584 | |||
| 585 | trait SealedInstance { | ||
| 586 | fn ptr() -> &'static pac::adc0::RegisterBlock; | ||
| 587 | fn wait_cell() -> &'static WaitCell; | ||
| 588 | } | ||
| 589 | |||
| 590 | /// ADC Instance | ||
| 591 | #[allow(private_bounds)] | ||
| 592 | pub trait Instance: SealedInstance + PeripheralType + Gate<MrccPeriphConfig = AdcConfig> { | ||
| 593 | /// Interrupt for this ADC instance. | ||
| 594 | type Interrupt: Interrupt; | ||
| 595 | } | ||
| 596 | |||
| 597 | macro_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 | |||
| 621 | impl_instance!(0, 1, 2, 3); | ||
| 622 | |||
| 623 | pub 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)] | ||
| 630 | pub trait ModeAdc: sealed::Sealed {} | ||
| 631 | |||
| 632 | /// Blocking mode. | ||
| 633 | pub struct Blocking; | ||
| 634 | impl sealed::Sealed for Blocking {} | ||
| 635 | impl ModeAdc for Blocking {} | ||
| 636 | |||
| 637 | /// Async mode. | ||
| 638 | pub struct Async; | ||
| 639 | impl sealed::Sealed for Async {} | ||
| 640 | impl ModeAdc for Async {} | ||
| 641 | |||
| 642 | macro_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 | |||
| 655 | impl_pin!(P2_0, ADC0, Mux0, AdcPin); | ||
| 656 | impl_pin!(P2_4, ADC0, Mux0, AdcPin); | ||
| 657 | impl_pin!(P2_15, ADC0, Mux0, AdcPin); | ||
| 658 | impl_pin!(P2_3, ADC0, Mux0, AdcPin); | ||
| 659 | impl_pin!(P2_2, ADC0, Mux0, AdcPin); | ||
| 660 | impl_pin!(P2_12, ADC0, Mux0, AdcPin); | ||
| 661 | impl_pin!(P2_16, ADC0, Mux0, AdcPin); | ||
| 662 | impl_pin!(P2_7, ADC0, Mux0, AdcPin); | ||
| 663 | impl_pin!(P0_18, ADC0, Mux0, AdcPin); | ||
| 664 | impl_pin!(P0_19, ADC0, Mux0, AdcPin); | ||
| 665 | impl_pin!(P0_20, ADC0, Mux0, AdcPin); | ||
| 666 | impl_pin!(P0_21, ADC0, Mux0, AdcPin); | ||
| 667 | impl_pin!(P0_22, ADC0, Mux0, AdcPin); | ||
| 668 | impl_pin!(P0_23, ADC0, Mux0, AdcPin); | ||
| 669 | impl_pin!(P0_3, ADC0, Mux0, AdcPin); | ||
| 670 | impl_pin!(P0_6, ADC0, Mux0, AdcPin); | ||
| 671 | impl_pin!(P1_0, ADC0, Mux0, AdcPin); | ||
| 672 | impl_pin!(P1_1, ADC0, Mux0, AdcPin); | ||
| 673 | impl_pin!(P1_2, ADC0, Mux0, AdcPin); | ||
| 674 | impl_pin!(P1_3, ADC0, Mux0, AdcPin); | ||
| 675 | impl_pin!(P1_4, ADC0, Mux0, AdcPin); | ||
| 676 | impl_pin!(P1_5, ADC0, Mux0, AdcPin); | ||
| 677 | impl_pin!(P1_6, ADC0, Mux0, AdcPin); | ||
| 678 | impl_pin!(P1_7, ADC0, Mux0, AdcPin); | ||
| 679 | impl_pin!(P1_10, ADC0, Mux0, AdcPin); | ||
| 680 | |||
| 681 | impl_pin!(P2_1, ADC1, Mux0, AdcPin); | ||
| 682 | impl_pin!(P2_5, ADC1, Mux0, AdcPin); | ||
| 683 | impl_pin!(P2_19, ADC1, Mux0, AdcPin); | ||
| 684 | impl_pin!(P2_6, ADC1, Mux0, AdcPin); | ||
| 685 | impl_pin!(P2_3, ADC1, Mux0, AdcPin); | ||
| 686 | impl_pin!(P2_13, ADC1, Mux0, AdcPin); | ||
| 687 | impl_pin!(P2_17, ADC1, Mux0, AdcPin); | ||
| 688 | impl_pin!(P2_7, ADC1, Mux0, AdcPin); | ||
| 689 | impl_pin!(P1_10, ADC1, Mux0, AdcPin); | ||
| 690 | impl_pin!(P1_11, ADC1, Mux0, AdcPin); | ||
| 691 | impl_pin!(P1_12, ADC1, Mux0, AdcPin); | ||
| 692 | impl_pin!(P1_13, ADC1, Mux0, AdcPin); | ||
| 693 | impl_pin!(P1_14, ADC1, Mux0, AdcPin); | ||
| 694 | impl_pin!(P1_15, ADC1, Mux0, AdcPin); | ||
| 695 | impl_pin!(P1_16, ADC1, Mux0, AdcPin); | ||
| 696 | impl_pin!(P1_17, ADC1, Mux0, AdcPin); | ||
| 697 | impl_pin!(P1_18, ADC1, Mux0, AdcPin); | ||
| 698 | impl_pin!(P1_19, ADC1, Mux0, AdcPin); | ||
| 699 | impl_pin!(P3_31, ADC1, Mux0, AdcPin); | ||
| 700 | impl_pin!(P3_30, ADC1, Mux0, AdcPin); | ||
| 701 | impl_pin!(P3_29, ADC1, Mux0, AdcPin); | ||
| 702 | |||
| 703 | impl_pin!(P2_4, ADC2, Mux0, AdcPin); | ||
| 704 | impl_pin!(P2_10, ADC2, Mux0, AdcPin); | ||
| 705 | impl_pin!(P4_4, ADC2, Mux0, AdcPin); | ||
| 706 | impl_pin!(P2_24, ADC2, Mux0, AdcPin); | ||
| 707 | impl_pin!(P2_16, ADC2, Mux0, AdcPin); | ||
| 708 | impl_pin!(P2_12, ADC2, Mux0, AdcPin); | ||
| 709 | impl_pin!(P2_20, ADC2, Mux0, AdcPin); | ||
| 710 | impl_pin!(P2_7, ADC2, Mux0, AdcPin); | ||
| 711 | impl_pin!(P0_2, ADC2, Mux0, AdcPin); | ||
| 712 | impl_pin!(P0_4, ADC2, Mux0, AdcPin); | ||
| 713 | impl_pin!(P0_5, ADC2, Mux0, AdcPin); | ||
| 714 | impl_pin!(P0_6, ADC2, Mux0, AdcPin); | ||
| 715 | impl_pin!(P0_7, ADC2, Mux0, AdcPin); | ||
| 716 | impl_pin!(P0_12, ADC2, Mux0, AdcPin); | ||
| 717 | impl_pin!(P0_13, ADC2, Mux0, AdcPin); | ||
| 718 | impl_pin!(P0_14, ADC2, Mux0, AdcPin); | ||
| 719 | impl_pin!(P0_15, ADC2, Mux0, AdcPin); | ||
| 720 | impl_pin!(P4_0, ADC2, Mux0, AdcPin); | ||
| 721 | impl_pin!(P4_1, ADC2, Mux0, AdcPin); | ||
| 722 | impl_pin!(P4_2, ADC2, Mux0, AdcPin); | ||
| 723 | impl_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 | ||
| 725 | impl_pin!(P4_5, ADC2, Mux0, AdcPin); | ||
| 726 | impl_pin!(P4_6, ADC2, Mux0, AdcPin); | ||
| 727 | impl_pin!(P4_7, ADC2, Mux0, AdcPin); | ||
| 728 | |||
| 729 | impl_pin!(P2_5, ADC3, Mux0, AdcPin); | ||
| 730 | impl_pin!(P2_11, ADC3, Mux0, AdcPin); | ||
| 731 | impl_pin!(P2_23, ADC3, Mux0, AdcPin); | ||
| 732 | impl_pin!(P2_25, ADC3, Mux0, AdcPin); | ||
| 733 | impl_pin!(P2_17, ADC3, Mux0, AdcPin); | ||
| 734 | impl_pin!(P2_13, ADC3, Mux0, AdcPin); | ||
| 735 | impl_pin!(P2_21, ADC3, Mux0, AdcPin); | ||
| 736 | impl_pin!(P2_7, ADC3, Mux0, AdcPin); | ||
| 737 | impl_pin!(P3_2, ADC3, Mux0, AdcPin); | ||
| 738 | impl_pin!(P3_3, ADC3, Mux0, AdcPin); | ||
| 739 | impl_pin!(P3_4, ADC3, Mux0, AdcPin); | ||
| 740 | impl_pin!(P3_5, ADC3, Mux0, AdcPin); | ||
| 741 | impl_pin!(P3_6, ADC3, Mux0, AdcPin); | ||
| 742 | impl_pin!(P3_7, ADC3, Mux0, AdcPin); | ||
| 743 | impl_pin!(P3_12, ADC3, Mux0, AdcPin); | ||
| 744 | impl_pin!(P3_13, ADC3, Mux0, AdcPin); | ||
| 745 | impl_pin!(P3_14, ADC3, Mux0, AdcPin); | ||
| 746 | impl_pin!(P3_15, ADC3, Mux0, AdcPin); | ||
| 747 | impl_pin!(P3_20, ADC3, Mux0, AdcPin); | ||
| 748 | impl_pin!(P3_21, ADC3, Mux0, AdcPin); | ||
| 749 | impl_pin!(P3_22, ADC3, Mux0, AdcPin); | ||
| 750 | impl_pin!(P3_23, ADC3, Mux0, AdcPin); | ||
| 751 | impl_pin!(P3_24, ADC3, Mux0, AdcPin); | ||
| 752 | impl_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 | |||
| 7 | use core::marker::PhantomData; | ||
| 8 | |||
| 9 | use embassy_hal_internal::Peri; | ||
| 10 | |||
| 11 | pub use crate::clocks::periph_helpers::Div4; | ||
| 12 | use crate::clocks::{ClockError, PoweredClock, with_clocks}; | ||
| 13 | use crate::pac::mrcc0::mrcc_clkout_clksel::Mux; | ||
| 14 | use crate::peripherals::CLKOUT; | ||
| 15 | |||
| 16 | /// A peripheral representing the CLKOUT pseudo-peripheral | ||
| 17 | pub struct ClockOut<'a> { | ||
| 18 | _p: PhantomData<&'a mut CLKOUT>, | ||
| 19 | freq: u32, | ||
| 20 | } | ||
| 21 | |||
| 22 | /// Selected clock source to output | ||
| 23 | pub 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 | ||
| 39 | pub 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 | |||
| 48 | impl<'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 | |||
| 79 | impl Drop for ClockOut<'_> { | ||
| 80 | fn drop(&mut self) { | ||
| 81 | disable_clkout(); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Check whether the given clock selection is valid | ||
| 86 | fn 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 | ||
| 104 | fn 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 | ||
| 127 | fn 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 | |||
| 143 | mod 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 | |||
| 6 | use 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)] | ||
| 13 | pub struct Div8(pub(super) u8); | ||
| 14 | |||
| 15 | impl 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] | ||
| 114 | pub 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] | ||
| 134 | pub 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 | ||
| 148 | pub 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] | ||
| 169 | pub 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] | ||
| 178 | pub struct Fro16KConfig { | ||
| 179 | pub vsys_domain_active: bool, | ||
| 180 | pub vdd_core_domain_active: bool, | ||
| 181 | } | ||
| 182 | |||
| 183 | impl 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 | |||
| 38 | use core::cell::RefCell; | ||
| 39 | |||
| 40 | use config::{ClocksConfig, FircConfig, FircFreqSel, Fro16KConfig, SircConfig}; | ||
| 41 | use mcxa_pac::scg0::firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten}; | ||
| 42 | use mcxa_pac::scg0::sirccsr::{SircClkPeriphEn, Sircsten}; | ||
| 43 | use periph_helpers::SPConfHelper; | ||
| 44 | |||
| 45 | use crate::pac; | ||
| 46 | pub mod config; | ||
| 47 | pub 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. | ||
| 56 | static 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. | ||
| 67 | pub 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. | ||
| 119 | pub 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] | ||
| 137 | pub 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] | ||
| 206 | pub 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)] | ||
| 224 | pub 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)] | ||
| 242 | pub 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. | ||
| 256 | struct 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. | ||
| 270 | pub 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] | ||
| 328 | pub 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] | ||
| 343 | pub 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] | ||
| 366 | pub 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] | ||
| 373 | pub 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] | ||
| 385 | pub 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] | ||
| 397 | pub 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] | ||
| 407 | pub 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] | ||
| 419 | pub 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`]. | ||
| 435 | impl 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 | |||
| 574 | impl 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 | |||
| 586 | impl 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. | ||
| 862 | macro_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. | ||
| 908 | pub(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 | |||
| 10 | use super::{ClockError, Clocks, PoweredClock}; | ||
| 11 | use 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()`. | ||
| 24 | pub 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 | ||
| 67 | macro_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)] | ||
| 98 | pub struct Div4(pub(super) u8); | ||
| 99 | |||
| 100 | impl 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. | ||
| 147 | pub struct UnimplementedConfig; | ||
| 148 | |||
| 149 | impl 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. | ||
| 159 | pub struct NoConfig; | ||
| 160 | impl 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)] | ||
| 172 | pub 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)] | ||
| 194 | pub 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. | ||
| 206 | pub 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 | |||
| 218 | impl 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)] | ||
| 270 | pub 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)] | ||
| 294 | pub 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. | ||
| 310 | pub 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 | |||
| 322 | impl 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)] | ||
| 385 | pub 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 | ||
| 395 | pub 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 | |||
| 402 | impl 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))] | ||
| 431 | pub 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 | ||
| 447 | pub 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 | |||
| 456 | impl 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 | |||
| 3 | use crate::clocks::config::ClocksConfig; | ||
| 4 | use crate::interrupt::Priority; | ||
| 5 | |||
| 6 | #[non_exhaustive] | ||
| 7 | pub 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 | |||
| 16 | impl 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 | |||
| 3 | use core::marker::PhantomData; | ||
| 4 | |||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | use mcxa_pac::crc0::ctrl::{Fxor, Tcrc, Tot, Totr}; | ||
| 7 | |||
| 8 | use crate::clocks::enable_and_reset; | ||
| 9 | use crate::clocks::periph_helpers::NoConfig; | ||
| 10 | use crate::peripherals::CRC0; | ||
| 11 | |||
| 12 | /// CRC driver. | ||
| 13 | pub struct Crc<'d, M> { | ||
| 14 | _peri: Peri<'d, CRC0>, | ||
| 15 | _phantom: PhantomData<M>, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl<'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 | |||
| 91 | impl<'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 | |||
| 234 | impl<'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 | |||
| 307 | mod 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)] | ||
| 318 | pub trait Mode: sealed::SealedMode {} | ||
| 319 | |||
| 320 | /// 16-bit CRC. | ||
| 321 | pub struct Crc16; | ||
| 322 | impl sealed::SealedMode for Crc16 {} | ||
| 323 | impl Mode for Crc16 {} | ||
| 324 | |||
| 325 | /// 32-bit CRC. | ||
| 326 | pub struct Crc32; | ||
| 327 | impl sealed::SealedMode for Crc32 {} | ||
| 328 | impl Mode for Crc32 {} | ||
| 329 | |||
| 330 | /// Word size for the CRC. | ||
| 331 | #[allow(private_bounds)] | ||
| 332 | pub trait Word: sealed::SealedWord {} | ||
| 333 | |||
| 334 | macro_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 | |||
| 352 | impl_word!(u8, 8, write_u8, read_u8); | ||
| 353 | impl_word!(u16, 16, write_u16, read_u16); | ||
| 354 | impl_word!(u32, 32, write_u32, read_u32); | ||
| 355 | |||
| 356 | fn write_u8(regs: &'static crate::pac::crc0::RegisterBlock, word: u8) { | ||
| 357 | regs.data8().write(|w| unsafe { w.bits(word) }); | ||
| 358 | } | ||
| 359 | |||
| 360 | fn read_u8(regs: &'static crate::pac::crc0::RegisterBlock) -> u8 { | ||
| 361 | regs.data8().read().bits() | ||
| 362 | } | ||
| 363 | |||
| 364 | fn write_u16(regs: &'static crate::pac::crc0::RegisterBlock, word: u16) { | ||
| 365 | regs.data16().write(|w| unsafe { w.bits(word) }); | ||
| 366 | } | ||
| 367 | |||
| 368 | fn 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 | |||
| 379 | fn write_u32(regs: &'static crate::pac::crc0::RegisterBlock, word: u32) { | ||
| 380 | regs.data32().write(|w| unsafe { w.bits(word) }); | ||
| 381 | } | ||
| 382 | |||
| 383 | fn 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] | ||
| 390 | pub 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 | |||
| 407 | impl 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 | |||
| 427 | impl 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)] | ||
| 441 | pub 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 | |||
| 467 | impl 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)] | ||
| 637 | pub enum Algorithm32 { | ||
| 638 | Bzip2, | ||
| 639 | C, | ||
| 640 | D, | ||
| 641 | IsoHdlc, | ||
| 642 | JamCrc, | ||
| 643 | Mpeg2, | ||
| 644 | Posix, | ||
| 645 | Q, | ||
| 646 | Xfer, | ||
| 647 | } | ||
| 648 | |||
| 649 | impl 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)] | ||
| 721 | pub enum Reflect { | ||
| 722 | No, | ||
| 723 | Yes, | ||
| 724 | } | ||
| 725 | |||
| 726 | impl 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 | |||
| 735 | impl 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)] | ||
| 746 | pub enum Complement { | ||
| 747 | No, | ||
| 748 | Yes, | ||
| 749 | } | ||
| 750 | |||
| 751 | impl 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 | |||
| 103 | use core::future::Future; | ||
| 104 | use core::marker::PhantomData; | ||
| 105 | use core::pin::Pin; | ||
| 106 | use core::ptr::NonNull; | ||
| 107 | use core::sync::atomic::{AtomicUsize, Ordering, fence}; | ||
| 108 | use core::task::{Context, Poll}; | ||
| 109 | |||
| 110 | use embassy_hal_internal::PeripheralType; | ||
| 111 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 112 | |||
| 113 | use crate::clocks::Gate; | ||
| 114 | use crate::pac; | ||
| 115 | use crate::pac::Interrupt; | ||
| 116 | use 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. | ||
| 124 | pub(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))] | ||
| 154 | pub 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))] | ||
| 166 | pub 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 | |||
| 178 | impl 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))] | ||
| 193 | pub 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 | |||
| 203 | impl 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. | ||
| 236 | pub trait Word: Copy + 'static { | ||
| 237 | /// The word size for this type. | ||
| 238 | fn size() -> WordSize; | ||
| 239 | } | ||
| 240 | |||
| 241 | impl Word for u8 { | ||
| 242 | fn size() -> WordSize { | ||
| 243 | WordSize::OneByte | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | impl Word for u16 { | ||
| 248 | fn size() -> WordSize { | ||
| 249 | WordSize::TwoBytes | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | impl 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] | ||
| 265 | pub 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 | |||
| 278 | impl 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))] | ||
| 292 | pub 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))] | ||
| 309 | pub 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. | ||
| 324 | pub 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)] | ||
| 346 | pub 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. | ||
| 355 | macro_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) | ||
| 370 | define_dma_request!( | ||
| 371 | /// DMA request source for LPUART0 RX. | ||
| 372 | Lpuart0RxRequest = 21 | ||
| 373 | ); | ||
| 374 | define_dma_request!( | ||
| 375 | /// DMA request source for LPUART0 TX. | ||
| 376 | Lpuart0TxRequest = 22 | ||
| 377 | ); | ||
| 378 | define_dma_request!( | ||
| 379 | /// DMA request source for LPUART1 RX. | ||
| 380 | Lpuart1RxRequest = 23 | ||
| 381 | ); | ||
| 382 | define_dma_request!( | ||
| 383 | /// DMA request source for LPUART1 TX. | ||
| 384 | Lpuart1TxRequest = 24 | ||
| 385 | ); | ||
| 386 | define_dma_request!( | ||
| 387 | /// DMA request source for LPUART2 RX. | ||
| 388 | Lpuart2RxRequest = 25 | ||
| 389 | ); | ||
| 390 | define_dma_request!( | ||
| 391 | /// DMA request source for LPUART2 TX. | ||
| 392 | Lpuart2TxRequest = 26 | ||
| 393 | ); | ||
| 394 | define_dma_request!( | ||
| 395 | /// DMA request source for LPUART3 RX. | ||
| 396 | Lpuart3RxRequest = 27 | ||
| 397 | ); | ||
| 398 | define_dma_request!( | ||
| 399 | /// DMA request source for LPUART3 TX. | ||
| 400 | Lpuart3TxRequest = 28 | ||
| 401 | ); | ||
| 402 | define_dma_request!( | ||
| 403 | /// DMA request source for LPUART4 RX. | ||
| 404 | Lpuart4RxRequest = 29 | ||
| 405 | ); | ||
| 406 | define_dma_request!( | ||
| 407 | /// DMA request source for LPUART4 TX. | ||
| 408 | Lpuart4TxRequest = 30 | ||
| 409 | ); | ||
| 410 | define_dma_request!( | ||
| 411 | /// DMA request source for LPUART5 RX. | ||
| 412 | Lpuart5RxRequest = 31 | ||
| 413 | ); | ||
| 414 | define_dma_request!( | ||
| 415 | /// DMA request source for LPUART5 TX. | ||
| 416 | Lpuart5TxRequest = 32 | ||
| 417 | ); | ||
| 418 | |||
| 419 | // ============================================================================ | ||
| 420 | // Channel Trait (Sealed Pattern) | ||
| 421 | // ============================================================================ | ||
| 422 | |||
| 423 | mod 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)] | ||
| 443 | pub 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)] | ||
| 455 | pub struct AnyChannel { | ||
| 456 | index: usize, | ||
| 457 | interrupt: Interrupt, | ||
| 458 | } | ||
| 459 | |||
| 460 | impl 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 | |||
| 495 | impl 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. | ||
| 506 | macro_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 | |||
| 534 | impl_channel!(DMA_CH0, 0, DMA_CH0); | ||
| 535 | impl_channel!(DMA_CH1, 1, DMA_CH1); | ||
| 536 | impl_channel!(DMA_CH2, 2, DMA_CH2); | ||
| 537 | impl_channel!(DMA_CH3, 3, DMA_CH3); | ||
| 538 | impl_channel!(DMA_CH4, 4, DMA_CH4); | ||
| 539 | impl_channel!(DMA_CH5, 5, DMA_CH5); | ||
| 540 | impl_channel!(DMA_CH6, 6, DMA_CH6); | ||
| 541 | impl_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. | ||
| 548 | pub 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 | |||
| 598 | impl<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))] | ||
| 1672 | pub 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 | |||
| 1686 | struct State { | ||
| 1687 | /// Waker for transfer complete interrupt | ||
| 1688 | waker: AtomicWaker, | ||
| 1689 | /// Waker for half-transfer interrupt | ||
| 1690 | half_waker: AtomicWaker, | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | impl State { | ||
| 1694 | const fn new() -> Self { | ||
| 1695 | Self { | ||
| 1696 | waker: AtomicWaker::new(), | ||
| 1697 | half_waker: AtomicWaker::new(), | ||
| 1698 | } | ||
| 1699 | } | ||
| 1700 | } | ||
| 1701 | |||
| 1702 | static 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 | |||
| 1713 | pub(crate) fn waker(idx: usize) -> &'static AtomicWaker { | ||
| 1714 | &STATES[idx].waker | ||
| 1715 | } | ||
| 1716 | |||
| 1717 | pub(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"] | ||
| 1730 | pub struct Transfer<'a> { | ||
| 1731 | channel: AnyChannel, | ||
| 1732 | _phantom: core::marker::PhantomData<&'a ()>, | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | impl<'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)] | ||
| 1839 | pub struct TransferErrorRaw(u8); | ||
| 1840 | |||
| 1841 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 1842 | #[derive(Copy, Clone, Debug)] | ||
| 1843 | pub struct TransferErrorRawIter(u8); | ||
| 1844 | |||
| 1845 | impl 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 | |||
| 1917 | impl 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))] | ||
| 1942 | pub 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 | |||
| 1967 | impl<'a> Unpin for Transfer<'a> {} | ||
| 1968 | |||
| 1969 | impl<'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 | |||
| 1998 | impl<'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 | /// ``` | ||
| 2044 | pub 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 | |||
| 2058 | impl<'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 | |||
| 2244 | impl<'a, W: Word> Drop for RingBuffer<'a, W> { | ||
| 2245 | fn drop(&mut self) { | ||
| 2246 | self.teardown(); | ||
| 2247 | } | ||
| 2248 | } | ||
| 2249 | |||
| 2250 | impl<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. | ||
| 2345 | pub 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 | /// ``` | ||
| 2368 | pub 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 | |||
| 2379 | impl<'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 | |||
| 2517 | impl<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)] | ||
| 2528 | pub 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. | ||
| 2546 | pub 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. | ||
| 2582 | macro_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 | |||
| 2593 | use crate::pac::interrupt; | ||
| 2594 | |||
| 2595 | impl_dma_interrupt_handler!(DMA_CH0, 0); | ||
| 2596 | impl_dma_interrupt_handler!(DMA_CH1, 1); | ||
| 2597 | impl_dma_interrupt_handler!(DMA_CH2, 2); | ||
| 2598 | impl_dma_interrupt_handler!(DMA_CH3, 3); | ||
| 2599 | impl_dma_interrupt_handler!(DMA_CH4, 4); | ||
| 2600 | impl_dma_interrupt_handler!(DMA_CH5, 5); | ||
| 2601 | impl_dma_interrupt_handler!(DMA_CH6, 6); | ||
| 2602 | impl_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 | |||
| 5 | use core::convert::Infallible; | ||
| 6 | use core::future::Future; | ||
| 7 | use core::marker::PhantomData; | ||
| 8 | use core::pin::pin; | ||
| 9 | |||
| 10 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 11 | use maitake_sync::WaitMap; | ||
| 12 | use paste::paste; | ||
| 13 | |||
| 14 | use crate::pac::interrupt; | ||
| 15 | use crate::pac::port0::pcr0::{Dse, Inv, Mux, Pe, Ps, Sre}; | ||
| 16 | |||
| 17 | struct BitIter(u32); | ||
| 18 | |||
| 19 | impl 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 | |||
| 33 | const PORT_COUNT: usize = 5; | ||
| 34 | |||
| 35 | static PORT_WAIT_MAPS: [WaitMap<usize, ()>; PORT_COUNT] = [ | ||
| 36 | WaitMap::new(), | ||
| 37 | WaitMap::new(), | ||
| 38 | WaitMap::new(), | ||
| 39 | WaitMap::new(), | ||
| 40 | WaitMap::new(), | ||
| 41 | ]; | ||
| 42 | |||
| 43 | fn 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] | ||
| 60 | fn GPIO0() { | ||
| 61 | irq_handler(0, crate::pac::Gpio0::ptr()); | ||
| 62 | } | ||
| 63 | |||
| 64 | #[interrupt] | ||
| 65 | fn GPIO1() { | ||
| 66 | irq_handler(1, crate::pac::Gpio1::ptr()); | ||
| 67 | } | ||
| 68 | |||
| 69 | #[interrupt] | ||
| 70 | fn GPIO2() { | ||
| 71 | irq_handler(2, crate::pac::Gpio2::ptr()); | ||
| 72 | } | ||
| 73 | |||
| 74 | #[interrupt] | ||
| 75 | fn GPIO3() { | ||
| 76 | irq_handler(3, crate::pac::Gpio3::ptr()); | ||
| 77 | } | ||
| 78 | |||
| 79 | #[interrupt] | ||
| 80 | fn GPIO4() { | ||
| 81 | irq_handler(4, crate::pac::Gpio4::ptr()); | ||
| 82 | } | ||
| 83 | |||
| 84 | pub(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))] | ||
| 99 | pub enum Level { | ||
| 100 | Low, | ||
| 101 | High, | ||
| 102 | } | ||
| 103 | |||
| 104 | impl 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)] | ||
| 114 | pub enum Pull { | ||
| 115 | Disabled, | ||
| 116 | Up, | ||
| 117 | Down, | ||
| 118 | } | ||
| 119 | |||
| 120 | impl 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)] | ||
| 131 | pub enum SlewRate { | ||
| 132 | Fast, | ||
| 133 | Slow, | ||
| 134 | } | ||
| 135 | |||
| 136 | impl 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)] | ||
| 146 | pub enum DriveStrength { | ||
| 147 | Normal, | ||
| 148 | Double, | ||
| 149 | } | ||
| 150 | |||
| 151 | impl 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)] | ||
| 161 | pub enum Inverter { | ||
| 162 | Disabled, | ||
| 163 | Enabled, | ||
| 164 | } | ||
| 165 | |||
| 166 | impl 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 | |||
| 175 | pub type Gpio = crate::peripherals::GPIO0; | ||
| 176 | |||
| 177 | /// Type-erased representation of a GPIO pin. | ||
| 178 | pub 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 | |||
| 186 | impl 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 | |||
| 235 | embassy_hal_internal::impl_peripheral!(AnyPin); | ||
| 236 | |||
| 237 | pub(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)] | ||
| 267 | pub 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 | |||
| 277 | impl 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 | |||
| 319 | impl GpioPin for AnyPin {} | ||
| 320 | |||
| 321 | macro_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 | |||
| 393 | impl_pin!(P0_0, 0, 0, Gpio0); | ||
| 394 | impl_pin!(P0_1, 0, 1, Gpio0); | ||
| 395 | impl_pin!(P0_2, 0, 2, Gpio0); | ||
| 396 | impl_pin!(P0_3, 0, 3, Gpio0); | ||
| 397 | impl_pin!(P0_4, 0, 4, Gpio0); | ||
| 398 | impl_pin!(P0_5, 0, 5, Gpio0); | ||
| 399 | impl_pin!(P0_6, 0, 6, Gpio0); | ||
| 400 | impl_pin!(P0_7, 0, 7, Gpio0); | ||
| 401 | impl_pin!(P0_8, 0, 8, Gpio0); | ||
| 402 | impl_pin!(P0_9, 0, 9, Gpio0); | ||
| 403 | impl_pin!(P0_10, 0, 10, Gpio0); | ||
| 404 | impl_pin!(P0_11, 0, 11, Gpio0); | ||
| 405 | impl_pin!(P0_12, 0, 12, Gpio0); | ||
| 406 | impl_pin!(P0_13, 0, 13, Gpio0); | ||
| 407 | impl_pin!(P0_14, 0, 14, Gpio0); | ||
| 408 | impl_pin!(P0_15, 0, 15, Gpio0); | ||
| 409 | impl_pin!(P0_16, 0, 16, Gpio0); | ||
| 410 | impl_pin!(P0_17, 0, 17, Gpio0); | ||
| 411 | impl_pin!(P0_18, 0, 18, Gpio0); | ||
| 412 | impl_pin!(P0_19, 0, 19, Gpio0); | ||
| 413 | impl_pin!(P0_20, 0, 20, Gpio0); | ||
| 414 | impl_pin!(P0_21, 0, 21, Gpio0); | ||
| 415 | impl_pin!(P0_22, 0, 22, Gpio0); | ||
| 416 | impl_pin!(P0_23, 0, 23, Gpio0); | ||
| 417 | impl_pin!(P0_24, 0, 24, Gpio0); | ||
| 418 | impl_pin!(P0_25, 0, 25, Gpio0); | ||
| 419 | impl_pin!(P0_26, 0, 26, Gpio0); | ||
| 420 | impl_pin!(P0_27, 0, 27, Gpio0); | ||
| 421 | impl_pin!(P0_28, 0, 28, Gpio0); | ||
| 422 | impl_pin!(P0_29, 0, 29, Gpio0); | ||
| 423 | impl_pin!(P0_30, 0, 30, Gpio0); | ||
| 424 | impl_pin!(P0_31, 0, 31, Gpio0); | ||
| 425 | |||
| 426 | impl_pin!(P1_0, 1, 0, Gpio1); | ||
| 427 | impl_pin!(P1_1, 1, 1, Gpio1); | ||
| 428 | impl_pin!(P1_2, 1, 2, Gpio1); | ||
| 429 | impl_pin!(P1_3, 1, 3, Gpio1); | ||
| 430 | impl_pin!(P1_4, 1, 4, Gpio1); | ||
| 431 | impl_pin!(P1_5, 1, 5, Gpio1); | ||
| 432 | impl_pin!(P1_6, 1, 6, Gpio1); | ||
| 433 | impl_pin!(P1_7, 1, 7, Gpio1); | ||
| 434 | impl_pin!(P1_8, 1, 8, Gpio1); | ||
| 435 | impl_pin!(P1_9, 1, 9, Gpio1); | ||
| 436 | impl_pin!(P1_10, 1, 10, Gpio1); | ||
| 437 | impl_pin!(P1_11, 1, 11, Gpio1); | ||
| 438 | impl_pin!(P1_12, 1, 12, Gpio1); | ||
| 439 | impl_pin!(P1_13, 1, 13, Gpio1); | ||
| 440 | impl_pin!(P1_14, 1, 14, Gpio1); | ||
| 441 | impl_pin!(P1_15, 1, 15, Gpio1); | ||
| 442 | impl_pin!(P1_16, 1, 16, Gpio1); | ||
| 443 | impl_pin!(P1_17, 1, 17, Gpio1); | ||
| 444 | impl_pin!(P1_18, 1, 18, Gpio1); | ||
| 445 | impl_pin!(P1_19, 1, 19, Gpio1); | ||
| 446 | impl_pin!(P1_20, 1, 20, Gpio1); | ||
| 447 | impl_pin!(P1_21, 1, 21, Gpio1); | ||
| 448 | impl_pin!(P1_22, 1, 22, Gpio1); | ||
| 449 | impl_pin!(P1_23, 1, 23, Gpio1); | ||
| 450 | impl_pin!(P1_24, 1, 24, Gpio1); | ||
| 451 | impl_pin!(P1_25, 1, 25, Gpio1); | ||
| 452 | impl_pin!(P1_26, 1, 26, Gpio1); | ||
| 453 | impl_pin!(P1_27, 1, 27, Gpio1); | ||
| 454 | impl_pin!(P1_28, 1, 28, Gpio1); | ||
| 455 | impl_pin!(P1_29, 1, 29, Gpio1); | ||
| 456 | impl_pin!(P1_30, 1, 30, Gpio1); | ||
| 457 | impl_pin!(P1_31, 1, 31, Gpio1); | ||
| 458 | |||
| 459 | impl_pin!(P2_0, 2, 0, Gpio2); | ||
| 460 | impl_pin!(P2_1, 2, 1, Gpio2); | ||
| 461 | impl_pin!(P2_2, 2, 2, Gpio2); | ||
| 462 | impl_pin!(P2_3, 2, 3, Gpio2); | ||
| 463 | impl_pin!(P2_4, 2, 4, Gpio2); | ||
| 464 | impl_pin!(P2_5, 2, 5, Gpio2); | ||
| 465 | impl_pin!(P2_6, 2, 6, Gpio2); | ||
| 466 | impl_pin!(P2_7, 2, 7, Gpio2); | ||
| 467 | impl_pin!(P2_8, 2, 8, Gpio2); | ||
| 468 | impl_pin!(P2_9, 2, 9, Gpio2); | ||
| 469 | impl_pin!(P2_10, 2, 10, Gpio2); | ||
| 470 | impl_pin!(P2_11, 2, 11, Gpio2); | ||
| 471 | impl_pin!(P2_12, 2, 12, Gpio2); | ||
| 472 | impl_pin!(P2_13, 2, 13, Gpio2); | ||
| 473 | impl_pin!(P2_14, 2, 14, Gpio2); | ||
| 474 | impl_pin!(P2_15, 2, 15, Gpio2); | ||
| 475 | impl_pin!(P2_16, 2, 16, Gpio2); | ||
| 476 | impl_pin!(P2_17, 2, 17, Gpio2); | ||
| 477 | impl_pin!(P2_18, 2, 18, Gpio2); | ||
| 478 | impl_pin!(P2_19, 2, 19, Gpio2); | ||
| 479 | impl_pin!(P2_20, 2, 20, Gpio2); | ||
| 480 | impl_pin!(P2_21, 2, 21, Gpio2); | ||
| 481 | impl_pin!(P2_22, 2, 22, Gpio2); | ||
| 482 | impl_pin!(P2_23, 2, 23, Gpio2); | ||
| 483 | impl_pin!(P2_24, 2, 24, Gpio2); | ||
| 484 | impl_pin!(P2_25, 2, 25, Gpio2); | ||
| 485 | impl_pin!(P2_26, 2, 26, Gpio2); | ||
| 486 | impl_pin!(P2_27, 2, 27, Gpio2); | ||
| 487 | impl_pin!(P2_28, 2, 28, Gpio2); | ||
| 488 | impl_pin!(P2_29, 2, 29, Gpio2); | ||
| 489 | impl_pin!(P2_30, 2, 30, Gpio2); | ||
| 490 | impl_pin!(P2_31, 2, 31, Gpio2); | ||
| 491 | |||
| 492 | impl_pin!(P3_0, 3, 0, Gpio3); | ||
| 493 | impl_pin!(P3_1, 3, 1, Gpio3); | ||
| 494 | impl_pin!(P3_2, 3, 2, Gpio3); | ||
| 495 | impl_pin!(P3_3, 3, 3, Gpio3); | ||
| 496 | impl_pin!(P3_4, 3, 4, Gpio3); | ||
| 497 | impl_pin!(P3_5, 3, 5, Gpio3); | ||
| 498 | impl_pin!(P3_6, 3, 6, Gpio3); | ||
| 499 | impl_pin!(P3_7, 3, 7, Gpio3); | ||
| 500 | impl_pin!(P3_8, 3, 8, Gpio3); | ||
| 501 | impl_pin!(P3_9, 3, 9, Gpio3); | ||
| 502 | impl_pin!(P3_10, 3, 10, Gpio3); | ||
| 503 | impl_pin!(P3_11, 3, 11, Gpio3); | ||
| 504 | impl_pin!(P3_12, 3, 12, Gpio3); | ||
| 505 | impl_pin!(P3_13, 3, 13, Gpio3); | ||
| 506 | impl_pin!(P3_14, 3, 14, Gpio3); | ||
| 507 | impl_pin!(P3_15, 3, 15, Gpio3); | ||
| 508 | impl_pin!(P3_16, 3, 16, Gpio3); | ||
| 509 | impl_pin!(P3_17, 3, 17, Gpio3); | ||
| 510 | impl_pin!(P3_18, 3, 18, Gpio3); | ||
| 511 | impl_pin!(P3_19, 3, 19, Gpio3); | ||
| 512 | impl_pin!(P3_20, 3, 20, Gpio3); | ||
| 513 | impl_pin!(P3_21, 3, 21, Gpio3); | ||
| 514 | impl_pin!(P3_22, 3, 22, Gpio3); | ||
| 515 | impl_pin!(P3_23, 3, 23, Gpio3); | ||
| 516 | impl_pin!(P3_24, 3, 24, Gpio3); | ||
| 517 | impl_pin!(P3_25, 3, 25, Gpio3); | ||
| 518 | impl_pin!(P3_26, 3, 26, Gpio3); | ||
| 519 | impl_pin!(P3_27, 3, 27, Gpio3); | ||
| 520 | impl_pin!(P3_28, 3, 28, Gpio3); | ||
| 521 | impl_pin!(P3_29, 3, 29, Gpio3); | ||
| 522 | impl_pin!(P3_30, 3, 30, Gpio3); | ||
| 523 | impl_pin!(P3_31, 3, 31, Gpio3); | ||
| 524 | |||
| 525 | impl_pin!(P4_0, 4, 0, Gpio4); | ||
| 526 | impl_pin!(P4_1, 4, 1, Gpio4); | ||
| 527 | impl_pin!(P4_2, 4, 2, Gpio4); | ||
| 528 | impl_pin!(P4_3, 4, 3, Gpio4); | ||
| 529 | impl_pin!(P4_4, 4, 4, Gpio4); | ||
| 530 | impl_pin!(P4_5, 4, 5, Gpio4); | ||
| 531 | impl_pin!(P4_6, 4, 6, Gpio4); | ||
| 532 | impl_pin!(P4_7, 4, 7, Gpio4); | ||
| 533 | impl_pin!(P4_8, 4, 8, Gpio4); | ||
| 534 | impl_pin!(P4_9, 4, 9, Gpio4); | ||
| 535 | impl_pin!(P4_10, 4, 10, Gpio4); | ||
| 536 | impl_pin!(P4_11, 4, 11, Gpio4); | ||
| 537 | impl_pin!(P4_12, 4, 12, Gpio4); | ||
| 538 | impl_pin!(P4_13, 4, 13, Gpio4); | ||
| 539 | impl_pin!(P4_14, 4, 14, Gpio4); | ||
| 540 | impl_pin!(P4_15, 4, 15, Gpio4); | ||
| 541 | impl_pin!(P4_16, 4, 16, Gpio4); | ||
| 542 | impl_pin!(P4_17, 4, 17, Gpio4); | ||
| 543 | impl_pin!(P4_18, 4, 18, Gpio4); | ||
| 544 | impl_pin!(P4_19, 4, 19, Gpio4); | ||
| 545 | impl_pin!(P4_20, 4, 20, Gpio4); | ||
| 546 | impl_pin!(P4_21, 4, 21, Gpio4); | ||
| 547 | impl_pin!(P4_22, 4, 22, Gpio4); | ||
| 548 | impl_pin!(P4_23, 4, 23, Gpio4); | ||
| 549 | impl_pin!(P4_24, 4, 24, Gpio4); | ||
| 550 | impl_pin!(P4_25, 4, 25, Gpio4); | ||
| 551 | impl_pin!(P4_26, 4, 26, Gpio4); | ||
| 552 | impl_pin!(P4_27, 4, 27, Gpio4); | ||
| 553 | impl_pin!(P4_28, 4, 28, Gpio4); | ||
| 554 | impl_pin!(P4_29, 4, 29, Gpio4); | ||
| 555 | impl_pin!(P4_30, 4, 30, Gpio4); | ||
| 556 | impl_pin!(P4_31, 4, 31, Gpio4); | ||
| 557 | |||
| 558 | /// A flexible pin that can be configured as input or output. | ||
| 559 | pub struct Flex<'d> { | ||
| 560 | pin: Peri<'d, AnyPin>, | ||
| 561 | _marker: PhantomData<&'d mut ()>, | ||
| 562 | } | ||
| 563 | |||
| 564 | impl<'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 | ||
| 685 | impl<'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. | ||
| 756 | pub struct Output<'d> { | ||
| 757 | flex: Flex<'d>, | ||
| 758 | } | ||
| 759 | |||
| 760 | impl<'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. | ||
| 815 | pub struct Input<'d> { | ||
| 816 | flex: Flex<'d>, | ||
| 817 | } | ||
| 818 | |||
| 819 | impl<'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 | ||
| 859 | impl<'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 | |||
| 891 | impl 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 | |||
| 918 | impl 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. | ||
| 946 | impl 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 | |||
| 962 | impl 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 | |||
| 976 | impl 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 | |||
| 992 | impl 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 | |||
| 1004 | impl 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 | |||
| 1014 | impl embedded_hal_1::digital::ErrorType for Flex<'_> { | ||
| 1015 | type Error = Infallible; | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | impl embedded_hal_1::digital::ErrorType for Input<'_> { | ||
| 1019 | type Error = Infallible; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | impl embedded_hal_1::digital::ErrorType for Output<'_> { | ||
| 1023 | type Error = Infallible; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | impl 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 | |||
| 1038 | impl 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 | |||
| 1052 | impl 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 | |||
| 3 | use core::future::Future; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | |||
| 6 | use embassy_hal_internal::Peri; | ||
| 7 | use embassy_hal_internal::drop::OnDrop; | ||
| 8 | use mcxa_pac::lpi2c0::mtdr::Cmd; | ||
| 9 | |||
| 10 | use super::{Async, Blocking, Error, Instance, InterruptHandler, Mode, Result, SclPin, SdaPin}; | ||
| 11 | use crate::AnyPin; | ||
| 12 | use crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig}; | ||
| 13 | use crate::clocks::{PoweredClock, enable_and_reset}; | ||
| 14 | use 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))] | ||
| 19 | pub 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 | |||
| 31 | impl 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))] | ||
| 47 | enum SendStop { | ||
| 48 | No, | ||
| 49 | Yes, | ||
| 50 | } | ||
| 51 | |||
| 52 | /// I2C controller configuration | ||
| 53 | #[derive(Clone, Copy, Default)] | ||
| 54 | #[non_exhaustive] | ||
| 55 | pub struct Config { | ||
| 56 | /// Bus speed | ||
| 57 | pub speed: Speed, | ||
| 58 | } | ||
| 59 | |||
| 60 | /// I2C Controller Driver. | ||
| 61 | pub 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 | |||
| 69 | impl<'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 | |||
| 81 | impl<'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 | |||
| 389 | impl<'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 | |||
| 609 | impl<'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 | |||
| 617 | impl<'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 | |||
| 625 | impl<'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 | |||
| 633 | impl<'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 | |||
| 663 | impl 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 | |||
| 675 | impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> { | ||
| 676 | type Error = Error; | ||
| 677 | } | ||
| 678 | |||
| 679 | impl<'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 | |||
| 703 | impl<'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 | |||
| 735 | impl<'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 | |||
| 3 | use core::marker::PhantomData; | ||
| 4 | |||
| 5 | use embassy_hal_internal::PeripheralType; | ||
| 6 | use maitake_sync::WaitCell; | ||
| 7 | use paste::paste; | ||
| 8 | |||
| 9 | use crate::clocks::periph_helpers::Lpi2cConfig; | ||
| 10 | use crate::clocks::{ClockError, Gate}; | ||
| 11 | use crate::gpio::{GpioPin, SealedPin}; | ||
| 12 | use crate::{interrupt, pac}; | ||
| 13 | |||
| 14 | /// Shorthand for `Result<T>`. | ||
| 15 | pub type Result<T> = core::result::Result<T, Error>; | ||
| 16 | |||
| 17 | pub mod controller; | ||
| 18 | |||
| 19 | /// Error information type | ||
| 20 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 21 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 22 | pub 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. | ||
| 46 | pub struct InterruptHandler<T: Instance> { | ||
| 47 | _phantom: PhantomData<T>, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl<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 | |||
| 81 | mod sealed { | ||
| 82 | /// Seal a trait | ||
| 83 | pub trait Sealed {} | ||
| 84 | } | ||
| 85 | |||
| 86 | impl<T: GpioPin> sealed::Sealed for T {} | ||
| 87 | |||
| 88 | trait SealedInstance { | ||
| 89 | fn regs() -> &'static pac::lpi2c0::RegisterBlock; | ||
| 90 | fn wait_cell() -> &'static WaitCell; | ||
| 91 | } | ||
| 92 | |||
| 93 | /// I2C Instance | ||
| 94 | #[allow(private_bounds)] | ||
| 95 | pub 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 | |||
| 102 | macro_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 | |||
| 127 | impl_instance!(0, 1, 2, 3); | ||
| 128 | |||
| 129 | /// SCL pin trait. | ||
| 130 | pub trait SclPin<Instance>: GpioPin + sealed::Sealed + PeripheralType { | ||
| 131 | fn mux(&self); | ||
| 132 | } | ||
| 133 | |||
| 134 | /// SDA pin trait. | ||
| 135 | pub trait SdaPin<Instance>: GpioPin + sealed::Sealed + PeripheralType { | ||
| 136 | fn mux(&self); | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Driver mode. | ||
| 140 | #[allow(private_bounds)] | ||
| 141 | pub trait Mode: sealed::Sealed {} | ||
| 142 | |||
| 143 | /// Blocking mode. | ||
| 144 | pub struct Blocking; | ||
| 145 | impl sealed::Sealed for Blocking {} | ||
| 146 | impl Mode for Blocking {} | ||
| 147 | |||
| 148 | /// Async mode. | ||
| 149 | pub struct Async; | ||
| 150 | impl sealed::Sealed for Async {} | ||
| 151 | impl Mode for Async {} | ||
| 152 | |||
| 153 | macro_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 | |||
| 167 | impl_pin!(P0_16, LPI2C0, Mux2, SdaPin); | ||
| 168 | impl_pin!(P0_17, LPI2C0, Mux2, SclPin); | ||
| 169 | impl_pin!(P0_18, LPI2C0, Mux2, SclPin); | ||
| 170 | impl_pin!(P0_19, LPI2C0, Mux2, SdaPin); | ||
| 171 | impl_pin!(P1_0, LPI2C1, Mux3, SdaPin); | ||
| 172 | impl_pin!(P1_1, LPI2C1, Mux3, SclPin); | ||
| 173 | impl_pin!(P1_2, LPI2C1, Mux3, SdaPin); | ||
| 174 | impl_pin!(P1_3, LPI2C1, Mux3, SclPin); | ||
| 175 | impl_pin!(P1_8, LPI2C2, Mux3, SdaPin); | ||
| 176 | impl_pin!(P1_9, LPI2C2, Mux3, SclPin); | ||
| 177 | impl_pin!(P1_10, LPI2C2, Mux3, SdaPin); | ||
| 178 | impl_pin!(P1_11, LPI2C2, Mux3, SclPin); | ||
| 179 | impl_pin!(P1_12, LPI2C1, Mux2, SdaPin); | ||
| 180 | impl_pin!(P1_13, LPI2C1, Mux2, SclPin); | ||
| 181 | impl_pin!(P1_14, LPI2C1, Mux2, SclPin); | ||
| 182 | impl_pin!(P1_15, LPI2C1, Mux2, SdaPin); | ||
| 183 | impl_pin!(P1_30, LPI2C0, Mux3, SdaPin); | ||
| 184 | impl_pin!(P1_31, LPI2C0, Mux3, SclPin); | ||
| 185 | impl_pin!(P3_27, LPI2C3, Mux2, SclPin); | ||
| 186 | impl_pin!(P3_28, LPI2C3, Mux2, SdaPin); | ||
| 187 | // impl_pin!(P3_29, LPI2C3, Mux2, HreqPin); What is this HREQ pin? | ||
| 188 | impl_pin!(P3_30, LPI2C3, Mux2, SclPin); | ||
| 189 | impl_pin!(P3_31, LPI2C3, Mux2, SdaPin); | ||
| 190 | impl_pin!(P4_2, LPI2C2, Mux2, SdaPin); | ||
| 191 | impl_pin!(P4_3, LPI2C0, Mux2, SclPin); | ||
| 192 | impl_pin!(P4_4, LPI2C2, Mux2, SdaPin); | ||
| 193 | impl_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 | |||
| 9 | mod 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 | |||
| 44 | use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; | ||
| 45 | |||
| 46 | pub use generated::interrupt::{Priority, typelevel}; | ||
| 47 | |||
| 48 | use 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. | ||
| 54 | pub 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)] | ||
| 80 | pub 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 | |||
| 90 | static LAST_DEFAULT_VECTOR: AtomicU16 = AtomicU16::new(0); | ||
| 91 | static LAST_DEFAULT_COUNT: AtomicU32 = AtomicU32::new(0); | ||
| 92 | static LAST_DEFAULT_CFSR: AtomicU32 = AtomicU32::new(0); | ||
| 93 | static LAST_DEFAULT_HFSR: AtomicU32 = AtomicU32::new(0); | ||
| 94 | static LAST_DEFAULT_PC: AtomicU32 = AtomicU32::new(0); | ||
| 95 | static LAST_DEFAULT_LR: AtomicU32 = AtomicU32::new(0); | ||
| 96 | static LAST_DEFAULT_SP: AtomicU32 = AtomicU32::new(0); | ||
| 97 | |||
| 98 | #[inline] | ||
| 99 | pub 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] | ||
| 112 | pub 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. | ||
| 123 | pub struct OsEvent; | ||
| 124 | pub const OS_EVENT: OsEvent = OsEvent; | ||
| 125 | |||
| 126 | impl 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 | |||
| 161 | impl 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. | ||
| 185 | pub struct Lpuart2; | ||
| 186 | pub const LPUART2: Lpuart2 = Lpuart2; | ||
| 187 | |||
| 188 | impl 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 | |||
| 223 | impl 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 | |||
| 248 | pub struct Rtc; | ||
| 249 | pub const RTC: Rtc = Rtc; | ||
| 250 | |||
| 251 | impl 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 | |||
| 286 | pub struct Gpio0; | ||
| 287 | pub const GPIO0: Gpio0 = Gpio0; | ||
| 288 | |||
| 289 | impl 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 | |||
| 324 | pub struct Gpio1; | ||
| 325 | pub const GPIO1: Gpio1 = Gpio1; | ||
| 326 | |||
| 327 | impl 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 | |||
| 362 | pub struct Gpio2; | ||
| 363 | pub const GPIO2: Gpio2 = Gpio2; | ||
| 364 | |||
| 365 | impl 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 | |||
| 400 | pub struct Gpio3; | ||
| 401 | pub const GPIO3: Gpio3 = Gpio3; | ||
| 402 | |||
| 403 | impl 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 | |||
| 438 | pub struct Gpio4; | ||
| 439 | pub const GPIO4: Gpio4 = Gpio4; | ||
| 440 | |||
| 441 | impl 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. | ||
| 479 | pub 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. | ||
| 487 | pub 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 | |||
| 493 | impl 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. | ||
| 503 | pub 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] | ||
| 513 | pub 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 | |||
| 8 | pub mod clocks; // still provide clock helpers | ||
| 9 | pub mod dma; | ||
| 10 | pub mod gpio; | ||
| 11 | |||
| 12 | pub mod adc; | ||
| 13 | pub mod clkout; | ||
| 14 | pub mod config; | ||
| 15 | pub mod crc; | ||
| 16 | pub mod i2c; | ||
| 17 | pub mod interrupt; | ||
| 18 | pub mod lpuart; | ||
| 19 | pub mod ostimer; | ||
| 20 | pub mod rtc; | ||
| 21 | |||
| 22 | pub use crate::pac::NVIC_PRIO_BITS; | ||
| 23 | |||
| 24 | #[rustfmt::skip] | ||
| 25 | embassy_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 | ||
| 340 | pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output}; | ||
| 341 | pub use interrupt::InterruptExt; | ||
| 342 | #[cfg(feature = "unstable-pac")] | ||
| 343 | pub use mcxa_pac as pac; | ||
| 344 | #[cfg(not(feature = "unstable-pac"))] | ||
| 345 | pub(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). | ||
| 349 | pub 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] | ||
| 408 | macro_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 @@ | |||
| 1 | use core::future::poll_fn; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 4 | use core::task::Poll; | ||
| 5 | |||
| 6 | use embassy_hal_internal::Peri; | ||
| 7 | use embassy_hal_internal::atomic_ring_buffer::RingBuffer; | ||
| 8 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 9 | |||
| 10 | use super::*; | ||
| 11 | use crate::interrupt; | ||
| 12 | |||
| 13 | // ============================================================================ | ||
| 14 | // STATIC STATE MANAGEMENT | ||
| 15 | // ============================================================================ | ||
| 16 | |||
| 17 | /// State for buffered LPUART operations | ||
| 18 | pub 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 | |||
| 27 | impl Default for State { | ||
| 28 | fn default() -> Self { | ||
| 29 | Self::new() | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | impl 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 | ||
| 51 | pub struct BufferedLpuart<'a> { | ||
| 52 | tx: BufferedLpuartTx<'a>, | ||
| 53 | rx: BufferedLpuartRx<'a>, | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Buffered LPUART TX driver | ||
| 57 | pub 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 | ||
| 65 | pub 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 | |||
| 76 | impl<'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 | |||
| 346 | impl<'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 | |||
| 402 | impl<'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 | |||
| 486 | impl<'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 | |||
| 543 | impl<'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 | ||
| 621 | pub struct BufferedInterruptHandler<T: Instance> { | ||
| 622 | _phantom: PhantomData<T>, | ||
| 623 | } | ||
| 624 | |||
| 625 | impl<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 | |||
| 738 | impl embedded_io_async::ErrorType for BufferedLpuartTx<'_> { | ||
| 739 | type Error = Error; | ||
| 740 | } | ||
| 741 | |||
| 742 | impl embedded_io_async::ErrorType for BufferedLpuartRx<'_> { | ||
| 743 | type Error = Error; | ||
| 744 | } | ||
| 745 | |||
| 746 | impl embedded_io_async::ErrorType for BufferedLpuart<'_> { | ||
| 747 | type Error = Error; | ||
| 748 | } | ||
| 749 | |||
| 750 | impl 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 | |||
| 760 | impl 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 | |||
| 766 | impl 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 | |||
| 776 | impl 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 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | |||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 5 | use paste::paste; | ||
| 6 | |||
| 7 | use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig}; | ||
| 8 | use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; | ||
| 9 | use crate::gpio::SealedPin; | ||
| 10 | use crate::pac::lpuart0::baud::Sbns as StopBits; | ||
| 11 | use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, M as DataBits, Pt as Parity}; | ||
| 12 | use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource}; | ||
| 13 | use crate::pac::lpuart0::stat::Msbf as MsbFirst; | ||
| 14 | use crate::{AnyPin, interrupt, pac}; | ||
| 15 | |||
| 16 | pub mod buffered; | ||
| 17 | |||
| 18 | // ============================================================================ | ||
| 19 | // DMA INTEGRATION | ||
| 20 | // ============================================================================ | ||
| 21 | |||
| 22 | use crate::dma::{ | ||
| 23 | Channel as DmaChannelTrait, DMA_MAX_TRANSFER_SIZE, DmaChannel, DmaRequest, EnableInterrupt, RingBuffer, | ||
| 24 | }; | ||
| 25 | |||
| 26 | // ============================================================================ | ||
| 27 | // MISC | ||
| 28 | // ============================================================================ | ||
| 29 | |||
| 30 | mod sealed { | ||
| 31 | /// Simply seal a trait to prevent external implementations | ||
| 32 | pub trait Sealed {} | ||
| 33 | } | ||
| 34 | |||
| 35 | // ============================================================================ | ||
| 36 | // INSTANCE TRAIT | ||
| 37 | // ============================================================================ | ||
| 38 | |||
| 39 | pub type Regs = &'static crate::pac::lpuart0::RegisterBlock; | ||
| 40 | |||
| 41 | pub trait SealedInstance { | ||
| 42 | fn info() -> Info; | ||
| 43 | fn index() -> usize; | ||
| 44 | fn buffered_state() -> &'static buffered::State; | ||
| 45 | } | ||
| 46 | |||
| 47 | pub struct Info { | ||
| 48 | pub regs: Regs, | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Trait for LPUART peripheral instances | ||
| 52 | #[allow(private_bounds)] | ||
| 53 | pub 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 | |||
| 62 | macro_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 | ||
| 104 | impl_instance!(0; 1; 2; 3; 4; 5); | ||
| 105 | |||
| 106 | // ============================================================================ | ||
| 107 | // INSTANCE HELPER FUNCTIONS | ||
| 108 | // ============================================================================ | ||
| 109 | |||
| 110 | /// Perform software reset on the LPUART peripheral | ||
| 111 | pub 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 | ||
| 118 | pub fn disable_transceiver(regs: Regs) { | ||
| 119 | regs.ctrl().modify(|_, w| w.te().disabled().re().disabled()); | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Calculate and configure baudrate settings | ||
| 123 | pub 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) | ||
| 144 | pub 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) | ||
| 153 | pub 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 | ||
| 188 | pub 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 | ||
| 206 | pub fn clear_all_status_flags(regs: Regs) { | ||
| 207 | regs.stat().reset(); | ||
| 208 | } | ||
| 209 | |||
| 210 | /// Configure hardware flow control if enabled | ||
| 211 | pub 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) | ||
| 238 | pub 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 | ||
| 243 | pub 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 | |||
| 256 | pub 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 | ||
| 295 | pub 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 | |||
| 307 | pub 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 | |||
| 336 | pub 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 | |||
| 350 | impl<T: SealedPin> sealed::Sealed for T {} | ||
| 351 | |||
| 352 | /// io configuration trait for Lpuart Tx configuration | ||
| 353 | pub 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 | ||
| 359 | pub 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 | ||
| 365 | pub 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 | ||
| 371 | pub 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 | |||
| 376 | macro_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 | |||
| 391 | macro_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 | ||
| 407 | macro_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 | |||
| 417 | macro_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 | ||
| 428 | impl_tx_pin!(LPUART0, P0_3, Mux2); | ||
| 429 | impl_tx_pin!(LPUART0, P0_21, Mux3); | ||
| 430 | impl_tx_pin!(LPUART0, P2_1, Mux2); | ||
| 431 | |||
| 432 | impl_rx_pin!(LPUART0, P0_2, Mux2); | ||
| 433 | impl_rx_pin!(LPUART0, P0_20, Mux3); | ||
| 434 | impl_rx_pin!(LPUART0, P2_0, Mux2); | ||
| 435 | |||
| 436 | impl_cts_pin!(LPUART0, P0_1, Mux2); | ||
| 437 | impl_cts_pin!(LPUART0, P0_23, Mux3); | ||
| 438 | impl_cts_pin!(LPUART0, P2_3, Mux2); | ||
| 439 | |||
| 440 | impl_rts_pin!(LPUART0, P0_0, Mux2); | ||
| 441 | impl_rts_pin!(LPUART0, P0_22, Mux3); | ||
| 442 | impl_rts_pin!(LPUART0, P2_2, Mux2); | ||
| 443 | |||
| 444 | // LPUART 1 | ||
| 445 | impl_tx_pin!(LPUART1, P1_9, Mux2); | ||
| 446 | impl_tx_pin!(LPUART1, P2_13, Mux3); | ||
| 447 | impl_tx_pin!(LPUART1, P3_9, Mux3); | ||
| 448 | impl_tx_pin!(LPUART1, P3_21, Mux3); | ||
| 449 | |||
| 450 | impl_rx_pin!(LPUART1, P1_8, Mux2); | ||
| 451 | impl_rx_pin!(LPUART1, P2_12, Mux3); | ||
| 452 | impl_rx_pin!(LPUART1, P3_8, Mux3); | ||
| 453 | impl_rx_pin!(LPUART1, P3_20, Mux3); | ||
| 454 | |||
| 455 | impl_cts_pin!(LPUART1, P1_11, Mux2); | ||
| 456 | impl_cts_pin!(LPUART1, P2_17, Mux3); | ||
| 457 | impl_cts_pin!(LPUART1, P3_11, Mux3); | ||
| 458 | impl_cts_pin!(LPUART1, P3_23, Mux3); | ||
| 459 | |||
| 460 | impl_rts_pin!(LPUART1, P1_10, Mux2); | ||
| 461 | impl_rts_pin!(LPUART1, P2_15, Mux3); | ||
| 462 | impl_rts_pin!(LPUART1, P2_16, Mux3); | ||
| 463 | impl_rts_pin!(LPUART1, P3_10, Mux3); | ||
| 464 | |||
| 465 | // LPUART 2 | ||
| 466 | impl_tx_pin!(LPUART2, P1_5, Mux3); | ||
| 467 | impl_tx_pin!(LPUART2, P1_13, Mux3); | ||
| 468 | impl_tx_pin!(LPUART2, P2_2, Mux3); | ||
| 469 | impl_tx_pin!(LPUART2, P2_10, Mux3); | ||
| 470 | impl_tx_pin!(LPUART2, P3_15, Mux2); | ||
| 471 | |||
| 472 | impl_rx_pin!(LPUART2, P1_4, Mux3); | ||
| 473 | impl_rx_pin!(LPUART2, P1_12, Mux3); | ||
| 474 | impl_rx_pin!(LPUART2, P2_3, Mux3); | ||
| 475 | impl_rx_pin!(LPUART2, P2_11, Mux3); | ||
| 476 | impl_rx_pin!(LPUART2, P3_14, Mux2); | ||
| 477 | |||
| 478 | impl_cts_pin!(LPUART2, P1_7, Mux3); | ||
| 479 | impl_cts_pin!(LPUART2, P1_15, Mux3); | ||
| 480 | impl_cts_pin!(LPUART2, P2_4, Mux3); | ||
| 481 | impl_cts_pin!(LPUART2, P3_13, Mux2); | ||
| 482 | |||
| 483 | impl_rts_pin!(LPUART2, P1_6, Mux3); | ||
| 484 | impl_rts_pin!(LPUART2, P1_14, Mux3); | ||
| 485 | impl_rts_pin!(LPUART2, P2_5, Mux3); | ||
| 486 | impl_rts_pin!(LPUART2, P3_12, Mux2); | ||
| 487 | |||
| 488 | // LPUART 3 | ||
| 489 | impl_tx_pin!(LPUART3, P3_1, Mux3); | ||
| 490 | impl_tx_pin!(LPUART3, P3_12, Mux3); | ||
| 491 | impl_tx_pin!(LPUART3, P4_5, Mux3); | ||
| 492 | |||
| 493 | impl_rx_pin!(LPUART3, P3_0, Mux3); | ||
| 494 | impl_rx_pin!(LPUART3, P3_13, Mux3); | ||
| 495 | impl_rx_pin!(LPUART3, P4_2, Mux3); | ||
| 496 | |||
| 497 | impl_cts_pin!(LPUART3, P3_7, Mux3); | ||
| 498 | impl_cts_pin!(LPUART3, P3_14, Mux3); | ||
| 499 | impl_cts_pin!(LPUART3, P4_6, Mux3); | ||
| 500 | |||
| 501 | impl_rts_pin!(LPUART3, P3_6, Mux3); | ||
| 502 | impl_rts_pin!(LPUART3, P3_15, Mux3); | ||
| 503 | impl_rts_pin!(LPUART3, P4_7, Mux3); | ||
| 504 | |||
| 505 | // LPUART 4 | ||
| 506 | impl_tx_pin!(LPUART4, P2_7, Mux3); | ||
| 507 | impl_tx_pin!(LPUART4, P3_19, Mux2); | ||
| 508 | impl_tx_pin!(LPUART4, P3_27, Mux3); | ||
| 509 | impl_tx_pin!(LPUART4, P4_3, Mux3); | ||
| 510 | |||
| 511 | impl_rx_pin!(LPUART4, P2_6, Mux3); | ||
| 512 | impl_rx_pin!(LPUART4, P3_18, Mux2); | ||
| 513 | impl_rx_pin!(LPUART4, P3_28, Mux3); | ||
| 514 | impl_rx_pin!(LPUART4, P4_4, Mux3); | ||
| 515 | |||
| 516 | impl_cts_pin!(LPUART4, P2_0, Mux3); | ||
| 517 | impl_cts_pin!(LPUART4, P3_17, Mux2); | ||
| 518 | impl_cts_pin!(LPUART4, P3_31, Mux3); | ||
| 519 | |||
| 520 | impl_rts_pin!(LPUART4, P2_1, Mux3); | ||
| 521 | impl_rts_pin!(LPUART4, P3_16, Mux2); | ||
| 522 | impl_rts_pin!(LPUART4, P3_30, Mux3); | ||
| 523 | |||
| 524 | // LPUART 5 | ||
| 525 | impl_tx_pin!(LPUART5, P1_10, Mux8); | ||
| 526 | impl_tx_pin!(LPUART5, P1_17, Mux8); | ||
| 527 | |||
| 528 | impl_rx_pin!(LPUART5, P1_11, Mux8); | ||
| 529 | impl_rx_pin!(LPUART5, P1_16, Mux8); | ||
| 530 | |||
| 531 | impl_cts_pin!(LPUART5, P1_12, Mux8); | ||
| 532 | impl_cts_pin!(LPUART5, P1_19, Mux8); | ||
| 533 | |||
| 534 | impl_rts_pin!(LPUART5, P1_13, Mux8); | ||
| 535 | impl_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))] | ||
| 544 | pub 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 | ||
| 572 | pub type Result<T> = core::result::Result<T, Error>; | ||
| 573 | |||
| 574 | // ============================================================================ | ||
| 575 | // CONFIGURATION STRUCTURES | ||
| 576 | // ============================================================================ | ||
| 577 | |||
| 578 | /// Lpuart config | ||
| 579 | #[derive(Debug, Clone, Copy)] | ||
| 580 | pub 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 | |||
| 613 | impl 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))] | ||
| 638 | pub 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)] | ||
| 663 | pub trait Mode: sealed::Sealed {} | ||
| 664 | |||
| 665 | /// Blocking mode. | ||
| 666 | pub struct Blocking; | ||
| 667 | impl sealed::Sealed for Blocking {} | ||
| 668 | impl Mode for Blocking {} | ||
| 669 | |||
| 670 | /// Async mode. | ||
| 671 | pub struct Async; | ||
| 672 | impl sealed::Sealed for Async {} | ||
| 673 | impl Mode for Async {} | ||
| 674 | |||
| 675 | // ============================================================================ | ||
| 676 | // CORE DRIVER STRUCTURES | ||
| 677 | // ============================================================================ | ||
| 678 | |||
| 679 | /// Lpuart driver. | ||
| 680 | pub struct Lpuart<'a, M: Mode> { | ||
| 681 | info: Info, | ||
| 682 | tx: LpuartTx<'a, M>, | ||
| 683 | rx: LpuartRx<'a, M>, | ||
| 684 | } | ||
| 685 | |||
| 686 | /// Lpuart TX driver. | ||
| 687 | pub 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. | ||
| 695 | pub 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. | ||
| 703 | pub 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. | ||
| 711 | pub 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. | ||
| 719 | pub 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. | ||
| 725 | pub 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 | |||
| 734 | impl<'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 | |||
| 799 | impl<'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 | |||
| 851 | impl<'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 | |||
| 862 | impl<'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 | |||
| 968 | impl<'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 | |||
| 979 | impl<'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 | |||
| 1051 | impl<'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. | ||
| 1108 | struct TxDmaGuard<'a, C: DmaChannelTrait> { | ||
| 1109 | dma: &'a DmaChannel<C>, | ||
| 1110 | regs: Regs, | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | impl<'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 | |||
| 1131 | impl<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. | ||
| 1145 | struct RxDmaGuard<'a, C: DmaChannelTrait> { | ||
| 1146 | dma: &'a DmaChannel<C>, | ||
| 1147 | regs: Regs, | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | impl<'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 | |||
| 1170 | impl<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 | |||
| 1183 | impl<'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 | |||
| 1299 | impl<'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 | |||
| 1483 | impl<'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 | |||
| 1498 | impl<'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 | |||
| 1559 | impl<T: Instance, C: DmaChannelTrait> embedded_io::ErrorType for LpuartTxDma<'_, T, C> { | ||
| 1560 | type Error = Error; | ||
| 1561 | } | ||
| 1562 | |||
| 1563 | impl<T: Instance, C: DmaChannelTrait> embedded_io::ErrorType for LpuartRxDma<'_, T, C> { | ||
| 1564 | type Error = Error; | ||
| 1565 | } | ||
| 1566 | |||
| 1567 | impl<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 | |||
| 1575 | impl 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 | |||
| 1588 | impl 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 | |||
| 1608 | impl 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 | |||
| 1620 | impl 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 | |||
| 1628 | impl 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 | |||
| 1640 | impl 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 | |||
| 1656 | impl 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 | |||
| 1668 | impl embedded_hal_nb::serial::ErrorType for LpuartRx<'_, Blocking> { | ||
| 1669 | type Error = Error; | ||
| 1670 | } | ||
| 1671 | |||
| 1672 | impl embedded_hal_nb::serial::ErrorType for LpuartTx<'_, Blocking> { | ||
| 1673 | type Error = Error; | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | impl embedded_hal_nb::serial::ErrorType for Lpuart<'_, Blocking> { | ||
| 1677 | type Error = Error; | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | impl 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 | |||
| 1691 | impl 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 | |||
| 1709 | impl 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 | |||
| 1715 | impl 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 | |||
| 1721 | impl 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 | |||
| 1735 | impl embedded_io::Error for Error { | ||
| 1736 | fn kind(&self) -> embedded_io::ErrorKind { | ||
| 1737 | embedded_io::ErrorKind::Other | ||
| 1738 | } | ||
| 1739 | } | ||
| 1740 | |||
| 1741 | impl embedded_io::ErrorType for LpuartRx<'_, Blocking> { | ||
| 1742 | type Error = Error; | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | impl embedded_io::ErrorType for LpuartTx<'_, Blocking> { | ||
| 1746 | type Error = Error; | ||
| 1747 | } | ||
| 1748 | |||
| 1749 | impl embedded_io::ErrorType for Lpuart<'_, Blocking> { | ||
| 1750 | type Error = Error; | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | impl 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 | |||
| 1759 | impl 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 | |||
| 1769 | impl 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 | |||
| 1775 | impl 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 | |||
| 30 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 31 | |||
| 32 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 33 | |||
| 34 | use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel}; | ||
| 35 | use crate::clocks::{Gate, PoweredClock, assert_reset, enable_and_reset, is_reset_released, release_reset}; | ||
| 36 | use crate::interrupt::InterruptExt; | ||
| 37 | use crate::pac; | ||
| 38 | |||
| 39 | // PAC defines the shared RegisterBlock under `ostimer0`. | ||
| 40 | type Regs = pac::ostimer0::RegisterBlock; | ||
| 41 | |||
| 42 | // OSTIMER EVTIMER register layout constants | ||
| 43 | /// Total width of the EVTIMER counter in bits (42 bits total) | ||
| 44 | const EVTIMER_TOTAL_BITS: u32 = 42; | ||
| 45 | /// Width of the low part of EVTIMER (bits 31:0) | ||
| 46 | const EVTIMER_LO_BITS: u32 = 32; | ||
| 47 | /// Width of the high part of EVTIMER (bits 41:32) | ||
| 48 | const EVTIMER_HI_BITS: u32 = 10; | ||
| 49 | /// Bit position where high part starts in the combined 64-bit value | ||
| 50 | const EVTIMER_HI_SHIFT: u32 = 32; | ||
| 51 | |||
| 52 | /// Bit mask for the high part of EVTIMER | ||
| 53 | const EVTIMER_HI_MASK: u16 = (1 << EVTIMER_HI_BITS) - 1; | ||
| 54 | |||
| 55 | /// Maximum value for MATCH_L register (32-bit) | ||
| 56 | const MATCH_L_MAX: u32 = u32::MAX; | ||
| 57 | /// Maximum value for MATCH_H register (10-bit) | ||
| 58 | const MATCH_H_MAX: u16 = EVTIMER_HI_MASK; | ||
| 59 | |||
| 60 | /// Bit mask for extracting the low 32 bits from a 64-bit value | ||
| 61 | const LOW_32_BIT_MASK: u64 = u32::MAX as u64; | ||
| 62 | |||
| 63 | /// Gray code conversion bit shifts (most significant to least) | ||
| 64 | const 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 | ||
| 68 | const TIMER_MAX_VALUE: u64 = (1u64 << EVTIMER_TOTAL_BITS) - 1; | ||
| 69 | |||
| 70 | /// Threshold for detecting timer rollover in comparisons (1 second at 1MHz) | ||
| 71 | const TIMER_ROLLOVER_THRESHOLD: u64 = 1_000_000; | ||
| 72 | |||
| 73 | /// Common default interrupt priority for OSTIMER | ||
| 74 | const DEFAULT_INTERRUPT_PRIORITY: u8 = 3; | ||
| 75 | |||
| 76 | // Global alarm state for interrupt handling | ||
| 77 | static ALARM_ACTIVE: AtomicBool = AtomicBool::new(false); | ||
| 78 | static mut ALARM_CALLBACK: Option<fn()> = None; | ||
| 79 | static mut ALARM_FLAG: Option<*const AtomicBool> = None; | ||
| 80 | static 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. | ||
| 83 | const 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). | ||
| 85 | const 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. | ||
| 87 | const RESET_STABILIZE_SPINS: usize = 512; | ||
| 88 | |||
| 89 | pub(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 | |||
| 112 | pub(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 | |||
| 135 | fn 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 | ||
| 148 | pub 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 | |||
| 158 | impl<'d> Default for Alarm<'d> { | ||
| 159 | fn default() -> Self { | ||
| 160 | Self::new() | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | impl<'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)] | ||
| 201 | pub 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 | |||
| 208 | impl 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 | ||
| 219 | pub 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 | |||
| 225 | impl<'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)] | ||
| 476 | fn 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 | ||
| 488 | pub trait Instance: Gate<MrccPeriphConfig = OsTimerConfig> + PeripheralType { | ||
| 489 | fn ptr() -> *const Regs; | ||
| 490 | } | ||
| 491 | |||
| 492 | #[cfg(not(feature = "time"))] | ||
| 493 | impl Instance for crate::peripherals::OSTIMER0 { | ||
| 494 | #[inline(always)] | ||
| 495 | fn ptr() -> *const Regs { | ||
| 496 | pac::Ostimer0::ptr() | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | #[inline(always)] | ||
| 501 | fn bin_to_gray(x: u64) -> u64 { | ||
| 502 | x ^ (x >> 1) | ||
| 503 | } | ||
| 504 | |||
| 505 | #[inline(always)] | ||
| 506 | fn 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")] | ||
| 516 | pub 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")] | ||
| 738 | use crate::pac::interrupt; | ||
| 739 | |||
| 740 | #[cfg(feature = "time")] | ||
| 741 | #[allow(non_snake_case)] | ||
| 742 | #[interrupt] | ||
| 743 | fn 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. | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | |||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 5 | use maitake_sync::WaitCell; | ||
| 6 | |||
| 7 | use crate::clocks::with_clocks; | ||
| 8 | use crate::interrupt::typelevel::{Handler, Interrupt}; | ||
| 9 | use crate::pac; | ||
| 10 | use crate::pac::rtc0::cr::Um; | ||
| 11 | |||
| 12 | type Regs = pac::rtc0::RegisterBlock; | ||
| 13 | |||
| 14 | /// Global wait cell for alarm notifications | ||
| 15 | static WAKER: WaitCell = WaitCell::new(); | ||
| 16 | |||
| 17 | /// RTC interrupt handler. | ||
| 18 | pub struct InterruptHandler<I: Instance> { | ||
| 19 | _phantom: PhantomData<I>, | ||
| 20 | } | ||
| 21 | |||
| 22 | /// Trait for RTC peripheral instances | ||
| 23 | pub trait Instance: PeripheralType { | ||
| 24 | type Interrupt: Interrupt; | ||
| 25 | fn ptr() -> *const Regs; | ||
| 26 | } | ||
| 27 | |||
| 28 | /// Token for RTC0 | ||
| 29 | pub type Rtc0 = crate::peripherals::RTC0; | ||
| 30 | impl 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 | ||
| 39 | const DAYS_IN_A_YEAR: u32 = 365; | ||
| 40 | /// Number of seconds in a day | ||
| 41 | const SECONDS_IN_A_DAY: u32 = 86400; | ||
| 42 | /// Number of seconds in an hour | ||
| 43 | const SECONDS_IN_A_HOUR: u32 = 3600; | ||
| 44 | /// Number of seconds in a minute | ||
| 45 | const SECONDS_IN_A_MINUTE: u32 = 60; | ||
| 46 | /// Unix epoch start year | ||
| 47 | const YEAR_RANGE_START: u16 = 1970; | ||
| 48 | |||
| 49 | /// Date and time structure for RTC operations | ||
| 50 | #[derive(Debug, Clone, Copy)] | ||
| 51 | pub 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)] | ||
| 60 | pub 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)] | ||
| 72 | pub struct RtcInterruptEnable; | ||
| 73 | impl 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. | ||
| 93 | pub 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. | ||
| 126 | pub 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 | ||
| 197 | pub 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) | ||
| 207 | pub struct Rtc<'a, I: Instance> { | ||
| 208 | _inst: core::marker::PhantomData<&'a mut I>, | ||
| 209 | } | ||
| 210 | |||
| 211 | impl<'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. | ||
| 443 | impl<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 | ||
| 24 | struct SharedInner { | 24 | struct 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)] | ||
| 32 | pub(crate) enum ControlState { | ||
| 33 | Init, | ||
| 34 | Reboot, | ||
| 35 | Ready, | ||
| 36 | } | ||
| 37 | |||
| 31 | impl Shared { | 38 | impl 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))] |
| 4298 | pub struct CtrlMsg_Req_OTAWrite { | 4298 | pub 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 | } |
| 4301 | impl CtrlMsg_Req_OTAWrite { | 4301 | impl 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"] | |||
| 18 | defmt = { version = "1.0.1", optional = true } | 18 | defmt = { version = "1.0.1", optional = true } |
| 19 | log = { version = "0.4.14", optional = true } | 19 | log = { version = "0.4.14", optional = true } |
| 20 | 20 | ||
| 21 | nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "176dc4afe1dd8df78f3cbda4479ab5151aa32252" } | 21 | nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "52fd51ce762a3d3a81660dea62947e6d2d1e9d91" } |
| 22 | cortex-m = "0.7.7" | 22 | cortex-m = "0.7.7" |
| 23 | 23 | ||
| 24 | embassy-time = { version = "0.5.0", path = "../embassy-time" } | 24 | embassy-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"] | |||
| 130 | nrf54l15-app-s = ["_nrf54l15-app", "_s", "_multi_wdt"] | 135 | nrf54l15-app-s = ["_nrf54l15-app", "_s", "_multi_wdt"] |
| 131 | ## nRF54L15 application core in Non-Secure mode | 136 | ## nRF54L15 application core in Non-Secure mode |
| 132 | nrf54l15-app-ns = ["_nrf54l15-app", "_ns"] | 137 | nrf54l15-app-ns = ["_nrf54l15-app", "_ns"] |
| 138 | ## nRF54L10 application core in Secure mode | ||
| 139 | nrf54l10-app-s = ["_nrf54l10-app", "_s", "_multi_wdt"] | ||
| 140 | ## nRF54L10 application core in Non-Secure mode | ||
| 141 | nrf54l10-app-ns = ["_nrf54l10-app", "_ns"] | ||
| 142 | ## nRF54L05 application core in Secure mode | ||
| 143 | nrf54l05-app-s = ["_nrf54l05-app", "_s", "_multi_wdt"] | ||
| 144 | ## nRF54L05 application core in Non-Secure mode | ||
| 145 | nrf54l05-app-ns = ["_nrf54l05-app", "_ns"] | ||
| 146 | ## nRF54LM20 application core in Secure mode | ||
| 147 | nrf54lm20-app-s = ["_nrf54lm20-app", "_s", "_multi_wdt"] | ||
| 133 | 148 | ||
| 134 | ## nRF9160 in Secure mode | 149 | ## nRF9160 in Secure mode |
| 135 | nrf9160-s = ["_nrf9160", "_s", "_nrf91"] | 150 | nrf9160-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" } | |||
| 202 | rand-core-06 = { package = "rand_core", version = "0.6" } | 223 | rand-core-06 = { package = "rand_core", version = "0.6" } |
| 203 | rand-core-09 = { package = "rand_core", version = "0.9" } | 224 | rand-core-09 = { package = "rand_core", version = "0.9" } |
| 204 | 225 | ||
| 205 | nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "176dc4afe1dd8df78f3cbda4479ab5151aa32252" } | 226 | nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "52fd51ce762a3d3a81660dea62947e6d2d1e9d91" } |
| 206 | 227 | ||
| 207 | defmt = { version = "1.0.1", optional = true } | 228 | defmt = { version = "1.0.1", optional = true } |
| 208 | bitflags = "2.4.2" | 229 | bitflags = "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] | ||
| 4 | pub 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. | ||
| 198 | pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; | ||
| 199 | pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; | ||
| 200 | |||
| 201 | // 1.5 MB NVM | ||
| 202 | #[allow(unused)] | ||
| 203 | pub const FLASH_SIZE: usize = 1524 * 1024; | ||
| 204 | |||
| 205 | embassy_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 | |||
| 487 | impl_pin!(P0_00, 0, 0); | ||
| 488 | impl_pin!(P0_01, 0, 1); | ||
| 489 | impl_pin!(P0_02, 0, 2); | ||
| 490 | impl_pin!(P0_03, 0, 3); | ||
| 491 | impl_pin!(P0_04, 0, 4); | ||
| 492 | impl_pin!(P0_05, 0, 5); | ||
| 493 | impl_pin!(P0_06, 0, 6); | ||
| 494 | |||
| 495 | impl_pin!(P1_00, 1, 0); | ||
| 496 | impl_pin!(P1_01, 1, 1); | ||
| 497 | impl_pin!(P1_02, 1, 2); | ||
| 498 | impl_pin!(P1_03, 1, 3); | ||
| 499 | impl_pin!(P1_04, 1, 4); | ||
| 500 | impl_pin!(P1_05, 1, 5); | ||
| 501 | impl_pin!(P1_06, 1, 6); | ||
| 502 | impl_pin!(P1_07, 1, 7); | ||
| 503 | impl_pin!(P1_08, 1, 8); | ||
| 504 | impl_pin!(P1_09, 1, 9); | ||
| 505 | impl_pin!(P1_10, 1, 10); | ||
| 506 | impl_pin!(P1_11, 1, 11); | ||
| 507 | impl_pin!(P1_12, 1, 12); | ||
| 508 | impl_pin!(P1_13, 1, 13); | ||
| 509 | impl_pin!(P1_14, 1, 14); | ||
| 510 | impl_pin!(P1_15, 1, 15); | ||
| 511 | impl_pin!(P1_16, 1, 16); | ||
| 512 | |||
| 513 | impl_pin!(P2_00, 2, 0); | ||
| 514 | impl_pin!(P2_01, 2, 1); | ||
| 515 | impl_pin!(P2_02, 2, 2); | ||
| 516 | impl_pin!(P2_03, 2, 3); | ||
| 517 | impl_pin!(P2_04, 2, 4); | ||
| 518 | impl_pin!(P2_05, 2, 5); | ||
| 519 | impl_pin!(P2_06, 2, 6); | ||
| 520 | impl_pin!(P2_07, 2, 7); | ||
| 521 | impl_pin!(P2_08, 2, 8); | ||
| 522 | impl_pin!(P2_09, 2, 9); | ||
| 523 | impl_pin!(P2_10, 2, 10); | ||
| 524 | |||
| 525 | cfg_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")] | ||
| 556 | impl_wdt!(WDT, WDT31, WDT31, 0); | ||
| 557 | #[cfg(feature = "_s")] | ||
| 558 | impl_wdt!(WDT0, WDT31, WDT31, 0); | ||
| 559 | #[cfg(feature = "_s")] | ||
| 560 | impl_wdt!(WDT1, WDT30, WDT30, 1); | ||
| 561 | // DPPI00 channels | ||
| 562 | impl_ppi_channel!(PPI00_CH0, DPPIC00, 0 => configurable); | ||
| 563 | impl_ppi_channel!(PPI00_CH1, DPPIC00, 1 => configurable); | ||
| 564 | impl_ppi_channel!(PPI00_CH2, DPPIC00, 2 => configurable); | ||
| 565 | impl_ppi_channel!(PPI00_CH3, DPPIC00, 3 => configurable); | ||
| 566 | impl_ppi_channel!(PPI00_CH4, DPPIC00, 4 => configurable); | ||
| 567 | impl_ppi_channel!(PPI00_CH5, DPPIC00, 5 => configurable); | ||
| 568 | impl_ppi_channel!(PPI00_CH6, DPPIC00, 6 => configurable); | ||
| 569 | impl_ppi_channel!(PPI00_CH7, DPPIC00, 7 => configurable); | ||
| 570 | |||
| 571 | // DPPI10 channels | ||
| 572 | impl_ppi_channel!(PPI10_CH0, DPPIC10, 0 => static); | ||
| 573 | |||
| 574 | // DPPI20 channels | ||
| 575 | impl_ppi_channel!(PPI20_CH0, DPPIC20, 0 => configurable); | ||
| 576 | impl_ppi_channel!(PPI20_CH1, DPPIC20, 1 => configurable); | ||
| 577 | impl_ppi_channel!(PPI20_CH2, DPPIC20, 2 => configurable); | ||
| 578 | impl_ppi_channel!(PPI20_CH3, DPPIC20, 3 => configurable); | ||
| 579 | impl_ppi_channel!(PPI20_CH4, DPPIC20, 4 => configurable); | ||
| 580 | impl_ppi_channel!(PPI20_CH5, DPPIC20, 5 => configurable); | ||
| 581 | impl_ppi_channel!(PPI20_CH6, DPPIC20, 6 => configurable); | ||
| 582 | impl_ppi_channel!(PPI20_CH7, DPPIC20, 7 => configurable); | ||
| 583 | impl_ppi_channel!(PPI20_CH8, DPPIC20, 8 => configurable); | ||
| 584 | impl_ppi_channel!(PPI20_CH9, DPPIC20, 9 => configurable); | ||
| 585 | impl_ppi_channel!(PPI20_CH10, DPPIC20, 10 => configurable); | ||
| 586 | impl_ppi_channel!(PPI20_CH11, DPPIC20, 11 => configurable); | ||
| 587 | impl_ppi_channel!(PPI20_CH12, DPPIC20, 12 => configurable); | ||
| 588 | impl_ppi_channel!(PPI20_CH13, DPPIC20, 13 => configurable); | ||
| 589 | impl_ppi_channel!(PPI20_CH14, DPPIC20, 14 => configurable); | ||
| 590 | impl_ppi_channel!(PPI20_CH15, DPPIC20, 15 => configurable); | ||
| 591 | |||
| 592 | // DPPI30 channels | ||
| 593 | impl_ppi_channel!(PPI30_CH0, DPPIC30, 0 => configurable); | ||
| 594 | impl_ppi_channel!(PPI30_CH1, DPPIC30, 1 => configurable); | ||
| 595 | impl_ppi_channel!(PPI30_CH2, DPPIC30, 2 => configurable); | ||
| 596 | impl_ppi_channel!(PPI30_CH3, DPPIC30, 3 => configurable); | ||
| 597 | |||
| 598 | // DPPI00 groups | ||
| 599 | impl_ppi_group!(PPI00_GROUP0, DPPIC00, 0); | ||
| 600 | impl_ppi_group!(PPI00_GROUP1, DPPIC00, 1); | ||
| 601 | |||
| 602 | // DPPI10 groups | ||
| 603 | impl_ppi_group!(PPI10_GROUP0, DPPIC10, 0); | ||
| 604 | |||
| 605 | // DPPI20 groups | ||
| 606 | impl_ppi_group!(PPI20_GROUP0, DPPIC20, 0); | ||
| 607 | impl_ppi_group!(PPI20_GROUP1, DPPIC20, 1); | ||
| 608 | impl_ppi_group!(PPI20_GROUP2, DPPIC20, 2); | ||
| 609 | impl_ppi_group!(PPI20_GROUP3, DPPIC20, 3); | ||
| 610 | impl_ppi_group!(PPI20_GROUP4, DPPIC20, 4); | ||
| 611 | impl_ppi_group!(PPI20_GROUP5, DPPIC20, 5); | ||
| 612 | |||
| 613 | // DPPI30 groups | ||
| 614 | impl_ppi_group!(PPI30_GROUP0, DPPIC30, 0); | ||
| 615 | impl_ppi_group!(PPI30_GROUP1, DPPIC30, 1); | ||
| 616 | |||
| 617 | impl_timer!(TIMER00, TIMER00, TIMER00); | ||
| 618 | impl_timer!(TIMER10, TIMER10, TIMER10); | ||
| 619 | impl_timer!(TIMER20, TIMER20, TIMER20); | ||
| 620 | impl_timer!(TIMER21, TIMER21, TIMER21); | ||
| 621 | impl_timer!(TIMER22, TIMER22, TIMER22); | ||
| 622 | impl_timer!(TIMER23, TIMER23, TIMER23); | ||
| 623 | impl_timer!(TIMER24, TIMER24, TIMER24); | ||
| 624 | |||
| 625 | impl_twim!(SERIAL20, TWIM20, SERIAL20); | ||
| 626 | impl_twim!(SERIAL21, TWIM21, SERIAL21); | ||
| 627 | impl_twim!(SERIAL22, TWIM22, SERIAL22); | ||
| 628 | impl_twim!(SERIAL30, TWIM30, SERIAL30); | ||
| 629 | |||
| 630 | impl_twis!(SERIAL20, TWIS20, SERIAL20); | ||
| 631 | impl_twis!(SERIAL21, TWIS21, SERIAL21); | ||
| 632 | impl_twis!(SERIAL22, TWIS22, SERIAL22); | ||
| 633 | impl_twis!(SERIAL30, TWIS30, SERIAL30); | ||
| 634 | |||
| 635 | impl_pwm!(PWM20, PWM20, PWM20); | ||
| 636 | impl_pwm!(PWM21, PWM21, PWM21); | ||
| 637 | impl_pwm!(PWM22, PWM22, PWM22); | ||
| 638 | |||
| 639 | #[cfg(feature = "_s")] | ||
| 640 | impl_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")] | ||
| 651 | impl_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 | ); | ||
| 661 | impl_spim!(SERIAL20, SPIM20, SERIAL20, 16_000_000); | ||
| 662 | impl_spim!(SERIAL21, SPIM21, SERIAL21, 16_000_000); | ||
| 663 | impl_spim!(SERIAL22, SPIM22, SERIAL22, 16_000_000); | ||
| 664 | impl_spim!(SERIAL30, SPIM30, SERIAL30, 16_000_000); | ||
| 665 | |||
| 666 | impl_spis!(SERIAL20, SPIS20, SERIAL20); | ||
| 667 | impl_spis!(SERIAL21, SPIS21, SERIAL21); | ||
| 668 | impl_spis!(SERIAL22, SPIS22, SERIAL22); | ||
| 669 | impl_spis!(SERIAL30, SPIS30, SERIAL30); | ||
| 670 | |||
| 671 | impl_uarte!(SERIAL00, UARTE00, SERIAL00); | ||
| 672 | impl_uarte!(SERIAL20, UARTE20, SERIAL20); | ||
| 673 | impl_uarte!(SERIAL21, UARTE21, SERIAL21); | ||
| 674 | impl_uarte!(SERIAL22, UARTE22, SERIAL22); | ||
| 675 | impl_uarte!(SERIAL30, UARTE30, SERIAL30); | ||
| 676 | |||
| 677 | // NB: SAADC uses "pin" abstraction, not "AIN" | ||
| 678 | impl_saadc_input!(P1_04, 1, 4); | ||
| 679 | impl_saadc_input!(P1_05, 1, 5); | ||
| 680 | impl_saadc_input!(P1_06, 1, 6); | ||
| 681 | impl_saadc_input!(P1_07, 1, 7); | ||
| 682 | impl_saadc_input!(P1_11, 1, 11); | ||
| 683 | impl_saadc_input!(P1_12, 1, 12); | ||
| 684 | impl_saadc_input!(P1_13, 1, 13); | ||
| 685 | impl_saadc_input!(P1_14, 1, 14); | ||
| 686 | |||
| 687 | #[cfg(feature = "_s")] | ||
| 688 | impl_cracen!(CRACEN, CRACEN, CRACEN); | ||
| 689 | |||
| 690 | embassy_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] | ||
| 4 | pub 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. | ||
| 198 | pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; | ||
| 199 | pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; | ||
| 200 | |||
| 201 | // 1.5 MB NVM | ||
| 202 | #[allow(unused)] | ||
| 203 | pub const FLASH_SIZE: usize = 1524 * 1024; | ||
| 204 | |||
| 205 | embassy_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 | |||
| 487 | impl_pin!(P0_00, 0, 0); | ||
| 488 | impl_pin!(P0_01, 0, 1); | ||
| 489 | impl_pin!(P0_02, 0, 2); | ||
| 490 | impl_pin!(P0_03, 0, 3); | ||
| 491 | impl_pin!(P0_04, 0, 4); | ||
| 492 | impl_pin!(P0_05, 0, 5); | ||
| 493 | impl_pin!(P0_06, 0, 6); | ||
| 494 | |||
| 495 | impl_pin!(P1_00, 1, 0); | ||
| 496 | impl_pin!(P1_01, 1, 1); | ||
| 497 | impl_pin!(P1_02, 1, 2); | ||
| 498 | impl_pin!(P1_03, 1, 3); | ||
| 499 | impl_pin!(P1_04, 1, 4); | ||
| 500 | impl_pin!(P1_05, 1, 5); | ||
| 501 | impl_pin!(P1_06, 1, 6); | ||
| 502 | impl_pin!(P1_07, 1, 7); | ||
| 503 | impl_pin!(P1_08, 1, 8); | ||
| 504 | impl_pin!(P1_09, 1, 9); | ||
| 505 | impl_pin!(P1_10, 1, 10); | ||
| 506 | impl_pin!(P1_11, 1, 11); | ||
| 507 | impl_pin!(P1_12, 1, 12); | ||
| 508 | impl_pin!(P1_13, 1, 13); | ||
| 509 | impl_pin!(P1_14, 1, 14); | ||
| 510 | impl_pin!(P1_15, 1, 15); | ||
| 511 | impl_pin!(P1_16, 1, 16); | ||
| 512 | |||
| 513 | impl_pin!(P2_00, 2, 0); | ||
| 514 | impl_pin!(P2_01, 2, 1); | ||
| 515 | impl_pin!(P2_02, 2, 2); | ||
| 516 | impl_pin!(P2_03, 2, 3); | ||
| 517 | impl_pin!(P2_04, 2, 4); | ||
| 518 | impl_pin!(P2_05, 2, 5); | ||
| 519 | impl_pin!(P2_06, 2, 6); | ||
| 520 | impl_pin!(P2_07, 2, 7); | ||
| 521 | impl_pin!(P2_08, 2, 8); | ||
| 522 | impl_pin!(P2_09, 2, 9); | ||
| 523 | impl_pin!(P2_10, 2, 10); | ||
| 524 | |||
| 525 | cfg_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")] | ||
| 556 | impl_wdt!(WDT, WDT31, WDT31, 0); | ||
| 557 | #[cfg(feature = "_s")] | ||
| 558 | impl_wdt!(WDT0, WDT31, WDT31, 0); | ||
| 559 | #[cfg(feature = "_s")] | ||
| 560 | impl_wdt!(WDT1, WDT30, WDT30, 1); | ||
| 561 | // DPPI00 channels | ||
| 562 | impl_ppi_channel!(PPI00_CH0, DPPIC00, 0 => configurable); | ||
| 563 | impl_ppi_channel!(PPI00_CH1, DPPIC00, 1 => configurable); | ||
| 564 | impl_ppi_channel!(PPI00_CH2, DPPIC00, 2 => configurable); | ||
| 565 | impl_ppi_channel!(PPI00_CH3, DPPIC00, 3 => configurable); | ||
| 566 | impl_ppi_channel!(PPI00_CH4, DPPIC00, 4 => configurable); | ||
| 567 | impl_ppi_channel!(PPI00_CH5, DPPIC00, 5 => configurable); | ||
| 568 | impl_ppi_channel!(PPI00_CH6, DPPIC00, 6 => configurable); | ||
| 569 | impl_ppi_channel!(PPI00_CH7, DPPIC00, 7 => configurable); | ||
| 570 | |||
| 571 | // DPPI10 channels | ||
| 572 | impl_ppi_channel!(PPI10_CH0, DPPIC10, 0 => static); | ||
| 573 | |||
| 574 | // DPPI20 channels | ||
| 575 | impl_ppi_channel!(PPI20_CH0, DPPIC20, 0 => configurable); | ||
| 576 | impl_ppi_channel!(PPI20_CH1, DPPIC20, 1 => configurable); | ||
| 577 | impl_ppi_channel!(PPI20_CH2, DPPIC20, 2 => configurable); | ||
| 578 | impl_ppi_channel!(PPI20_CH3, DPPIC20, 3 => configurable); | ||
| 579 | impl_ppi_channel!(PPI20_CH4, DPPIC20, 4 => configurable); | ||
| 580 | impl_ppi_channel!(PPI20_CH5, DPPIC20, 5 => configurable); | ||
| 581 | impl_ppi_channel!(PPI20_CH6, DPPIC20, 6 => configurable); | ||
| 582 | impl_ppi_channel!(PPI20_CH7, DPPIC20, 7 => configurable); | ||
| 583 | impl_ppi_channel!(PPI20_CH8, DPPIC20, 8 => configurable); | ||
| 584 | impl_ppi_channel!(PPI20_CH9, DPPIC20, 9 => configurable); | ||
| 585 | impl_ppi_channel!(PPI20_CH10, DPPIC20, 10 => configurable); | ||
| 586 | impl_ppi_channel!(PPI20_CH11, DPPIC20, 11 => configurable); | ||
| 587 | impl_ppi_channel!(PPI20_CH12, DPPIC20, 12 => configurable); | ||
| 588 | impl_ppi_channel!(PPI20_CH13, DPPIC20, 13 => configurable); | ||
| 589 | impl_ppi_channel!(PPI20_CH14, DPPIC20, 14 => configurable); | ||
| 590 | impl_ppi_channel!(PPI20_CH15, DPPIC20, 15 => configurable); | ||
| 591 | |||
| 592 | // DPPI30 channels | ||
| 593 | impl_ppi_channel!(PPI30_CH0, DPPIC30, 0 => configurable); | ||
| 594 | impl_ppi_channel!(PPI30_CH1, DPPIC30, 1 => configurable); | ||
| 595 | impl_ppi_channel!(PPI30_CH2, DPPIC30, 2 => configurable); | ||
| 596 | impl_ppi_channel!(PPI30_CH3, DPPIC30, 3 => configurable); | ||
| 597 | |||
| 598 | // DPPI00 groups | ||
| 599 | impl_ppi_group!(PPI00_GROUP0, DPPIC00, 0); | ||
| 600 | impl_ppi_group!(PPI00_GROUP1, DPPIC00, 1); | ||
| 601 | |||
| 602 | // DPPI10 groups | ||
| 603 | impl_ppi_group!(PPI10_GROUP0, DPPIC10, 0); | ||
| 604 | |||
| 605 | // DPPI20 groups | ||
| 606 | impl_ppi_group!(PPI20_GROUP0, DPPIC20, 0); | ||
| 607 | impl_ppi_group!(PPI20_GROUP1, DPPIC20, 1); | ||
| 608 | impl_ppi_group!(PPI20_GROUP2, DPPIC20, 2); | ||
| 609 | impl_ppi_group!(PPI20_GROUP3, DPPIC20, 3); | ||
| 610 | impl_ppi_group!(PPI20_GROUP4, DPPIC20, 4); | ||
| 611 | impl_ppi_group!(PPI20_GROUP5, DPPIC20, 5); | ||
| 612 | |||
| 613 | // DPPI30 groups | ||
| 614 | impl_ppi_group!(PPI30_GROUP0, DPPIC30, 0); | ||
| 615 | impl_ppi_group!(PPI30_GROUP1, DPPIC30, 1); | ||
| 616 | |||
| 617 | impl_timer!(TIMER00, TIMER00, TIMER00); | ||
| 618 | impl_timer!(TIMER10, TIMER10, TIMER10); | ||
| 619 | impl_timer!(TIMER20, TIMER20, TIMER20); | ||
| 620 | impl_timer!(TIMER21, TIMER21, TIMER21); | ||
| 621 | impl_timer!(TIMER22, TIMER22, TIMER22); | ||
| 622 | impl_timer!(TIMER23, TIMER23, TIMER23); | ||
| 623 | impl_timer!(TIMER24, TIMER24, TIMER24); | ||
| 624 | |||
| 625 | impl_twim!(SERIAL20, TWIM20, SERIAL20); | ||
| 626 | impl_twim!(SERIAL21, TWIM21, SERIAL21); | ||
| 627 | impl_twim!(SERIAL22, TWIM22, SERIAL22); | ||
| 628 | impl_twim!(SERIAL30, TWIM30, SERIAL30); | ||
| 629 | |||
| 630 | impl_twis!(SERIAL20, TWIS20, SERIAL20); | ||
| 631 | impl_twis!(SERIAL21, TWIS21, SERIAL21); | ||
| 632 | impl_twis!(SERIAL22, TWIS22, SERIAL22); | ||
| 633 | impl_twis!(SERIAL30, TWIS30, SERIAL30); | ||
| 634 | |||
| 635 | impl_pwm!(PWM20, PWM20, PWM20); | ||
| 636 | impl_pwm!(PWM21, PWM21, PWM21); | ||
| 637 | impl_pwm!(PWM22, PWM22, PWM22); | ||
| 638 | |||
| 639 | #[cfg(feature = "_s")] | ||
| 640 | impl_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")] | ||
| 651 | impl_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 | ); | ||
| 661 | impl_spim!(SERIAL20, SPIM20, SERIAL20, 16_000_000); | ||
| 662 | impl_spim!(SERIAL21, SPIM21, SERIAL21, 16_000_000); | ||
| 663 | impl_spim!(SERIAL22, SPIM22, SERIAL22, 16_000_000); | ||
| 664 | impl_spim!(SERIAL30, SPIM30, SERIAL30, 16_000_000); | ||
| 665 | |||
| 666 | impl_spis!(SERIAL20, SPIS20, SERIAL20); | ||
| 667 | impl_spis!(SERIAL21, SPIS21, SERIAL21); | ||
| 668 | impl_spis!(SERIAL22, SPIS22, SERIAL22); | ||
| 669 | impl_spis!(SERIAL30, SPIS30, SERIAL30); | ||
| 670 | |||
| 671 | impl_uarte!(SERIAL00, UARTE00, SERIAL00); | ||
| 672 | impl_uarte!(SERIAL20, UARTE20, SERIAL20); | ||
| 673 | impl_uarte!(SERIAL21, UARTE21, SERIAL21); | ||
| 674 | impl_uarte!(SERIAL22, UARTE22, SERIAL22); | ||
| 675 | impl_uarte!(SERIAL30, UARTE30, SERIAL30); | ||
| 676 | |||
| 677 | // NB: SAADC uses "pin" abstraction, not "AIN" | ||
| 678 | impl_saadc_input!(P1_04, 1, 4); | ||
| 679 | impl_saadc_input!(P1_05, 1, 5); | ||
| 680 | impl_saadc_input!(P1_06, 1, 6); | ||
| 681 | impl_saadc_input!(P1_07, 1, 7); | ||
| 682 | impl_saadc_input!(P1_11, 1, 11); | ||
| 683 | impl_saadc_input!(P1_12, 1, 12); | ||
| 684 | impl_saadc_input!(P1_13, 1, 13); | ||
| 685 | impl_saadc_input!(P1_14, 1, 14); | ||
| 686 | |||
| 687 | #[cfg(feature = "_s")] | ||
| 688 | impl_cracen!(CRACEN, CRACEN, CRACEN); | ||
| 689 | |||
| 690 | embassy_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] | ||
| 4 | pub 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. | ||
| 197 | pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; | ||
| 198 | pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; | ||
| 199 | |||
| 200 | // 2 MB NVM | ||
| 201 | #[allow(unused)] | ||
| 202 | pub const FLASH_SIZE: usize = 2048 * 1024; | ||
| 203 | |||
| 204 | embassy_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 | |||
| 519 | impl_pin!(P0_00, 0, 0); | ||
| 520 | impl_pin!(P0_01, 0, 1); | ||
| 521 | impl_pin!(P0_02, 0, 2); | ||
| 522 | impl_pin!(P0_03, 0, 3); | ||
| 523 | impl_pin!(P0_04, 0, 4); | ||
| 524 | impl_pin!(P0_05, 0, 5); | ||
| 525 | impl_pin!(P0_06, 0, 6); | ||
| 526 | impl_pin!(P0_07, 0, 7); | ||
| 527 | impl_pin!(P0_08, 0, 8); | ||
| 528 | impl_pin!(P0_09, 0, 9); | ||
| 529 | |||
| 530 | impl_pin!(P1_00, 1, 0); | ||
| 531 | impl_pin!(P1_01, 1, 1); | ||
| 532 | impl_pin!(P1_02, 1, 2); | ||
| 533 | impl_pin!(P1_03, 1, 3); | ||
| 534 | impl_pin!(P1_04, 1, 4); | ||
| 535 | impl_pin!(P1_05, 1, 5); | ||
| 536 | impl_pin!(P1_06, 1, 6); | ||
| 537 | impl_pin!(P1_07, 1, 7); | ||
| 538 | impl_pin!(P1_08, 1, 8); | ||
| 539 | impl_pin!(P1_09, 1, 9); | ||
| 540 | impl_pin!(P1_10, 1, 10); | ||
| 541 | impl_pin!(P1_11, 1, 11); | ||
| 542 | impl_pin!(P1_12, 1, 12); | ||
| 543 | impl_pin!(P1_13, 1, 13); | ||
| 544 | impl_pin!(P1_14, 1, 14); | ||
| 545 | impl_pin!(P1_15, 1, 15); | ||
| 546 | impl_pin!(P1_16, 1, 16); | ||
| 547 | impl_pin!(P1_17, 1, 17); | ||
| 548 | impl_pin!(P1_18, 1, 18); | ||
| 549 | impl_pin!(P1_19, 1, 19); | ||
| 550 | impl_pin!(P1_20, 1, 20); | ||
| 551 | impl_pin!(P1_21, 1, 21); | ||
| 552 | impl_pin!(P1_22, 1, 22); | ||
| 553 | impl_pin!(P1_23, 1, 23); | ||
| 554 | impl_pin!(P1_24, 1, 24); | ||
| 555 | impl_pin!(P1_25, 1, 25); | ||
| 556 | impl_pin!(P1_26, 1, 26); | ||
| 557 | impl_pin!(P1_27, 1, 27); | ||
| 558 | impl_pin!(P1_28, 1, 28); | ||
| 559 | impl_pin!(P1_29, 1, 29); | ||
| 560 | impl_pin!(P1_30, 1, 30); | ||
| 561 | impl_pin!(P1_31, 1, 31); | ||
| 562 | |||
| 563 | impl_pin!(P2_00, 2, 0); | ||
| 564 | impl_pin!(P2_01, 2, 1); | ||
| 565 | impl_pin!(P2_02, 2, 2); | ||
| 566 | impl_pin!(P2_03, 2, 3); | ||
| 567 | impl_pin!(P2_04, 2, 4); | ||
| 568 | impl_pin!(P2_05, 2, 5); | ||
| 569 | impl_pin!(P2_06, 2, 6); | ||
| 570 | impl_pin!(P2_07, 2, 7); | ||
| 571 | impl_pin!(P2_08, 2, 8); | ||
| 572 | impl_pin!(P2_09, 2, 9); | ||
| 573 | impl_pin!(P2_10, 2, 10); | ||
| 574 | |||
| 575 | impl_pin!(P3_00, 3, 0); | ||
| 576 | impl_pin!(P3_01, 3, 1); | ||
| 577 | impl_pin!(P3_02, 3, 2); | ||
| 578 | impl_pin!(P3_03, 3, 3); | ||
| 579 | impl_pin!(P3_04, 3, 4); | ||
| 580 | impl_pin!(P3_05, 3, 5); | ||
| 581 | impl_pin!(P3_06, 3, 6); | ||
| 582 | impl_pin!(P3_07, 3, 7); | ||
| 583 | impl_pin!(P3_08, 3, 8); | ||
| 584 | impl_pin!(P3_09, 3, 9); | ||
| 585 | impl_pin!(P3_10, 3, 10); | ||
| 586 | impl_pin!(P3_11, 3, 11); | ||
| 587 | impl_pin!(P3_12, 3, 12); | ||
| 588 | |||
| 589 | cfg_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")] | ||
| 652 | impl_wdt!(WDT, WDT31, WDT31, 0); | ||
| 653 | #[cfg(feature = "_s")] | ||
| 654 | impl_wdt!(WDT0, WDT31, WDT31, 0); | ||
| 655 | #[cfg(feature = "_s")] | ||
| 656 | impl_wdt!(WDT1, WDT30, WDT30, 1); | ||
| 657 | // DPPI00 channels | ||
| 658 | impl_ppi_channel!(PPI00_CH0, DPPIC00, 0 => configurable); | ||
| 659 | impl_ppi_channel!(PPI00_CH1, DPPIC00, 1 => configurable); | ||
| 660 | impl_ppi_channel!(PPI00_CH2, DPPIC00, 2 => configurable); | ||
| 661 | impl_ppi_channel!(PPI00_CH3, DPPIC00, 3 => configurable); | ||
| 662 | impl_ppi_channel!(PPI00_CH4, DPPIC00, 4 => configurable); | ||
| 663 | impl_ppi_channel!(PPI00_CH5, DPPIC00, 5 => configurable); | ||
| 664 | impl_ppi_channel!(PPI00_CH6, DPPIC00, 6 => configurable); | ||
| 665 | impl_ppi_channel!(PPI00_CH7, DPPIC00, 7 => configurable); | ||
| 666 | |||
| 667 | // DPPI10 channels | ||
| 668 | impl_ppi_channel!(PPI10_CH0, DPPIC10, 0 => static); | ||
| 669 | |||
| 670 | // DPPI20 channels | ||
| 671 | impl_ppi_channel!(PPI20_CH0, DPPIC20, 0 => configurable); | ||
| 672 | impl_ppi_channel!(PPI20_CH1, DPPIC20, 1 => configurable); | ||
| 673 | impl_ppi_channel!(PPI20_CH2, DPPIC20, 2 => configurable); | ||
| 674 | impl_ppi_channel!(PPI20_CH3, DPPIC20, 3 => configurable); | ||
| 675 | impl_ppi_channel!(PPI20_CH4, DPPIC20, 4 => configurable); | ||
| 676 | impl_ppi_channel!(PPI20_CH5, DPPIC20, 5 => configurable); | ||
| 677 | impl_ppi_channel!(PPI20_CH6, DPPIC20, 6 => configurable); | ||
| 678 | impl_ppi_channel!(PPI20_CH7, DPPIC20, 7 => configurable); | ||
| 679 | impl_ppi_channel!(PPI20_CH8, DPPIC20, 8 => configurable); | ||
| 680 | impl_ppi_channel!(PPI20_CH9, DPPIC20, 9 => configurable); | ||
| 681 | impl_ppi_channel!(PPI20_CH10, DPPIC20, 10 => configurable); | ||
| 682 | impl_ppi_channel!(PPI20_CH11, DPPIC20, 11 => configurable); | ||
| 683 | impl_ppi_channel!(PPI20_CH12, DPPIC20, 12 => configurable); | ||
| 684 | impl_ppi_channel!(PPI20_CH13, DPPIC20, 13 => configurable); | ||
| 685 | impl_ppi_channel!(PPI20_CH14, DPPIC20, 14 => configurable); | ||
| 686 | impl_ppi_channel!(PPI20_CH15, DPPIC20, 15 => configurable); | ||
| 687 | |||
| 688 | // DPPI30 channels | ||
| 689 | impl_ppi_channel!(PPI30_CH0, DPPIC30, 0 => configurable); | ||
| 690 | impl_ppi_channel!(PPI30_CH1, DPPIC30, 1 => configurable); | ||
| 691 | impl_ppi_channel!(PPI30_CH2, DPPIC30, 2 => configurable); | ||
| 692 | impl_ppi_channel!(PPI30_CH3, DPPIC30, 3 => configurable); | ||
| 693 | |||
| 694 | // DPPI00 groups | ||
| 695 | impl_ppi_group!(PPI00_GROUP0, DPPIC00, 0); | ||
| 696 | impl_ppi_group!(PPI00_GROUP1, DPPIC00, 1); | ||
| 697 | |||
| 698 | // DPPI10 groups | ||
| 699 | impl_ppi_group!(PPI10_GROUP0, DPPIC10, 0); | ||
| 700 | |||
| 701 | // DPPI20 groups | ||
| 702 | impl_ppi_group!(PPI20_GROUP0, DPPIC20, 0); | ||
| 703 | impl_ppi_group!(PPI20_GROUP1, DPPIC20, 1); | ||
| 704 | impl_ppi_group!(PPI20_GROUP2, DPPIC20, 2); | ||
| 705 | impl_ppi_group!(PPI20_GROUP3, DPPIC20, 3); | ||
| 706 | impl_ppi_group!(PPI20_GROUP4, DPPIC20, 4); | ||
| 707 | impl_ppi_group!(PPI20_GROUP5, DPPIC20, 5); | ||
| 708 | |||
| 709 | // DPPI30 groups | ||
| 710 | impl_ppi_group!(PPI30_GROUP0, DPPIC30, 0); | ||
| 711 | impl_ppi_group!(PPI30_GROUP1, DPPIC30, 1); | ||
| 712 | |||
| 713 | impl_timer!(TIMER00, TIMER00, TIMER00); | ||
| 714 | impl_timer!(TIMER10, TIMER10, TIMER10); | ||
| 715 | impl_timer!(TIMER20, TIMER20, TIMER20); | ||
| 716 | impl_timer!(TIMER21, TIMER21, TIMER21); | ||
| 717 | impl_timer!(TIMER22, TIMER22, TIMER22); | ||
| 718 | impl_timer!(TIMER23, TIMER23, TIMER23); | ||
| 719 | impl_timer!(TIMER24, TIMER24, TIMER24); | ||
| 720 | |||
| 721 | impl_twim!(SERIAL20, TWIM20, SERIAL20); | ||
| 722 | impl_twim!(SERIAL21, TWIM21, SERIAL21); | ||
| 723 | impl_twim!(SERIAL22, TWIM22, SERIAL22); | ||
| 724 | impl_twim!(SERIAL30, TWIM30, SERIAL30); | ||
| 725 | |||
| 726 | impl_twis!(SERIAL20, TWIS20, SERIAL20); | ||
| 727 | impl_twis!(SERIAL21, TWIS21, SERIAL21); | ||
| 728 | impl_twis!(SERIAL22, TWIS22, SERIAL22); | ||
| 729 | impl_twis!(SERIAL30, TWIS30, SERIAL30); | ||
| 730 | |||
| 731 | impl_pwm!(PWM20, PWM20, PWM20); | ||
| 732 | impl_pwm!(PWM21, PWM21, PWM21); | ||
| 733 | impl_pwm!(PWM22, PWM22, PWM22); | ||
| 734 | |||
| 735 | #[cfg(feature = "_s")] | ||
| 736 | impl_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")] | ||
| 747 | impl_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 | ); | ||
| 757 | impl_spim!(SERIAL20, SPIM20, SERIAL20, 16_000_000); | ||
| 758 | impl_spim!(SERIAL21, SPIM21, SERIAL21, 16_000_000); | ||
| 759 | impl_spim!(SERIAL22, SPIM22, SERIAL22, 16_000_000); | ||
| 760 | impl_spim!(SERIAL30, SPIM30, SERIAL30, 16_000_000); | ||
| 761 | |||
| 762 | impl_spis!(SERIAL20, SPIS20, SERIAL20); | ||
| 763 | impl_spis!(SERIAL21, SPIS21, SERIAL21); | ||
| 764 | impl_spis!(SERIAL22, SPIS22, SERIAL22); | ||
| 765 | impl_spis!(SERIAL30, SPIS30, SERIAL30); | ||
| 766 | |||
| 767 | impl_uarte!(SERIAL00, UARTE00, SERIAL00); | ||
| 768 | impl_uarte!(SERIAL20, UARTE20, SERIAL20); | ||
| 769 | impl_uarte!(SERIAL21, UARTE21, SERIAL21); | ||
| 770 | impl_uarte!(SERIAL22, UARTE22, SERIAL22); | ||
| 771 | impl_uarte!(SERIAL30, UARTE30, SERIAL30); | ||
| 772 | |||
| 773 | // NB: SAADC uses "pin" abstraction, not "AIN" | ||
| 774 | impl_saadc_input!(P1_04, 1, 4); | ||
| 775 | impl_saadc_input!(P1_05, 1, 5); | ||
| 776 | impl_saadc_input!(P1_06, 1, 6); | ||
| 777 | impl_saadc_input!(P1_07, 1, 7); | ||
| 778 | impl_saadc_input!(P1_11, 1, 11); | ||
| 779 | impl_saadc_input!(P1_12, 1, 12); | ||
| 780 | impl_saadc_input!(P1_13, 1, 13); | ||
| 781 | impl_saadc_input!(P1_14, 1, 14); | ||
| 782 | |||
| 783 | #[cfg(feature = "_s")] | ||
| 784 | impl_cracen!(CRACEN, CRACEN, CRACEN); | ||
| 785 | |||
| 786 | embassy_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> { | |||
| 18 | impl<'d> Cracen<'d, Blocking> { | 18 | impl<'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 | ||
| 91 | impl<'d, M: Mode> Drop for Cracen<'d, M> { | 95 | impl<'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 | ))] |
| 38 | const PIN_COUNT: usize = 48; | 40 | const PIN_COUNT: usize = 48; |
| 41 | #[cfg(feature = "_nrf54lm20")] | ||
| 42 | const 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 | )))] |
| 45 | const PIN_COUNT: usize = 32; | 49 | const 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; | |||
| 99 | pub mod nfct; | 109 | pub mod nfct; |
| 100 | #[cfg(not(feature = "_nrf54l"))] | 110 | #[cfg(not(feature = "_nrf54l"))] |
| 101 | pub mod nvmc; | 111 | pub mod nvmc; |
| 102 | #[cfg(feature = "nrf54l15-app-s")] | 112 | #[cfg(all(feature = "_nrf54l", feature = "_s"))] |
| 103 | pub mod rramc; | 113 | pub mod rramc; |
| 104 | #[cfg(feature = "nrf54l15-app-s")] | 114 | #[cfg(all(feature = "_nrf54l", feature = "_s"))] |
| 105 | pub use rramc as nvmc; | 115 | pub 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")] |
| 197 | mod chip; | 210 | mod 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))] |
| 663 | enum WriteResult { | 677 | pub 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")))] |
| 673 | unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult { | 691 | pub 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")))] |
| 678 | unsafe 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. | ||
| 700 | pub 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 = [ | |||
| 20 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/" | 20 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/" |
| 21 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/" | 21 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/" |
| 22 | target = "thumbv7em-none-eabihf" | 22 | target = "thumbv7em-none-eabihf" |
| 23 | features = ["stm32wb55rg", "ble", "mac"] | 23 | features = ["stm32wb55rg", "wb55_ble", "wb55_mac"] |
| 24 | 24 | ||
| 25 | [package.metadata.docs.rs] | 25 | [package.metadata.docs.rs] |
| 26 | features = ["stm32wb55rg", "ble", "mac"] | 26 | features = ["stm32wb55rg", "wb55_ble", "wb55_mac"] |
| 27 | 27 | ||
| 28 | [dependencies] | 28 | [dependencies] |
| 29 | embassy-stm32 = { version = "0.4.0", path = "../embassy-stm32" } | 29 | embassy-stm32 = { version = "0.4.0", path = "../embassy-stm32" } |
| @@ -34,6 +34,7 @@ embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } | |||
| 34 | embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } | 34 | embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } |
| 35 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } | 35 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } |
| 36 | smoltcp = { version = "0.12.0", optional=true, default-features = false } | 36 | smoltcp = { version = "0.12.0", optional=true, default-features = false } |
| 37 | stm32-bindings = { version = "0.1.3", optional=true, default-features = false } | ||
| 37 | 38 | ||
| 38 | defmt = { version = "1.0.1", optional = true } | 39 | defmt = { version = "1.0.1", optional = true } |
| 39 | log = { version = "0.4.17", optional = true } | 40 | log = { version = "0.4.17", optional = true } |
| @@ -52,24 +53,63 @@ bitflags = { version = "2.3.3", optional = true } | |||
| 52 | [features] | 53 | [features] |
| 53 | defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"] | 54 | defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"] |
| 54 | 55 | ||
| 55 | ble = ["dep:stm32wb-hci"] | 56 | wb55 = [] |
| 56 | mac = ["dep:bitflags", "dep:embassy-net-driver", "dep:smoltcp", "smoltcp/medium-ieee802154"] | 57 | wb55_ble = ["dep:stm32wb-hci"] |
| 58 | wb55_mac = ["dep:bitflags", "dep:embassy-net-driver", "dep:smoltcp", "smoltcp/medium-ieee802154"] | ||
| 59 | |||
| 60 | wba = [ "dep:stm32-bindings" ] | ||
| 61 | wba_ble = [ "stm32-bindings/wba_wpan_mac" , "stm32-bindings/wba_wpan" ] | ||
| 62 | wba_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 | ||
| 58 | extended = [] | 64 | extended = [] |
| 59 | 65 | ||
| 60 | stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] | 66 | stm32wb10cc = [ "embassy-stm32/stm32wb10cc" , "wb55" ] |
| 61 | stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ] | 67 | stm32wb15cc = [ "embassy-stm32/stm32wb15cc" , "wb55" ] |
| 62 | stm32wb30ce = [ "embassy-stm32/stm32wb30ce" ] | 68 | stm32wb30ce = [ "embassy-stm32/stm32wb30ce" , "wb55" ] |
| 63 | stm32wb35cc = [ "embassy-stm32/stm32wb35cc" ] | 69 | stm32wb35cc = [ "embassy-stm32/stm32wb35cc" , "wb55" ] |
| 64 | stm32wb35ce = [ "embassy-stm32/stm32wb35ce" ] | 70 | stm32wb35ce = [ "embassy-stm32/stm32wb35ce" , "wb55" ] |
| 65 | stm32wb50cg = [ "embassy-stm32/stm32wb50cg" ] | 71 | stm32wb50cg = [ "embassy-stm32/stm32wb50cg" , "wb55" ] |
| 66 | stm32wb55cc = [ "embassy-stm32/stm32wb55cc" ] | 72 | stm32wb55cc = [ "embassy-stm32/stm32wb55cc" , "wb55" ] |
| 67 | stm32wb55ce = [ "embassy-stm32/stm32wb55ce" ] | 73 | stm32wb55ce = [ "embassy-stm32/stm32wb55ce" , "wb55" ] |
| 68 | stm32wb55cg = [ "embassy-stm32/stm32wb55cg" ] | 74 | stm32wb55cg = [ "embassy-stm32/stm32wb55cg" , "wb55" ] |
| 69 | stm32wb55rc = [ "embassy-stm32/stm32wb55rc" ] | 75 | stm32wb55rc = [ "embassy-stm32/stm32wb55rc" , "wb55" ] |
| 70 | stm32wb55re = [ "embassy-stm32/stm32wb55re" ] | 76 | stm32wb55re = [ "embassy-stm32/stm32wb55re" , "wb55" ] |
| 71 | stm32wb55rg = [ "embassy-stm32/stm32wb55rg" ] | 77 | stm32wb55rg = [ "embassy-stm32/stm32wb55rg" , "wb55" ] |
| 72 | stm32wb55vc = [ "embassy-stm32/stm32wb55vc" ] | 78 | stm32wb55vc = [ "embassy-stm32/stm32wb55vc" , "wb55" ] |
| 73 | stm32wb55ve = [ "embassy-stm32/stm32wb55ve" ] | 79 | stm32wb55ve = [ "embassy-stm32/stm32wb55ve" , "wb55" ] |
| 74 | stm32wb55vg = [ "embassy-stm32/stm32wb55vg" ] | 80 | stm32wb55vg = [ "embassy-stm32/stm32wb55vg" , "wb55" ] |
| 75 | stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ] | 81 | stm32wb55vy = [ "embassy-stm32/stm32wb55vy" , "wb55" ] |
| 82 | stm32wba50ke = [ "embassy-stm32/stm32wba50ke", "wba" ] | ||
| 83 | stm32wba50kg = [ "embassy-stm32/stm32wba50kg", "wba" ] | ||
| 84 | stm32wba52ce = [ "embassy-stm32/stm32wba52ce", "wba" ] | ||
| 85 | stm32wba52cg = [ "embassy-stm32/stm32wba52cg", "wba" ] | ||
| 86 | stm32wba52ke = [ "embassy-stm32/stm32wba52ke", "wba" ] | ||
| 87 | stm32wba52kg = [ "embassy-stm32/stm32wba52kg", "wba" ] | ||
| 88 | stm32wba54ce = [ "embassy-stm32/stm32wba54ce", "wba" ] | ||
| 89 | stm32wba54cg = [ "embassy-stm32/stm32wba54cg", "wba" ] | ||
| 90 | stm32wba54ke = [ "embassy-stm32/stm32wba54ke", "wba" ] | ||
| 91 | stm32wba54kg = [ "embassy-stm32/stm32wba54kg", "wba" ] | ||
| 92 | stm32wba55ce = [ "embassy-stm32/stm32wba55ce", "wba" ] | ||
| 93 | stm32wba55cg = [ "embassy-stm32/stm32wba55cg", "wba" ] | ||
| 94 | stm32wba55he = [ "embassy-stm32/stm32wba55he", "wba" ] | ||
| 95 | stm32wba55hg = [ "embassy-stm32/stm32wba55hg", "wba" ] | ||
| 96 | stm32wba55ue = [ "embassy-stm32/stm32wba55ue", "wba" ] | ||
| 97 | stm32wba55ug = [ "embassy-stm32/stm32wba55ug", "wba" ] | ||
| 98 | stm32wba62cg = [ "embassy-stm32/stm32wba62cg", "wba" ] | ||
| 99 | stm32wba62ci = [ "embassy-stm32/stm32wba62ci", "wba" ] | ||
| 100 | stm32wba62mg = [ "embassy-stm32/stm32wba62mg", "wba" ] | ||
| 101 | stm32wba62mi = [ "embassy-stm32/stm32wba62mi", "wba" ] | ||
| 102 | stm32wba62pg = [ "embassy-stm32/stm32wba62pg", "wba" ] | ||
| 103 | stm32wba62pi = [ "embassy-stm32/stm32wba62pi", "wba" ] | ||
| 104 | stm32wba63cg = [ "embassy-stm32/stm32wba63cg", "wba" ] | ||
| 105 | stm32wba63ci = [ "embassy-stm32/stm32wba63ci", "wba" ] | ||
| 106 | stm32wba64cg = [ "embassy-stm32/stm32wba64cg", "wba" ] | ||
| 107 | stm32wba64ci = [ "embassy-stm32/stm32wba64ci", "wba" ] | ||
| 108 | stm32wba65cg = [ "embassy-stm32/stm32wba65cg", "wba" ] | ||
| 109 | stm32wba65ci = [ "embassy-stm32/stm32wba65ci", "wba" ] | ||
| 110 | stm32wba65mg = [ "embassy-stm32/stm32wba65mg", "wba" ] | ||
| 111 | stm32wba65mi = [ "embassy-stm32/stm32wba65mi", "wba" ] | ||
| 112 | stm32wba65pg = [ "embassy-stm32/stm32wba65pg", "wba" ] | ||
| 113 | stm32wba65pi = [ "embassy-stm32/stm32wba65pi", "wba" ] | ||
| 114 | stm32wba65rg = [ "embassy-stm32/stm32wba65rg", "wba" ] | ||
| 115 | stm32wba65ri = [ "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")] |
| 22 | mod fmt; | 22 | mod wb55; |
| 23 | 23 | ||
| 24 | use core::mem::MaybeUninit; | 24 | #[cfg(feature = "wb55")] |
| 25 | use core::sync::atomic::{Ordering, compiler_fence}; | 25 | pub use wb55::*; |
| 26 | 26 | ||
| 27 | use embassy_hal_internal::Peri; | 27 | #[cfg(feature = "wba")] |
| 28 | use embassy_stm32::interrupt; | 28 | mod wba; |
| 29 | use embassy_stm32::ipcc::{Config, Ipcc, IpccRxChannel, ReceiveInterruptHandler, TransmitInterruptHandler}; | ||
| 30 | use embassy_stm32::peripherals::IPCC; | ||
| 31 | use sub::mm::MemoryManager; | ||
| 32 | use sub::sys::Sys; | ||
| 33 | use tables::*; | ||
| 34 | use unsafe_linked_list::LinkedListNode; | ||
| 35 | 29 | ||
| 36 | pub mod channels; | 30 | #[cfg(feature = "wba")] |
| 37 | pub mod cmd; | 31 | pub use wba::*; |
| 38 | pub mod consts; | ||
| 39 | pub mod evt; | ||
| 40 | pub mod lhci; | ||
| 41 | pub mod shci; | ||
| 42 | pub mod sub; | ||
| 43 | pub mod tables; | ||
| 44 | pub mod unsafe_linked_list; | ||
| 45 | |||
| 46 | #[cfg(feature = "mac")] | ||
| 47 | pub mod mac; | ||
| 48 | |||
| 49 | #[cfg(feature = "ble")] | ||
| 50 | pub use crate::sub::ble::hci; | ||
| 51 | |||
| 52 | type PacketHeader = LinkedListNode; | ||
| 53 | |||
| 54 | /// Transport Layer for the Mailbox interface | ||
| 55 | pub 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 | |||
| 65 | impl<'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")] | ||
| 2 | pub mod ble; | ||
| 3 | #[cfg(feature = "mac")] | ||
| 4 | pub mod mac; | ||
| 5 | pub mod mm; | ||
| 6 | pub 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 @@ | |||
| 1 | use core::ptr; | 1 | use core::ptr; |
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | 2 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 3 | 3 | ||
| 4 | use crate::PacketHeader; | ||
| 5 | use crate::consts::TlPacketType; | 4 | use crate::consts::TlPacketType; |
| 5 | use 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 @@ | |||
| 1 | use crate::PacketHeader; | ||
| 2 | use crate::evt::CsEvt; | 1 | use crate::evt::CsEvt; |
| 2 | use 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. | ||
| 2 | mod fmt; | ||
| 3 | |||
| 4 | use core::mem::MaybeUninit; | ||
| 5 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 6 | |||
| 7 | use embassy_hal_internal::Peri; | ||
| 8 | use embassy_stm32::interrupt; | ||
| 9 | use embassy_stm32::ipcc::{Config, Ipcc, IpccRxChannel, ReceiveInterruptHandler, TransmitInterruptHandler}; | ||
| 10 | use embassy_stm32::peripherals::IPCC; | ||
| 11 | use sub::mm::MemoryManager; | ||
| 12 | use sub::sys::Sys; | ||
| 13 | use tables::*; | ||
| 14 | use unsafe_linked_list::LinkedListNode; | ||
| 15 | |||
| 16 | pub mod channels; | ||
| 17 | pub mod cmd; | ||
| 18 | pub mod consts; | ||
| 19 | pub mod evt; | ||
| 20 | pub mod lhci; | ||
| 21 | pub mod shci; | ||
| 22 | pub mod sub; | ||
| 23 | pub mod tables; | ||
| 24 | pub mod unsafe_linked_list; | ||
| 25 | |||
| 26 | #[cfg(feature = "wb55_mac")] | ||
| 27 | pub mod mac; | ||
| 28 | |||
| 29 | #[cfg(feature = "wb55_ble")] | ||
| 30 | pub use crate::sub::ble::hci; | ||
| 31 | |||
| 32 | type PacketHeader = LinkedListNode; | ||
| 33 | |||
| 34 | /// Transport Layer for the Mailbox interface | ||
| 35 | pub 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 | |||
| 45 | impl<'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 @@ | |||
| 1 | use core::sync::atomic::{Ordering, compiler_fence}; | 1 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 2 | use core::{mem, ptr, slice}; | 2 | use core::{mem, ptr, slice}; |
| 3 | 3 | ||
| 4 | use crate::PacketHeader; | ||
| 5 | use crate::cmd::CmdPacket; | 4 | use crate::cmd::CmdPacket; |
| 6 | use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; | 5 | use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; |
| 7 | use crate::evt::{CcEvt, EvtStub}; | 6 | use crate::evt::{CcEvt, EvtStub}; |
| 7 | use crate::wb55::PacketHeader; | ||
| 8 | 8 | ||
| 9 | const SHCI_OGF: u16 = 0x3F; | 9 | const 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; | |||
| 11 | use crate::consts::POOL_SIZE; | 11 | use crate::consts::POOL_SIZE; |
| 12 | use crate::evt; | 12 | use crate::evt; |
| 13 | use crate::evt::EvtPacket; | 13 | use crate::evt::EvtPacket; |
| 14 | #[cfg(feature = "ble")] | 14 | #[cfg(feature = "wb55_ble")] |
| 15 | use crate::tables::BLE_SPARE_EVT_BUF; | 15 | use crate::tables::BLE_SPARE_EVT_BUF; |
| 16 | use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; | 16 | use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; |
| 17 | use crate::unsafe_linked_list::LinkedListNode; | 17 | use 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")] | ||
| 2 | pub mod ble; | ||
| 3 | #[cfg(feature = "wb55_mac")] | ||
| 4 | pub mod mac; | ||
| 5 | pub mod mm; | ||
| 6 | pub 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}; | |||
| 3 | use crate::cmd::CmdPacket; | 3 | use crate::cmd::CmdPacket; |
| 4 | use crate::consts::TlPacketType; | 4 | use crate::consts::TlPacketType; |
| 5 | use crate::evt::EvtBox; | 5 | use crate::evt::EvtBox; |
| 6 | #[cfg(feature = "ble")] | 6 | #[cfg(feature = "wb55_ble")] |
| 7 | use crate::shci::ShciBleInitCmdParam; | 7 | use crate::shci::ShciBleInitCmdParam; |
| 8 | use crate::shci::{SchiCommandStatus, ShciOpcode}; | 8 | use crate::shci::{SchiCommandStatus, ShciOpcode}; |
| 9 | use crate::sub::mm; | 9 | use crate::sub::mm; |
| 10 | use crate::tables::{SysTable, WirelessFwInfoTable}; | 10 | use crate::tables::{SysTable, WirelessFwInfoTable}; |
| 11 | use crate::unsafe_linked_list::LinkedListNode; | 11 | use crate::unsafe_linked_list::LinkedListNode; |
| 12 | use crate::{SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; | 12 | use 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. |
| 15 | pub struct Sys<'a> { | 15 | pub 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}; | |||
| 4 | use bit_field::BitField; | 4 | use bit_field::BitField; |
| 5 | 5 | ||
| 6 | use crate::cmd::{AclDataPacket, CmdPacket}; | 6 | use crate::cmd::{AclDataPacket, CmdPacket}; |
| 7 | #[cfg(feature = "mac")] | 7 | #[cfg(feature = "wb55_mac")] |
| 8 | use crate::consts::C_SIZE_CMD_STRING; | 8 | use crate::consts::C_SIZE_CMD_STRING; |
| 9 | use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; | 9 | use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; |
| 10 | use crate::unsafe_linked_list::LinkedListNode; | 10 | use crate::unsafe_linked_list::LinkedListNode; |
| @@ -242,11 +242,11 @@ pub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(May | |||
| 242 | pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed()); | 242 | pub 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")] |
| 247 | pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed()); | 247 | pub 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")] |
| 251 | pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< | 251 | pub 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 | |||
| 262 | pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = | 262 | pub 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")] |
| 267 | pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> = | 267 | pub 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")] |
| 272 | pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed()); | 272 | pub 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")] |
| 276 | pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = | 276 | pub 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 |
| 282 | pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> = | 282 | pub 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)] | ||
| 2 | fn 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 @@ | |||
| 1 | use 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)] | ||
| 34 | unsafe 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)] | ||
| 44 | unsafe 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)] | ||
| 55 | unsafe 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)] | ||
| 65 | unsafe extern "C" fn ll_sys_disable_specific_irq(isr_type: u8) { | ||
| 66 | LINKLAYER_PLAT_DisableSpecificIRQ(isr_type); | ||
| 67 | } | ||
| 68 | // | ||
| 69 | #[unsafe(no_mangle)] | ||
| 70 | unsafe extern "C" fn ll_sys_phy_start_clbr() { | ||
| 71 | LINKLAYER_PLAT_PhyStartClbr(); | ||
| 72 | } | ||
| 73 | // | ||
| 74 | #[unsafe(no_mangle)] | ||
| 75 | unsafe 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 @@ | |||
| 1 | use 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 | |||
| 10 | macro_rules! LL_DP_SLP_NO_WAKEUP { | ||
| 11 | () => { | ||
| 12 | !0u32 | ||
| 13 | }; | ||
| 14 | } | ||
| 15 | |||
| 16 | macro_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 */ | ||
| 45 | static mut RADIO_DP_SLP_TMR_ID: os_timer_id = NULL as *mut _; | ||
| 46 | // | ||
| 47 | // /* Link Layer deep sleep state */ | ||
| 48 | static 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)] | ||
| 56 | unsafe 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)] | ||
| 82 | unsafe 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)] | ||
| 92 | unsafe 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)] | ||
| 124 | unsafe 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)] | ||
| 160 | unsafe 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 @@ | |||
| 1 | use 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)] | ||
| 40 | unsafe 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)] | ||
| 50 | unsafe 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)] | ||
| 60 | unsafe 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)] | ||
| 70 | unsafe 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)] | ||
| 80 | unsafe 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)] | ||
| 90 | unsafe 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)] | ||
| 100 | unsafe 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)] | ||
| 111 | unsafe 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)] | ||
| 121 | unsafe 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)] | ||
| 131 | unsafe 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)] | ||
| 145 | unsafe 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)] | ||
| 159 | unsafe 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)] | ||
| 169 | unsafe 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)] | ||
| 182 | unsafe 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)] | ||
| 192 | unsafe 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 @@ | |||
| 1 | use 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 | }; | ||
| 6 | use 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 | */ | ||
| 42 | static 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)] | ||
| 49 | unsafe 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)] | ||
| 60 | unsafe 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)] | ||
| 83 | unsafe 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)] | ||
| 95 | unsafe 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 | */ | ||
| 106 | unsafe 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 @@ | |||
| 1 | use 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 | |||
| 41 | const 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 | |||
| 49 | macro_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)] | ||
| 61 | struct 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 | // */ | ||
| 71 | const 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 | // */ | ||
| 88 | const 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 | // */ | ||
| 96 | const 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)] | ||
| 103 | unsafe extern "C" fn ll_sys_get_brief_fw_version() -> u8 { | ||
| 104 | return LL_SYS_BRIEF_VERSION; | ||
| 105 | } | ||
| 106 | |||
| 107 | #[unsafe(no_mangle)] | ||
| 108 | unsafe extern "C" fn ll_sys_get_system_fw_version() -> u32 { | ||
| 109 | return LL_SYS_SYSTEM_VERSION.version; | ||
| 110 | } | ||
| 111 | |||
| 112 | #[unsafe(no_mangle)] | ||
| 113 | unsafe 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 @@ | |||
| 1 | mod ll_sys_cs; | ||
| 2 | mod ll_sys_dp_slp; | ||
| 3 | mod ll_sys_intf; | ||
| 4 | mod ll_sys_startup; | ||
| 5 | mod 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 | // | ||
| 334 | use super::bindings::{link_layer, mac}; | ||
| 335 | use super::util_seq; | ||
| 336 | |||
| 337 | const UTIL_SEQ_RFU: u32 = 0; | ||
| 338 | const TASK_LINK_LAYER_MASK: u32 = 1 << mac::CFG_TASK_ID_T_CFG_TASK_LINK_LAYER; | ||
| 339 | const 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)] | ||
| 347 | pub 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)] | ||
| 357 | pub 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)] | ||
| 367 | pub 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)] | ||
| 377 | pub 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)] | ||
| 392 | pub 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)] | ||
| 402 | pub 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)] | ||
| 413 | pub 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 | |||
| 118 | use super::util_seq; | ||
| 119 | use crate::bindings::mac; | ||
| 120 | |||
| 121 | /// Placeholder value used by the original ST middleware when registering tasks. | ||
| 122 | const UTIL_SEQ_RFU: u32 = 0; | ||
| 123 | |||
| 124 | /// Bit mask identifying the MAC layer task within the sequencer. | ||
| 125 | const 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. | ||
| 128 | const 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. | ||
| 131 | const 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)] | ||
| 138 | pub 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)] | ||
| 148 | pub 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)] | ||
| 158 | pub 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)] | ||
| 168 | pub 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)] | ||
| 176 | pub 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)] | ||
| 186 | pub 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 @@ | |||
| 1 | pub mod bindings; | ||
| 2 | pub mod linklayer_plat; | ||
| 3 | pub mod ll_sys; | ||
| 4 | pub mod ll_sys_if; | ||
| 5 | pub mod mac_sys_if; | ||
| 6 | pub 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 | |||
| 3 | use core::cell::UnsafeCell; | ||
| 4 | use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; | ||
| 5 | |||
| 6 | use critical_section::with as critical; | ||
| 7 | |||
| 8 | type TaskFn = unsafe extern "C" fn(); | ||
| 9 | |||
| 10 | const MAX_TASKS: usize = 32; | ||
| 11 | const DEFAULT_PRIORITY: u8 = u8::MAX; | ||
| 12 | |||
| 13 | struct TaskTable { | ||
| 14 | funcs: UnsafeCell<[Option<TaskFn>; MAX_TASKS]>, | ||
| 15 | priorities: UnsafeCell<[u8; MAX_TASKS]>, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl 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 | |||
| 44 | unsafe impl Sync for TaskTable {} | ||
| 45 | |||
| 46 | #[inline(always)] | ||
| 47 | fn 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)] | ||
| 60 | fn 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 | |||
| 72 | static TASKS: TaskTable = TaskTable::new(); | ||
| 73 | static PENDING_TASKS: AtomicU32 = AtomicU32::new(0); | ||
| 74 | static EVENTS: AtomicU32 = AtomicU32::new(0); | ||
| 75 | static SCHEDULING: AtomicBool = AtomicBool::new(false); | ||
| 76 | |||
| 77 | fn 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 | |||
| 85 | fn 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. | ||
| 115 | pub fn poll_pending_tasks() { | ||
| 116 | drain_pending_tasks(); | ||
| 117 | } | ||
| 118 | |||
| 119 | fn 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)] | ||
| 163 | pub 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)] | ||
| 172 | pub 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)] | ||
| 182 | pub 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)] | ||
| 203 | pub 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)] | ||
| 209 | pub extern "C" fn UTIL_SEQ_PauseTask(task_mask: u32) { | ||
| 210 | PENDING_TASKS.fetch_and(!task_mask, Ordering::AcqRel); | ||
| 211 | } | ||
| 212 | |||
| 213 | #[unsafe(no_mangle)] | ||
| 214 | pub 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)] | ||
| 220 | pub extern "C" fn UTIL_SEQ_ClrEvt(event_mask: u32) { | ||
| 221 | EVENTS.fetch_and(!event_mask, Ordering::AcqRel); | ||
| 222 | } | ||
| 223 | |||
| 224 | #[unsafe(no_mangle)] | ||
| 225 | pub 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)] | ||
| 231 | pub 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" | |||
| 200 | heapless = "0.9.1" | 200 | heapless = "0.9.1" |
| 201 | 201 | ||
| 202 | #stm32-metapac = { version = "18" } | 202 | #stm32-metapac = { version = "18" } |
| 203 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb" } | 203 | stm32-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"]} |
| 207 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb", default-features = false, features = ["metadata"] } | 207 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-497fb3042b49b765d8974aac87b8ab4fa3566d74", default-features = false, features = ["metadata"] } |
| 208 | 208 | ||
| 209 | proc-macro2 = "1.0.36" | 209 | proc-macro2 = "1.0.36" |
| 210 | quote = "1.0.15" | 210 | quote = "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 | |||
| 5 | use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; | 5 | use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; |
| 6 | 6 | ||
| 7 | use super::blocking_delay_us; | 7 | use super::blocking_delay_us; |
| 8 | use crate::adc::ConversionMode; | 8 | use crate::adc::{AdcRegs, ConversionMode, Instance}; |
| 9 | #[cfg(stm32u5)] | 9 | #[cfg(stm32u5)] |
| 10 | pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; | 10 | pub 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 | ||
| 93 | pub trait SealedInstance { | 93 | impl 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 | ||
| 98 | pub 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 | ||
| 102 | foreach_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 | ||
| 223 | pub struct Adc4<'d, T: Instance> { | 200 | pub 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 | ||
| 234 | impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> { | 211 | impl<'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; | |||
| 4 | use stm32_metapac::adc::vals::{SampleTime, Scandir}; | 4 | use stm32_metapac::adc::vals::{SampleTime, Scandir}; |
| 5 | 5 | ||
| 6 | use super::{Adc, Instance, Resolution, blocking_delay_us}; | 6 | use super::{Adc, Instance, Resolution, blocking_delay_us}; |
| 7 | use crate::adc::{AnyInstance, ConversionMode}; | 7 | use crate::adc::{AdcRegs, ConversionMode}; |
| 8 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| 9 | use crate::{Peri, pac, rcc}; | 9 | use crate::{Peri, pac, rcc}; |
| 10 | 10 | ||
| @@ -43,52 +43,52 @@ fn from_ker_ck(frequency: Hertz) -> Presc { | |||
| 43 | } | 43 | } |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | impl<T: Instance> super::SealedAnyInstance for T { | 46 | impl 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 | ||
| 184 | impl<'d, T: AnyInstance> Adc<'d, T> { | 182 | impl<'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 | }; |
| 15 | use crate::adc::{AnyInstance, SealedAdcChannel}; | 15 | use crate::adc::{AdcRegs, BasicAdcRegs, SealedAdcChannel}; |
| 16 | use crate::pac::adc::regs::{Smpr, Smpr2, Sqr1, Sqr2, Sqr3, Sqr4}; | ||
| 16 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 17 | use crate::{Peri, pac, rcc}; | 18 | use 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 | ||
| 71 | impl<T: Instance> super::SealedAnyInstance for T { | 72 | impl 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 | ||
| 244 | impl<'d, T: Instance + AnyInstance> Adc<'d, T> { | 241 | impl<'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 | ||
| 534 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { | 531 | impl<'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)] |
| 5 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| 6 | 6 | ||
| 7 | use super::{AnyAdcChannel, SampleTime}; | 7 | use super::{AdcRegs, AnyAdcChannel, SampleTime}; |
| 8 | use crate::adc::Adc; | ||
| 8 | #[allow(unused_imports)] | 9 | #[allow(unused_imports)] |
| 9 | use crate::adc::Instance; | 10 | use crate::adc::Instance; |
| 10 | use crate::adc::{Adc, AnyInstance}; | ||
| 11 | 11 | ||
| 12 | /// Injected ADC sequence with owned channels. | 12 | /// Injected ADC sequence with owned channels. |
| 13 | pub struct InjectedAdc<T: Instance, const N: usize> { | 13 | pub 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 | ||
| 18 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { | 18 | impl<'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 | ||
| 39 | impl<T: Instance + AnyInstance, const N: usize> Drop for InjectedAdc<T, N> { | 39 | impl<'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)))] |
| 27 | pub use _version::*; | 27 | pub use _version::*; |
| 28 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; | 28 | #[allow(unused)] |
| 29 | use 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))] |
| 30 | use embassy_sync::waitqueue::AtomicWaker; | 31 | use 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))] |
| 32 | pub use ringbuffered::RingBufferedAdc; | 33 | pub use ringbuffered::RingBufferedAdc; |
| 33 | 34 | ||
| 35 | #[cfg(adc_u5)] | ||
| 36 | use crate::pac::adc::vals::Adc4SampleTime; | ||
| 37 | #[cfg(adc_wba)] | ||
| 38 | use 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"] |
| 36 | pub mod adc4; | 42 | pub mod adc4; |
| @@ -43,10 +49,10 @@ pub use crate::pac::adc::vals::Res as Resolution; | |||
| 43 | pub use crate::pac::adc::vals::SampleTime; | 49 | pub use crate::pac::adc::vals::SampleTime; |
| 44 | use crate::peripherals; | 50 | use crate::peripherals; |
| 45 | 51 | ||
| 46 | dma_trait!(RxDma, AnyInstance); | 52 | dma_trait!(RxDma, Instance); |
| 47 | 53 | ||
| 48 | /// Analog to Digital driver. | 54 | /// Analog to Digital driver. |
| 49 | pub struct Adc<'d, T: AnyInstance> { | 55 | pub 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 | ||
| 68 | trait SealedInstance { | 74 | #[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))] |
| 69 | #[cfg(not(adc_wba))] | 75 | trait_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 | ||
| 79 | pub(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))] | 80 | trait_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))] | ||
| 94 | trait_set::trait_set! { | 85 | trait_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( | 89 | pub 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 | ))] | ||
| 101 | pub 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 | ))] |
| 108 | pub(self) trait SealedAnyInstance: BasicAnyInstance { | 96 | trait 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)] |
| 122 | pub trait AnyInstance: SealedAnyInstance + Instance {} | 107 | pub 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; |
| 127 | pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} | 112 | } |
| 128 | 113 | ||
| 129 | // Implement AnyInstance automatically for SealedAnyInstance | 114 | trait 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( |
| 133 | impl<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( | 128 | pub(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) {} |
| 140 | impl<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 | ||
| 186 | impl<'d, T: AnyInstance> Adc<'d, T> { | 185 | impl<'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)] |
| 418 | pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { | 422 | pub 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. |
| 436 | pub struct AnyAdcChannel<T> { | 443 | pub 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 | } |
| 441 | impl_peripheral!(AnyAdcChannel<T: AnyInstance>); | 448 | impl<T: Instance> AdcChannel<T> for AnyAdcChannel<'_, T> {} |
| 442 | impl<T: AnyInstance> AdcChannel<T> for AnyAdcChannel<T> {} | 449 | impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<'_, T> { |
| 443 | impl<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 | ||
| 453 | impl<T> AnyAdcChannel<T> { | 459 | impl<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))] | ||
| 467 | impl BasicAdcRegs for crate::pac::adc::Adc { | ||
| 468 | type SampleTime = SampleTime; | ||
| 469 | } | ||
| 470 | |||
| 471 | #[cfg(any(adc_wba, adc_u5))] | ||
| 472 | impl BasicAdcRegs for crate::pac::adc::Adc4 { | ||
| 473 | type SampleTime = Adc4SampleTime; | ||
| 474 | } | ||
| 475 | |||
| 459 | #[cfg(adc_wba)] | 476 | #[cfg(adc_wba)] |
| 460 | foreach_adc!( | 477 | foreach_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)] |
| 491 | foreach_adc!( | 516 | foreach_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)))] |
| 522 | foreach_adc!( | 559 | foreach_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)] |
| 5 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| 6 | 6 | ||
| 7 | use crate::adc::AnyInstance; | 7 | use super::AdcRegs; |
| 8 | #[allow(unused_imports)] | 8 | #[allow(unused_imports)] |
| 9 | use crate::adc::{Instance, RxDma}; | 9 | use 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 | ||
| 22 | impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { | 22 | impl<'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 | ||
| 171 | impl<T: Instance + AnyInstance> Drop for RingBufferedAdc<'_, T> { | 171 | impl<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 @@ | |||
| 1 | use core::sync::atomic::{Ordering, compiler_fence}; | 1 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 2 | 2 | ||
| 3 | use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; | 3 | use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 4 | use crate::adc::{Adc, Instance, Resolution, SampleTime}; | 4 | use crate::adc::{Adc, AdcRegs, Instance, Resolution, SampleTime}; |
| 5 | use crate::pac::adc::vals; | 5 | use crate::pac::adc::vals; |
| 6 | pub use crate::pac::adccommon::vals::Adcpre; | 6 | pub use crate::pac::adccommon::vals::Adcpre; |
| 7 | use crate::time::Hertz; | 7 | use 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)] |
| 73 | pub struct AdcConfig { | 73 | pub struct AdcConfig { |
| 74 | resolution: Option<Resolution>, | 74 | pub resolution: Option<Resolution>, |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | impl<T: Instance> super::SealedAnyInstance for T { | 77 | impl 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 | ||
| 204 | impl<'d, T> Adc<'d, T> | 202 | impl<'d, T> Adc<'d, T> |
| 205 | where | 203 | where |
| 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 | ||
| 260 | impl<'d, T: Instance> Drop for Adc<'d, T> { | 258 | impl<'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 | ||
| 149 | impl<T: Instance> super::SealedAnyInstance for T { | 149 | impl 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 | ||
| 391 | impl<'d, T: Instance> Adc<'d, T> { | 388 | impl<'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}; | |||
| 5 | use pac::adccommon::vals::Presc; | 5 | use pac::adccommon::vals::Presc; |
| 6 | 6 | ||
| 7 | use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; | 7 | use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; |
| 8 | use crate::adc::ConversionMode; | 8 | use crate::adc::{AdcRegs, ConversionMode}; |
| 9 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 10 | use crate::{Peri, pac, rcc}; | 10 | use 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 | ||
| 83 | impl<T: Instance> super::SealedAnyInstance for T { | 83 | impl 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 | ||
| 203 | impl<'d, T: Instance + super::AnyInstance> Adc<'d, T> { | 197 | impl<'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 | ||
| 138 | impl AnyChannel { | 138 | impl 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::*; | |||
| 25 | pub(crate) mod ringbuffer; | 25 | pub(crate) mod ringbuffer; |
| 26 | pub mod word; | 26 | pub mod word; |
| 27 | 27 | ||
| 28 | use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; | 28 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; |
| 29 | 29 | ||
| 30 | use crate::interrupt; | 30 | use crate::interrupt; |
| 31 | use crate::rcc::StoppablePeripheral; | 31 | use 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)))] |
| 48 | pub type Request = (); | 48 | pub type Request = (); |
| 49 | 49 | ||
| 50 | impl<'a> StoppablePeripheral for Peri<'a, AnyChannel> { | 50 | pub(crate) trait SealedChannel: StoppablePeripheral { |
| 51 | #[cfg(feature = "low-power")] | ||
| 52 | fn stop_mode(&self) -> crate::rcc::StopMode { | ||
| 53 | self.stop_mode | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | pub(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))] | ||
| 65 | pub(crate) trait ChannelInterrupt { | 54 | pub(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)] |
| 72 | pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} | 61 | pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} |
| 73 | 62 | ||
| 74 | #[cfg(not(stm32n6))] | ||
| 75 | macro_rules! dma_channel_impl { | 63 | macro_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 | ||
| 126 | impl SealedChannel for AnyChannel { | 117 | impl 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 | |||
| 124 | impl SealedChannel for AnyChannel { | ||
| 125 | fn id(&self) -> u8 { | ||
| 126 | self.id | ||
| 127 | } | ||
| 128 | } | ||
| 137 | impl Channel for AnyChannel {} | 129 | impl Channel for AnyChannel {} |
| 138 | 130 | ||
| 139 | const CHANNEL_COUNT: usize = crate::_generated::DMA_CHANNELS.len(); | 131 | const 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 | ||
| 36 | macro_rules! impl_word { | 40 | macro_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 | ||
| 80 | struct BitIter(u32); | 80 | struct 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")] |
| 210 | trait_set::trait_set! { | ||
| 211 | pub trait Debuggable = Debug + defmt::Format; | ||
| 212 | } | ||
| 213 | |||
| 214 | #[cfg(not(feature = "defmt"))] | ||
| 215 | trait_set::trait_set! { | ||
| 216 | pub trait Debuggable = Debug; | ||
| 217 | } | ||
| 218 | |||
| 219 | #[cfg(feature = "defmt")] | ||
| 210 | #[collapse_debuginfo(yes)] | 220 | #[collapse_debuginfo(yes)] |
| 211 | macro_rules! unwrap { | 221 | macro_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))] |
| 13 | mod mco; | 13 | mod mco; |
| 14 | use critical_section::CriticalSection; | 14 | use critical_section::CriticalSection; |
| 15 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 15 | #[cfg(any(mco, mco1, mco2))] | 16 | #[cfg(any(mco, mco1, mco2))] |
| 16 | pub use mco::*; | 17 | pub 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)] |
| 176 | pub enum StopMode { | 177 | pub 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")] |
| 384 | impl<'a> StoppablePeripheral for StopMode { | 385 | impl 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 | ||
| 391 | impl<'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 | |||
| 390 | pub(crate) struct BusyPeripheral<T: StoppablePeripheral> { | 398 | pub(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`? | ||
| 507 | pub 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 @@ | |||
| 4 | use core::default::Default; | 4 | use core::default::Default; |
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| 7 | use core::ops::{Deref, DerefMut}; | 7 | use core::slice; |
| 8 | use core::task::Poll; | 8 | use core::task::Poll; |
| 9 | 9 | ||
| 10 | use embassy_hal_internal::drop::OnDrop; | ||
| 11 | use embassy_hal_internal::{Peri, PeripheralType}; | 10 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 12 | use embassy_sync::waitqueue::AtomicWaker; | 11 | use embassy_sync::waitqueue::AtomicWaker; |
| 13 | use sdio_host::common_cmd::{self, Resp, ResponseLen}; | 12 | use sdio_host::Cmd; |
| 14 | use sdio_host::emmc::{EMMC, ExtCSD}; | 13 | use sdio_host::common_cmd::{self, R1, R2, R3, Resp, ResponseLen, Rz}; |
| 15 | use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; | 14 | use sdio_host::sd::{BusWidth, CardStatus}; |
| 16 | use sdio_host::{Cmd, emmc_cmd, sd_cmd}; | 15 | use sdio_host::sd_cmd::{R6, R7}; |
| 17 | 16 | ||
| 18 | #[cfg(sdmmc_v1)] | 17 | #[cfg(sdmmc_v1)] |
| 19 | use crate::dma::ChannelAndRequest; | 18 | use crate::dma::ChannelAndRequest; |
| @@ -22,37 +21,27 @@ use crate::gpio::Pull; | |||
| 22 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | 21 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; |
| 23 | use crate::interrupt::typelevel::Interrupt; | 22 | use crate::interrupt::typelevel::Interrupt; |
| 24 | use crate::pac::sdmmc::Sdmmc as RegBlock; | 23 | use crate::pac::sdmmc::Sdmmc as RegBlock; |
| 25 | use crate::rcc::{self, RccPeripheral}; | 24 | use crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral}; |
| 25 | use crate::sdmmc::sd::Addressable; | ||
| 26 | use crate::time::Hertz; | 26 | use crate::time::Hertz; |
| 27 | use crate::{interrupt, peripherals}; | 27 | use crate::{interrupt, peripherals}; |
| 28 | 28 | ||
| 29 | /// Module for SD and EMMC cards | ||
| 30 | pub mod sd; | ||
| 31 | |||
| 32 | /// Module for SDIO interface | ||
| 33 | pub mod sdio; | ||
| 34 | |||
| 29 | /// Interrupt handler. | 35 | /// Interrupt handler. |
| 30 | pub struct InterruptHandler<T: Instance> { | 36 | pub struct InterruptHandler<T: Instance> { |
| 31 | _phantom: PhantomData<T>, | 37 | _phantom: PhantomData<T>, |
| 32 | } | 38 | } |
| 33 | 39 | ||
| 34 | impl<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 | |||
| 51 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | 40 | impl<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 | ||
| 69 | struct U128(pub u128); | ||
| 70 | |||
| 71 | trait TypedResp: Resp { | ||
| 72 | type Word: From<U128>; | ||
| 73 | } | ||
| 74 | |||
| 75 | impl From<U128> for () { | ||
| 76 | fn from(value: U128) -> Self { | ||
| 77 | match value.0 { | ||
| 78 | 0 => (), | ||
| 79 | _ => unreachable!(), | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | impl From<U128> for u32 { | ||
| 85 | fn from(value: U128) -> Self { | ||
| 86 | unwrap!(value.0.try_into()) | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | impl From<U128> for u128 { | ||
| 91 | fn from(value: U128) -> Self { | ||
| 92 | value.0 | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | impl TypedResp for Rz { | ||
| 97 | type Word = (); | ||
| 98 | } | ||
| 99 | |||
| 100 | impl TypedResp for R1 { | ||
| 101 | type Word = u32; | ||
| 102 | } | ||
| 103 | |||
| 104 | impl TypedResp for R2 { | ||
| 105 | type Word = u128; | ||
| 106 | } | ||
| 107 | |||
| 108 | impl TypedResp for R3 { | ||
| 109 | type Word = u32; | ||
| 110 | } | ||
| 111 | |||
| 112 | impl TypedResp for R6 { | ||
| 113 | type Word = u32; | ||
| 114 | } | ||
| 115 | |||
| 116 | impl 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. |
| 81 | const SD_INIT_FREQ: Hertz = Hertz(400_000); | 121 | const 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. | 142 | const 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))] | ||
| 108 | pub struct DataBlock(pub [u8; 512]); | ||
| 109 | |||
| 110 | impl Deref for DataBlock { | ||
| 111 | type Target = [u8; 512]; | ||
| 112 | |||
| 113 | fn deref(&self) -> &Self::Target { | ||
| 114 | &self.0 | ||
| 115 | } | ||
| 116 | } | 145 | } |
| 117 | 146 | ||
| 118 | impl DerefMut for DataBlock { | 147 | const 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))] | ||
| 129 | pub struct CmdBlock(pub [u32; 16]); | ||
| 130 | |||
| 131 | impl CmdBlock { | ||
| 132 | /// Creates a new instance of CmdBlock | ||
| 133 | pub const fn new() -> Self { | ||
| 134 | Self([0u32; 16]) | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | impl Deref for CmdBlock { | ||
| 139 | type Target = [u32; 16]; | ||
| 140 | |||
| 141 | fn deref(&self) -> &Self::Target { | ||
| 142 | &self.0 | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | impl 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 | ||
| 186 | pub 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 | ||
| 205 | pub 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)] |
| 221 | enum PowerCtrl { | 185 | enum 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 | ||
| 226 | fn 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)] | ||
| 236 | enum 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 | |||
| 254 | const 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 | ||
| 302 | struct WrappedTransfer<'a> { | ||
| 303 | _transfer: Transfer<'a>, | ||
| 304 | sdmmc: &'a Sdmmc<'a>, | ||
| 305 | defused: bool, | ||
| 306 | } | ||
| 307 | |||
| 308 | impl<'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 | |||
| 322 | impl<'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))] |
| 290 | const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { | 331 | const 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)] | ||
| 328 | pub enum SdmmcPeripheral { | ||
| 329 | /// SD Card | ||
| 330 | SdCard(Card), | ||
| 331 | /// eMMC memory | ||
| 332 | Emmc(Emmc), | ||
| 333 | } | ||
| 334 | |||
| 335 | impl 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 |
| 382 | pub struct Sdmmc<'d, T: Instance> { | 368 | pub 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 | ||
| 411 | const CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh); | 393 | const CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh); |
| @@ -416,9 +398,9 @@ const CMD_AF: AfType = AfType::output_pull(OutputType::PushPull, Speed::VeryHigh | |||
| 416 | const DATA_AF: AfType = CMD_AF; | 398 | const DATA_AF: AfType = CMD_AF; |
| 417 | 399 | ||
| 418 | #[cfg(sdmmc_v1)] | 400 | #[cfg(sdmmc_v1)] |
| 419 | impl<'d, T: Instance> Sdmmc<'d, T> { | 401 | impl<'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)] |
| 494 | impl<'d, T: Instance> Sdmmc<'d, T> { | 476 | impl<'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)] |
| 544 | impl<'d, T: Instance> Sdmmc<'d, T> { | 526 | impl<'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)] |
| 615 | impl<'d, T: Instance> Sdmmc<'d, T> { | 597 | impl<'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 | ||
| 662 | impl<'d, T: Instance> Sdmmc<'d, T> { | 644 | impl<'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 | ||
| 1758 | impl<'d, T: Instance> Drop for Sdmmc<'d, T> { | 1140 | impl<'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 | ||
| 1176 | type Regs = RegBlock; | ||
| 1177 | |||
| 1178 | struct Info { | ||
| 1179 | regs: Regs, | ||
| 1180 | rcc: RccInfo, | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | struct State { | ||
| 1184 | waker: AtomicWaker, | ||
| 1185 | } | ||
| 1186 | |||
| 1187 | impl State { | ||
| 1188 | const fn new() -> Self { | ||
| 1189 | Self { | ||
| 1190 | waker: AtomicWaker::new(), | ||
| 1191 | } | ||
| 1192 | } | ||
| 1193 | } | ||
| 1194 | |||
| 1794 | trait SealedInstance { | 1195 | trait 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); | |||
| 1820 | foreach_peripheral!( | 1221 | foreach_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 | |||
| 1839 | impl<'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 @@ | |||
| 1 | use core::default::Default; | ||
| 2 | use core::ops::{Deref, DerefMut}; | ||
| 3 | |||
| 4 | use sdio_host::emmc::{EMMC, ExtCSD}; | ||
| 5 | use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; | ||
| 6 | use sdio_host::{common_cmd, emmc_cmd, sd_cmd}; | ||
| 7 | |||
| 8 | use crate::sdmmc::{BlockSize, Error, Sdmmc, Signalling, block_size, bus_width_vals, slice8_mut, slice8_ref}; | ||
| 9 | use 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))] | ||
| 17 | pub struct DataBlock(pub [u32; 128]); | ||
| 18 | |||
| 19 | impl DataBlock { | ||
| 20 | /// Create a new DataBlock | ||
| 21 | pub const fn new() -> Self { | ||
| 22 | DataBlock([0u32; 128]) | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | impl 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 | |||
| 34 | impl 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))] | ||
| 45 | pub struct CmdBlock(pub [u32; 16]); | ||
| 46 | |||
| 47 | impl CmdBlock { | ||
| 48 | /// Creates a new instance of CmdBlock | ||
| 49 | pub const fn new() -> Self { | ||
| 50 | Self([0u32; 16]) | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | impl Deref for CmdBlock { | ||
| 55 | type Target = [u32; 16]; | ||
| 56 | |||
| 57 | fn deref(&self) -> &Self::Target { | ||
| 58 | &self.0 | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | impl 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 | ||
| 69 | pub 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 | ||
| 84 | pub 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 | ||
| 91 | impl<'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 | ||
| 310 | impl<'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 | ||
| 413 | impl<'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 | ||
| 580 | pub 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 | |||
| 597 | impl 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 | ||
| 618 | pub 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 | |||
| 633 | impl 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 | |||
| 652 | impl<'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 @@ | |||
| 1 | use core::ops::{Deref, DerefMut}; | ||
| 2 | |||
| 3 | use sdio_host::common_cmd::{R1, Rz, cmd}; | ||
| 4 | use sdio_host::sd::BusWidth; | ||
| 5 | use sdio_host::sd_cmd; | ||
| 6 | |||
| 7 | use crate::sdmmc::{Error, Sdmmc, block_size, slice8_mut, slice8_ref}; | ||
| 8 | use 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))] | ||
| 16 | pub struct DataBlock(pub [u32; 16]); | ||
| 17 | |||
| 18 | impl DataBlock { | ||
| 19 | /// Create a new DataBlock | ||
| 20 | pub const fn new() -> Self { | ||
| 21 | DataBlock([0u32; 16]) | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | impl 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 | |||
| 33 | impl 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 | ||
| 40 | pub struct SerialDataInterface<'a, 'b> { | ||
| 41 | /// Inner member | ||
| 42 | pub sdmmc: &'a mut Sdmmc<'b>, | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Card Storage Device | ||
| 46 | impl<'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))] | ||
| 60 | pub 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 | ||
| 77 | impl Default for Config { | 91 | impl 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> { | |||
| 763 | impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | 848 | impl<'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; | |||
| 4 | use core::ops::{Div, Mul}; | 4 | use 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)] |
| 8 | pub struct Hertz(pub u32); | 8 | pub struct Hertz(pub u32); |
| 9 | 9 | ||
| 10 | impl Display for Hertz { | 10 | impl 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 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | 4 | ||
| 5 | pub use stm32_metapac::timer::vals::{Ckd, Mms2, Ossi, Ossr}; | ||
| 6 | |||
| 7 | use super::low_level::{CountingMode, OutputPolarity, Timer}; | 5 | use super::low_level::{CountingMode, OutputPolarity, Timer}; |
| 8 | use super::simple_pwm::PwmPin; | 6 | use super::simple_pwm::PwmPin; |
| 9 | use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; | 7 | use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; |
| 10 | use crate::Peri; | 8 | use crate::Peri; |
| 11 | use crate::gpio::{AnyPin, OutputType}; | 9 | use crate::dma::word::Word; |
| 10 | use crate::gpio::{AfType, AnyPin, OutputType}; | ||
| 11 | pub use crate::pac::timer::vals::{Ccds, Ckd, Mms2, Ossi, Ossr}; | ||
| 12 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 13 | use crate::timer::TimerChannel; | 13 | use crate::timer::TimerChannel; |
| 14 | use crate::timer::low_level::OutputCompareMode; | 14 | use crate::timer::low_level::OutputCompareMode; |
| 15 | use 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; | |||
| 13 | pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; | 13 | pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; |
| 14 | 14 | ||
| 15 | use super::*; | 15 | use super::*; |
| 16 | use crate::dma::{Transfer, WritableRingBuffer}; | 16 | use crate::dma::{self, Transfer, WritableRingBuffer}; |
| 17 | use crate::pac::timer::vals; | 17 | use crate::pac::timer::vals; |
| 18 | use crate::rcc; | 18 | use crate::rcc; |
| 19 | use crate::time::Hertz; | 19 | use 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; | |||
| 15 | pub mod ringbuffered; | 15 | pub mod ringbuffered; |
| 16 | pub mod simple_pwm; | 16 | pub mod simple_pwm; |
| 17 | 17 | ||
| 18 | use crate::dma::word::Word; | ||
| 19 | use crate::fmt::Debuggable; | ||
| 18 | use crate::interrupt; | 20 | use crate::interrupt; |
| 19 | use crate::rcc::RccPeripheral; | 21 | use 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)] |
| 243 | macro_rules! impl_core_timer { | 250 | macro_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 | ||
| 307 | foreach_interrupt! { | 313 | foreach_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> { | |||
| 400 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { | 406 | impl<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> { | |||
| 327 | impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { | 327 | impl<'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; | |||
| 7 | use super::{Channel, GeneralInstance4Channel}; | 7 | use super::{Channel, GeneralInstance4Channel}; |
| 8 | use crate::dma::WritableRingBuffer; | 8 | use crate::dma::WritableRingBuffer; |
| 9 | use crate::dma::ringbuffer::Error; | 9 | use crate::dma::ringbuffer::Error; |
| 10 | use 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 | /// ``` |
| 26 | pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { | 27 | pub 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 | ||
| 32 | impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { | 33 | impl<'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`]. | ||
| 160 | pub 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}; | |||
| 7 | use super::ringbuffered::RingBufferedPwmChannel; | 7 | use super::ringbuffered::RingBufferedPwmChannel; |
| 8 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; | 8 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; |
| 9 | use crate::Peri; | 9 | use crate::Peri; |
| 10 | use crate::dma::word::Word; | ||
| 10 | #[cfg(gpio_v2)] | 11 | #[cfg(gpio_v2)] |
| 11 | use crate::gpio::Pull; | 12 | use crate::gpio::Pull; |
| 12 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 13 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 14 | use crate::pac::timer::vals::Ccds; | ||
| 13 | use crate::time::Hertz; | 15 | use 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 | ||
| 402 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { | 434 | impl<'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 | |||
| 9 | embassy-sync = { version = "0.7.2", path = "../../../../embassy-sync" } | 9 | embassy-sync = { version = "0.7.2", path = "../../../../embassy-sync" } |
| 10 | embassy-executor = { version = "0.9.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } | 10 | embassy-executor = { version = "0.9.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } |
| 11 | embassy-time = { version = "0.5.0", path = "../../../../embassy-time", features = [] } | 11 | embassy-time = { version = "0.5.0", path = "../../../../embassy-time", features = [] } |
| 12 | embassy-nrf = { version = "0.8.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } | 12 | embassy-nrf = { version = "0.8.0", path = "../../../../embassy-nrf", features = ["gpiote", ] } |
| 13 | embassy-boot = { version = "0.6.1", path = "../../../../embassy-boot", features = [] } | 13 | embassy-boot = { version = "0.6.1", path = "../../../../embassy-boot", features = [] } |
| 14 | embassy-boot-nrf = { version = "0.9.0", path = "../../../../embassy-boot-nrf", features = [] } | 14 | embassy-boot-nrf = { version = "0.9.0", path = "../../../../embassy-boot-nrf", features = [] } |
| 15 | embassy-embedded-hal = { version = "0.5.0", path = "../../../../embassy-embedded-hal" } | 15 | embassy-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 | ] |
| 36 | nrf54 = ["embassy-nrf/time-driver-grtc"] | ||
| 36 | 37 | ||
| 37 | [package.metadata.embassy] | 38 | [package.metadata.embassy] |
| 38 | build = [ | 39 | build = [ |
| 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 |
| 23 | cargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --release --chip nRF52840_xxAA | 23 | cargo 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' |
| 25 | cargo build --release --bin b --features embassy-nrf/nrf52840 | 25 | cargo build --release --bin b --features embassy-nrf/nrf52840,time-driver-rtc1 |
| 26 | # Generate binary for 'b' | 26 | # Generate binary for 'b' |
| 27 | cargo objcopy --release --bin b --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi -- -O binary b.bin | 27 | cargo 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"); | |||
| 23 | async fn main(_spawner: Spawner) { | 23 | async 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] |
| 11 | async fn main(_spawner: Spawner) { | 11 | async 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 = [ | |||
| 27 | softdevice = [ | 27 | softdevice = [ |
| 28 | "embassy-boot-nrf/softdevice", | 28 | "embassy-boot-nrf/softdevice", |
| 29 | ] | 29 | ] |
| 30 | nrf54 = [] | ||
| 30 | 31 | ||
| 31 | [profile.dev] | 32 | [profile.dev] |
| 32 | debug = 2 | 33 | debug = 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] | ||
| 2 | runner = 'probe-rs run --chip MCXA276 --preverify --verify --protocol swd --speed 12000' | ||
| 3 | |||
| 4 | rustflags = [ | ||
| 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] | ||
| 14 | target = "thumbv8m.main-none-eabihf" # Cortex-M33 | ||
| 15 | |||
| 16 | [env] | ||
| 17 | DEFMT_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] | ||
| 2 | name = "embassy-mcxa-examples" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2021" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | publish = false | ||
| 7 | |||
| 8 | [dependencies] | ||
| 9 | cortex-m = { version = "0.7", features = ["critical-section-single-core"] } | ||
| 10 | cortex-m-rt = { version = "0.7", features = ["set-sp", "set-vtor"] } | ||
| 11 | crc = "3.4.0" | ||
| 12 | critical-section = "1.2.0" | ||
| 13 | defmt = "1.0" | ||
| 14 | defmt-rtt = "1.0" | ||
| 15 | embassy-embedded-hal = "0.5.0" | ||
| 16 | embassy-executor = { version = "0.9.0", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"], default-features = false } | ||
| 17 | embassy-mcxa = { path = "../../embassy-mcxa", features = ["defmt", "unstable-pac", "time"] } | ||
| 18 | embassy-sync = "0.7.2" | ||
| 19 | embassy-time = "0.5.0" | ||
| 20 | embassy-time-driver = "0.2.1" | ||
| 21 | embedded-io-async = "0.6.1" | ||
| 22 | heapless = "0.9.2" | ||
| 23 | panic-probe = { version = "1.0", features = ["print-defmt"] } | ||
| 24 | static_cell = "2.1.1" | ||
| 25 | tmp108 = "0.4.0" | ||
| 26 | |||
| 27 | [profile.release] | ||
| 28 | lto = true # better optimizations | ||
| 29 | debug = 2 # enough information for defmt/rtt locations | ||
| 30 | |||
| 31 | [package.metadata.embassy] | ||
| 32 | build = [ | ||
| 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 @@ | |||
| 1 | use std::env; | ||
| 2 | use std::fs::File; | ||
| 3 | use std::io::Write; | ||
| 4 | use std::path::PathBuf; | ||
| 5 | |||
| 6 | fn 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 @@ | |||
| 1 | MEMORY | ||
| 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use hal::adc::{Adc, InterruptHandler, LpadcConfig, TriggerPriorityPolicy}; | ||
| 6 | use hal::bind_interrupts; | ||
| 7 | use hal::clocks::PoweredClock; | ||
| 8 | use hal::clocks::config::Div8; | ||
| 9 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | ||
| 10 | use hal::config::Config; | ||
| 11 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | ||
| 12 | use hal::pac::adc1::cmdl1::{Adch, Mode}; | ||
| 13 | use hal::pac::adc1::ctrl::CalAvgs; | ||
| 14 | use hal::pac::adc1::tctrl::Tcmd; | ||
| 15 | use hal::peripherals::ADC1; | ||
| 16 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 17 | |||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | ADC1 => InterruptHandler<ADC1>; | ||
| 20 | }); | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy}; | ||
| 6 | use hal::clocks::PoweredClock; | ||
| 7 | use hal::clocks::config::Div8; | ||
| 8 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | ||
| 9 | use hal::config::Config; | ||
| 10 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | ||
| 11 | use hal::pac::adc1::cmdl1::{Adch, Mode}; | ||
| 12 | use hal::pac::adc1::ctrl::CalAvgs; | ||
| 13 | use hal::pac::adc1::tctrl::Tcmd; | ||
| 14 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 15 | |||
| 16 | const G_LPADC_RESULT_SHIFT: u32 = 0; | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::Timer; | ||
| 6 | use hal::gpio::{DriveStrength, Level, Output, SlewRate}; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::Timer; | ||
| 6 | use hal::gpio::{Input, Pull}; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::Timer; | ||
| 6 | use hal::gpio::{Input, Pull}; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; | ||
| 6 | use embassy_mcxa::clocks::PoweredClock; | ||
| 7 | use embassy_mcxa::gpio::{DriveStrength, SlewRate}; | ||
| 8 | use embassy_mcxa::{Level, Output}; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 11 | |||
| 12 | /// Demonstrate CLKOUT, using Pin P4.2 | ||
| 13 | #[embassy_executor::main] | ||
| 14 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use hal::config::Config; | ||
| 6 | use hal::crc::Crc; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | const 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 | |||
| 20 | const 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] | ||
| 32 | async 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 | |||
| 16 | use embassy_executor::Spawner; | ||
| 17 | use embassy_mcxa::clocks::config::Div8; | ||
| 18 | use embassy_mcxa::dma::{DmaChannel, TransferOptions}; | ||
| 19 | use static_cell::ConstStaticCell; | ||
| 20 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 21 | |||
| 22 | const BUFFER_LENGTH: usize = 4; | ||
| 23 | |||
| 24 | // Buffers in RAM (static mut is automatically placed in .bss/.data) | ||
| 25 | static SRC_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([1, 2, 3, 4]); | ||
| 26 | static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); | ||
| 27 | static MEMSET_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); | ||
| 28 | |||
| 29 | #[embassy_executor::main] | ||
| 30 | async 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 | |||
| 23 | use embassy_executor::Spawner; | ||
| 24 | use embassy_mcxa::clocks::config::Div8; | ||
| 25 | use embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder}; | ||
| 26 | use static_cell::ConstStaticCell; | ||
| 27 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 28 | |||
| 29 | // Source buffers (multiple segments) | ||
| 30 | static SRC1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x11111111, 0x22222222, 0x33333333, 0x44444444]); | ||
| 31 | static SRC2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD]); | ||
| 32 | static SRC3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210]); | ||
| 33 | |||
| 34 | // Destination buffers (one per segment) | ||
| 35 | static DST1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 36 | static DST2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 37 | static DST3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 38 | |||
| 39 | #[embassy_executor::main] | ||
| 40 | async 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 | |||
| 13 | use core::fmt::Write as _; | ||
| 14 | |||
| 15 | use embassy_executor::Spawner; | ||
| 16 | use embassy_mcxa::clocks::config::Div8; | ||
| 17 | use embassy_mcxa::dma::DmaChannel; | ||
| 18 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | ||
| 19 | use {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))] | ||
| 23 | struct AlignedSrc([u32; 4]); | ||
| 24 | |||
| 25 | static mut SRC: AlignedSrc = AlignedSrc([0; 4]); | ||
| 26 | static mut DST: [u32; 8] = [0; 8]; | ||
| 27 | |||
| 28 | /// Helper to print a buffer to UART | ||
| 29 | fn 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] | ||
| 34 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::clocks::config::Div8; | ||
| 6 | use hal::lpuart::{Blocking, Config, Lpuart}; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | /// Simple helper to write a byte as hex to UART | ||
| 10 | fn 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] | ||
| 17 | async 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 | ||
| 108 | fn 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::Timer; | ||
| 6 | use hal::bind_interrupts; | ||
| 7 | use hal::clocks::config::Div8; | ||
| 8 | use hal::config::Config; | ||
| 9 | use hal::i2c::InterruptHandler; | ||
| 10 | use hal::i2c::controller::{self, I2c, Speed}; | ||
| 11 | use hal::peripherals::LPI2C3; | ||
| 12 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!( | ||
| 15 | struct Irqs { | ||
| 16 | LPI2C3 => InterruptHandler<LPI2C3>; | ||
| 17 | } | ||
| 18 | ); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::Timer; | ||
| 6 | use hal::clocks::config::Div8; | ||
| 7 | use hal::config::Config; | ||
| 8 | use hal::i2c::controller::{self, I2c, Speed}; | ||
| 9 | use tmp108::Tmp108; | ||
| 10 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::Input; | ||
| 6 | use embassy_mcxa::gpio::Pull; | ||
| 7 | use embassy_time::Timer; | ||
| 8 | use hal::clocks::config::Div8; | ||
| 9 | use hal::config::Config; | ||
| 10 | use hal::i2c::controller::{self, I2c, Speed}; | ||
| 11 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::clocks::config::Div8; | ||
| 6 | use embassy_mcxa::lpuart::Config; | ||
| 7 | use embassy_mcxa::lpuart::buffered::BufferedLpuart; | ||
| 8 | use embassy_mcxa::{bind_interrupts, lpuart}; | ||
| 9 | use embedded_io_async::Write; | ||
| 10 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 11 | |||
| 12 | // Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver | ||
| 13 | bind_interrupts!(struct Irqs { | ||
| 14 | LPUART2 => lpuart::buffered::BufferedInterruptHandler::<hal::peripherals::LPUART2>; | ||
| 15 | }); | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async 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 | |||
| 13 | use embassy_executor::Spawner; | ||
| 14 | use embassy_mcxa::clocks::config::Div8; | ||
| 15 | use embassy_mcxa::lpuart::{Config, LpuartDma}; | ||
| 16 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::clocks::config::Div8; | ||
| 6 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 7 | |||
| 8 | use crate::hal::lpuart::{Config, Lpuart}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async 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 | |||
| 21 | use embassy_executor::Spawner; | ||
| 22 | use embassy_mcxa::clocks::config::Div8; | ||
| 23 | use embassy_mcxa::lpuart::{Config, LpuartDma, LpuartTxDma}; | ||
| 24 | use static_cell::ConstStaticCell; | ||
| 25 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 26 | |||
| 27 | // Ring buffer for RX - power of 2 is ideal for modulo efficiency | ||
| 28 | static RX_RING_BUFFER: ConstStaticCell<[u8; 64]> = ConstStaticCell::new([0; 64]); | ||
| 29 | |||
| 30 | /// Helper to write a byte as hex to UART | ||
| 31 | fn 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] | ||
| 41 | async 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 | |||
| 22 | use embassy_executor::Spawner; | ||
| 23 | use embassy_mcxa::clocks::config::Div8; | ||
| 24 | use embassy_mcxa::dma::DmaChannel; | ||
| 25 | use embassy_mcxa::pac; | ||
| 26 | use static_cell::ConstStaticCell; | ||
| 27 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 28 | |||
| 29 | // Buffers | ||
| 30 | static SRC_BUFFER: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([1, 2, 3, 4]); | ||
| 31 | static DEST_BUFFER0: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 32 | static DEST_BUFFER1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 33 | static DEST_BUFFER2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 34 | |||
| 35 | #[embassy_executor::main] | ||
| 36 | async 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 | |||
| 17 | use embassy_executor::Spawner; | ||
| 18 | use embassy_mcxa::clocks::config::Div8; | ||
| 19 | use embassy_mcxa::dma::DmaChannel; | ||
| 20 | use static_cell::ConstStaticCell; | ||
| 21 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 22 | |||
| 23 | const BUFFER_LENGTH: usize = 16; | ||
| 24 | const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2; | ||
| 25 | |||
| 26 | // Buffers in RAM | ||
| 27 | static SRC_BUFFER: ConstStaticCell<[u32; HALF_BUFF_LENGTH]> = ConstStaticCell::new([0; HALF_BUFF_LENGTH]); | ||
| 28 | static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); | ||
| 29 | |||
| 30 | #[embassy_executor::main] | ||
| 31 | async 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 | |||
| 17 | use embassy_executor::Spawner; | ||
| 18 | use embassy_mcxa::clocks::config::Div8; | ||
| 19 | use embassy_mcxa::dma::DmaChannel; | ||
| 20 | use static_cell::ConstStaticCell; | ||
| 21 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 22 | |||
| 23 | const BUFFER_LENGTH: usize = 4; | ||
| 24 | |||
| 25 | // Buffers in RAM | ||
| 26 | static PATTERN: u32 = 0xDEADBEEF; | ||
| 27 | static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); | ||
| 28 | |||
| 29 | #[embassy_executor::main] | ||
| 30 | async 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 | |||
| 30 | use embassy_executor::Spawner; | ||
| 31 | use embassy_mcxa::clocks::config::Div8; | ||
| 32 | use embassy_mcxa::dma::{DmaChannel, Tcd, TransferOptions}; | ||
| 33 | use embassy_mcxa::pac; | ||
| 34 | use static_cell::ConstStaticCell; | ||
| 35 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 36 | |||
| 37 | // Source and destination buffers for Approach 1 (scatter/gather) | ||
| 38 | static SRC: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8]); | ||
| 39 | static DST: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]); | ||
| 40 | |||
| 41 | // Source and destination buffers for Approach 2 (wait_half) | ||
| 42 | static SRC2: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]); | ||
| 43 | static 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))] | ||
| 47 | struct TcdPool([Tcd; 2]); | ||
| 48 | |||
| 49 | static 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] | ||
| 66 | async 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 | |||
| 19 | use embassy_executor::Spawner; | ||
| 20 | use embassy_mcxa::clocks::config::Div8; | ||
| 21 | use embassy_mcxa::dma::{DmaChannel, Tcd}; | ||
| 22 | use static_cell::ConstStaticCell; | ||
| 23 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 24 | |||
| 25 | // Source and destination buffers | ||
| 26 | static SRC: ConstStaticCell<[u32; 12]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); | ||
| 27 | static 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))] | ||
| 31 | struct TcdPool([Tcd; 2]); | ||
| 32 | |||
| 33 | static 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] | ||
| 50 | async 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 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::bind_interrupts; | ||
| 6 | use hal::rtc::{InterruptHandler, Rtc, RtcDateTime}; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | bind_interrupts!(struct Irqs { | ||
| 10 | RTC => InterruptHandler<hal::rtc::Rtc0>; | ||
| 11 | }); | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async 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 | ||
| 3 | runner = "probe-rs run --chip nrf54lm20a" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv8m.main-none-eabihf" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_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] | ||
| 2 | edition = "2024" | ||
| 3 | name = "embassy-nrf54lm20-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | publish = false | ||
| 7 | |||
| 8 | [dependencies] | ||
| 9 | embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } | ||
| 10 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | ||
| 11 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | ||
| 12 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } | ||
| 13 | embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf54lm20-app-s", "time-driver-grtc", "gpiote", "unstable-pac", "time"] } | ||
| 14 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } | ||
| 15 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | ||
| 16 | |||
| 17 | rand = { version = "0.9.0", default-features = false } | ||
| 18 | |||
| 19 | defmt = "1.0.1" | ||
| 20 | defmt-rtt = "1.0.0" | ||
| 21 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 22 | |||
| 23 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 24 | cortex-m-rt = "0.7.0" | ||
| 25 | |||
| 26 | embedded-storage = "0.3.1" | ||
| 27 | portable-atomic = "1" | ||
| 28 | |||
| 29 | static_cell = "2" | ||
| 30 | |||
| 31 | [profile.release] | ||
| 32 | debug = 2 | ||
| 33 | |||
| 34 | [package.metadata.embassy] | ||
| 35 | build = [ | ||
| 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 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn main() { | ||
| 17 | // Put `memory.x` in our output directory and ensure it's | ||
| 18 | // on the linker search path. | ||
| 19 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 20 | File::create(out.join("memory.x")) | ||
| 21 | .unwrap() | ||
| 22 | .write_all(include_bytes!("memory.x")) | ||
| 23 | .unwrap(); | ||
| 24 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 25 | |||
| 26 | // By default, Cargo will re-run a build script whenever | ||
| 27 | // any file in the project changes. By specifying `memory.x` | ||
| 28 | // here, we ensure the build script is only re-run when | ||
| 29 | // `memory.x` is changed. | ||
| 30 | println!("cargo:rerun-if-changed=memory.x"); | ||
| 31 | |||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 34 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 35 | } | ||
diff --git a/examples/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 @@ | |||
| 1 | MEMORY | ||
| 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 | |||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | ||
| 7 | use embassy_time::Timer; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async 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 | |||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nrf::gpio::{Input, Pull}; | ||
| 7 | use embassy_time::Timer; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async 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 | |||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nrf::gpio::Pull; | ||
| 7 | use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async 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 | |||
| 4 | use defmt::{info, unwrap}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nrf::gpio::{Input, Pull}; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::task(pool_size = 4)] | ||
| 10 | async 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] | ||
| 20 | async 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 | |||
| 4 | use defmt::{info, unwrap}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_time::Timer; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::task] | ||
| 10 | async fn run1() { | ||
| 11 | loop { | ||
| 12 | info!("BIG INFREQUENT TICK"); | ||
| 13 | Timer::after_secs(10).await; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | #[embassy_executor::task] | ||
| 18 | async fn run2() { | ||
| 19 | loop { | ||
| 20 | info!("tick"); | ||
| 21 | Timer::after_secs(1).await; | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | #[embassy_executor::main] | ||
| 26 | async 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 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; | 6 | use embassy_stm32::sdmmc::Sdmmc; |
| 7 | use embassy_stm32::sdmmc::sd::{CmdBlock, DataBlock, StorageDevice}; | ||
| 7 | use embassy_stm32::time::{Hertz, mhz}; | 8 | use embassy_stm32::time::{Hertz, mhz}; |
| 8 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; | 9 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {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 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::sdmmc::Sdmmc; | 6 | use embassy_stm32::sdmmc::Sdmmc; |
| 7 | use embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice}; | ||
| 7 | use embassy_stm32::time::{Hertz, mhz}; | 8 | use embassy_stm32::time::{Hertz, mhz}; |
| 8 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; | 9 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {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 | ||
| 69 | async fn adc_task<'a, T: adc::Instance>( | 69 | async 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 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::sdmmc::Sdmmc; | 6 | use embassy_stm32::sdmmc::Sdmmc; |
| 7 | use embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice}; | ||
| 7 | use embassy_stm32::time::mhz; | 8 | use embassy_stm32::time::mhz; |
| 8 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; | 9 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {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] |
| 16 | async fn main(_spawner: Spawner) -> ! { | 17 | async 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" | |||
| 32 | stm32-fmc = "0.3.0" | 32 | stm32-fmc = "0.3.0" |
| 33 | embedded-storage = "0.3.1" | 33 | embedded-storage = "0.3.1" |
| 34 | static_cell = "2" | 34 | static_cell = "2" |
| 35 | hmac = "0.12.1" | ||
| 36 | sha2 = { 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 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::crc::{Config, Crc, InputReverseConfig, PolySize}; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async 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 | |||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::hash::*; | ||
| 7 | use embassy_stm32::{Config, bind_interrupts, hash, peripherals}; | ||
| 8 | use embassy_time::Instant; | ||
| 9 | use hmac::{Hmac, Mac}; | ||
| 10 | use sha2::{Digest, Sha256}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | type HmacSha256 = Hmac<Sha256>; | ||
| 14 | |||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | HASH => hash::InterruptHandler<peripherals::HASH>; | ||
| 17 | }); | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async 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] |
| 28 | default = ["ble", "mac"] | 28 | default = ["ble", "mac"] |
| 29 | mac = ["embassy-stm32-wpan/mac", "dep:embassy-net"] | 29 | mac = ["embassy-stm32-wpan/wb55_mac", "dep:embassy-net"] |
| 30 | ble = ["embassy-stm32-wpan/ble"] | 30 | ble = ["embassy-stm32-wpan/wb55_ble"] |
| 31 | 31 | ||
| 32 | [[bin]] | 32 | [[bin]] |
| 33 | name = "tl_mbox_ble" | 33 | name = "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" | |||
| 6 | publish = false | 6 | publish = false |
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba55cg", "time-driver-any", "memory-x", "exti"] } | 9 | embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] } |
| 10 | embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wba52cg"] } | ||
| 10 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } | 11 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 12 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 13 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| @@ -22,6 +23,11 @@ panic-probe = { version = "1.0.0", features = ["print-defmt"] } | |||
| 22 | heapless = { version = "0.8", default-features = false } | 23 | heapless = { version = "0.8", default-features = false } |
| 23 | static_cell = "2" | 24 | static_cell = "2" |
| 24 | 25 | ||
| 26 | [features] | ||
| 27 | default = ["ble", "mac"] | ||
| 28 | mac = ["embassy-stm32-wpan/wba_mac", "dep:embassy-net"] | ||
| 29 | ble = ["embassy-stm32-wpan/wba_ble"] | ||
| 30 | |||
| 25 | [profile.release] | 31 | [profile.release] |
| 26 | debug = 2 | 32 | debug = 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 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::Config; | ||
| 7 | use embassy_stm32::rcc::{Sysclk, mux}; | ||
| 8 | use embassy_stm32_wpan::bindings::mac::ST_MAC_callbacks_t; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | static _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] | ||
| 44 | async 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 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::Config; | ||
| 7 | use embassy_stm32::rcc::*; | ||
| 8 | use embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig}; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | pub 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] | ||
| 37 | async 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 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::Config; | ||
| 6 | use embassy_stm32::gpio::{Level, Output, Speed}; | 7 | use embassy_stm32::gpio::{Level, Output, Speed}; |
| 8 | use embassy_stm32::rcc::{ | ||
| 9 | AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, | ||
| 10 | }; | ||
| 7 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 13 | ||
| 10 | #[embassy_executor::main] | 14 | #[embassy_executor::main] |
| 11 | async fn main(_spawner: Spawner) { | 15 | async 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 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::Config; | ||
| 7 | use embassy_stm32::rcc::*; | ||
| 8 | use embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig}; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | pub 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] | ||
| 37 | async 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 | |||
| 3 | set -euo pipefail | ||
| 4 | |||
| 5 | # We need the nightly toolchain for this | ||
| 6 | mv rust-toolchain-nightly.toml rust-toolchain.toml | ||
| 7 | |||
| 8 | # Similar to the CI workflow, but don't just CHECK, actualy DO the formatting | ||
| 9 | find . -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 | ||
| 12 | mv rust-toolchain.toml rust-toolchain-nightly.toml | ||
| 13 | git 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"] | |||
| 51 | chrono = ["embassy-stm32/chrono", "dep:chrono"] | 51 | chrono = ["embassy-stm32/chrono", "dep:chrono"] |
| 52 | can = [] | 52 | can = [] |
| 53 | fdcan = [] | 53 | fdcan = [] |
| 54 | ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] | 54 | ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/wb55_ble"] |
| 55 | mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] | 55 | mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/wb55_mac"] |
| 56 | embassy-stm32-wpan = [] | 56 | embassy-stm32-wpan = [] |
| 57 | not-gpdma = [] | 57 | not-gpdma = [] |
| 58 | dac = [] | 58 | dac = [] |
| @@ -77,7 +77,7 @@ embassy-executor = { version = "0.9.0", path = "../../embassy-executor", feature | |||
| 77 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "tick-hz-131_072", "defmt-timestamp-uptime"] } | 77 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "tick-hz-131_072", "defmt-timestamp-uptime"] } |
| 78 | embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "memory-x", "time-driver-any", "_allow-disable-rtc"] } | 78 | embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "memory-x", "time-driver-any", "_allow-disable-rtc"] } |
| 79 | embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } | 79 | embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } |
| 80 | embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg", "ble"] } | 80 | embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg", "wb55_ble"] } |
| 81 | embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } | 81 | embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } |
| 82 | perf-client = { path = "../perf-client" } | 82 | perf-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; | |||
| 7 | use common::*; | 7 | use common::*; |
| 8 | use defmt::assert_eq; | 8 | use defmt::assert_eq; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; | 10 | use embassy_stm32::sdmmc::Sdmmc; |
| 11 | use embassy_stm32::sdmmc::sd::{CmdBlock, DataBlock, StorageDevice}; | ||
| 11 | use embassy_stm32::time::mhz; | 12 | use embassy_stm32::time::mhz; |
| 12 | use embassy_stm32::{bind_interrupts, peripherals, sdmmc}; | 13 | use embassy_stm32::{bind_interrupts, peripherals, sdmmc}; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {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; | |||
| 6 | use common::*; | 6 | use common::*; |
| 7 | use defmt::{assert, assert_eq, unreachable}; | 7 | use defmt::{assert, assert_eq, unreachable}; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::mode::Blocking; | ||
| 9 | use embassy_stm32::usart::{Config, ConfigError, Error, Uart}; | 10 | use embassy_stm32::usart::{Config, ConfigError, Error, Uart}; |
| 10 | use embassy_time::{Duration, Instant, block_for}; | 11 | use 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::*; | |||
| 7 | use defmt::assert_eq; | 7 | use defmt::assert_eq; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_futures::join::join; | 9 | use embassy_futures::join::join; |
| 10 | use embassy_stm32::usart::{Config, Uart}; | 10 | use embassy_stm32::usart::{Config, Error, Uart}; |
| 11 | 11 | ||
| 12 | #[embassy_executor::main] | 12 | #[embassy_executor::main] |
| 13 | async fn main(_spawner: Spawner) { | 13 | async 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(); |
