diff options
183 files changed, 22124 insertions, 2299 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." |
| @@ -67,6 +67,8 @@ rm out/tests/rpi-pico/pwm | |||
| 67 | rm out/tests/rpi-pico/cyw43-perf | 67 | rm out/tests/rpi-pico/cyw43-perf |
| 68 | rm out/tests/rpi-pico/uart_buffered | 68 | rm out/tests/rpi-pico/uart_buffered |
| 69 | 69 | ||
| 70 | rm out/tests/stm32h563zi/usart_dma | ||
| 71 | |||
| 70 | # tests are implemented but the HIL test farm doesn't actually have these boards, yet | 72 | # tests are implemented but the HIL test farm doesn't actually have these boards, yet |
| 71 | rm -rf out/tests/stm32c071rb | 73 | rm -rf out/tests/stm32c071rb |
| 72 | rm -rf out/tests/stm32f100rd | 74 | rm -rf out/tests/stm32f100rd |
diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index 36d5ee7f8..240c0e728 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs | |||
| @@ -568,6 +568,14 @@ impl<'a> Control<'a> { | |||
| 568 | n | 568 | n |
| 569 | } | 569 | } |
| 570 | 570 | ||
| 571 | /// Retrieve the latest RSSI value | ||
| 572 | pub async fn get_rssi(&mut self) -> i32 { | ||
| 573 | let mut rssi_buf = [0u8; 4]; | ||
| 574 | let n = self.ioctl(IoctlType::Get, Ioctl::GetRssi, 0, &mut rssi_buf).await; | ||
| 575 | assert_eq!(n, 4); | ||
| 576 | i32::from_ne_bytes(rssi_buf) | ||
| 577 | } | ||
| 578 | |||
| 571 | async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { | 579 | async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { |
| 572 | let mut buf = [0; 8]; | 580 | let mut buf = [0; 8]; |
| 573 | buf[0..4].copy_from_slice(&val1.to_le_bytes()); | 581 | 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/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..f88bb6b37 --- /dev/null +++ b/embassy-mcxa/src/adc.rs | |||
| @@ -0,0 +1,849 @@ | |||
| 1 | //! ADC driver | ||
| 2 | use core::future::Future; | ||
| 3 | use core::marker::PhantomData; | ||
| 4 | |||
| 5 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 6 | use maitake_sync::WaitCell; | ||
| 7 | use paste::paste; | ||
| 8 | |||
| 9 | use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; | ||
| 10 | use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; | ||
| 11 | use crate::gpio::{GpioPin, SealedPin}; | ||
| 12 | use crate::interrupt::typelevel::{Handler, Interrupt}; | ||
| 13 | use crate::pac; | ||
| 14 | use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; | ||
| 15 | use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; | ||
| 16 | use crate::pac::adc1::cmdl1::Mode; | ||
| 17 | use crate::pac::adc1::ctrl::CalAvgs; | ||
| 18 | use crate::pac::adc1::tctrl::{Tcmd, Tpri}; | ||
| 19 | |||
| 20 | /// Trigger priority policy for ADC conversions. | ||
| 21 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 22 | #[repr(u8)] | ||
| 23 | pub enum TriggerPriorityPolicy { | ||
| 24 | ConvPreemptImmediatelyNotAutoResumed = 0, | ||
| 25 | ConvPreemptSoftlyNotAutoResumed = 1, | ||
| 26 | ConvPreemptImmediatelyAutoRestarted = 4, | ||
| 27 | ConvPreemptSoftlyAutoRestarted = 5, | ||
| 28 | ConvPreemptImmediatelyAutoResumed = 12, | ||
| 29 | ConvPreemptSoftlyAutoResumed = 13, | ||
| 30 | ConvPreemptSubsequentlyNotAutoResumed = 2, | ||
| 31 | ConvPreemptSubsequentlyAutoRestarted = 6, | ||
| 32 | ConvPreemptSubsequentlyAutoResumed = 14, | ||
| 33 | TriggerPriorityExceptionDisabled = 16, | ||
| 34 | } | ||
| 35 | |||
| 36 | /// Configuration for the LPADC peripheral. | ||
| 37 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 38 | pub struct LpadcConfig { | ||
| 39 | /// Control system transition to Stop and Wait power modes while ADC is converting. | ||
| 40 | /// When enabled in Doze mode, immediate entries to Wait or Stop are allowed. | ||
| 41 | /// When disabled, the ADC will wait for the current averaging iteration/FIFO storage to complete before acknowledging stop or wait mode entry. | ||
| 42 | pub enable_in_doze_mode: bool, | ||
| 43 | /// Auto-Calibration Averages. | ||
| 44 | pub conversion_average_mode: CalAvgs, | ||
| 45 | /// ADC analog circuits are pre-enabled and ready to execute conversions without startup delays(at the cost of higher DC current consumption). | ||
| 46 | pub enable_analog_preliminary: bool, | ||
| 47 | /// Power-up delay value (in ADC clock cycles) | ||
| 48 | pub power_up_delay: u8, | ||
| 49 | /// Reference voltage source selection | ||
| 50 | pub reference_voltage_source: Refsel, | ||
| 51 | /// Power configuration selection. | ||
| 52 | pub power_level_mode: Pwrsel, | ||
| 53 | /// Trigger priority policy for handling multiple triggers | ||
| 54 | pub trigger_priority_policy: TriggerPriorityPolicy, | ||
| 55 | /// Enables the ADC pausing function. When enabled, a programmable delay is inserted during command execution sequencing between LOOP iterations, | ||
| 56 | /// between commands in a sequence, and between conversions when command is executing in "Compare Until True" configuration. | ||
| 57 | pub enable_conv_pause: bool, | ||
| 58 | /// Controls the duration of pausing during command execution sequencing. The pause delay is a count of (convPauseDelay*4) ADCK cycles. | ||
| 59 | /// Only available when ADC pausing function is enabled. The available value range is in 9-bit. | ||
| 60 | pub conv_pause_delay: u16, | ||
| 61 | /// Power configuration (normal/deep sleep behavior) | ||
| 62 | pub power: PoweredClock, | ||
| 63 | /// ADC clock source selection | ||
| 64 | pub source: AdcClockSel, | ||
| 65 | /// Clock divider for ADC clock | ||
| 66 | pub div: Div4, | ||
| 67 | } | ||
| 68 | |||
| 69 | impl Default for LpadcConfig { | ||
| 70 | fn default() -> Self { | ||
| 71 | LpadcConfig { | ||
| 72 | enable_in_doze_mode: true, | ||
| 73 | conversion_average_mode: CalAvgs::NoAverage, | ||
| 74 | enable_analog_preliminary: false, | ||
| 75 | power_up_delay: 0x80, | ||
| 76 | reference_voltage_source: Refsel::Option1, | ||
| 77 | power_level_mode: Pwrsel::Lowest, | ||
| 78 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, | ||
| 79 | enable_conv_pause: false, | ||
| 80 | conv_pause_delay: 0, | ||
| 81 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 82 | source: AdcClockSel::FroLfDiv, | ||
| 83 | div: Div4::no_div(), | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Configuration for a conversion command. | ||
| 89 | /// | ||
| 90 | /// Defines the parameters for a single ADC conversion operation. | ||
| 91 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 92 | pub struct ConvCommandConfig { | ||
| 93 | pub chained_next_command_number: Next, | ||
| 94 | pub enable_auto_channel_increment: bool, | ||
| 95 | pub loop_count: u8, | ||
| 96 | pub hardware_average_mode: Avgs, | ||
| 97 | pub sample_time_mode: Sts, | ||
| 98 | pub hardware_compare_mode: Cmpen, | ||
| 99 | pub hardware_compare_value_high: u32, | ||
| 100 | pub hardware_compare_value_low: u32, | ||
| 101 | pub conversion_resolution_mode: Mode, | ||
| 102 | pub enable_wait_trigger: bool, | ||
| 103 | } | ||
| 104 | |||
| 105 | impl Default for ConvCommandConfig { | ||
| 106 | fn default() -> Self { | ||
| 107 | ConvCommandConfig { | ||
| 108 | chained_next_command_number: Next::NoNextCmdTerminateOnFinish, | ||
| 109 | enable_auto_channel_increment: false, | ||
| 110 | loop_count: 0, | ||
| 111 | hardware_average_mode: Avgs::NoAverage, | ||
| 112 | sample_time_mode: Sts::Sample3p5, | ||
| 113 | hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, | ||
| 114 | hardware_compare_value_high: 0, | ||
| 115 | hardware_compare_value_low: 0, | ||
| 116 | conversion_resolution_mode: Mode::Data12Bits, | ||
| 117 | enable_wait_trigger: false, | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Configuration for a conversion trigger. | ||
| 123 | /// | ||
| 124 | /// Defines how a trigger initiates ADC conversions. | ||
| 125 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 126 | pub struct ConvTriggerConfig { | ||
| 127 | pub target_command_id: Tcmd, | ||
| 128 | pub delay_power: u8, | ||
| 129 | pub priority: Tpri, | ||
| 130 | pub enable_hardware_trigger: bool, | ||
| 131 | } | ||
| 132 | |||
| 133 | impl Default for ConvTriggerConfig { | ||
| 134 | fn default() -> Self { | ||
| 135 | ConvTriggerConfig { | ||
| 136 | target_command_id: Tcmd::NotValid, | ||
| 137 | delay_power: 0, | ||
| 138 | priority: Tpri::HighestPriority, | ||
| 139 | enable_hardware_trigger: false, | ||
| 140 | } | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Shorthand for `Result<T>`. | ||
| 145 | pub type Result<T> = core::result::Result<T, Error>; | ||
| 146 | |||
| 147 | /// ADC Error types | ||
| 148 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 149 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 150 | pub enum Error { | ||
| 151 | /// FIFO is empty, no conversion result available | ||
| 152 | FifoEmpty, | ||
| 153 | /// Invalid configuration | ||
| 154 | InvalidConfig, | ||
| 155 | /// Clock configuration error. | ||
| 156 | ClockSetup(ClockError), | ||
| 157 | } | ||
| 158 | |||
| 159 | /// Result of an ADC conversion. | ||
| 160 | /// | ||
| 161 | /// Contains the conversion value and metadata about the conversion. | ||
| 162 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 163 | pub struct ConvResult { | ||
| 164 | pub command_id_source: u8, | ||
| 165 | pub loop_count_index: u8, | ||
| 166 | pub trigger_id_source: u8, | ||
| 167 | pub conv_value: u16, | ||
| 168 | } | ||
| 169 | |||
| 170 | /// ADC interrupt handler. | ||
| 171 | pub struct InterruptHandler<I: Instance> { | ||
| 172 | _phantom: PhantomData<I>, | ||
| 173 | } | ||
| 174 | |||
| 175 | /// ADC driver instance. | ||
| 176 | pub struct Adc<'a, M: ModeAdc> { | ||
| 177 | _inst: PhantomData<&'a mut ()>, | ||
| 178 | mode: M, | ||
| 179 | |||
| 180 | // The channel index of the pin used to create our ADC instance | ||
| 181 | channel_idx: u8, | ||
| 182 | |||
| 183 | // The register block of the ADC instance | ||
| 184 | info: &'static pac::adc0::RegisterBlock, | ||
| 185 | } | ||
| 186 | |||
| 187 | impl<'a> Adc<'a, Blocking> { | ||
| 188 | /// Create a new blocking instance of the ADC driver. | ||
| 189 | /// # Arguments | ||
| 190 | /// * `_inst` - ADC peripheral instance | ||
| 191 | /// * `pin` - GPIO pin to use for ADC | ||
| 192 | /// * `config` - ADC configuration | ||
| 193 | pub fn new_blocking<I: Instance>( | ||
| 194 | _inst: Peri<'a, I>, | ||
| 195 | pin: Peri<'a, impl AdcPin<I>>, | ||
| 196 | config: LpadcConfig, | ||
| 197 | ) -> Result<Self> { | ||
| 198 | Self::new_inner(_inst, pin, config, Blocking) | ||
| 199 | } | ||
| 200 | |||
| 201 | /// Enable ADC interrupts. | ||
| 202 | /// | ||
| 203 | /// Enables the interrupt sources specified in the bitmask. | ||
| 204 | /// | ||
| 205 | /// # Arguments | ||
| 206 | /// * `mask` - Bitmask of interrupt sources to enable | ||
| 207 | pub fn enable_interrupt(&mut self, mask: u32) { | ||
| 208 | self.info.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) }); | ||
| 209 | } | ||
| 210 | |||
| 211 | /// Disable ADC interrupts. | ||
| 212 | /// | ||
| 213 | /// Disables the interrupt sources specified in the bitmask. | ||
| 214 | /// | ||
| 215 | /// # Arguments | ||
| 216 | /// * `mask` - Bitmask of interrupt sources to disable | ||
| 217 | pub fn disable_interrupt(&mut self, mask: u32) { | ||
| 218 | self.info.ie().modify(|r, w| unsafe { w.bits(r.bits() & !mask) }); | ||
| 219 | } | ||
| 220 | |||
| 221 | pub fn set_fifo_watermark(&mut self, watermark: u8) -> Result<()> { | ||
| 222 | if watermark > 0b111 { | ||
| 223 | return Err(Error::InvalidConfig); | ||
| 224 | } | ||
| 225 | self.info.fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(watermark) }); | ||
| 226 | Ok(()) | ||
| 227 | } | ||
| 228 | |||
| 229 | /// Trigger ADC conversion(s) via software. | ||
| 230 | /// | ||
| 231 | /// Initiates conversion(s) for the trigger(s) specified in the bitmask. | ||
| 232 | /// Each bit in the mask corresponds to a trigger ID (bit 0 = trigger 0, etc.). | ||
| 233 | /// | ||
| 234 | /// # Arguments | ||
| 235 | /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N) | ||
| 236 | /// | ||
| 237 | /// # Returns | ||
| 238 | /// * `Ok(())` if the triger mask was valid | ||
| 239 | /// * `Err(Error::InvalidConfig)` if the mask was greater than `0b1111` | ||
| 240 | pub fn do_software_trigger(&self, trigger_id_mask: u8) -> Result<()> { | ||
| 241 | if trigger_id_mask > 0b1111 { | ||
| 242 | return Err(Error::InvalidConfig); | ||
| 243 | } | ||
| 244 | self.info.swtrig().write(|w| unsafe { w.bits(trigger_id_mask as u32) }); | ||
| 245 | Ok(()) | ||
| 246 | } | ||
| 247 | |||
| 248 | /// Set conversion command configuration. | ||
| 249 | /// | ||
| 250 | /// Configures a conversion command slot with the specified parameters. | ||
| 251 | /// Commands define how conversions are performed (channel, resolution, etc.). | ||
| 252 | /// | ||
| 253 | /// # Arguments | ||
| 254 | /// * `index` - Command index (Must be in range 1..=7) | ||
| 255 | /// * `config` - Command configuration | ||
| 256 | /// | ||
| 257 | /// # Returns | ||
| 258 | /// * `Ok(())` if the command was configured successfully | ||
| 259 | /// * `Err(Error::InvalidConfig)` if the index is out of range | ||
| 260 | pub fn set_conv_command_config(&self, index: usize, config: &ConvCommandConfig) -> Result<()> { | ||
| 261 | self.set_conv_command_config_inner(index, config) | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Set conversion trigger configuration. | ||
| 265 | /// | ||
| 266 | /// Configures a trigger to initiate conversions. Triggers can be | ||
| 267 | /// activated by software or hardware signals. | ||
| 268 | /// | ||
| 269 | /// # Arguments | ||
| 270 | /// * `trigger_id` - Trigger index (0..=3) | ||
| 271 | /// * `config` - Trigger configuration | ||
| 272 | pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) -> Result<()> { | ||
| 273 | self.set_conv_trigger_config_inner(trigger_id, config) | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Reset the FIFO buffer. | ||
| 277 | /// | ||
| 278 | /// Clears all pending conversion results from the FIFO. | ||
| 279 | pub fn do_reset_fifo(&self) { | ||
| 280 | self.info.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | ||
| 281 | } | ||
| 282 | |||
| 283 | /// Get conversion result from FIFO. | ||
| 284 | /// | ||
| 285 | /// Reads and returns the next conversion result from the FIFO. | ||
| 286 | /// Returns `None` if the FIFO is empty. | ||
| 287 | /// | ||
| 288 | /// # Returns | ||
| 289 | /// - `Some(ConvResult)` if a result is available | ||
| 290 | /// - `Err(Error::FifoEmpty)` if the FIFO is empty | ||
| 291 | pub fn get_conv_result(&self) -> Result<ConvResult> { | ||
| 292 | self.get_conv_result_inner() | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | impl<'a> Adc<'a, Async> { | ||
| 297 | /// Initialize ADC with interrupt support. | ||
| 298 | /// | ||
| 299 | /// # Arguments | ||
| 300 | /// * `_inst` - ADC peripheral instance | ||
| 301 | /// * `pin` - GPIO pin to use for ADC | ||
| 302 | /// * `_irq` - Interrupt binding for this ADC instance | ||
| 303 | /// * `config` - ADC configuration | ||
| 304 | pub fn new_async<I: Instance>( | ||
| 305 | _inst: Peri<'a, I>, | ||
| 306 | pin: Peri<'a, impl AdcPin<I>>, | ||
| 307 | _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a, | ||
| 308 | config: LpadcConfig, | ||
| 309 | ) -> Result<Self> { | ||
| 310 | let adc = Self::new_inner(_inst, pin, config, Async { waiter: I::wait_cell() })?; | ||
| 311 | |||
| 312 | I::Interrupt::unpend(); | ||
| 313 | unsafe { I::Interrupt::enable() }; | ||
| 314 | |||
| 315 | let cfg = ConvCommandConfig { | ||
| 316 | chained_next_command_number: Next::NoNextCmdTerminateOnFinish, | ||
| 317 | enable_auto_channel_increment: false, | ||
| 318 | loop_count: 0, | ||
| 319 | hardware_average_mode: Avgs::NoAverage, | ||
| 320 | sample_time_mode: Sts::Sample3p5, | ||
| 321 | hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult, | ||
| 322 | hardware_compare_value_high: 0, | ||
| 323 | hardware_compare_value_low: 0, | ||
| 324 | conversion_resolution_mode: Mode::Data16Bits, | ||
| 325 | enable_wait_trigger: false, | ||
| 326 | }; | ||
| 327 | |||
| 328 | // We always use command 1, so this cannot fail | ||
| 329 | _ = adc.set_conv_command_config_inner(1, &cfg); | ||
| 330 | |||
| 331 | let cfg = ConvTriggerConfig { | ||
| 332 | target_command_id: Tcmd::ExecuteCmd1, | ||
| 333 | delay_power: 0, | ||
| 334 | priority: Tpri::HighestPriority, | ||
| 335 | enable_hardware_trigger: false, | ||
| 336 | }; | ||
| 337 | |||
| 338 | // We always use trigger 0, so this cannot fail | ||
| 339 | _ = adc.set_conv_trigger_config_inner(0, &cfg); | ||
| 340 | |||
| 341 | // We always set the watermark to 0 (trigger when 1 is available) | ||
| 342 | I::ptr().fctrl0().modify(|_r, w| unsafe { w.fwmark().bits(0) }); | ||
| 343 | |||
| 344 | Ok(adc) | ||
| 345 | } | ||
| 346 | |||
| 347 | /// Set the number of averages | ||
| 348 | pub fn set_averages(&mut self, avgs: Avgs) { | ||
| 349 | // TODO: we should probably return a result or wait for idle? | ||
| 350 | // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." | ||
| 351 | self.info.cmdh1().modify(|_r, w| w.avgs().variant(avgs)); | ||
| 352 | } | ||
| 353 | |||
| 354 | /// Set the sample time | ||
| 355 | pub fn set_sample_time(&mut self, st: Sts) { | ||
| 356 | // TODO: we should probably return a result or wait for idle? | ||
| 357 | // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." | ||
| 358 | self.info.cmdh1().modify(|_r, w| w.sts().variant(st)); | ||
| 359 | } | ||
| 360 | |||
| 361 | pub fn set_resolution(&mut self, mode: Mode) { | ||
| 362 | // TODO: we should probably return a result or wait for idle? | ||
| 363 | // "A write to a CMD buffer while that CMD buffer is controlling the ADC operation may cause unpredictable behavior." | ||
| 364 | self.info.cmdl1().modify(|_r, w| w.mode().variant(mode)); | ||
| 365 | } | ||
| 366 | |||
| 367 | fn wait_idle(&mut self) -> impl Future<Output = core::result::Result<(), maitake_sync::Closed>> + use<'_> { | ||
| 368 | self.mode | ||
| 369 | .waiter | ||
| 370 | .wait_for(|| self.info.ie().read().fwmie0().bit_is_clear()) | ||
| 371 | } | ||
| 372 | |||
| 373 | /// Read ADC value asynchronously. | ||
| 374 | /// | ||
| 375 | /// Performs a single ADC conversion and returns the result when the ADC interrupt is triggered. | ||
| 376 | /// | ||
| 377 | /// The function: | ||
| 378 | /// 1. Enables the FIFO watermark interrupt | ||
| 379 | /// 2. Triggers a software conversion on trigger 0 | ||
| 380 | /// 3. Waits for the conversion to complete | ||
| 381 | /// 4. Returns the conversion result | ||
| 382 | /// | ||
| 383 | /// # Returns | ||
| 384 | /// 16-bit ADC conversion value | ||
| 385 | pub async fn read(&mut self) -> Result<u16> { | ||
| 386 | // If we cancelled a previous read, we might still be busy, wait | ||
| 387 | // until the interrupt is cleared (done by the interrupt) | ||
| 388 | _ = self.wait_idle().await; | ||
| 389 | |||
| 390 | // Clear the fifo | ||
| 391 | self.info.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | ||
| 392 | |||
| 393 | // Trigger a new conversion | ||
| 394 | self.info.ie().modify(|_r, w| w.fwmie0().set_bit()); | ||
| 395 | self.info.swtrig().write(|w| w.swt0().set_bit()); | ||
| 396 | |||
| 397 | // Wait for completion | ||
| 398 | _ = self.wait_idle().await; | ||
| 399 | |||
| 400 | self.get_conv_result_inner().map(|r| r.conv_value) | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | impl<'a, M: ModeAdc> Adc<'a, M> { | ||
| 405 | /// Internal initialization function shared by `new_async` and `new_blocking`. | ||
| 406 | fn new_inner<I: Instance, P: AdcPin<I>>( | ||
| 407 | _inst: Peri<'a, I>, | ||
| 408 | pin: Peri<'a, P>, | ||
| 409 | config: LpadcConfig, | ||
| 410 | mode: M, | ||
| 411 | ) -> Result<Self> { | ||
| 412 | let adc = I::ptr(); | ||
| 413 | |||
| 414 | _ = unsafe { | ||
| 415 | enable_and_reset::<I>(&AdcConfig { | ||
| 416 | power: config.power, | ||
| 417 | source: config.source, | ||
| 418 | div: config.div, | ||
| 419 | }) | ||
| 420 | .map_err(Error::ClockSetup)? | ||
| 421 | }; | ||
| 422 | |||
| 423 | pin.mux(); | ||
| 424 | |||
| 425 | /* Reset the module. */ | ||
| 426 | adc.ctrl().modify(|_, w| w.rst().held_in_reset()); | ||
| 427 | adc.ctrl().modify(|_, w| w.rst().released_from_reset()); | ||
| 428 | |||
| 429 | adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset()); | ||
| 430 | |||
| 431 | /* Disable the module before setting configuration. */ | ||
| 432 | adc.ctrl().modify(|_, w| w.adcen().disabled()); | ||
| 433 | |||
| 434 | /* Configure the module generally. */ | ||
| 435 | adc.ctrl().modify(|_, w| w.dozen().bit(config.enable_in_doze_mode)); | ||
| 436 | |||
| 437 | /* Set calibration average mode. */ | ||
| 438 | adc.ctrl() | ||
| 439 | .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode)); | ||
| 440 | |||
| 441 | adc.cfg().write(|w| unsafe { | ||
| 442 | w.pwren().bit(config.enable_analog_preliminary); | ||
| 443 | |||
| 444 | w.pudly() | ||
| 445 | .bits(config.power_up_delay) | ||
| 446 | .refsel() | ||
| 447 | .variant(config.reference_voltage_source) | ||
| 448 | .pwrsel() | ||
| 449 | .variant(config.power_level_mode) | ||
| 450 | .tprictrl() | ||
| 451 | .variant(match config.trigger_priority_policy { | ||
| 452 | TriggerPriorityPolicy::ConvPreemptSoftlyNotAutoResumed | ||
| 453 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted | ||
| 454 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed => Tprictrl::FinishCurrentOnPriority, | ||
| 455 | TriggerPriorityPolicy::ConvPreemptSubsequentlyNotAutoResumed | ||
| 456 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted | ||
| 457 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tprictrl::FinishSequenceOnPriority, | ||
| 458 | _ => Tprictrl::AbortCurrentOnPriority, | ||
| 459 | }) | ||
| 460 | .tres() | ||
| 461 | .variant(match config.trigger_priority_policy { | ||
| 462 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoRestarted | ||
| 463 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted | ||
| 464 | | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed | ||
| 465 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed | ||
| 466 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted | ||
| 467 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tres::Enabled, | ||
| 468 | _ => Tres::Disabled, | ||
| 469 | }) | ||
| 470 | .tcmdres() | ||
| 471 | .variant(match config.trigger_priority_policy { | ||
| 472 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed | ||
| 473 | | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed | ||
| 474 | | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed | ||
| 475 | | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => Tcmdres::Enabled, | ||
| 476 | _ => Tcmdres::Disabled, | ||
| 477 | }) | ||
| 478 | .hpt_exdi() | ||
| 479 | .variant(match config.trigger_priority_policy { | ||
| 480 | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => HptExdi::Disabled, | ||
| 481 | _ => HptExdi::Enabled, | ||
| 482 | }) | ||
| 483 | }); | ||
| 484 | |||
| 485 | if config.enable_conv_pause { | ||
| 486 | adc.pause() | ||
| 487 | .modify(|_, w| unsafe { w.pauseen().enabled().pausedly().bits(config.conv_pause_delay) }); | ||
| 488 | } else { | ||
| 489 | adc.pause().write(|w| unsafe { w.bits(0) }); | ||
| 490 | } | ||
| 491 | |||
| 492 | adc.fctrl0().write(|w| unsafe { w.fwmark().bits(0) }); | ||
| 493 | |||
| 494 | // Enable ADC | ||
| 495 | adc.ctrl().modify(|_, w| w.adcen().enabled()); | ||
| 496 | |||
| 497 | Ok(Self { | ||
| 498 | _inst: PhantomData, | ||
| 499 | mode, | ||
| 500 | channel_idx: P::CHANNEL, | ||
| 501 | info: adc, | ||
| 502 | }) | ||
| 503 | } | ||
| 504 | |||
| 505 | /// Perform offset calibration. | ||
| 506 | /// Waits for calibration to complete before returning. | ||
| 507 | pub fn do_offset_calibration(&self) { | ||
| 508 | // Enable calibration mode | ||
| 509 | self.info | ||
| 510 | .ctrl() | ||
| 511 | .modify(|_, w| w.calofs().offset_calibration_request_pending()); | ||
| 512 | |||
| 513 | // Wait for calibration to complete (polling status register) | ||
| 514 | while self.info.stat().read().cal_rdy().is_not_set() {} | ||
| 515 | } | ||
| 516 | |||
| 517 | /// Calculate gain conversion result from gain adjustment factor. | ||
| 518 | /// | ||
| 519 | /// # Arguments | ||
| 520 | /// * `gain_adjustment` - Gain adjustment factor | ||
| 521 | /// | ||
| 522 | /// # Returns | ||
| 523 | /// Gain calibration register value | ||
| 524 | pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 { | ||
| 525 | let mut gcra_array = [0u32; 17]; | ||
| 526 | let mut gcalr: u32 = 0; | ||
| 527 | |||
| 528 | for i in (1..=17).rev() { | ||
| 529 | let shift = 16 - (i - 1); | ||
| 530 | let step = 1.0 / (1u32 << shift) as f32; | ||
| 531 | let tmp = (gain_adjustment / step) as u32; | ||
| 532 | gcra_array[i - 1] = tmp; | ||
| 533 | gain_adjustment -= tmp as f32 * step; | ||
| 534 | } | ||
| 535 | |||
| 536 | for i in (1..=17).rev() { | ||
| 537 | gcalr += gcra_array[i - 1] << (i - 1); | ||
| 538 | } | ||
| 539 | gcalr | ||
| 540 | } | ||
| 541 | |||
| 542 | /// Perform automatic gain calibration. | ||
| 543 | pub fn do_auto_calibration(&self) { | ||
| 544 | self.info | ||
| 545 | .ctrl() | ||
| 546 | .modify(|_, w| w.cal_req().calibration_request_pending()); | ||
| 547 | |||
| 548 | while self.info.gcc0().read().rdy().is_gain_cal_not_valid() {} | ||
| 549 | |||
| 550 | let mut gcca = self.info.gcc0().read().gain_cal().bits() as u32; | ||
| 551 | if gcca & 0x8000 != 0 { | ||
| 552 | gcca |= !0xFFFF; | ||
| 553 | } | ||
| 554 | |||
| 555 | let gcra = 131072.0 / (131072.0 - gcca as f32); | ||
| 556 | |||
| 557 | // Write to GCR0 | ||
| 558 | self.info | ||
| 559 | .gcr0() | ||
| 560 | .write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) }); | ||
| 561 | |||
| 562 | self.info.gcr0().modify(|_, w| w.rdy().set_bit()); | ||
| 563 | |||
| 564 | // Wait for calibration to complete (polling status register) | ||
| 565 | while self.info.stat().read().cal_rdy().is_not_set() {} | ||
| 566 | } | ||
| 567 | |||
| 568 | fn set_conv_command_config_inner(&self, index: usize, config: &ConvCommandConfig) -> Result<()> { | ||
| 569 | let (cmdl, cmdh) = match index { | ||
| 570 | 1 => (self.info.cmdl1(), self.info.cmdh1()), | ||
| 571 | 2 => (self.info.cmdl2(), self.info.cmdh2()), | ||
| 572 | 3 => (self.info.cmdl3(), self.info.cmdh3()), | ||
| 573 | 4 => (self.info.cmdl4(), self.info.cmdh4()), | ||
| 574 | 5 => (self.info.cmdl5(), self.info.cmdh5()), | ||
| 575 | 6 => (self.info.cmdl6(), self.info.cmdh6()), | ||
| 576 | 7 => (self.info.cmdl7(), self.info.cmdh7()), | ||
| 577 | _ => return Err(Error::InvalidConfig), | ||
| 578 | }; | ||
| 579 | |||
| 580 | cmdl.write(|w| { | ||
| 581 | unsafe { | ||
| 582 | w.adch().bits(self.channel_idx); | ||
| 583 | } | ||
| 584 | w.mode().variant(config.conversion_resolution_mode) | ||
| 585 | }); | ||
| 586 | |||
| 587 | cmdh.write(|w| { | ||
| 588 | w.next().variant(config.chained_next_command_number); | ||
| 589 | unsafe { | ||
| 590 | w.loop_().bits(config.loop_count); | ||
| 591 | } | ||
| 592 | w.avgs().variant(config.hardware_average_mode); | ||
| 593 | w.sts().variant(config.sample_time_mode); | ||
| 594 | w.cmpen().variant(config.hardware_compare_mode); | ||
| 595 | w.wait_trig().bit(config.enable_wait_trigger); | ||
| 596 | w.lwi().bit(config.enable_auto_channel_increment); | ||
| 597 | w | ||
| 598 | }); | ||
| 599 | |||
| 600 | Ok(()) | ||
| 601 | } | ||
| 602 | |||
| 603 | fn set_conv_trigger_config_inner(&self, trigger_id: usize, config: &ConvTriggerConfig) -> Result<()> { | ||
| 604 | // 0..4 are valid | ||
| 605 | if trigger_id >= 4 { | ||
| 606 | return Err(Error::InvalidConfig); | ||
| 607 | } | ||
| 608 | |||
| 609 | let tctrl = &self.info.tctrl(trigger_id); | ||
| 610 | |||
| 611 | tctrl.write(|w| { | ||
| 612 | w.tcmd().variant(config.target_command_id); | ||
| 613 | unsafe { | ||
| 614 | w.tdly().bits(config.delay_power); | ||
| 615 | } | ||
| 616 | w.tpri().variant(config.priority); | ||
| 617 | if config.enable_hardware_trigger { | ||
| 618 | w.hten().enabled() | ||
| 619 | } else { | ||
| 620 | w | ||
| 621 | } | ||
| 622 | }); | ||
| 623 | |||
| 624 | Ok(()) | ||
| 625 | } | ||
| 626 | |||
| 627 | /// Get conversion result from FIFO. | ||
| 628 | /// | ||
| 629 | /// Reads and returns the next conversion result from the FIFO. | ||
| 630 | /// Returns `None` if the FIFO is empty. | ||
| 631 | /// | ||
| 632 | /// # Returns | ||
| 633 | /// - `Some(ConvResult)` if a result is available | ||
| 634 | /// - `Err(Error::FifoEmpty)` if the FIFO is empty | ||
| 635 | fn get_conv_result_inner(&self) -> Result<ConvResult> { | ||
| 636 | let fifo = self.info.resfifo0().read(); | ||
| 637 | if !fifo.valid().is_valid() { | ||
| 638 | return Err(Error::FifoEmpty); | ||
| 639 | } | ||
| 640 | |||
| 641 | Ok(ConvResult { | ||
| 642 | command_id_source: fifo.cmdsrc().bits(), | ||
| 643 | loop_count_index: fifo.loopcnt().bits(), | ||
| 644 | trigger_id_source: fifo.tsrc().bits(), | ||
| 645 | conv_value: fifo.d().bits(), | ||
| 646 | }) | ||
| 647 | } | ||
| 648 | } | ||
| 649 | |||
| 650 | impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> { | ||
| 651 | unsafe fn on_interrupt() { | ||
| 652 | T::ptr().ie().modify(|_r, w| w.fwmie0().clear_bit()); | ||
| 653 | T::wait_cell().wake(); | ||
| 654 | } | ||
| 655 | } | ||
| 656 | |||
| 657 | mod sealed { | ||
| 658 | /// Seal a trait | ||
| 659 | pub trait Sealed {} | ||
| 660 | } | ||
| 661 | |||
| 662 | impl<I: GpioPin> sealed::Sealed for I {} | ||
| 663 | |||
| 664 | trait SealedInstance { | ||
| 665 | fn ptr() -> &'static pac::adc0::RegisterBlock; | ||
| 666 | fn wait_cell() -> &'static WaitCell; | ||
| 667 | } | ||
| 668 | |||
| 669 | /// ADC Instance | ||
| 670 | #[allow(private_bounds)] | ||
| 671 | pub trait Instance: SealedInstance + PeripheralType + Gate<MrccPeriphConfig = AdcConfig> { | ||
| 672 | /// Interrupt for this ADC instance. | ||
| 673 | type Interrupt: Interrupt; | ||
| 674 | } | ||
| 675 | |||
| 676 | macro_rules! impl_instance { | ||
| 677 | ($($n:expr),*) => { | ||
| 678 | $( | ||
| 679 | paste!{ | ||
| 680 | impl SealedInstance for crate::peripherals::[<ADC $n>] { | ||
| 681 | fn ptr() -> &'static pac::adc0::RegisterBlock { | ||
| 682 | unsafe { &*pac::[<Adc $n>]::ptr() } | ||
| 683 | } | ||
| 684 | |||
| 685 | fn wait_cell() -> &'static WaitCell { | ||
| 686 | static WAIT_CELL: WaitCell = WaitCell::new(); | ||
| 687 | &WAIT_CELL | ||
| 688 | } | ||
| 689 | |||
| 690 | } | ||
| 691 | |||
| 692 | impl Instance for crate::peripherals::[<ADC $n>] { | ||
| 693 | type Interrupt = crate::interrupt::typelevel::[<ADC $n>]; | ||
| 694 | } | ||
| 695 | } | ||
| 696 | )* | ||
| 697 | }; | ||
| 698 | } | ||
| 699 | |||
| 700 | impl_instance!(0, 1, 2, 3); | ||
| 701 | |||
| 702 | pub trait AdcPin<Instance>: GpioPin + sealed::Sealed + PeripheralType { | ||
| 703 | const CHANNEL: u8; | ||
| 704 | |||
| 705 | /// Set the given pin to the correct muxing state | ||
| 706 | fn mux(&self); | ||
| 707 | } | ||
| 708 | |||
| 709 | /// Driver mode. | ||
| 710 | #[allow(private_bounds)] | ||
| 711 | pub trait ModeAdc: sealed::Sealed {} | ||
| 712 | |||
| 713 | /// Blocking mode. | ||
| 714 | pub struct Blocking; | ||
| 715 | impl sealed::Sealed for Blocking {} | ||
| 716 | impl ModeAdc for Blocking {} | ||
| 717 | |||
| 718 | /// Async mode. | ||
| 719 | pub struct Async { | ||
| 720 | waiter: &'static WaitCell, | ||
| 721 | } | ||
| 722 | impl sealed::Sealed for Async {} | ||
| 723 | impl ModeAdc for Async {} | ||
| 724 | |||
| 725 | macro_rules! impl_pin { | ||
| 726 | ($pin:ident, $peri:ident, $func:ident, $channel:literal) => { | ||
| 727 | impl AdcPin<crate::peripherals::$peri> for crate::peripherals::$pin { | ||
| 728 | const CHANNEL: u8 = $channel; | ||
| 729 | |||
| 730 | fn mux(&self) { | ||
| 731 | self.set_pull(crate::gpio::Pull::Disabled); | ||
| 732 | self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); | ||
| 733 | self.set_drive_strength(crate::gpio::DriveStrength::Normal.into()); | ||
| 734 | self.set_function(crate::pac::port0::pcr0::Mux::$func); | ||
| 735 | } | ||
| 736 | } | ||
| 737 | }; | ||
| 738 | } | ||
| 739 | |||
| 740 | impl_pin!(P2_0, ADC0, Mux0, 0); | ||
| 741 | impl_pin!(P2_4, ADC0, Mux0, 1); | ||
| 742 | impl_pin!(P2_15, ADC0, Mux0, 2); | ||
| 743 | impl_pin!(P2_3, ADC0, Mux0, 3); | ||
| 744 | impl_pin!(P2_2, ADC0, Mux0, 4); | ||
| 745 | impl_pin!(P2_12, ADC0, Mux0, 5); | ||
| 746 | impl_pin!(P2_16, ADC0, Mux0, 6); | ||
| 747 | impl_pin!(P2_7, ADC0, Mux0, 7); | ||
| 748 | impl_pin!(P0_18, ADC0, Mux0, 8); | ||
| 749 | impl_pin!(P0_19, ADC0, Mux0, 9); | ||
| 750 | impl_pin!(P0_20, ADC0, Mux0, 10); | ||
| 751 | impl_pin!(P0_21, ADC0, Mux0, 11); | ||
| 752 | impl_pin!(P0_22, ADC0, Mux0, 12); | ||
| 753 | impl_pin!(P0_23, ADC0, Mux0, 13); | ||
| 754 | impl_pin!(P0_3, ADC0, Mux0, 14); | ||
| 755 | impl_pin!(P0_6, ADC0, Mux0, 15); | ||
| 756 | impl_pin!(P1_0, ADC0, Mux0, 16); | ||
| 757 | impl_pin!(P1_1, ADC0, Mux0, 17); | ||
| 758 | impl_pin!(P1_2, ADC0, Mux0, 18); | ||
| 759 | impl_pin!(P1_3, ADC0, Mux0, 19); | ||
| 760 | impl_pin!(P1_4, ADC0, Mux0, 20); | ||
| 761 | impl_pin!(P1_5, ADC0, Mux0, 21); | ||
| 762 | impl_pin!(P1_6, ADC0, Mux0, 22); | ||
| 763 | impl_pin!(P1_7, ADC0, Mux0, 23); | ||
| 764 | |||
| 765 | // ??? | ||
| 766 | // impl_pin!(P1_10, ADC0, Mux0, 255); | ||
| 767 | |||
| 768 | impl_pin!(P2_1, ADC1, Mux0, 0); | ||
| 769 | impl_pin!(P2_5, ADC1, Mux0, 1); | ||
| 770 | impl_pin!(P2_19, ADC1, Mux0, 2); | ||
| 771 | impl_pin!(P2_6, ADC1, Mux0, 3); | ||
| 772 | impl_pin!(P2_3, ADC1, Mux0, 4); | ||
| 773 | impl_pin!(P2_13, ADC1, Mux0, 5); | ||
| 774 | impl_pin!(P2_17, ADC1, Mux0, 6); | ||
| 775 | impl_pin!(P2_7, ADC1, Mux0, 7); | ||
| 776 | impl_pin!(P1_10, ADC1, Mux0, 8); | ||
| 777 | impl_pin!(P1_11, ADC1, Mux0, 9); | ||
| 778 | impl_pin!(P1_12, ADC1, Mux0, 10); | ||
| 779 | impl_pin!(P1_13, ADC1, Mux0, 11); | ||
| 780 | impl_pin!(P1_14, ADC1, Mux0, 12); | ||
| 781 | impl_pin!(P1_15, ADC1, Mux0, 13); | ||
| 782 | // ??? | ||
| 783 | // impl_pin!(P1_16, ADC1, Mux0, 255); | ||
| 784 | // impl_pin!(P1_17, ADC1, Mux0, 255); | ||
| 785 | // impl_pin!(P1_18, ADC1, Mux0, 255); | ||
| 786 | // impl_pin!(P1_19, ADC1, Mux0, 255); | ||
| 787 | // ??? | ||
| 788 | impl_pin!(P3_31, ADC1, Mux0, 20); | ||
| 789 | impl_pin!(P3_30, ADC1, Mux0, 21); | ||
| 790 | impl_pin!(P3_29, ADC1, Mux0, 22); | ||
| 791 | |||
| 792 | impl_pin!(P2_4, ADC2, Mux0, 0); | ||
| 793 | impl_pin!(P2_10, ADC2, Mux0, 1); | ||
| 794 | impl_pin!(P4_4, ADC2, Mux0, 2); | ||
| 795 | // impl_pin!(P2_24, ADC2, Mux0, 255); ??? | ||
| 796 | impl_pin!(P2_16, ADC2, Mux0, 4); | ||
| 797 | impl_pin!(P2_12, ADC2, Mux0, 5); | ||
| 798 | impl_pin!(P2_20, ADC2, Mux0, 6); | ||
| 799 | impl_pin!(P2_7, ADC2, Mux0, 7); | ||
| 800 | impl_pin!(P0_2, ADC2, Mux0, 8); | ||
| 801 | // ??? | ||
| 802 | // impl_pin!(P0_4, ADC2, Mux0, 255); | ||
| 803 | // impl_pin!(P0_5, ADC2, Mux0, 255); | ||
| 804 | // impl_pin!(P0_6, ADC2, Mux0, 255); | ||
| 805 | // impl_pin!(P0_7, ADC2, Mux0, 255); | ||
| 806 | // impl_pin!(P0_12, ADC2, Mux0, 255); | ||
| 807 | // impl_pin!(P0_13, ADC2, Mux0, 255); | ||
| 808 | // ??? | ||
| 809 | impl_pin!(P0_14, ADC2, Mux0, 14); | ||
| 810 | impl_pin!(P0_15, ADC2, Mux0, 15); | ||
| 811 | // ??? | ||
| 812 | // impl_pin!(P4_0, ADC2, Mux0, 255); | ||
| 813 | // impl_pin!(P4_1, ADC2, Mux0, 255); | ||
| 814 | // ??? | ||
| 815 | impl_pin!(P4_2, ADC2, Mux0, 18); | ||
| 816 | impl_pin!(P4_3, ADC2, Mux0, 19); | ||
| 817 | //impl_pin!(P4_4, ADC2, Mux0, 20); // Conflit with ADC2_A3 and ADC2_A20 using the same pin | ||
| 818 | impl_pin!(P4_5, ADC2, Mux0, 21); | ||
| 819 | impl_pin!(P4_6, ADC2, Mux0, 22); | ||
| 820 | impl_pin!(P4_7, ADC2, Mux0, 23); | ||
| 821 | |||
| 822 | impl_pin!(P2_5, ADC3, Mux0, 0); | ||
| 823 | impl_pin!(P2_11, ADC3, Mux0, 1); | ||
| 824 | impl_pin!(P2_23, ADC3, Mux0, 2); | ||
| 825 | // impl_pin!(P2_25, ADC3, Mux0, 255); // ??? | ||
| 826 | impl_pin!(P2_17, ADC3, Mux0, 4); | ||
| 827 | impl_pin!(P2_13, ADC3, Mux0, 5); | ||
| 828 | impl_pin!(P2_21, ADC3, Mux0, 6); | ||
| 829 | impl_pin!(P2_7, ADC3, Mux0, 7); | ||
| 830 | // ??? | ||
| 831 | // impl_pin!(P3_2, ADC3, Mux0, 255); | ||
| 832 | // impl_pin!(P3_3, ADC3, Mux0, 255); | ||
| 833 | // impl_pin!(P3_4, ADC3, Mux0, 255); | ||
| 834 | // impl_pin!(P3_5, ADC3, Mux0, 255); | ||
| 835 | // ??? | ||
| 836 | impl_pin!(P3_6, ADC3, Mux0, 12); | ||
| 837 | impl_pin!(P3_7, ADC3, Mux0, 13); | ||
| 838 | impl_pin!(P3_12, ADC3, Mux0, 14); | ||
| 839 | impl_pin!(P3_13, ADC3, Mux0, 15); | ||
| 840 | impl_pin!(P3_14, ADC3, Mux0, 16); | ||
| 841 | impl_pin!(P3_15, ADC3, Mux0, 17); | ||
| 842 | impl_pin!(P3_20, ADC3, Mux0, 18); | ||
| 843 | impl_pin!(P3_21, ADC3, Mux0, 19); | ||
| 844 | impl_pin!(P3_22, ADC3, Mux0, 20); | ||
| 845 | // ??? | ||
| 846 | // impl_pin!(P3_23, ADC3, Mux0, 255); | ||
| 847 | // impl_pin!(P3_24, ADC3, Mux0, 255); | ||
| 848 | // impl_pin!(P3_25, ADC3, Mux0, 255); | ||
| 849 | // ??? | ||
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..62789f85f --- /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::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig}; | ||
| 12 | use crate::clocks::{PoweredClock, enable_and_reset}; | ||
| 13 | use crate::gpio::AnyPin; | ||
| 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..22bc40e35 --- /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 reset_reason; | ||
| 21 | pub mod rtc; | ||
| 22 | |||
| 23 | use crate::interrupt::InterruptExt; | ||
| 24 | pub use crate::pac::NVIC_PRIO_BITS; | ||
| 25 | |||
| 26 | #[rustfmt::skip] | ||
| 27 | embassy_hal_internal::peripherals!( | ||
| 28 | ADC0, | ||
| 29 | ADC1, | ||
| 30 | ADC2, | ||
| 31 | ADC3, | ||
| 32 | |||
| 33 | AOI0, | ||
| 34 | AOI1, | ||
| 35 | |||
| 36 | CAN0, | ||
| 37 | CAN1, | ||
| 38 | |||
| 39 | CDOG0, | ||
| 40 | CDOG1, | ||
| 41 | |||
| 42 | // CLKOUT is not specifically a peripheral (it's part of SYSCON), | ||
| 43 | // but we still want it to be a singleton. | ||
| 44 | CLKOUT, | ||
| 45 | |||
| 46 | CMC, | ||
| 47 | CMP0, | ||
| 48 | CMP1, | ||
| 49 | CRC0, | ||
| 50 | |||
| 51 | CTIMER0, | ||
| 52 | CTIMER1, | ||
| 53 | CTIMER2, | ||
| 54 | CTIMER3, | ||
| 55 | CTIMER4, | ||
| 56 | |||
| 57 | DBGMAILBOX, | ||
| 58 | DMA0, | ||
| 59 | DMA_CH0, | ||
| 60 | DMA_CH1, | ||
| 61 | DMA_CH2, | ||
| 62 | DMA_CH3, | ||
| 63 | DMA_CH4, | ||
| 64 | DMA_CH5, | ||
| 65 | DMA_CH6, | ||
| 66 | DMA_CH7, | ||
| 67 | EDMA0_TCD0, | ||
| 68 | EIM0, | ||
| 69 | EQDC0, | ||
| 70 | EQDC1, | ||
| 71 | ERM0, | ||
| 72 | FLEXIO0, | ||
| 73 | FLEXPWM0, | ||
| 74 | FLEXPWM1, | ||
| 75 | FMC0, | ||
| 76 | FMU0, | ||
| 77 | FREQME0, | ||
| 78 | GLIKEY0, | ||
| 79 | |||
| 80 | GPIO0, | ||
| 81 | GPIO1, | ||
| 82 | GPIO2, | ||
| 83 | GPIO3, | ||
| 84 | GPIO4, | ||
| 85 | |||
| 86 | I3C0, | ||
| 87 | INPUTMUX0, | ||
| 88 | |||
| 89 | LPI2C0, | ||
| 90 | LPI2C1, | ||
| 91 | LPI2C2, | ||
| 92 | LPI2C3, | ||
| 93 | |||
| 94 | LPSPI0, | ||
| 95 | LPSPI1, | ||
| 96 | |||
| 97 | LPTMR0, | ||
| 98 | |||
| 99 | LPUART0, | ||
| 100 | LPUART1, | ||
| 101 | LPUART2, | ||
| 102 | LPUART3, | ||
| 103 | LPUART4, | ||
| 104 | LPUART5, | ||
| 105 | |||
| 106 | MAU0, | ||
| 107 | MBC0, | ||
| 108 | MRCC0, | ||
| 109 | OPAMP0, | ||
| 110 | |||
| 111 | #[cfg(not(feature = "time"))] | ||
| 112 | OSTIMER0, | ||
| 113 | |||
| 114 | P0_0, | ||
| 115 | P0_1, | ||
| 116 | P0_2, | ||
| 117 | P0_3, | ||
| 118 | P0_4, | ||
| 119 | P0_5, | ||
| 120 | P0_6, | ||
| 121 | P0_7, | ||
| 122 | P0_8, | ||
| 123 | P0_9, | ||
| 124 | P0_10, | ||
| 125 | P0_11, | ||
| 126 | P0_12, | ||
| 127 | P0_13, | ||
| 128 | P0_14, | ||
| 129 | P0_15, | ||
| 130 | P0_16, | ||
| 131 | P0_17, | ||
| 132 | P0_18, | ||
| 133 | P0_19, | ||
| 134 | P0_20, | ||
| 135 | P0_21, | ||
| 136 | P0_22, | ||
| 137 | P0_23, | ||
| 138 | P0_24, | ||
| 139 | P0_25, | ||
| 140 | P0_26, | ||
| 141 | P0_27, | ||
| 142 | P0_28, | ||
| 143 | P0_29, | ||
| 144 | P0_30, | ||
| 145 | P0_31, | ||
| 146 | |||
| 147 | P1_0, | ||
| 148 | P1_1, | ||
| 149 | P1_2, | ||
| 150 | P1_3, | ||
| 151 | P1_4, | ||
| 152 | P1_5, | ||
| 153 | P1_6, | ||
| 154 | P1_7, | ||
| 155 | P1_8, | ||
| 156 | P1_9, | ||
| 157 | P1_10, | ||
| 158 | P1_11, | ||
| 159 | P1_12, | ||
| 160 | P1_13, | ||
| 161 | P1_14, | ||
| 162 | P1_15, | ||
| 163 | P1_16, | ||
| 164 | P1_17, | ||
| 165 | P1_18, | ||
| 166 | P1_19, | ||
| 167 | P1_20, | ||
| 168 | P1_21, | ||
| 169 | P1_22, | ||
| 170 | P1_23, | ||
| 171 | P1_24, | ||
| 172 | P1_25, | ||
| 173 | P1_26, | ||
| 174 | P1_27, | ||
| 175 | P1_28, | ||
| 176 | P1_29, | ||
| 177 | P1_30, | ||
| 178 | P1_31, | ||
| 179 | |||
| 180 | P2_0, | ||
| 181 | P2_1, | ||
| 182 | P2_2, | ||
| 183 | P2_3, | ||
| 184 | P2_4, | ||
| 185 | P2_5, | ||
| 186 | P2_6, | ||
| 187 | P2_7, | ||
| 188 | P2_8, | ||
| 189 | P2_9, | ||
| 190 | P2_10, | ||
| 191 | P2_11, | ||
| 192 | P2_12, | ||
| 193 | P2_13, | ||
| 194 | P2_14, | ||
| 195 | P2_15, | ||
| 196 | P2_16, | ||
| 197 | P2_17, | ||
| 198 | P2_18, | ||
| 199 | P2_19, | ||
| 200 | P2_20, | ||
| 201 | P2_21, | ||
| 202 | P2_22, | ||
| 203 | P2_23, | ||
| 204 | P2_24, | ||
| 205 | P2_25, | ||
| 206 | P2_26, | ||
| 207 | P2_27, | ||
| 208 | P2_28, | ||
| 209 | P2_29, | ||
| 210 | P2_30, | ||
| 211 | P2_31, | ||
| 212 | |||
| 213 | P3_0, | ||
| 214 | P3_1, | ||
| 215 | P3_2, | ||
| 216 | P3_3, | ||
| 217 | P3_4, | ||
| 218 | P3_5, | ||
| 219 | P3_6, | ||
| 220 | P3_7, | ||
| 221 | P3_8, | ||
| 222 | P3_9, | ||
| 223 | P3_10, | ||
| 224 | P3_11, | ||
| 225 | P3_12, | ||
| 226 | P3_13, | ||
| 227 | P3_14, | ||
| 228 | P3_15, | ||
| 229 | P3_16, | ||
| 230 | P3_17, | ||
| 231 | P3_18, | ||
| 232 | P3_19, | ||
| 233 | P3_20, | ||
| 234 | P3_21, | ||
| 235 | P3_22, | ||
| 236 | P3_23, | ||
| 237 | P3_24, | ||
| 238 | P3_25, | ||
| 239 | P3_26, | ||
| 240 | P3_27, | ||
| 241 | P3_28, | ||
| 242 | P3_29, | ||
| 243 | P3_30, | ||
| 244 | P3_31, | ||
| 245 | |||
| 246 | P4_0, | ||
| 247 | P4_1, | ||
| 248 | P4_2, | ||
| 249 | P4_3, | ||
| 250 | P4_4, | ||
| 251 | P4_5, | ||
| 252 | P4_6, | ||
| 253 | P4_7, | ||
| 254 | P4_8, | ||
| 255 | P4_9, | ||
| 256 | P4_10, | ||
| 257 | P4_11, | ||
| 258 | P4_12, | ||
| 259 | P4_13, | ||
| 260 | P4_14, | ||
| 261 | P4_15, | ||
| 262 | P4_16, | ||
| 263 | P4_17, | ||
| 264 | P4_18, | ||
| 265 | P4_19, | ||
| 266 | P4_20, | ||
| 267 | P4_21, | ||
| 268 | P4_22, | ||
| 269 | P4_23, | ||
| 270 | P4_24, | ||
| 271 | P4_25, | ||
| 272 | P4_26, | ||
| 273 | P4_27, | ||
| 274 | P4_28, | ||
| 275 | P4_29, | ||
| 276 | P4_30, | ||
| 277 | P4_31, | ||
| 278 | |||
| 279 | P5_0, | ||
| 280 | P5_1, | ||
| 281 | P5_2, | ||
| 282 | P5_3, | ||
| 283 | P5_4, | ||
| 284 | P5_5, | ||
| 285 | P5_6, | ||
| 286 | P5_7, | ||
| 287 | P5_8, | ||
| 288 | P5_9, | ||
| 289 | P5_10, | ||
| 290 | P5_11, | ||
| 291 | P5_12, | ||
| 292 | P5_13, | ||
| 293 | P5_14, | ||
| 294 | P5_15, | ||
| 295 | P5_16, | ||
| 296 | P5_17, | ||
| 297 | P5_18, | ||
| 298 | P5_19, | ||
| 299 | P5_20, | ||
| 300 | P5_21, | ||
| 301 | P5_22, | ||
| 302 | P5_23, | ||
| 303 | P5_24, | ||
| 304 | P5_25, | ||
| 305 | P5_26, | ||
| 306 | P5_27, | ||
| 307 | P5_28, | ||
| 308 | P5_29, | ||
| 309 | P5_30, | ||
| 310 | P5_31, | ||
| 311 | |||
| 312 | PKC0, | ||
| 313 | |||
| 314 | PORT0, | ||
| 315 | PORT1, | ||
| 316 | PORT2, | ||
| 317 | PORT3, | ||
| 318 | PORT4, | ||
| 319 | |||
| 320 | RTC0, | ||
| 321 | SAU, | ||
| 322 | SCG0, | ||
| 323 | SCN_SCB, | ||
| 324 | SGI0, | ||
| 325 | SMARTDMA0, | ||
| 326 | SPC0, | ||
| 327 | SYSCON, | ||
| 328 | TDET0, | ||
| 329 | TRNG0, | ||
| 330 | UDF0, | ||
| 331 | USB0, | ||
| 332 | UTICK0, | ||
| 333 | VBAT0, | ||
| 334 | WAKETIMER0, | ||
| 335 | WUU0, | ||
| 336 | WWDT0, | ||
| 337 | ); | ||
| 338 | |||
| 339 | // Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it. | ||
| 340 | |||
| 341 | // Re-export interrupt traits and types | ||
| 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..bce3986b5 --- /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::{AnyPin, 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::{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/reset_reason.rs b/embassy-mcxa/src/reset_reason.rs new file mode 100644 index 000000000..f9a9ce096 --- /dev/null +++ b/embassy-mcxa/src/reset_reason.rs | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | //! Reset reason | ||
| 2 | //! | ||
| 3 | //! MCXA families keep the most recent reset reason in the SRS | ||
| 4 | //! register of the CMC block. This lets users understand why the MCU | ||
| 5 | //! has reset and take appropriate corrective actions if required. | ||
| 6 | |||
| 7 | /// Reads the most recent reset reason from the Core Mode Controller | ||
| 8 | /// (CMC). | ||
| 9 | pub fn reset_reason() -> ResetReason { | ||
| 10 | let regs = unsafe { &*crate::pac::Cmc::steal() }; | ||
| 11 | |||
| 12 | let srs = regs.srs().read(); | ||
| 13 | |||
| 14 | if srs.wakeup().is_enabled() { | ||
| 15 | ResetReason::WakeUp | ||
| 16 | } else if srs.por().bit_is_set() { | ||
| 17 | ResetReason::Por | ||
| 18 | } else if srs.vd().bit_is_set() { | ||
| 19 | ResetReason::VoltageDetect | ||
| 20 | } else if srs.warm().bit_is_set() { | ||
| 21 | ResetReason::Warm | ||
| 22 | } else if srs.fatal().bit_is_set() { | ||
| 23 | ResetReason::Fatal | ||
| 24 | } else if srs.pin().bit_is_set() { | ||
| 25 | ResetReason::Pin | ||
| 26 | } else if srs.dap().bit_is_set() { | ||
| 27 | ResetReason::Dap | ||
| 28 | } else if srs.rstack().bit_is_set() { | ||
| 29 | ResetReason::ResetAckTimeout | ||
| 30 | } else if srs.lpack().bit_is_set() { | ||
| 31 | ResetReason::LowPowerAckTimeout | ||
| 32 | } else if srs.scg().bit_is_set() { | ||
| 33 | ResetReason::SystemClockGeneration | ||
| 34 | } else if srs.wwdt0().bit_is_set() { | ||
| 35 | ResetReason::Wwdt0 | ||
| 36 | } else if srs.sw().bit_is_set() { | ||
| 37 | ResetReason::Software | ||
| 38 | } else if srs.lockup().bit_is_set() { | ||
| 39 | ResetReason::Lockup | ||
| 40 | } else if srs.cdog0().bit_is_set() { | ||
| 41 | ResetReason::Cdog0 | ||
| 42 | } else if srs.cdog1().bit_is_set() { | ||
| 43 | ResetReason::Cdog1 | ||
| 44 | } else if srs.jtag().bit_is_set() { | ||
| 45 | ResetReason::Jtag | ||
| 46 | } else { | ||
| 47 | ResetReason::Tamper | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Indicates the type and source of the most recent reset. | ||
| 52 | #[derive(Clone, Copy, Debug)] | ||
| 53 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 54 | #[non_exhaustive] | ||
| 55 | pub enum ResetReason { | ||
| 56 | /// Tamper reset. | ||
| 57 | Tamper, | ||
| 58 | |||
| 59 | /// JTAG System Reset request. | ||
| 60 | Jtag, | ||
| 61 | |||
| 62 | /// Code Watchdog 0 reset. | ||
| 63 | Cdog0, | ||
| 64 | |||
| 65 | /// Code Watchdog 1 reset. | ||
| 66 | Cdog1, | ||
| 67 | |||
| 68 | /// Lockup reset. | ||
| 69 | Lockup, | ||
| 70 | |||
| 71 | /// Software reset. | ||
| 72 | Software, | ||
| 73 | |||
| 74 | /// Windowed Watchdog 0 reset. | ||
| 75 | Wwdt0, | ||
| 76 | |||
| 77 | /// System clock generation reset. | ||
| 78 | SystemClockGeneration, | ||
| 79 | |||
| 80 | /// Low Power Acknowledge Timeout reset. | ||
| 81 | LowPowerAckTimeout, | ||
| 82 | |||
| 83 | /// Reset Timeout. | ||
| 84 | ResetAckTimeout, | ||
| 85 | |||
| 86 | /// Debug Access Port reset. | ||
| 87 | Dap, | ||
| 88 | |||
| 89 | /// External assertion of RESET_b pin. | ||
| 90 | Pin, | ||
| 91 | |||
| 92 | /// Fatal reset. | ||
| 93 | Fatal, | ||
| 94 | |||
| 95 | /// Warm reset. | ||
| 96 | Warm, | ||
| 97 | |||
| 98 | /// Voltage detect reset. | ||
| 99 | VoltageDetect, | ||
| 100 | |||
| 101 | /// Power-on reset. | ||
| 102 | Por, | ||
| 103 | |||
| 104 | /// Wake-up reset. | ||
| 105 | WakeUp, | ||
| 106 | } | ||
diff --git a/embassy-mcxa/src/rtc.rs b/embassy-mcxa/src/rtc.rs new file mode 100644 index 000000000..c5474d34a --- /dev/null +++ b/embassy-mcxa/src/rtc.rs | |||
| @@ -0,0 +1,443 @@ | |||
| 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 | /// Global wait cell for alarm notifications | ||
| 13 | static WAKER: WaitCell = WaitCell::new(); | ||
| 14 | |||
| 15 | /// RTC interrupt handler. | ||
| 16 | pub struct InterruptHandler<I: Instance> { | ||
| 17 | _phantom: PhantomData<I>, | ||
| 18 | } | ||
| 19 | |||
| 20 | /// Trait for RTC peripheral instances | ||
| 21 | pub trait Instance: PeripheralType { | ||
| 22 | type Interrupt: Interrupt; | ||
| 23 | fn ptr() -> &'static pac::rtc0::RegisterBlock; | ||
| 24 | } | ||
| 25 | |||
| 26 | /// Token for RTC0 | ||
| 27 | pub type Rtc0 = crate::peripherals::RTC0; | ||
| 28 | impl Instance for crate::peripherals::RTC0 { | ||
| 29 | type Interrupt = crate::interrupt::typelevel::RTC; | ||
| 30 | #[inline(always)] | ||
| 31 | fn ptr() -> &'static pac::rtc0::RegisterBlock { | ||
| 32 | unsafe { &*pac::Rtc0::ptr() } | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | /// Number of days in a standard year | ||
| 37 | const DAYS_IN_A_YEAR: u32 = 365; | ||
| 38 | /// Number of seconds in a day | ||
| 39 | const SECONDS_IN_A_DAY: u32 = 86400; | ||
| 40 | /// Number of seconds in an hour | ||
| 41 | const SECONDS_IN_A_HOUR: u32 = 3600; | ||
| 42 | /// Number of seconds in a minute | ||
| 43 | const SECONDS_IN_A_MINUTE: u32 = 60; | ||
| 44 | /// Unix epoch start year | ||
| 45 | const YEAR_RANGE_START: u16 = 1970; | ||
| 46 | |||
| 47 | /// Date and time structure for RTC operations | ||
| 48 | #[derive(Debug, Clone, Copy)] | ||
| 49 | pub struct RtcDateTime { | ||
| 50 | pub year: u16, | ||
| 51 | pub month: u8, | ||
| 52 | pub day: u8, | ||
| 53 | pub hour: u8, | ||
| 54 | pub minute: u8, | ||
| 55 | pub second: u8, | ||
| 56 | } | ||
| 57 | #[derive(Copy, Clone)] | ||
| 58 | pub struct RtcConfig { | ||
| 59 | #[allow(dead_code)] | ||
| 60 | wakeup_select: bool, | ||
| 61 | update_mode: Um, | ||
| 62 | #[allow(dead_code)] | ||
| 63 | supervisor_access: bool, | ||
| 64 | compensation_interval: u8, | ||
| 65 | compensation_time: u8, | ||
| 66 | } | ||
| 67 | |||
| 68 | /// RTC interrupt enable flags | ||
| 69 | #[derive(Copy, Clone)] | ||
| 70 | pub struct RtcInterruptEnable; | ||
| 71 | impl RtcInterruptEnable { | ||
| 72 | pub const RTC_TIME_INVALID_INTERRUPT_ENABLE: u32 = 1 << 0; | ||
| 73 | pub const RTC_TIME_OVERFLOW_INTERRUPT_ENABLE: u32 = 1 << 1; | ||
| 74 | pub const RTC_ALARM_INTERRUPT_ENABLE: u32 = 1 << 2; | ||
| 75 | pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4; | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Converts a DateTime structure to Unix timestamp (seconds since 1970-01-01) | ||
| 79 | /// | ||
| 80 | /// # Arguments | ||
| 81 | /// | ||
| 82 | /// * `datetime` - The date and time to convert | ||
| 83 | /// | ||
| 84 | /// # Returns | ||
| 85 | /// | ||
| 86 | /// Unix timestamp as u32 | ||
| 87 | /// | ||
| 88 | /// # Note | ||
| 89 | /// | ||
| 90 | /// This function handles leap years correctly. | ||
| 91 | pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 { | ||
| 92 | let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; | ||
| 93 | |||
| 94 | let mut seconds = (datetime.year as u32 - 1970) * DAYS_IN_A_YEAR; | ||
| 95 | seconds += (datetime.year as u32 / 4) - (1970 / 4); | ||
| 96 | seconds += month_days[datetime.month as usize] as u32; | ||
| 97 | seconds += datetime.day as u32 - 1; | ||
| 98 | |||
| 99 | if (datetime.year & 3 == 0) && (datetime.month <= 2) { | ||
| 100 | seconds -= 1; | ||
| 101 | } | ||
| 102 | |||
| 103 | seconds = seconds * SECONDS_IN_A_DAY | ||
| 104 | + (datetime.hour as u32 * SECONDS_IN_A_HOUR) | ||
| 105 | + (datetime.minute as u32 * SECONDS_IN_A_MINUTE) | ||
| 106 | + datetime.second as u32; | ||
| 107 | |||
| 108 | seconds | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Converts Unix timestamp to DateTime structure | ||
| 112 | /// | ||
| 113 | /// # Arguments | ||
| 114 | /// | ||
| 115 | /// * `seconds` - Unix timestamp (seconds since 1970-01-01) | ||
| 116 | /// | ||
| 117 | /// # Returns | ||
| 118 | /// | ||
| 119 | /// RtcDateTime structure with the converted date and time | ||
| 120 | /// | ||
| 121 | /// # Note | ||
| 122 | /// | ||
| 123 | /// This function handles leap years correctly. | ||
| 124 | pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime { | ||
| 125 | let mut seconds_remaining = seconds; | ||
| 126 | let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1; | ||
| 127 | seconds_remaining %= SECONDS_IN_A_DAY; | ||
| 128 | |||
| 129 | let hour = (seconds_remaining / SECONDS_IN_A_HOUR) as u8; | ||
| 130 | seconds_remaining %= SECONDS_IN_A_HOUR; | ||
| 131 | let minute = (seconds_remaining / SECONDS_IN_A_MINUTE) as u8; | ||
| 132 | let second = (seconds_remaining % SECONDS_IN_A_MINUTE) as u8; | ||
| 133 | |||
| 134 | let mut year = YEAR_RANGE_START; | ||
| 135 | let mut days_in_year = DAYS_IN_A_YEAR; | ||
| 136 | |||
| 137 | while days > days_in_year { | ||
| 138 | days -= days_in_year; | ||
| 139 | year += 1; | ||
| 140 | |||
| 141 | days_in_year = if year.is_multiple_of(4) { | ||
| 142 | DAYS_IN_A_YEAR + 1 | ||
| 143 | } else { | ||
| 144 | DAYS_IN_A_YEAR | ||
| 145 | }; | ||
| 146 | } | ||
| 147 | |||
| 148 | let days_per_month = [ | ||
| 149 | 31, | ||
| 150 | if year.is_multiple_of(4) { 29 } else { 28 }, | ||
| 151 | 31, | ||
| 152 | 30, | ||
| 153 | 31, | ||
| 154 | 30, | ||
| 155 | 31, | ||
| 156 | 31, | ||
| 157 | 30, | ||
| 158 | 31, | ||
| 159 | 30, | ||
| 160 | 31, | ||
| 161 | ]; | ||
| 162 | |||
| 163 | let mut month = 1; | ||
| 164 | for (m, month_days) in days_per_month.iter().enumerate() { | ||
| 165 | let m = m + 1; | ||
| 166 | if days <= *month_days as u32 { | ||
| 167 | month = m; | ||
| 168 | break; | ||
| 169 | } else { | ||
| 170 | days -= *month_days as u32; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | let day = days as u8; | ||
| 175 | |||
| 176 | RtcDateTime { | ||
| 177 | year, | ||
| 178 | month: month as u8, | ||
| 179 | day, | ||
| 180 | hour, | ||
| 181 | minute, | ||
| 182 | second, | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Returns default RTC configuration | ||
| 187 | /// | ||
| 188 | /// # Returns | ||
| 189 | /// | ||
| 190 | /// RtcConfig with sensible default values: | ||
| 191 | /// - No wakeup selection | ||
| 192 | /// - Update mode 0 (immediate updates) | ||
| 193 | /// - No supervisor access restriction | ||
| 194 | /// - No compensation | ||
| 195 | pub fn get_default_config() -> RtcConfig { | ||
| 196 | RtcConfig { | ||
| 197 | wakeup_select: false, | ||
| 198 | update_mode: Um::Um0, | ||
| 199 | supervisor_access: false, | ||
| 200 | compensation_interval: 0, | ||
| 201 | compensation_time: 0, | ||
| 202 | } | ||
| 203 | } | ||
| 204 | /// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) | ||
| 205 | pub struct Rtc<'a> { | ||
| 206 | _inst: core::marker::PhantomData<&'a mut ()>, | ||
| 207 | info: &'static pac::rtc0::RegisterBlock, | ||
| 208 | } | ||
| 209 | |||
| 210 | impl<'a> Rtc<'a> { | ||
| 211 | /// Create a new instance of the real time clock. | ||
| 212 | pub fn new<I: Instance>( | ||
| 213 | _inst: Peri<'a, I>, | ||
| 214 | _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a, | ||
| 215 | config: RtcConfig, | ||
| 216 | ) -> Self { | ||
| 217 | let info = I::ptr(); | ||
| 218 | |||
| 219 | // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock | ||
| 220 | // on the vsys domain is active | ||
| 221 | let clocks = with_clocks(|c| c.clk_16k_vsys.clone()); | ||
| 222 | match clocks { | ||
| 223 | None => panic!("Clocks have not been initialized"), | ||
| 224 | Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"), | ||
| 225 | Some(Some(_)) => {} | ||
| 226 | } | ||
| 227 | |||
| 228 | // RTC reset | ||
| 229 | info.cr().modify(|_, w| w.swr().set_bit()); | ||
| 230 | info.cr().modify(|_, w| w.swr().clear_bit()); | ||
| 231 | info.tsr().write(|w| unsafe { w.bits(1) }); | ||
| 232 | |||
| 233 | info.cr().modify(|_, w| w.um().variant(config.update_mode)); | ||
| 234 | |||
| 235 | info.tcr().modify(|_, w| unsafe { | ||
| 236 | w.cir() | ||
| 237 | .bits(config.compensation_interval) | ||
| 238 | .tcr() | ||
| 239 | .bits(config.compensation_time) | ||
| 240 | }); | ||
| 241 | |||
| 242 | // Enable RTC interrupt | ||
| 243 | I::Interrupt::unpend(); | ||
| 244 | unsafe { I::Interrupt::enable() }; | ||
| 245 | |||
| 246 | Self { | ||
| 247 | _inst: core::marker::PhantomData, | ||
| 248 | info, | ||
| 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 seconds = convert_datetime_to_seconds(&datetime); | ||
| 263 | self.info.tsr().write(|w| unsafe { w.bits(seconds) }); | ||
| 264 | } | ||
| 265 | |||
| 266 | /// Get the current date and time | ||
| 267 | /// | ||
| 268 | /// # Returns | ||
| 269 | /// | ||
| 270 | /// Current date and time as RtcDateTime | ||
| 271 | /// | ||
| 272 | /// # Note | ||
| 273 | /// | ||
| 274 | /// Reads the current Unix timestamp from the time seconds register and converts it. | ||
| 275 | pub fn get_datetime(&self) -> RtcDateTime { | ||
| 276 | let seconds = self.info.tsr().read().bits(); | ||
| 277 | convert_seconds_to_datetime(seconds) | ||
| 278 | } | ||
| 279 | |||
| 280 | /// Set the alarm date and time | ||
| 281 | /// | ||
| 282 | /// # Arguments | ||
| 283 | /// | ||
| 284 | /// * `alarm` - The date and time when the alarm should trigger | ||
| 285 | /// | ||
| 286 | /// # Note | ||
| 287 | /// | ||
| 288 | /// This function: | ||
| 289 | /// - Clears any existing alarm by writing 0 to the alarm register | ||
| 290 | /// - Waits for the clear operation to complete | ||
| 291 | /// - Sets the new alarm time | ||
| 292 | /// - Waits for the write operation to complete | ||
| 293 | /// - Uses timeouts to prevent infinite loops | ||
| 294 | /// - Enables the alarm interrupt after setting | ||
| 295 | pub fn set_alarm(&self, alarm: RtcDateTime) { | ||
| 296 | let seconds = convert_datetime_to_seconds(&alarm); | ||
| 297 | |||
| 298 | self.info.tar().write(|w| unsafe { w.bits(0) }); | ||
| 299 | let mut timeout = 10000; | ||
| 300 | while self.info.tar().read().bits() != 0 && timeout > 0 { | ||
| 301 | timeout -= 1; | ||
| 302 | } | ||
| 303 | |||
| 304 | self.info.tar().write(|w| unsafe { w.bits(seconds) }); | ||
| 305 | |||
| 306 | let mut timeout = 10000; | ||
| 307 | while self.info.tar().read().bits() != seconds && timeout > 0 { | ||
| 308 | timeout -= 1; | ||
| 309 | } | ||
| 310 | |||
| 311 | self.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE); | ||
| 312 | } | ||
| 313 | |||
| 314 | /// Get the current alarm date and time | ||
| 315 | /// | ||
| 316 | /// # Returns | ||
| 317 | /// | ||
| 318 | /// Alarm date and time as RtcDateTime | ||
| 319 | /// | ||
| 320 | /// # Note | ||
| 321 | /// | ||
| 322 | /// Reads the alarm timestamp from the time alarm register and converts it. | ||
| 323 | pub fn get_alarm(&self) -> RtcDateTime { | ||
| 324 | let alarm_seconds = self.info.tar().read().bits(); | ||
| 325 | convert_seconds_to_datetime(alarm_seconds) | ||
| 326 | } | ||
| 327 | |||
| 328 | /// Start the RTC time counter | ||
| 329 | /// | ||
| 330 | /// # Note | ||
| 331 | /// | ||
| 332 | /// Sets the Time Counter Enable (TCE) bit in the status register. | ||
| 333 | pub fn start(&self) { | ||
| 334 | self.info.sr().modify(|_, w| w.tce().set_bit()); | ||
| 335 | } | ||
| 336 | |||
| 337 | /// Stop the RTC time counter | ||
| 338 | /// | ||
| 339 | /// # Note | ||
| 340 | /// | ||
| 341 | /// Clears the Time Counter Enable (TCE) bit in the status register. | ||
| 342 | pub fn stop(&self) { | ||
| 343 | self.info.sr().modify(|_, w| w.tce().clear_bit()); | ||
| 344 | } | ||
| 345 | |||
| 346 | /// Enable specific RTC interrupts | ||
| 347 | /// | ||
| 348 | /// # Arguments | ||
| 349 | /// | ||
| 350 | /// * `mask` - Bitmask of interrupts to enable (use RtcInterruptEnable constants) | ||
| 351 | /// | ||
| 352 | /// # Note | ||
| 353 | /// | ||
| 354 | /// This function enables the specified interrupt types and resets the alarm occurred flag. | ||
| 355 | /// Available interrupts: | ||
| 356 | /// - Time Invalid Interrupt | ||
| 357 | /// - Time Overflow Interrupt | ||
| 358 | /// - Alarm Interrupt | ||
| 359 | /// - Seconds Interrupt | ||
| 360 | pub fn set_interrupt(&self, mask: u32) { | ||
| 361 | if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 { | ||
| 362 | self.info.ier().modify(|_, w| w.tiie().tiie_1()); | ||
| 363 | } | ||
| 364 | if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 { | ||
| 365 | self.info.ier().modify(|_, w| w.toie().toie_1()); | ||
| 366 | } | ||
| 367 | if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 { | ||
| 368 | self.info.ier().modify(|_, w| w.taie().taie_1()); | ||
| 369 | } | ||
| 370 | if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 { | ||
| 371 | self.info.ier().modify(|_, w| w.tsie().tsie_1()); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | /// Disable specific RTC interrupts | ||
| 376 | /// | ||
| 377 | /// # Arguments | ||
| 378 | /// | ||
| 379 | /// * `mask` - Bitmask of interrupts to disable (use RtcInterruptEnable constants) | ||
| 380 | /// | ||
| 381 | /// # Note | ||
| 382 | /// | ||
| 383 | /// This function disables the specified interrupt types. | ||
| 384 | pub fn disable_interrupt(&self, mask: u32) { | ||
| 385 | if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 { | ||
| 386 | self.info.ier().modify(|_, w| w.tiie().tiie_0()); | ||
| 387 | } | ||
| 388 | if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 { | ||
| 389 | self.info.ier().modify(|_, w| w.toie().toie_0()); | ||
| 390 | } | ||
| 391 | if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 { | ||
| 392 | self.info.ier().modify(|_, w| w.taie().taie_0()); | ||
| 393 | } | ||
| 394 | if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 { | ||
| 395 | self.info.ier().modify(|_, w| w.tsie().tsie_0()); | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 399 | /// Clear the alarm interrupt flag | ||
| 400 | /// | ||
| 401 | /// # Note | ||
| 402 | /// | ||
| 403 | /// This function clears the Time Alarm Interrupt Enable bit. | ||
| 404 | pub fn clear_alarm_flag(&self) { | ||
| 405 | self.info.ier().modify(|_, w| w.taie().clear_bit()); | ||
| 406 | } | ||
| 407 | |||
| 408 | /// Wait for an RTC alarm to trigger. | ||
| 409 | /// | ||
| 410 | /// # Arguments | ||
| 411 | /// | ||
| 412 | /// * `alarm` - The date and time when the alarm should trigger | ||
| 413 | /// | ||
| 414 | /// This function will wait until the RTC alarm is triggered. | ||
| 415 | /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered. | ||
| 416 | pub async fn wait_for_alarm(&mut self, alarm: RtcDateTime) { | ||
| 417 | let wait = WAKER.subscribe().await; | ||
| 418 | |||
| 419 | self.set_alarm(alarm); | ||
| 420 | self.start(); | ||
| 421 | |||
| 422 | // REVISIT: propagate error? | ||
| 423 | let _ = wait.await; | ||
| 424 | |||
| 425 | // Clear the interrupt and disable the alarm after waking up | ||
| 426 | self.disable_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE); | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | /// RTC interrupt handler | ||
| 431 | /// | ||
| 432 | /// This struct implements the interrupt handler for RTC events. | ||
| 433 | impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> { | ||
| 434 | unsafe fn on_interrupt() { | ||
| 435 | let rtc = &*pac::Rtc0::ptr(); | ||
| 436 | // Check if this is actually a time alarm interrupt | ||
| 437 | let sr = rtc.sr().read(); | ||
| 438 | if sr.taf().bit_is_set() { | ||
| 439 | rtc.ier().modify(|_, w| w.taie().clear_bit()); | ||
| 440 | WAKER.wake(); | ||
| 441 | } | ||
| 442 | } | ||
| 443 | } | ||
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index d26671674..9c53e66a7 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md | |||
| @@ -30,6 +30,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 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 | 32 | * added: support for nrf54l10 and nrf54l05 |
| 33 | * added: expose uicr write functions | ||
| 34 | * added: support for nrf54lm20a | ||
| 33 | 35 | ||
| 34 | ## 0.8.0 - 2025-09-30 | 36 | ## 0.8.0 - 2025-09-30 |
| 35 | 37 | ||
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index c51af59d2..ee070f0c0 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml | |||
| @@ -29,6 +29,7 @@ build = [ | |||
| 29 | {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l10-app-ns", "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"]}, | 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"]}, | 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"]}, | ||
| 32 | {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time"]}, | 33 | {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time"]}, |
| 33 | {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time-driver-rtc1"]}, | 34 | {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time-driver-rtc1"]}, |
| 34 | {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time", "time-driver-rtc1"]}, | 35 | {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time", "time-driver-rtc1"]}, |
| @@ -142,6 +143,8 @@ nrf54l10-app-ns = ["_nrf54l10-app", "_ns"] | |||
| 142 | nrf54l05-app-s = ["_nrf54l05-app", "_s", "_multi_wdt"] | 143 | nrf54l05-app-s = ["_nrf54l05-app", "_s", "_multi_wdt"] |
| 143 | ## nRF54L05 application core in Non-Secure mode | 144 | ## nRF54L05 application core in Non-Secure mode |
| 144 | nrf54l05-app-ns = ["_nrf54l05-app", "_ns"] | 145 | nrf54l05-app-ns = ["_nrf54l05-app", "_ns"] |
| 146 | ## nRF54LM20 application core in Secure mode | ||
| 147 | nrf54lm20-app-s = ["_nrf54lm20-app", "_s", "_multi_wdt"] | ||
| 145 | 148 | ||
| 146 | ## nRF9160 in Secure mode | 149 | ## nRF9160 in Secure mode |
| 147 | nrf9160-s = ["_nrf9160", "_s", "_nrf91"] | 150 | nrf9160-s = ["_nrf9160", "_s", "_nrf91"] |
| @@ -169,6 +172,8 @@ _nrf54l10-app = ["_nrf54l10", "nrf-pac/nrf54l10-app"] | |||
| 169 | _nrf54l10 = ["_nrf54l", "_gpio-p1", "_gpio-p2"] | 172 | _nrf54l10 = ["_nrf54l", "_gpio-p1", "_gpio-p2"] |
| 170 | _nrf54l05-app = ["_nrf54l05", "nrf-pac/nrf54l05-app"] | 173 | _nrf54l05-app = ["_nrf54l05", "nrf-pac/nrf54l05-app"] |
| 171 | _nrf54l05 = ["_nrf54l", "_gpio-p1", "_gpio-p2"] | 174 | _nrf54l05 = ["_nrf54l", "_gpio-p1", "_gpio-p2"] |
| 175 | _nrf54lm20-app = ["_nrf54lm20", "nrf-pac/nrf54lm20a-app"] | ||
| 176 | _nrf54lm20 = ["_nrf54l", "_gpio-p1", "_gpio-p2"] | ||
| 172 | _nrf54l = ["_dppi", "_grtc"] | 177 | _nrf54l = ["_dppi", "_grtc"] |
| 173 | 178 | ||
| 174 | _nrf9160 = ["nrf-pac/nrf9160", "_dppi", "_spi-v1"] | 179 | _nrf9160 = ["nrf-pac/nrf9160", "_dppi", "_spi-v1"] |
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 4cb291626..db71dee10 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -29,6 +29,7 @@ | |||
| 29 | feature = "nrf54l10-app-ns", | 29 | feature = "nrf54l10-app-ns", |
| 30 | feature = "nrf54l05-app-s", | 30 | feature = "nrf54l05-app-s", |
| 31 | feature = "nrf54l05-app-ns", | 31 | feature = "nrf54l05-app-ns", |
| 32 | feature = "nrf54lm20-app-s", | ||
| 32 | feature = "nrf9160-s", | 33 | feature = "nrf9160-s", |
| 33 | feature = "nrf9160-ns", | 34 | feature = "nrf9160-ns", |
| 34 | feature = "nrf9120-s", | 35 | feature = "nrf9120-s", |
| @@ -57,6 +58,7 @@ compile_error!( | |||
| 57 | nrf54l10-app-ns, | 58 | nrf54l10-app-ns, |
| 58 | nrf54l05-app-s, | 59 | nrf54l05-app-s, |
| 59 | nrf54l05-app-ns, | 60 | nrf54l05-app-ns, |
| 61 | nrf54lm20-app-s, | ||
| 60 | nrf9160-s, | 62 | nrf9160-s, |
| 61 | nrf9160-ns, | 63 | nrf9160-ns, |
| 62 | nrf9120-s, | 64 | nrf9120-s, |
| @@ -202,6 +204,7 @@ pub mod wdt; | |||
| 202 | #[cfg_attr(feature = "_nrf54l15-app", path = "chips/nrf54l15_app.rs")] | 204 | #[cfg_attr(feature = "_nrf54l15-app", path = "chips/nrf54l15_app.rs")] |
| 203 | #[cfg_attr(feature = "_nrf54l10-app", path = "chips/nrf54l10_app.rs")] | 205 | #[cfg_attr(feature = "_nrf54l10-app", path = "chips/nrf54l10_app.rs")] |
| 204 | #[cfg_attr(feature = "_nrf54l05-app", path = "chips/nrf54l05_app.rs")] | 206 | #[cfg_attr(feature = "_nrf54l05-app", path = "chips/nrf54l05_app.rs")] |
| 207 | #[cfg_attr(feature = "_nrf54lm20-app", path = "chips/nrf54lm20_app.rs")] | ||
| 205 | #[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")] | 208 | #[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")] |
| 206 | #[cfg_attr(feature = "_nrf9120", path = "chips/nrf9120.rs")] | 209 | #[cfg_attr(feature = "_nrf9120", path = "chips/nrf9120.rs")] |
| 207 | mod chip; | 210 | mod chip; |
| @@ -667,10 +670,11 @@ mod consts { | |||
| 667 | pub const APPROTECT_DISABLED: u32 = 0x0000_005a; | 670 | pub const APPROTECT_DISABLED: u32 = 0x0000_005a; |
| 668 | } | 671 | } |
| 669 | 672 | ||
| 673 | /// Result from writing UICR. | ||
| 670 | #[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] | 674 | #[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] |
| 671 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | 675 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| 672 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 676 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 673 | enum WriteResult { | 677 | pub enum WriteResult { |
| 674 | /// Word was written successfully, needs reset. | 678 | /// Word was written successfully, needs reset. |
| 675 | Written, | 679 | Written, |
| 676 | /// 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. |
| @@ -679,13 +683,21 @@ enum WriteResult { | |||
| 679 | Failed, | 683 | Failed, |
| 680 | } | 684 | } |
| 681 | 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. | ||
| 682 | #[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] | 690 | #[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] |
| 683 | unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult { | 691 | pub unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult { |
| 684 | uicr_write_masked(address, value, 0xFFFF_FFFF) | 692 | uicr_write_masked(address, value, 0xFFFF_FFFF) |
| 685 | } | 693 | } |
| 686 | 694 | ||
| 687 | #[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] | 695 | #[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] |
| 688 | 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 { | ||
| 689 | let curr_val = address.read_volatile(); | 701 | let curr_val = address.read_volatile(); |
| 690 | if curr_val & mask == value & mask { | 702 | if curr_val & mask == value & mask { |
| 691 | 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 2b9e0a89a..38f22b1c3 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -7,6 +7,7 @@ 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 | ||
| 10 | - fix: stm32: SPI driver SSOE and SSM manegment, add `nss_output_disable` to SPI Config | 11 | - fix: stm32: SPI driver SSOE and SSM manegment, add `nss_output_disable` to SPI Config |
| 11 | - change: stm32: use typelevel timer type to allow dma for 32 bit timers | 12 | - change: stm32: use typelevel timer type to allow dma for 32 bit timers |
| 12 | - fix: fix incorrect handling of split interrupts in timer driver | 13 | - fix: fix incorrect handling of split interrupts in timer driver |
| @@ -89,7 +90,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 89 | - 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)) |
| 90 | - feat: stm32/spi bidirectional mode | 91 | - feat: stm32/spi bidirectional mode |
| 91 | - fix: stm32/i2c v2: add stop flag on stop received | 92 | - fix: stm32/i2c v2: add stop flag on stop received |
| 93 | - stm32: Add blocking_listen for blocking I2C driver | ||
| 92 | - fix: stm32l47*/stm32l48* adc analog pin setup | 94 | - fix: stm32l47*/stm32l48* adc analog pin setup |
| 95 | - fix: keep stm32/sai: make NODIV independent of MCKDIV | ||
| 93 | 96 | ||
| 94 | ## 0.4.0 - 2025-08-26 | 97 | ## 0.4.0 - 2025-08-26 |
| 95 | 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 a55b99e6a..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,10 +71,53 @@ 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; |
| 77 | } | ||
| 78 | |||
| 79 | #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_g4, adc_c0))] | ||
| 80 | trait_set::trait_set! { | ||
| 81 | pub trait DefaultInstance = Instance<Regs = crate::pac::adc::Adc>; | ||
| 82 | } | ||
| 83 | |||
| 84 | #[cfg(adc_wba)] | ||
| 85 | trait_set::trait_set! { | ||
| 86 | pub trait DefaultInstance = Instance<Regs = crate::pac::adc::Adc4>; | ||
| 87 | } | ||
| 88 | |||
| 89 | pub trait BasicAdcRegs { | ||
| 90 | type SampleTime; | ||
| 91 | } | ||
| 92 | |||
| 93 | #[cfg(any( | ||
| 94 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 95 | ))] | ||
| 96 | trait AdcRegs: BasicAdcRegs { | ||
| 97 | fn enable(&self); | ||
| 98 | fn start(&self); | ||
| 99 | fn stop(&self); | ||
| 100 | fn convert(&self); | ||
| 101 | fn configure_dma(&self, conversion_mode: ConversionMode); | ||
| 102 | fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); | ||
| 103 | fn data(&self) -> *mut u16; | ||
| 104 | } | ||
| 105 | |||
| 106 | #[allow(private_bounds)] | ||
| 107 | pub trait BasicInstance { | ||
| 108 | #[cfg(any( | ||
| 109 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 110 | ))] | ||
| 111 | type Regs: AdcRegs; | ||
| 112 | } | ||
| 113 | |||
| 114 | trait SealedInstance: BasicInstance { | ||
| 115 | #[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))] | ||
| 71 | fn regs() -> crate::pac::adc::Adc; | 116 | fn regs() -> crate::pac::adc::Adc; |
| 117 | #[cfg(any( | ||
| 118 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 119 | ))] | ||
| 120 | fn regs() -> Self::Regs; | ||
| 72 | #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0)))] | 121 | #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0)))] |
| 73 | #[allow(unused)] | 122 | #[allow(unused)] |
| 74 | fn common_regs() -> crate::pac::adccommon::AdcCommon; | 123 | fn common_regs() -> crate::pac::adccommon::AdcCommon; |
| @@ -89,56 +138,6 @@ pub(crate) trait SealedAdcChannel<T> { | |||
| 89 | } | 138 | } |
| 90 | } | 139 | } |
| 91 | 140 | ||
| 92 | // Temporary patch for ADCs that have not implemented the standard iface yet | ||
| 93 | #[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1))] | ||
| 94 | trait_set::trait_set! { | ||
| 95 | pub trait AnyInstance = Instance; | ||
| 96 | } | ||
| 97 | |||
| 98 | #[cfg(any( | ||
| 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; | ||
| 103 | } | ||
| 104 | |||
| 105 | #[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 | ||
| 107 | ))] | ||
| 108 | pub(self) trait SealedAnyInstance: BasicAnyInstance { | ||
| 109 | fn enable(); | ||
| 110 | fn start(); | ||
| 111 | fn stop(); | ||
| 112 | fn convert() -> u16; | ||
| 113 | fn configure_dma(conversion_mode: ConversionMode); | ||
| 114 | fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); | ||
| 115 | #[allow(dead_code)] | ||
| 116 | fn dr() -> *mut u16; | ||
| 117 | } | ||
| 118 | |||
| 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)] | ||
| 122 | pub trait AnyInstance: SealedAnyInstance + Instance {} | ||
| 123 | |||
| 124 | // On chips with ADC4, AnyInstance is an Instance or adc4::Instance | ||
| 125 | #[cfg(any(adc_v4, adc_u5, adc_wba))] | ||
| 126 | #[allow(private_bounds)] | ||
| 127 | pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} | ||
| 128 | |||
| 129 | // Implement AnyInstance automatically for SealedAnyInstance | ||
| 130 | #[cfg(any( | ||
| 131 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 132 | ))] | ||
| 133 | impl<T: SealedAnyInstance + Instance> BasicAnyInstance for T { | ||
| 134 | type SampleTime = SampleTime; | ||
| 135 | } | ||
| 136 | |||
| 137 | #[cfg(any( | ||
| 138 | adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 | ||
| 139 | ))] | ||
| 140 | impl<T: SealedAnyInstance + Instance> AnyInstance for T {} | ||
| 141 | |||
| 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. |
| 144 | #[derive(Copy, Clone, Debug)] | 143 | #[derive(Copy, Clone, Debug)] |
| @@ -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_v3, 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( |
| 191 | &mut self, | ||
| 192 | channel: &mut impl AdcChannel<T>, | ||
| 193 | sample_time: <T::Regs as BasicAdcRegs>::SampleTime, | ||
| 194 | ) -> u16 { | ||
| 192 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] | 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,7 +421,10 @@ 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> |
| 425 | where | ||
| 426 | Self: 'a, | ||
| 427 | { | ||
| 421 | #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_wba))] | 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 | ||
| @@ -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 | ||
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/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/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 6b213484c..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 | } |
| @@ -1675,42 +1680,50 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1675 | 1680 | ||
| 1676 | /// Listen for incoming I2C messages. | 1681 | /// Listen for incoming I2C messages. |
| 1677 | /// | 1682 | /// |
| 1678 | /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. | 1683 | /// This method blocks until the slave address is matched by a master. |
| 1679 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | 1684 | pub fn blocking_listen(&mut self) -> Result<SlaveCommand, Error> { |
| 1680 | let state = self.state; | 1685 | let timeout = self.timeout(); |
| 1686 | |||
| 1681 | self.info.regs.cr1().modify(|reg| { | 1687 | self.info.regs.cr1().modify(|reg| { |
| 1682 | reg.set_addrie(true); | 1688 | reg.set_addrie(true); |
| 1683 | trace!("Enable ADDRIE"); | 1689 | trace!("Enable ADDRIE"); |
| 1684 | }); | 1690 | }); |
| 1685 | 1691 | ||
| 1686 | poll_fn(|cx| { | 1692 | loop { |
| 1687 | state.waker.register(cx.waker()); | ||
| 1688 | let isr = self.info.regs.isr().read(); | 1693 | let isr = self.info.regs.isr().read(); |
| 1689 | if !isr.addr() { | 1694 | if isr.addr() { |
| 1690 | Poll::Pending | 1695 | break; |
| 1691 | } else { | ||
| 1692 | trace!("ADDR triggered (address match)"); | ||
| 1693 | // we do not clear the address flag here as it will be cleared by the dma read/write | ||
| 1694 | // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it | ||
| 1695 | match isr.dir() { | ||
| 1696 | i2c::vals::Dir::WRITE => { | ||
| 1697 | trace!("DIR: write"); | ||
| 1698 | Poll::Ready(Ok(SlaveCommand { | ||
| 1699 | kind: SlaveCommandKind::Write, | ||
| 1700 | address: self.determine_matched_address()?, | ||
| 1701 | })) | ||
| 1702 | } | ||
| 1703 | i2c::vals::Dir::READ => { | ||
| 1704 | trace!("DIR: read"); | ||
| 1705 | Poll::Ready(Ok(SlaveCommand { | ||
| 1706 | kind: SlaveCommandKind::Read, | ||
| 1707 | address: self.determine_matched_address()?, | ||
| 1708 | })) | ||
| 1709 | } | ||
| 1710 | } | ||
| 1711 | } | 1696 | } |
| 1712 | }) | 1697 | timeout.check()?; |
| 1713 | .await | 1698 | } |
| 1699 | |||
| 1700 | trace!("ADDR triggered (address match)"); | ||
| 1701 | |||
| 1702 | // we do not clear the address flag here as it will be cleared by the dma read/write | ||
| 1703 | // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it | ||
| 1704 | self.slave_command() | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | /// Determine the received slave command. | ||
| 1708 | fn slave_command(&self) -> Result<SlaveCommand, Error> { | ||
| 1709 | let isr = self.info.regs.isr().read(); | ||
| 1710 | |||
| 1711 | match isr.dir() { | ||
| 1712 | i2c::vals::Dir::WRITE => { | ||
| 1713 | trace!("DIR: write"); | ||
| 1714 | Ok(SlaveCommand { | ||
| 1715 | kind: SlaveCommandKind::Write, | ||
| 1716 | address: self.determine_matched_address()?, | ||
| 1717 | }) | ||
| 1718 | } | ||
| 1719 | i2c::vals::Dir::READ => { | ||
| 1720 | trace!("DIR: read"); | ||
| 1721 | Ok(SlaveCommand { | ||
| 1722 | kind: SlaveCommandKind::Read, | ||
| 1723 | address: self.determine_matched_address()?, | ||
| 1724 | }) | ||
| 1725 | } | ||
| 1726 | } | ||
| 1714 | } | 1727 | } |
| 1715 | 1728 | ||
| 1716 | /// Respond to a write command. | 1729 | /// Respond to a write command. |
| @@ -1729,16 +1742,44 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1729 | } | 1742 | } |
| 1730 | 1743 | ||
| 1731 | impl<'d> I2c<'d, Async, MultiMaster> { | 1744 | impl<'d> I2c<'d, Async, MultiMaster> { |
| 1745 | /// Listen for incoming I2C messages. | ||
| 1746 | /// | ||
| 1747 | /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. | ||
| 1748 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 1749 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1750 | let state = self.state; | ||
| 1751 | self.info.regs.cr1().modify(|reg| { | ||
| 1752 | reg.set_addrie(true); | ||
| 1753 | trace!("Enable ADDRIE"); | ||
| 1754 | }); | ||
| 1755 | |||
| 1756 | poll_fn(|cx| { | ||
| 1757 | state.waker.register(cx.waker()); | ||
| 1758 | let isr = self.info.regs.isr().read(); | ||
| 1759 | if !isr.addr() { | ||
| 1760 | Poll::Pending | ||
| 1761 | } else { | ||
| 1762 | trace!("ADDR triggered (address match)"); | ||
| 1763 | // we do not clear the address flag here as it will be cleared by the dma read/write | ||
| 1764 | // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it | ||
| 1765 | Poll::Ready(self.slave_command()) | ||
| 1766 | } | ||
| 1767 | }) | ||
| 1768 | .await | ||
| 1769 | } | ||
| 1770 | |||
| 1732 | /// Respond to a write command. | 1771 | /// Respond to a write command. |
| 1733 | /// | 1772 | /// |
| 1734 | /// Returns the total number of bytes received. | 1773 | /// Returns the total number of bytes received. |
| 1735 | 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(); | ||
| 1736 | let timeout = self.timeout(); | 1776 | let timeout = self.timeout(); |
| 1737 | timeout.with(self.read_dma_internal_slave(buffer, timeout)).await | 1777 | timeout.with(self.read_dma_internal_slave(buffer, timeout)).await |
| 1738 | } | 1778 | } |
| 1739 | 1779 | ||
| 1740 | /// Respond to a read request from an I2C master. | 1780 | /// Respond to a read request from an I2C master. |
| 1741 | 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(); | ||
| 1742 | let timeout = self.timeout(); | 1783 | let timeout = self.timeout(); |
| 1743 | timeout.with(self.write_dma_internal_slave(write, timeout)).await | 1784 | timeout.with(self.write_dma_internal_slave(write, timeout)).await |
| 1744 | } | 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 3b39fd2fb..af51b79b4 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -64,6 +64,16 @@ pub enum Direction { | |||
| 64 | Receive, | 64 | Receive, |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | /// Slave Select (SS) pin polarity. | ||
| 68 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 69 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 70 | pub enum SlaveSelectPolarity { | ||
| 71 | /// SS active high | ||
| 72 | ActiveHigh, | ||
| 73 | /// SS active low | ||
| 74 | ActiveLow, | ||
| 75 | } | ||
| 76 | |||
| 67 | /// SPI configuration. | 77 | /// SPI configuration. |
| 68 | #[non_exhaustive] | 78 | #[non_exhaustive] |
| 69 | #[derive(Copy, Clone)] | 79 | #[derive(Copy, Clone)] |
| @@ -86,6 +96,9 @@ pub struct Config { | |||
| 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. | 96 | /// 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. | 97 | /// 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, | 98 | pub nss_output_disable: bool, |
| 99 | /// Slave Select (SS) pin polarity. | ||
| 100 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 101 | pub nss_polarity: SlaveSelectPolarity, | ||
| 89 | } | 102 | } |
| 90 | 103 | ||
| 91 | impl Default for Config { | 104 | impl Default for Config { |
| @@ -97,6 +110,8 @@ impl Default for Config { | |||
| 97 | miso_pull: Pull::None, | 110 | miso_pull: Pull::None, |
| 98 | gpio_speed: Speed::VeryHigh, | 111 | gpio_speed: Speed::VeryHigh, |
| 99 | nss_output_disable: false, | 112 | nss_output_disable: false, |
| 113 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 114 | nss_polarity: SlaveSelectPolarity::ActiveHigh, | ||
| 100 | } | 115 | } |
| 101 | } | 116 | } |
| 102 | } | 117 | } |
| @@ -123,6 +138,14 @@ impl Config { | |||
| 123 | } | 138 | } |
| 124 | } | 139 | } |
| 125 | 140 | ||
| 141 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 142 | fn raw_nss_polarity(&self) -> vals::Ssiop { | ||
| 143 | match self.nss_polarity { | ||
| 144 | SlaveSelectPolarity::ActiveHigh => vals::Ssiop::ACTIVE_HIGH, | ||
| 145 | SlaveSelectPolarity::ActiveLow => vals::Ssiop::ACTIVE_LOW, | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 126 | #[cfg(gpio_v1)] | 149 | #[cfg(gpio_v1)] |
| 127 | fn sck_af(&self) -> AfType { | 150 | fn sck_af(&self) -> AfType { |
| 128 | AfType::output(OutputType::PushPull, self.gpio_speed) | 151 | AfType::output(OutputType::PushPull, self.gpio_speed) |
| @@ -230,7 +253,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 230 | let cpol = config.raw_polarity(); | 253 | let cpol = config.raw_polarity(); |
| 231 | let lsbfirst = config.raw_byte_order(); | 254 | let lsbfirst = config.raw_byte_order(); |
| 232 | 255 | ||
| 233 | self.info.rcc.enable_and_reset(); | 256 | self.info.rcc.enable_and_reset_without_stop(); |
| 234 | 257 | ||
| 235 | /* | 258 | /* |
| 236 | - Software NSS management (SSM = 1) | 259 | - Software NSS management (SSM = 1) |
| @@ -305,6 +328,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 305 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 328 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 306 | { | 329 | { |
| 307 | let ssoe = CM::MASTER == vals::Master::MASTER && !config.nss_output_disable; | 330 | let ssoe = CM::MASTER == vals::Master::MASTER && !config.nss_output_disable; |
| 331 | let ssiop = config.raw_nss_polarity(); | ||
| 308 | regs.ifcr().write(|w| w.0 = 0xffff_ffff); | 332 | regs.ifcr().write(|w| w.0 = 0xffff_ffff); |
| 309 | regs.cfg2().modify(|w| { | 333 | regs.cfg2().modify(|w| { |
| 310 | w.set_ssoe(ssoe); | 334 | w.set_ssoe(ssoe); |
| @@ -318,7 +342,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 318 | w.set_midi(0); | 342 | w.set_midi(0); |
| 319 | w.set_mssi(0); | 343 | w.set_mssi(0); |
| 320 | w.set_afcntr(true); | 344 | w.set_afcntr(true); |
| 321 | w.set_ssiop(vals::Ssiop::ACTIVE_HIGH); | 345 | w.set_ssiop(ssiop); |
| 322 | }); | 346 | }); |
| 323 | regs.cfg1().modify(|w| { | 347 | regs.cfg1().modify(|w| { |
| 324 | w.set_crcen(false); | 348 | w.set_crcen(false); |
| @@ -366,6 +390,8 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 366 | 390 | ||
| 367 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 391 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 368 | { | 392 | { |
| 393 | let ssiop = config.raw_nss_polarity(); | ||
| 394 | |||
| 369 | self.info.regs.cr1().modify(|w| { | 395 | self.info.regs.cr1().modify(|w| { |
| 370 | w.set_spe(false); | 396 | w.set_spe(false); |
| 371 | }); | 397 | }); |
| @@ -374,6 +400,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 374 | w.set_cpha(cpha); | 400 | w.set_cpha(cpha); |
| 375 | w.set_cpol(cpol); | 401 | w.set_cpol(cpol); |
| 376 | w.set_lsbfirst(lsbfirst); | 402 | w.set_lsbfirst(lsbfirst); |
| 403 | w.set_ssiop(ssiop); | ||
| 377 | }); | 404 | }); |
| 378 | self.info.regs.cfg1().modify(|w| { | 405 | self.info.regs.cfg1().modify(|w| { |
| 379 | w.set_mbr(br); | 406 | w.set_mbr(br); |
| @@ -446,6 +473,13 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 446 | // NSS output disabled if SSOE=0 or if SSM=1 software slave management enabled | 473 | // NSS output disabled if SSOE=0 or if SSM=1 software slave management enabled |
| 447 | let nss_output_disable = !ssoe || cfg.ssm(); | 474 | let nss_output_disable = !ssoe || cfg.ssm(); |
| 448 | 475 | ||
| 476 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 477 | let nss_polarity = if cfg.ssiop() == vals::Ssiop::ACTIVE_LOW { | ||
| 478 | SlaveSelectPolarity::ActiveLow | ||
| 479 | } else { | ||
| 480 | SlaveSelectPolarity::ActiveHigh | ||
| 481 | }; | ||
| 482 | |||
| 449 | Config { | 483 | Config { |
| 450 | mode: Mode { polarity, phase }, | 484 | mode: Mode { polarity, phase }, |
| 451 | bit_order, | 485 | bit_order, |
| @@ -453,6 +487,8 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { | |||
| 453 | miso_pull, | 487 | miso_pull, |
| 454 | gpio_speed: self.gpio_speed, | 488 | gpio_speed: self.gpio_speed, |
| 455 | nss_output_disable, | 489 | nss_output_disable, |
| 490 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 491 | nss_polarity, | ||
| 456 | } | 492 | } |
| 457 | } | 493 | } |
| 458 | 494 | ||
| @@ -769,7 +805,7 @@ impl<'d> Spi<'d, Async, Master> { | |||
| 769 | ) | 805 | ) |
| 770 | } | 806 | } |
| 771 | 807 | ||
| 772 | /// Create a new SPI driver, in bidirectional mode, specifically in tranmit mode | 808 | /// Create a new SPI driver, in bidirectional mode, specifically in tranmit mode |
| 773 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 809 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 774 | pub fn new_bidi<T: Instance, #[cfg(afio)] A>( | 810 | pub fn new_bidi<T: Instance, #[cfg(afio)] A>( |
| 775 | peri: Peri<'d, T>, | 811 | peri: Peri<'d, T>, |
| @@ -848,6 +884,7 @@ impl<'d> Spi<'d, Async, Master> { | |||
| 848 | impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | 884 | impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { |
| 849 | /// SPI write, using DMA. | 885 | /// SPI write, using DMA. |
| 850 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { | 886 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { |
| 887 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 851 | if data.is_empty() { | 888 | if data.is_empty() { |
| 852 | return Ok(()); | 889 | return Ok(()); |
| 853 | } | 890 | } |
| @@ -879,6 +916,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | |||
| 879 | /// SPI read, using DMA. | 916 | /// SPI read, using DMA. |
| 880 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 917 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 881 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 918 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 919 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 882 | if data.is_empty() { | 920 | if data.is_empty() { |
| 883 | return Ok(()); | 921 | return Ok(()); |
| 884 | } | 922 | } |
| @@ -966,6 +1004,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | |||
| 966 | /// SPI read, using DMA. | 1004 | /// SPI read, using DMA. |
| 967 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 1005 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 968 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 1006 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 1007 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 969 | if data.is_empty() { | 1008 | if data.is_empty() { |
| 970 | return Ok(()); | 1009 | return Ok(()); |
| 971 | } | 1010 | } |
| @@ -1013,6 +1052,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | |||
| 1013 | } | 1052 | } |
| 1014 | 1053 | ||
| 1015 | async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { | 1054 | async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { |
| 1055 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1016 | assert_eq!(read.len(), write.len()); | 1056 | assert_eq!(read.len(), write.len()); |
| 1017 | if read.len() == 0 { | 1057 | if read.len() == 0 { |
| 1018 | return Ok(()); | 1058 | return Ok(()); |
| @@ -1064,6 +1104,8 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | |||
| 1064 | /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. | 1104 | /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. |
| 1065 | /// If `write` is shorter it is padded with zero bytes. | 1105 | /// If `write` is shorter it is padded with zero bytes. |
| 1066 | pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { | 1106 | pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { |
| 1107 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1108 | |||
| 1067 | self.transfer_inner(read, write).await | 1109 | self.transfer_inner(read, write).await |
| 1068 | } | 1110 | } |
| 1069 | 1111 | ||
| @@ -1071,6 +1113,8 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | |||
| 1071 | /// | 1113 | /// |
| 1072 | /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. | 1114 | /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. |
| 1073 | pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 1115 | pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 1116 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1117 | |||
| 1074 | self.transfer_inner(data, data).await | 1118 | self.transfer_inner(data, data).await |
| 1075 | } | 1119 | } |
| 1076 | } | 1120 | } |
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 6ca13820a..620d7858e 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -2,17 +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::dma::word::Word; | 9 | use crate::dma::word::Word; |
| 12 | use crate::gpio::{AnyPin, OutputType}; | 10 | use crate::gpio::{AfType, AnyPin, OutputType}; |
| 11 | pub use crate::pac::timer::vals::{Ccds, Ckd, Mms2, Ossi, Ossr}; | ||
| 13 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 14 | use crate::timer::TimerChannel; | 13 | use crate::timer::TimerChannel; |
| 15 | use crate::timer::low_level::OutputCompareMode; | 14 | use crate::timer::low_level::OutputCompareMode; |
| 15 | use crate::timer::simple_pwm::PwmPinConfig; | ||
| 16 | 16 | ||
| 17 | /// Complementary PWM pin wrapper. | 17 | /// Complementary PWM pin wrapper. |
| 18 | /// | 18 | /// |
| @@ -28,9 +28,27 @@ impl<'d, T: AdvancedInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!( | |||
| 28 | 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 { |
| 29 | critical_section::with(|_| { | 29 | critical_section::with(|_| { |
| 30 | pin.set_low(); | 30 | pin.set_low(); |
| 31 | set_as_af!( | 31 | set_as_af!(pin, AfType::output(output_type, crate::gpio::Speed::VeryHigh)); |
| 32 | pin, | 32 | }); |
| 33 | 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), | ||
| 34 | ); | 52 | ); |
| 35 | }); | 53 | }); |
| 36 | ComplementaryPwmPin { | 54 | ComplementaryPwmPin { |
| @@ -220,6 +238,25 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 220 | /// Generate a sequence of PWM waveform | 238 | /// Generate a sequence of PWM waveform |
| 221 | /// | 239 | /// |
| 222 | /// 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: | ||
| 223 | /// 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. |
| 224 | pub async fn waveform_up<W: Word + Into<T::Word>>( | 261 | pub async fn waveform_up<W: Word + Into<T::Word>>( |
| 225 | &mut self, | 262 | &mut self, |
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 6a70d2a40..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; |
| @@ -682,9 +682,29 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 682 | channel: Channel, | 682 | channel: Channel, |
| 683 | duty: &'a [W], | 683 | duty: &'a [W], |
| 684 | ) -> Transfer<'a> { | 684 | ) -> Transfer<'a> { |
| 685 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 685 | self.setup_update_dma_inner(dma.request(), dma, channel, duty) |
| 686 | let req = dma.request(); | 686 | } |
| 687 | 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> { | ||
| 688 | unsafe { | 708 | unsafe { |
| 689 | #[cfg(not(any(bdma, gpdma)))] | 709 | #[cfg(not(any(bdma, gpdma)))] |
| 690 | use crate::dma::{Burst, FifoThreshold}; | 710 | use crate::dma::{Burst, FifoThreshold}; |
| @@ -700,7 +720,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 700 | 720 | ||
| 701 | Transfer::new_write( | 721 | Transfer::new_write( |
| 702 | dma, | 722 | dma, |
| 703 | req, | 723 | request, |
| 704 | duty, | 724 | duty, |
| 705 | self.regs_gp16().ccr(channel.index()).as_ptr() as *mut W, | 725 | self.regs_gp16().ccr(channel.index()).as_ptr() as *mut W, |
| 706 | dma_transfer_option, | 726 | dma_transfer_option, |
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 74d1c9e77..998e3a6f4 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -406,7 +406,7 @@ pub struct UpdateInterruptHandler<T: CoreInstance> { | |||
| 406 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { | 406 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { |
| 407 | unsafe fn on_interrupt() { | 407 | unsafe fn on_interrupt() { |
| 408 | #[cfg(feature = "low-power")] | 408 | #[cfg(feature = "low-power")] |
| 409 | crate::low_power::Executor::on_wakeup_irq(); | 409 | crate::low_power::Executor::on_wakeup_irq_or_event(); |
| 410 | 410 | ||
| 411 | let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); | 411 | let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); |
| 412 | 412 | ||
| @@ -436,7 +436,7 @@ impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompare | |||
| 436 | { | 436 | { |
| 437 | unsafe fn on_interrupt() { | 437 | unsafe fn on_interrupt() { |
| 438 | #[cfg(feature = "low-power")] | 438 | #[cfg(feature = "low-power")] |
| 439 | crate::low_power::Executor::on_wakeup_irq(); | 439 | crate::low_power::Executor::on_wakeup_irq_or_event(); |
| 440 | 440 | ||
| 441 | let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); | 441 | let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); |
| 442 | 442 | ||
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index b79ed364b..9a5f0fd1d 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -11,6 +11,7 @@ use crate::dma::word::Word; | |||
| 11 | #[cfg(gpio_v2)] | 11 | #[cfg(gpio_v2)] |
| 12 | use crate::gpio::Pull; | 12 | use crate::gpio::Pull; |
| 13 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 13 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 14 | use crate::pac::timer::vals::Ccds; | ||
| 14 | use crate::time::Hertz; | 15 | use crate::time::Hertz; |
| 15 | 16 | ||
| 16 | /// PWM pin wrapper. | 17 | /// PWM pin wrapper. |
| @@ -339,6 +340,25 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 339 | /// Generate a sequence of PWM waveform | 340 | /// Generate a sequence of PWM waveform |
| 340 | /// | 341 | /// |
| 341 | /// Note: | 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); | ||
| 357 | } | ||
| 358 | |||
| 359 | /// Generate a sequence of PWM waveform | ||
| 360 | /// | ||
| 361 | /// Note: | ||
| 342 | /// 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. |
| 343 | /// 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 |
| 344 | /// switch this timer by using `time-driver-timX` feature. | 364 | /// switch this timer by using `time-driver-timX` feature. |
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 f90875166..431f4ddde 100644 --- a/embassy-usb/CHANGELOG.md +++ b/embassy-usb/CHANGELOG.md | |||
| @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 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 | 13 | - Fix a bug where CDC ACM BufferedReceiver repeats data when its future is dropped |
| 14 | - Expose `dtr()` and `rts()` on `cdc_acm::ControlChanged` | ||
| 14 | 15 | ||
| 15 | ## 0.5.1 - 2025-08-26 | 16 | ## 0.5.1 - 2025-08-26 |
| 16 | 17 | ||
diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index c990b679e..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. |
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..d2cda631c --- /dev/null +++ b/examples/mcxa/src/bin/adc_interrupt.rs | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::{Duration, Ticker}; | ||
| 6 | use hal::adc::{Adc, InterruptHandler, LpadcConfig, TriggerPriorityPolicy}; | ||
| 7 | use hal::bind_interrupts; | ||
| 8 | use hal::clocks::PoweredClock; | ||
| 9 | use hal::clocks::config::Div8; | ||
| 10 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | ||
| 11 | use hal::config::Config; | ||
| 12 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | ||
| 13 | use hal::pac::adc1::cmdl1::Mode; | ||
| 14 | use hal::pac::adc1::ctrl::CalAvgs; | ||
| 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 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 42 | source: AdcClockSel::FroLfDiv, | ||
| 43 | div: Div4::no_div(), | ||
| 44 | }; | ||
| 45 | let mut adc = Adc::new_async(p.ADC1, p.P1_10, Irqs, adc_config).unwrap(); | ||
| 46 | |||
| 47 | adc.do_offset_calibration(); | ||
| 48 | adc.do_auto_calibration(); | ||
| 49 | adc.set_resolution(Mode::Data16Bits); | ||
| 50 | |||
| 51 | defmt::info!("ADC configuration done..."); | ||
| 52 | let mut ticker = Ticker::every(Duration::from_millis(100)); | ||
| 53 | |||
| 54 | loop { | ||
| 55 | ticker.next().await; | ||
| 56 | match adc.read().await { | ||
| 57 | Ok(value) => { | ||
| 58 | defmt::info!("ADC value: {}", value); | ||
| 59 | } | ||
| 60 | Err(e) => { | ||
| 61 | defmt::error!("ADC read error: {:?}", e); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs new file mode 100644 index 000000000..5c4d5524c --- /dev/null +++ b/examples/mcxa/src/bin/adc_polling.rs | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::adc::{ConvCommandConfig, ConvTriggerConfig}; | ||
| 6 | use embassy_time::{Duration, Ticker}; | ||
| 7 | use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy}; | ||
| 8 | use hal::clocks::PoweredClock; | ||
| 9 | use hal::clocks::config::Div8; | ||
| 10 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | ||
| 11 | use hal::config::Config; | ||
| 12 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | ||
| 13 | use hal::pac::adc1::cmdl1::Mode; | ||
| 14 | use hal::pac::adc1::ctrl::CalAvgs; | ||
| 15 | use hal::pac::adc1::tctrl::Tcmd; | ||
| 16 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 17 | |||
| 18 | const G_LPADC_RESULT_SHIFT: u32 = 0; | ||
| 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!("=== ADC polling Example ==="); | ||
| 28 | |||
| 29 | let adc_config = LpadcConfig { | ||
| 30 | enable_in_doze_mode: true, | ||
| 31 | conversion_average_mode: CalAvgs::Average128, | ||
| 32 | enable_analog_preliminary: true, | ||
| 33 | power_up_delay: 0x80, | ||
| 34 | reference_voltage_source: Refsel::Option3, | ||
| 35 | power_level_mode: Pwrsel::Lowest, | ||
| 36 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, | ||
| 37 | enable_conv_pause: false, | ||
| 38 | conv_pause_delay: 0, | ||
| 39 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 40 | source: AdcClockSel::FroLfDiv, | ||
| 41 | div: Div4::no_div(), | ||
| 42 | }; | ||
| 43 | let adc = Adc::new_blocking(p.ADC1, p.P1_10, adc_config).unwrap(); | ||
| 44 | |||
| 45 | adc.do_offset_calibration(); | ||
| 46 | adc.do_auto_calibration(); | ||
| 47 | |||
| 48 | let conv_command_config = ConvCommandConfig { | ||
| 49 | conversion_resolution_mode: Mode::Data16Bits, | ||
| 50 | ..ConvCommandConfig::default() | ||
| 51 | }; | ||
| 52 | adc.set_conv_command_config(1, &conv_command_config).unwrap(); | ||
| 53 | |||
| 54 | let conv_trigger_config = ConvTriggerConfig { | ||
| 55 | target_command_id: Tcmd::ExecuteCmd1, | ||
| 56 | enable_hardware_trigger: false, | ||
| 57 | ..Default::default() | ||
| 58 | }; | ||
| 59 | adc.set_conv_trigger_config(0, &conv_trigger_config).unwrap(); | ||
| 60 | |||
| 61 | defmt::info!("=== ADC configuration done... ==="); | ||
| 62 | let mut tick = Ticker::every(Duration::from_millis(100)); | ||
| 63 | |||
| 64 | loop { | ||
| 65 | tick.next().await; | ||
| 66 | adc.do_software_trigger(1).unwrap(); | ||
| 67 | let result = loop { | ||
| 68 | match adc.get_conv_result() { | ||
| 69 | Ok(res) => break res, | ||
| 70 | Err(_) => { | ||
| 71 | // Conversion not ready, continue polling | ||
| 72 | } | ||
| 73 | } | ||
| 74 | }; | ||
| 75 | let value = result.conv_value >> G_LPADC_RESULT_SHIFT; | ||
| 76 | defmt::info!("ADC value: {=u16}", value); | ||
| 77 | } | ||
| 78 | } | ||
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..1e52912d3 --- /dev/null +++ b/examples/mcxa/src/bin/clkout.rs | |||
| @@ -0,0 +1,68 @@ | |||
| 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, Level, Output, SlewRate}; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 10 | |||
| 11 | /// Demonstrate CLKOUT, using Pin P4.2 | ||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let p = hal::init(hal::config::Config::default()); | ||
| 15 | let mut pin = p.P4_2; | ||
| 16 | let mut clkout = p.CLKOUT; | ||
| 17 | |||
| 18 | loop { | ||
| 19 | defmt::info!("Set Low..."); | ||
| 20 | let mut output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); | ||
| 21 | Timer::after_millis(500).await; | ||
| 22 | |||
| 23 | defmt::info!("Set High..."); | ||
| 24 | output.set_high(); | ||
| 25 | Timer::after_millis(400).await; | ||
| 26 | |||
| 27 | defmt::info!("Set Low..."); | ||
| 28 | output.set_low(); | ||
| 29 | Timer::after_millis(500).await; | ||
| 30 | |||
| 31 | defmt::info!("16k..."); | ||
| 32 | // Run Clock Out with the 16K clock | ||
| 33 | let _clock_out = ClockOut::new( | ||
| 34 | clkout.reborrow(), | ||
| 35 | pin.reborrow(), | ||
| 36 | Config { | ||
| 37 | sel: ClockOutSel::Clk16K, | ||
| 38 | div: Div4::no_div(), | ||
| 39 | level: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 40 | }, | ||
| 41 | ) | ||
| 42 | .unwrap(); | ||
| 43 | |||
| 44 | Timer::after_millis(3000).await; | ||
| 45 | |||
| 46 | defmt::info!("Set Low..."); | ||
| 47 | drop(_clock_out); | ||
| 48 | |||
| 49 | let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); | ||
| 50 | Timer::after_millis(500).await; | ||
| 51 | |||
| 52 | // Run Clock Out with the 12M clock, divided by 3 | ||
| 53 | defmt::info!("4M..."); | ||
| 54 | let _clock_out = ClockOut::new( | ||
| 55 | clkout.reborrow(), | ||
| 56 | pin.reborrow(), | ||
| 57 | Config { | ||
| 58 | sel: ClockOutSel::Fro12M, | ||
| 59 | div: const { Div4::from_divisor(3).unwrap() }, | ||
| 60 | level: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 61 | }, | ||
| 62 | ) | ||
| 63 | .unwrap(); | ||
| 64 | |||
| 65 | // Let it run for 3 seconds... | ||
| 66 | Timer::after_millis(3000).await; | ||
| 67 | } | ||
| 68 | } | ||
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..bd706d712 --- /dev/null +++ b/examples/mcxa/src/bin/i2c-scan-blocking.rs | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::gpio::{Input, Pull}; | ||
| 6 | use embassy_time::Timer; | ||
| 7 | use hal::clocks::config::Div8; | ||
| 8 | use hal::config::Config; | ||
| 9 | use hal::i2c::controller::{self, I2c, Speed}; | ||
| 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 | |||
| 24 | // Note: P0_2 is connected to P1_8 on the FRDM_MCXA276 via a resistor, and | ||
| 25 | // defaults to SWO on the debug peripheral. Explicitly make it a high-z | ||
| 26 | // input. | ||
| 27 | let _pin = Input::new(p.P0_2, Pull::Disabled); | ||
| 28 | let mut i2c = I2c::new_blocking(p.LPI2C2, p.P1_9, p.P1_8, config).unwrap(); | ||
| 29 | |||
| 30 | for addr in 0x01..=0x7f { | ||
| 31 | let result = i2c.blocking_write(addr, &[]); | ||
| 32 | if result.is_ok() { | ||
| 33 | defmt::info!("Device found at addr {:02x}", addr); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | loop { | ||
| 38 | Timer::after_secs(10).await; | ||
| 39 | } | ||
| 40 | } | ||
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/reset-reason.rs b/examples/mcxa/src/bin/reset-reason.rs new file mode 100644 index 000000000..c244fbe04 --- /dev/null +++ b/examples/mcxa/src/bin/reset-reason.rs | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use hal::config::Config; | ||
| 6 | use hal::reset_reason::reset_reason; | ||
| 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 config = Config::default(); | ||
| 12 | let _p = hal::init(config); | ||
| 13 | |||
| 14 | defmt::info!("Reset Reason: '{}'", reset_reason()); | ||
| 15 | } | ||
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/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/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(); |
