aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-mcxa/.cargo/config.toml2
-rw-r--r--embassy-mcxa/.github/DOCS.md23
-rw-r--r--embassy-mcxa/.github/codecov.yml21
-rw-r--r--embassy-mcxa/.github/dependabot.yml19
-rw-r--r--embassy-mcxa/.github/workflows/cargo-vet-pr-comment.yml137
-rw-r--r--embassy-mcxa/.github/workflows/cargo-vet.yml53
-rw-r--r--embassy-mcxa/.github/workflows/check.yml215
-rw-r--r--embassy-mcxa/.github/workflows/nostd.yml38
-rw-r--r--embassy-mcxa/.github/workflows/rolling.yml58
-rw-r--r--embassy-mcxa/.gitignore19
-rw-r--r--embassy-mcxa/CODEOWNERS5
-rw-r--r--embassy-mcxa/CODE_OF_CONDUCT.md132
-rw-r--r--embassy-mcxa/CONTRIBUTING.md28
-rw-r--r--embassy-mcxa/Cargo.lock918
-rw-r--r--embassy-mcxa/Cargo.toml53
-rw-r--r--embassy-mcxa/Embed.toml28
-rw-r--r--embassy-mcxa/LICENSE21
-rw-r--r--embassy-mcxa/README.md13
-rw-r--r--embassy-mcxa/SECURITY.md66
-rw-r--r--embassy-mcxa/SW-Content-Register.txt78
-rw-r--r--embassy-mcxa/defmt.x6
-rw-r--r--embassy-mcxa/deny.toml241
-rw-r--r--embassy-mcxa/ram.ld109
-rw-r--r--embassy-mcxa/run.sh93
-rw-r--r--embassy-mcxa/rustfmt.toml3
-rw-r--r--embassy-mcxa/src/adc.rs409
-rw-r--r--embassy-mcxa/src/clkout.rs169
-rw-r--r--embassy-mcxa/src/clocks/config.rs204
-rw-r--r--embassy-mcxa/src/clocks/mod.rs943
-rw-r--r--embassy-mcxa/src/clocks/periph_helpers.rs502
-rw-r--r--embassy-mcxa/src/config.rs27
-rw-r--r--embassy-mcxa/src/gpio.rs1062
-rw-r--r--embassy-mcxa/src/i2c/controller.rs742
-rw-r--r--embassy-mcxa/src/i2c/mod.rs194
-rw-r--r--embassy-mcxa/src/interrupt.rs563
-rw-r--r--embassy-mcxa/src/lib.rs471
-rw-r--r--embassy-mcxa/src/lpuart/buffered.rs780
-rw-r--r--embassy-mcxa/src/lpuart/mod.rs1292
-rw-r--r--embassy-mcxa/src/ostimer.rs745
-rw-r--r--embassy-mcxa/src/pins.rs28
-rw-r--r--embassy-mcxa/src/rtc.rs453
-rw-r--r--embassy-mcxa/supply-chain/README.md149
-rw-r--r--embassy-mcxa/supply-chain/audits.toml349
-rw-r--r--embassy-mcxa/supply-chain/config.toml141
-rw-r--r--embassy-mcxa/supply-chain/imports.lock523
-rw-r--r--embassy-mcxa/tools/run_and_attach_rtt.sh24
-rw-r--r--embassy-mcxa/tools/run_jlink_noblock.sh76
-rw-r--r--examples/mcxa/.cargo/config.toml17
-rw-r--r--examples/mcxa/.gitignore1
-rw-r--r--examples/mcxa/Cargo.lock1555
-rw-r--r--examples/mcxa/Cargo.toml26
-rw-r--r--examples/mcxa/build.rs20
-rw-r--r--examples/mcxa/memory.x5
-rw-r--r--examples/mcxa/src/bin/adc_interrupt.rs84
-rw-r--r--examples/mcxa/src/bin/adc_polling.rs68
-rw-r--r--examples/mcxa/src/bin/blinky.rs36
-rw-r--r--examples/mcxa/src/bin/button.rs23
-rw-r--r--examples/mcxa/src/bin/button_async.rs29
-rw-r--r--examples/mcxa/src/bin/clkout.rs69
-rw-r--r--examples/mcxa/src/bin/hello.rs119
-rw-r--r--examples/mcxa/src/bin/i2c-async.rs39
-rw-r--r--examples/mcxa/src/bin/i2c-blocking.rs31
-rw-r--r--examples/mcxa/src/bin/i2c-scan-blocking.rs41
-rw-r--r--examples/mcxa/src/bin/lpuart_buffered.rs62
-rw-r--r--examples/mcxa/src/bin/lpuart_polling.rs47
-rw-r--r--examples/mcxa/src/bin/rtc_alarm.rs47
-rw-r--r--examples/mcxa/src/lib.rs16
67 files changed, 14560 insertions, 0 deletions
diff --git a/embassy-mcxa/.cargo/config.toml b/embassy-mcxa/.cargo/config.toml
new file mode 100644
index 000000000..55dd5ea5f
--- /dev/null
+++ b/embassy-mcxa/.cargo/config.toml
@@ -0,0 +1,2 @@
1[build]
2target = "thumbv8m.main-none-eabihf" # Cortex-M33
diff --git a/embassy-mcxa/.github/DOCS.md b/embassy-mcxa/.github/DOCS.md
new file mode 100644
index 000000000..e932784c7
--- /dev/null
+++ b/embassy-mcxa/.github/DOCS.md
@@ -0,0 +1,23 @@
1# Github config and workflows
2
3In this folder there is configuration for codecoverage, dependabot, and ci
4workflows that check the library more deeply than the default configurations.
5
6This folder can be or was merged using a --allow-unrelated-histories merge
7strategy from <https://github.com/jonhoo/rust-ci-conf/> which provides a
8reasonably sensible base for writing your own ci on. By using this strategy
9the history of the CI repo is included in your repo, and future updates to
10the CI can be merged later.
11
12To perform this merge run:
13
14```shell
15git remote add ci https://github.com/jonhoo/rust-ci-conf.git
16git fetch ci
17git merge --allow-unrelated-histories ci/main
18```
19
20An overview of the files in this project is available at:
21<https://www.youtube.com/watch?v=xUH-4y92jPg&t=491s>, which contains some
22rationale for decisions and runs through an example of solving minimal version
23and OpenSSL issues.
diff --git a/embassy-mcxa/.github/codecov.yml b/embassy-mcxa/.github/codecov.yml
new file mode 100644
index 000000000..cd5ce8fc1
--- /dev/null
+++ b/embassy-mcxa/.github/codecov.yml
@@ -0,0 +1,21 @@
1# ref: https://docs.codecov.com/docs/codecovyml-reference
2coverage:
3 # Hold ourselves to a high bar
4 range: 85..100
5 round: down
6 precision: 1
7 status:
8 # ref: https://docs.codecov.com/docs/commit-status
9 project:
10 default:
11 # Avoid false negatives
12 threshold: 1%
13
14# Test files aren't important for coverage
15ignore:
16 - "tests"
17
18# Make comments less noisy
19comment:
20 layout: "files"
21 require_changes: true
diff --git a/embassy-mcxa/.github/dependabot.yml b/embassy-mcxa/.github/dependabot.yml
new file mode 100644
index 000000000..d0f091e7b
--- /dev/null
+++ b/embassy-mcxa/.github/dependabot.yml
@@ -0,0 +1,19 @@
1version: 2
2updates:
3 - package-ecosystem: github-actions
4 directory: /
5 schedule:
6 interval: daily
7 - package-ecosystem: cargo
8 directory: /
9 schedule:
10 interval: daily
11 ignore:
12 - dependency-name: "*"
13 # patch and minor updates don't matter for libraries as consumers of this library build
14 # with their own lockfile, rather than the version specified in this library's lockfile
15 # remove this ignore rule if your package has binaries to ensure that the binaries are
16 # built with the exact set of dependencies and those are up to date.
17 update-types:
18 - "version-update:semver-patch"
19 - "version-update:semver-minor"
diff --git a/embassy-mcxa/.github/workflows/cargo-vet-pr-comment.yml b/embassy-mcxa/.github/workflows/cargo-vet-pr-comment.yml
new file mode 100644
index 000000000..66f27ceab
--- /dev/null
+++ b/embassy-mcxa/.github/workflows/cargo-vet-pr-comment.yml
@@ -0,0 +1,137 @@
1# This workflow triggers after cargo-vet workflow has run.
2# It adds a comment to the PR with the results of the cargo vet run.
3# It first adds a comment if the cargo vet run fails,
4# and updates the comment if the cargo vet run succeeds after having failed at least once.
5
6name: Cargo vet PR comment
7
8on:
9 workflow_run:
10 workflows: [cargo-vet]
11 types:
12 - completed
13
14permissions:
15 contents: read
16 pull-requests: write
17
18concurrency:
19 group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
20 cancel-in-progress: true
21
22jobs:
23
24 find-pr-comment:
25 # This job runs when the cargo-vet job fails or succeeds
26 # It will download the artifact from the failed job and post a comment on the PR
27 runs-on: ubuntu-latest
28 outputs:
29 comment-id: ${{ steps.get-comment-id.outputs.comment-id }}
30 pr-number: ${{ steps.get-pr-number.outputs.pr_number }}
31 if: github.event.workflow_run.event == 'pull_request'
32 steps:
33 - name: 'Download artifact'
34 uses: actions/download-artifact@v6
35 with:
36 github-token: ${{ secrets.GITHUB_TOKEN }}
37 name: pr
38 path: pr/
39 run-id: ${{ github.event.workflow_run.id }}
40
41 - name: 'Get PR number'
42 id: get-pr-number
43 run: echo "pr_number=$(cat ./pr/NR)" >> $GITHUB_OUTPUT
44
45 - name: 'Find existing comment'
46 id: find-comment
47 uses: peter-evans/find-comment@v4
48 with:
49 issue-number: ${{ steps.get-pr-number.outputs.pr_number }}
50 comment-author: 'github-actions[bot]'
51 body-includes: 'comment-tag: [cargo-vet]'
52
53 - name: 'Get comment ID'
54 id: get-comment-id
55 if: ${{ steps.find-comment.outputs.comment-id != '' }}
56 run: echo "comment-id=${{ steps.find-comment.outputs.comment-id }}" >> $GITHUB_OUTPUT
57
58 post-comment-failure:
59 # This job runs when the cargo-vet job fails
60 # It will download the artifact from the failed job and post a comment on the PR
61 runs-on: ubuntu-latest
62 needs: find-pr-comment
63 if: github.event.workflow_run.conclusion == 'failure'
64 steps:
65 - name: 'Comment on PR - Failure'
66 uses: peter-evans/create-or-update-comment@v5
67 with:
68 comment-id: ${{ needs.find-pr-comment.outputs.comment-id }}
69 issue-number: ${{ needs.find-pr-comment.outputs.pr-number }}
70 body: |
71 # Cargo Vet Audit Failed
72
73 `cargo vet` has failed in this PR. Please run `cargo vet --locked` locally to check for new or updated unvetted dependencies.
74 Details about the vetting process can be found in [supply-chain/README.md](../blob/main/supply-chain/README.md)
75
76 ## If the unvetted dependencies are not needed
77 Please modify Cargo.toml file to avoid including the dependencies.
78
79 ## If the unvetted dependencies are needed
80 Post a new comment with the questionnaire below to the PR to help the auditors vet the dependencies.
81 After the auditors have vetted the dependencies, the PR will need to be rebased to pick up the new audits and pass this check.
82
83 ### Copy and paste the questionnaire as a new comment and provide your answers:
84
85 **1. What crates (with version) need to be audited?**
86
87 **2. How many of the crates are version updates vs new dependencies?**
88
89 **3. To confirm none of the already included crates serve your needs, please provide a brief description of the purpose of the new crates.**
90
91 **4. Any extra notes to the auditors to help with their audits.**
92
93 <!--
94 This comment is auto-generated by the cargo-vet workflow.
95 Please do not edit it directly.
96
97 comment-tag: [cargo-vet]
98 -->
99 edit-mode: replace
100
101 - name: 'Label PR'
102 uses: actions/github-script@v8
103 with:
104 script: |
105 github.rest.issues.addLabels({
106 issue_number: ${{ needs.find-pr-comment.outputs.pr-number }},
107 owner: context.repo.owner,
108 repo: context.repo.repo,
109 labels: ['cargo vet']
110 })
111
112 post-comment-success:
113 # This job runs when the cargo-vet job succeeds
114 # It will update the comment on the PR with a success message
115 runs-on: ubuntu-latest
116 needs: find-pr-comment
117 if: github.event.workflow_run.conclusion == 'success'
118 steps:
119 - name: 'Comment on PR - Success'
120 # Only update the comment if it exists
121 # This is to avoid creating a new comment if the cargo-vet job has never failed before
122 if: ${{ needs.find-pr-comment.outputs.comment-id }}
123 uses: peter-evans/create-or-update-comment@v5
124 with:
125 comment-id: ${{ needs.find-pr-comment.outputs.comment-id }}
126 issue-number: ${{ needs.find-pr-comment.outputs.pr-number }}
127 body: |
128 # Cargo Vet Audit Passed
129 `cargo vet` has passed in this PR. No new unvetted dependencies were found.
130
131 <!--
132 This comment is auto-generated by the cargo-vet workflow.
133 Please do not edit it directly.
134
135 comment-tag: [cargo-vet]
136 -->
137 edit-mode: replace
diff --git a/embassy-mcxa/.github/workflows/cargo-vet.yml b/embassy-mcxa/.github/workflows/cargo-vet.yml
new file mode 100644
index 000000000..d585d1df5
--- /dev/null
+++ b/embassy-mcxa/.github/workflows/cargo-vet.yml
@@ -0,0 +1,53 @@
1# This workflow runs whenever a PR is opened or updated. It runs cargo vet to check for unvetted dependencies in the Cargo.lock file.
2permissions:
3 contents: read
4on:
5 pull_request:
6
7concurrency:
8 group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
9 cancel-in-progress: true
10
11name: cargo-vet
12jobs:
13 vet:
14 # cargo-vet checks for unvetted dependencies in the Cargo.lock file
15 # This is to ensure that new dependencies are vetted before they are added to the project
16 name: vet-dependencies
17 runs-on: ubuntu-latest
18 env:
19 CARGO_VET_VERSION: 0.10.1
20
21 steps:
22 - uses: actions/checkout@v6
23 with:
24 submodules: true
25
26 - uses: actions/cache@v4
27 with:
28 path: ${{ runner.tool_cache }}/cargo-vet
29 key: cargo-vet-bin-${{ env.CARGO_VET_VERSION }}
30
31 - name: Add the tool cache directory to the search path
32 run: echo "${{ runner.tool_cache }}/cargo-vet/bin" >> $GITHUB_PATH
33
34 - name: Ensure that the tool cache is populated with the cargo-vet binary
35 run: cargo install --root ${{ runner.tool_cache }}/cargo-vet --version ${{ env.CARGO_VET_VERSION }} cargo-vet
36
37 - name: Invoke cargo-vet
38 run: cargo vet --locked
39
40 - name: Save PR number
41 # PR number is saved as an artifact so it can be used to determine the PR to comment on by the vet-pr-comment workflow
42 # vet-pr-comment workflow is triggered by the workflow_run event so it runs in the context of the base branch and not the PR branch
43 if: ${{ failure() }} || ${{ success() }}
44 run: |
45 mkdir -p ./pr
46 echo ${{ github.event.number }} > ./pr/NR
47 - uses: actions/upload-artifact@v5
48 # Need to upload the artifact in both success and failure cases so comment can be updated in either case
49 if: ${{ failure() }} || ${{ success() }}
50 with:
51 name: pr
52 path: pr/
53 overwrite: true \ No newline at end of file
diff --git a/embassy-mcxa/.github/workflows/check.yml b/embassy-mcxa/.github/workflows/check.yml
new file mode 100644
index 000000000..ad9f44428
--- /dev/null
+++ b/embassy-mcxa/.github/workflows/check.yml
@@ -0,0 +1,215 @@
1# This workflow runs whenever a PR is opened or updated, or a commit is pushed to main. It runs
2# several checks:
3# - fmt: checks that the code is formatted according to rustfmt
4# - clippy: checks that the code does not contain any clippy warnings
5# - doc: checks that the code can be documented without errors
6# - hack: check combinations of feature flags
7# - msrv: check that the msrv specified in the crate is correct
8permissions:
9 contents: read
10
11# This configuration allows maintainers of this repo to create a branch and pull request based on
12# the new branch. Restricting the push trigger to the main branch ensures that the PR only gets
13# built once.
14on:
15
16 push:
17 branches: [main, main-nextgen]
18 pull_request:
19
20# If new code is pushed to a PR branch, then cancel in progress workflows for that PR. Ensures that
21# we don't waste CI time, and returns results quicker https://github.com/jonhoo/rust-ci-conf/pull/5
22concurrency:
23 group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
24 cancel-in-progress: true
25
26name: check
27
28jobs:
29
30 fmt:
31 runs-on: ubuntu-latest
32 name: nightly / fmt
33
34 strategy:
35 fail-fast: false
36 matrix:
37 workdir: [ ".", "examples",]
38
39 steps:
40 - uses: actions/checkout@v6
41 with:
42 submodules: true
43
44 - name: Install nightly
45 uses: dtolnay/rust-toolchain@nightly
46 with:
47 components: rustfmt
48 targets: thumbv8m.main-none-eabihf
49
50 - name: cargo fmt --check
51 run: cargo fmt --check
52 working-directory: ${{ matrix.workdir }}
53
54 clippy-examples:
55 runs-on: ubuntu-latest
56 name: ${{ matrix.toolchain }} / clippy
57
58 permissions:
59 contents: read
60 checks: write
61
62 strategy:
63 fail-fast: false
64 matrix:
65 # Get early warning of new lints which are regularly introduced in beta channels.
66 toolchain: [stable]
67 workdir: ["examples"]
68
69 steps:
70 - uses: actions/checkout@v6
71 with:
72 submodules: true
73
74 - name: Install ${{ matrix.toolchain }}
75 uses: dtolnay/rust-toolchain@master
76 with:
77 toolchain: ${{ matrix.toolchain }}
78 components: clippy
79 targets: thumbv8m.main-none-eabihf
80
81 - name: cargo clippy
82 working-directory: ${{ matrix.workdir }}
83 run: |
84 cargo clippy --locked -- -Dwarnings -D clippy::suspicious -D clippy::correctness -D clippy::perf -D clippy::style
85
86 # Enable once we have a released crate
87 # semver:
88 # runs-on: ubuntu-latest
89 # name: semver
90 # steps:
91 # - uses: actions/checkout@v6
92 # with:
93 # submodules: true
94 # - name: Install stable
95 # uses: dtolnay/rust-toolchain@stable
96 # with:
97 # components: rustfmt
98 # - name: cargo-semver-checks
99 # uses: obi1kenobi/cargo-semver-checks-action@v2
100
101 doc:
102 # run docs generation on nightly rather than stable. This enables features like
103 # https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html which allows an
104 # API be documented as only available in some specific platforms.
105 runs-on: ubuntu-latest
106 name: nightly / doc
107
108 steps:
109 - uses: actions/checkout@v6
110 with:
111 submodules: true
112
113 - name: Install nightly
114 uses: dtolnay/rust-toolchain@nightly
115 with:
116 targets: thumbv8m.main-none-eabihf
117
118 - name: cargo doc
119 run: |
120 cargo doc --no-deps --all-features --locked
121 env:
122 RUSTDOCFLAGS: --cfg docsrs
123
124 hack:
125 # cargo-hack checks combinations of feature flags to ensure that features are all additive
126 # which is required for feature unification
127 runs-on: ubuntu-latest
128 name: ubuntu / stable / features
129
130 strategy:
131 fail-fast: false
132
133 steps:
134 - uses: actions/checkout@v6
135 with:
136 submodules: true
137
138 - name: Install stable
139 uses: dtolnay/rust-toolchain@stable
140 with:
141 toolchain: stable
142 components: clippy
143 targets: thumbv8m.main-none-eabihf
144
145 - name: cargo install cargo-hack
146 uses: taiki-e/install-action@cargo-hack
147
148 - name: cargo hack
149 run: cargo hack --feature-powerset check
150
151 deny:
152 # cargo-deny checks licenses, advisories, sources, and bans for
153 # our dependencies.
154 runs-on: ubuntu-latest
155 name: ubuntu / stable / deny
156
157 steps:
158 - uses: actions/checkout@v6
159 with:
160 submodules: true
161
162 - name: Install stable
163 uses: dtolnay/rust-toolchain@stable
164 with:
165 targets: thumbv8m.main-none-eabihf
166
167 - name: cargo install cargo-deny
168 uses: EmbarkStudios/cargo-deny-action@v2
169 with:
170 log-level: warn
171 manifest-path: ./Cargo.toml
172 command: check
173 arguments: --all-features --locked
174
175 msrv:
176 # check that we can build using the minimal rust version that is specified by this crate
177 runs-on: ubuntu-latest
178 # we use a matrix here just because env can't be used in job names
179 # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability
180 strategy:
181 fail-fast: false
182 matrix:
183 msrv: ["1.91"] # We're relying on namespaced-features, which
184 # was released in 1.60
185 #
186 # We also depend on `fixed' which requires rust
187 # 1.71
188 #
189 # Additionally, we depend on embedded-hal-async
190 # which requires 1.75
191 #
192 # embassy-time requires 1.79 due to
193 # collapse_debuginfo
194 #
195 # embassy upstream switched to rust 1.85
196 #
197 # unsigned_is_multiple_of requires 1.90, else we get clippy warnings
198 #
199 # [email protected] requires rustc 1.91
200
201 name: ubuntu / ${{ matrix.msrv }}
202 steps:
203 - uses: actions/checkout@v6
204 with:
205 submodules: true
206
207 - name: Install ${{ matrix.msrv }}
208 uses: dtolnay/rust-toolchain@master
209 with:
210 toolchain: ${{ matrix.msrv }}
211 targets: thumbv8m.main-none-eabihf
212
213 - name: cargo +${{ matrix.msrv }} check
214 run: |
215 cargo check --all-features --locked
diff --git a/embassy-mcxa/.github/workflows/nostd.yml b/embassy-mcxa/.github/workflows/nostd.yml
new file mode 100644
index 000000000..cf6629854
--- /dev/null
+++ b/embassy-mcxa/.github/workflows/nostd.yml
@@ -0,0 +1,38 @@
1# This workflow checks whether the library is able to run without the std library (e.g., embedded).
2# This entire file should be removed if this crate does not support no-std. See check.yml for
3# information about how the concurrency cancellation and workflow triggering works
4permissions:
5 contents: read
6
7on:
8 push:
9 branches: [main, main-nextgen]
10 pull_request:
11
12concurrency:
13 group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
14 cancel-in-progress: true
15
16name: no-std
17
18jobs:
19 nostd:
20 runs-on: ubuntu-latest
21 name: ${{ matrix.target }}
22
23 steps:
24 - uses: actions/checkout@v6
25 with:
26 submodules: true
27
28 - name: Install stable
29 uses: dtolnay/rust-toolchain@stable
30 with:
31 targets: thumbv8m.main-none-eabihf
32
33 - name: Show variable
34 run: echo ${{ env.TOKEN }}
35
36 - name: cargo check
37 run: |
38 cargo check --target thumbv8m.main-none-eabihf --all-features --locked
diff --git a/embassy-mcxa/.github/workflows/rolling.yml b/embassy-mcxa/.github/workflows/rolling.yml
new file mode 100644
index 000000000..73b48484a
--- /dev/null
+++ b/embassy-mcxa/.github/workflows/rolling.yml
@@ -0,0 +1,58 @@
1# This workflow runs every morning at midnight. It will run cargo hack
2# and a build with msrv. If any dependency breaks our crate, we will
3# know ASAP.
4#
5# - check: build with all features
6# - msrv: check that the msrv specified in the crate is correct
7permissions:
8 contents: read
9
10on:
11 schedule:
12 - cron: '0 0 * * *'
13
14name: rolling
15jobs:
16
17 check:
18 runs-on: ubuntu-latest
19 name: ubuntu / stable / features
20 strategy:
21 fail-fast: false
22 steps:
23 - uses: actions/checkout@v6
24 with:
25 submodules: true
26 - name: Install stable
27 uses: dtolnay/rust-toolchain@stable
28 with:
29 targets: thumbv8m.main-none-eabihf
30
31 - name: cargo install cargo-hack
32 uses: taiki-e/install-action@cargo-hack
33 - name: cargo check
34 run: |
35 cargo update
36 cargo check --all-features
37
38 msrv:
39 runs-on: ubuntu-latest
40 strategy:
41 fail-fast: false
42 matrix:
43 msrv: ["1.91"]
44
45 name: ubuntu / ${{ matrix.msrv }} (${{ matrix.commit }})
46 steps:
47 - uses: actions/checkout@v6
48 with:
49 submodules: true
50 - name: Install ${{ matrix.msrv }}
51 uses: dtolnay/rust-toolchain@master
52 with:
53 toolchain: ${{ matrix.msrv }}
54 targets: thumbv8m.main-none-eabihf
55 - name: cargo +${{ matrix.msrv }} check
56 run: |
57 cargo update
58 cargo check --all-features
diff --git a/embassy-mcxa/.gitignore b/embassy-mcxa/.gitignore
new file mode 100644
index 000000000..d128a49cb
--- /dev/null
+++ b/embassy-mcxa/.gitignore
@@ -0,0 +1,19 @@
1# Rust
2/target/
3
4# IDE
5.vscode/
6.idea/
7
8# OS
9.DS_Store
10Thumbs.db
11
12# Embedded
13*.bin
14*.hex
15*.elf
16*.map
17
18# Debug
19*.log
diff --git a/embassy-mcxa/CODEOWNERS b/embassy-mcxa/CODEOWNERS
new file mode 100644
index 000000000..6e0f4c4c6
--- /dev/null
+++ b/embassy-mcxa/CODEOWNERS
@@ -0,0 +1,5 @@
1* @jamesmunns @felipebalbi
2
3# Any changes in the supply-chain folder needs approval
4# from auditors as it relates to cargo vet
5**/supply-chain @OpenDevicePartnership/crate-auditors \ No newline at end of file
diff --git a/embassy-mcxa/CODE_OF_CONDUCT.md b/embassy-mcxa/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..54a673e04
--- /dev/null
+++ b/embassy-mcxa/CODE_OF_CONDUCT.md
@@ -0,0 +1,132 @@
1# Contributor Covenant Code of Conduct
2
3## Our Pledge
4
5We as members, contributors, and leaders pledge to make participation in our
6community a harassment-free experience for everyone, regardless of age, body
7size, visible or invisible disability, ethnicity, sex characteristics, gender
8identity and expression, level of experience, education, socio-economic status,
9nationality, personal appearance, race, caste, color, religion, or sexual
10identity and orientation.
11
12We pledge to act and interact in ways that contribute to an open, welcoming,
13diverse, inclusive, and healthy community.
14
15## Our Standards
16
17Examples of behavior that contributes to a positive environment for our
18community include:
19
20* Demonstrating empathy and kindness toward other people
21* Being respectful of differing opinions, viewpoints, and experiences
22* Giving and gracefully accepting constructive feedback
23* Accepting responsibility and apologizing to those affected by our mistakes,
24 and learning from the experience
25* Focusing on what is best not just for us as individuals, but for the overall
26 community
27
28Examples of unacceptable behavior include:
29
30* The use of sexualized language or imagery, and sexual attention or advances of
31 any kind
32* Trolling, insulting or derogatory comments, and personal or political attacks
33* Public or private harassment
34* Publishing others' private information, such as a physical or email address,
35 without their explicit permission
36* Other conduct which could reasonably be considered inappropriate in a
37 professional setting
38
39## Enforcement Responsibilities
40
41Community leaders are responsible for clarifying and enforcing our standards of
42acceptable behavior and will take appropriate and fair corrective action in
43response to any behavior that they deem inappropriate, threatening, offensive,
44or harmful.
45
46Community leaders have the right and responsibility to remove, edit, or reject
47comments, commits, code, wiki edits, issues, and other contributions that are
48not aligned to this Code of Conduct, and will communicate reasons for moderation
49decisions when appropriate.
50
51## Scope
52
53This Code of Conduct applies within all community spaces, and also applies when
54an individual is officially representing the community in public spaces.
55Examples of representing our community include using an official e-mail address,
56posting via an official social media account, or acting as an appointed
57representative at an online or offline event.
58
59## Enforcement
60
61Instances of abusive, harassing, or otherwise unacceptable behavior may be
62reported to the community leaders responsible for enforcement at
63[email protected].
64All complaints will be reviewed and investigated promptly and fairly.
65
66All community leaders are obligated to respect the privacy and security of the
67reporter of any incident.
68
69## Enforcement Guidelines
70
71Community leaders will follow these Community Impact Guidelines in determining
72the consequences for any action they deem in violation of this Code of Conduct:
73
74### 1. Correction
75
76**Community Impact**: Use of inappropriate language or other behavior deemed
77unprofessional or unwelcome in the community.
78
79**Consequence**: A private, written warning from community leaders, providing
80clarity around the nature of the violation and an explanation of why the
81behavior was inappropriate. A public apology may be requested.
82
83### 2. Warning
84
85**Community Impact**: A violation through a single incident or series of
86actions.
87
88**Consequence**: A warning with consequences for continued behavior. No
89interaction with the people involved, including unsolicited interaction with
90those enforcing the Code of Conduct, for a specified period of time. This
91includes avoiding interactions in community spaces as well as external channels
92like social media. Violating these terms may lead to a temporary or permanent
93ban.
94
95### 3. Temporary Ban
96
97**Community Impact**: A serious violation of community standards, including
98sustained inappropriate behavior.
99
100**Consequence**: A temporary ban from any sort of interaction or public
101communication with the community for a specified period of time. No public or
102private interaction with the people involved, including unsolicited interaction
103with those enforcing the Code of Conduct, is allowed during this period.
104Violating these terms may lead to a permanent ban.
105
106### 4. Permanent Ban
107
108**Community Impact**: Demonstrating a pattern of violation of community
109standards, including sustained inappropriate behavior, harassment of an
110individual, or aggression toward or disparagement of classes of individuals.
111
112**Consequence**: A permanent ban from any sort of public interaction within the
113community.
114
115## Attribution
116
117This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118version 2.1, available at
119[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120
121Community Impact Guidelines were inspired by
122[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123
124For answers to common questions about this code of conduct, see the FAQ at
125[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126[https://www.contributor-covenant.org/translations][translations].
127
128[homepage]: https://www.contributor-covenant.org
129[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130[Mozilla CoC]: https://github.com/mozilla/diversity
131[FAQ]: https://www.contributor-covenant.org/faq
132[translations]: https://www.contributor-covenant.org/translations
diff --git a/embassy-mcxa/CONTRIBUTING.md b/embassy-mcxa/CONTRIBUTING.md
new file mode 100644
index 000000000..f10eb5be9
--- /dev/null
+++ b/embassy-mcxa/CONTRIBUTING.md
@@ -0,0 +1,28 @@
1# Contributing to Open Device Partnership
2
3The Open Device Partnership project welcomes your suggestions and contributions! Before opening your first issue or pull request, please review our
4[Code of Conduct](CODE_OF_CONDUCT.md) to understand how our community interacts in an inclusive and respectful manner.
5
6## Contribution Licensing
7
8Most of our code is distributed under the terms of the [MIT license](LICENSE), and when you contribute code that you wrote to our repositories,
9you agree that you are contributing under those same terms. In addition, by submitting your contributions you are indicating that
10you have the right to submit those contributions under those terms.
11
12## Other Contribution Information
13
14If you wish to contribute code or documentation authored by others, or using the terms of any other license, please indicate that clearly in your
15pull request so that the project team can discuss the situation with you.
16
17## Commit Message
18
19* Use meaningful commit messages. See [this blogpost](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
20
21## PR Etiquette
22
23* Create a draft PR first
24* Make sure that your branch has `.github` folder and all the code linting/sanity check workflows are passing in your draft PR before sending it out to code reviewers.
25
26## Regressions
27
28When reporting a regression, please ensure that you use `git bisect` to find the first offending commit, as that will help us finding the culprit a lot faster.
diff --git a/embassy-mcxa/Cargo.lock b/embassy-mcxa/Cargo.lock
new file mode 100644
index 000000000..747a69c08
--- /dev/null
+++ b/embassy-mcxa/Cargo.lock
@@ -0,0 +1,918 @@
1# This file is automatically @generated by Cargo.
2# It is not intended for manual editing.
3version = 4
4
5[[package]]
6name = "aho-corasick"
7version = "1.1.4"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
10dependencies = [
11 "memchr",
12]
13
14[[package]]
15name = "autocfg"
16version = "1.5.0"
17source = "registry+https://github.com/rust-lang/crates.io-index"
18checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
19
20[[package]]
21name = "bare-metal"
22version = "0.2.5"
23source = "registry+https://github.com/rust-lang/crates.io-index"
24checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
25dependencies = [
26 "rustc_version",
27]
28
29[[package]]
30name = "bitfield"
31version = "0.13.2"
32source = "registry+https://github.com/rust-lang/crates.io-index"
33checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
34
35[[package]]
36name = "bitflags"
37version = "1.3.2"
38source = "registry+https://github.com/rust-lang/crates.io-index"
39checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
40
41[[package]]
42name = "byteorder"
43version = "1.5.0"
44source = "registry+https://github.com/rust-lang/crates.io-index"
45checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
46
47[[package]]
48name = "cc"
49version = "1.2.47"
50source = "registry+https://github.com/rust-lang/crates.io-index"
51checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07"
52dependencies = [
53 "find-msvc-tools",
54 "shlex",
55]
56
57[[package]]
58name = "cfg-if"
59version = "1.0.4"
60source = "registry+https://github.com/rust-lang/crates.io-index"
61checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
62
63[[package]]
64name = "cordyceps"
65version = "0.3.4"
66source = "registry+https://github.com/rust-lang/crates.io-index"
67checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a"
68dependencies = [
69 "loom",
70 "tracing",
71]
72
73[[package]]
74name = "cortex-m"
75version = "0.7.7"
76source = "registry+https://github.com/rust-lang/crates.io-index"
77checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
78dependencies = [
79 "bare-metal",
80 "bitfield",
81 "critical-section",
82 "embedded-hal 0.2.7",
83 "volatile-register",
84]
85
86[[package]]
87name = "cortex-m-rt"
88version = "0.7.5"
89source = "registry+https://github.com/rust-lang/crates.io-index"
90checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6"
91dependencies = [
92 "cortex-m-rt-macros",
93]
94
95[[package]]
96name = "cortex-m-rt-macros"
97version = "0.7.5"
98source = "registry+https://github.com/rust-lang/crates.io-index"
99checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
100dependencies = [
101 "proc-macro2",
102 "quote",
103 "syn",
104]
105
106[[package]]
107name = "critical-section"
108version = "1.2.0"
109source = "registry+https://github.com/rust-lang/crates.io-index"
110checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
111
112[[package]]
113name = "defmt"
114version = "1.0.1"
115source = "registry+https://github.com/rust-lang/crates.io-index"
116checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78"
117dependencies = [
118 "bitflags",
119 "defmt-macros",
120]
121
122[[package]]
123name = "defmt-macros"
124version = "1.0.1"
125source = "registry+https://github.com/rust-lang/crates.io-index"
126checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e"
127dependencies = [
128 "defmt-parser",
129 "proc-macro-error2",
130 "proc-macro2",
131 "quote",
132 "syn",
133]
134
135[[package]]
136name = "defmt-parser"
137version = "1.0.0"
138source = "registry+https://github.com/rust-lang/crates.io-index"
139checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
140dependencies = [
141 "thiserror",
142]
143
144[[package]]
145name = "document-features"
146version = "0.2.12"
147source = "registry+https://github.com/rust-lang/crates.io-index"
148checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
149dependencies = [
150 "litrs",
151]
152
153[[package]]
154name = "embassy-embedded-hal"
155version = "0.5.0"
156source = "registry+https://github.com/rust-lang/crates.io-index"
157checksum = "554e3e840696f54b4c9afcf28a0f24da431c927f4151040020416e7393d6d0d8"
158dependencies = [
159 "embassy-futures",
160 "embassy-hal-internal",
161 "embassy-sync",
162 "embedded-hal 0.2.7",
163 "embedded-hal 1.0.0",
164 "embedded-hal-async",
165 "embedded-storage",
166 "embedded-storage-async",
167 "nb 1.1.0",
168]
169
170[[package]]
171name = "embassy-futures"
172version = "0.1.2"
173source = "registry+https://github.com/rust-lang/crates.io-index"
174checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01"
175
176[[package]]
177name = "embassy-hal-internal"
178version = "0.3.0"
179source = "registry+https://github.com/rust-lang/crates.io-index"
180checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a"
181dependencies = [
182 "cortex-m",
183 "critical-section",
184 "num-traits",
185]
186
187[[package]]
188name = "embassy-mcxa"
189version = "0.1.0"
190dependencies = [
191 "cortex-m",
192 "cortex-m-rt",
193 "critical-section",
194 "defmt",
195 "embassy-embedded-hal",
196 "embassy-hal-internal",
197 "embassy-sync",
198 "embassy-time",
199 "embassy-time-driver",
200 "embedded-hal 0.2.7",
201 "embedded-hal 1.0.0",
202 "embedded-hal-async",
203 "embedded-hal-nb",
204 "embedded-io",
205 "embedded-io-async",
206 "heapless",
207 "maitake-sync",
208 "mcxa-pac",
209 "nb 1.1.0",
210 "paste",
211]
212
213[[package]]
214name = "embassy-sync"
215version = "0.7.2"
216source = "registry+https://github.com/rust-lang/crates.io-index"
217checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b"
218dependencies = [
219 "cfg-if",
220 "critical-section",
221 "embedded-io-async",
222 "futures-core",
223 "futures-sink",
224 "heapless",
225]
226
227[[package]]
228name = "embassy-time"
229version = "0.5.0"
230source = "registry+https://github.com/rust-lang/crates.io-index"
231checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65"
232dependencies = [
233 "cfg-if",
234 "critical-section",
235 "document-features",
236 "embassy-time-driver",
237 "embedded-hal 0.2.7",
238 "embedded-hal 1.0.0",
239 "embedded-hal-async",
240 "futures-core",
241]
242
243[[package]]
244name = "embassy-time-driver"
245version = "0.2.1"
246source = "registry+https://github.com/rust-lang/crates.io-index"
247checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6"
248dependencies = [
249 "document-features",
250]
251
252[[package]]
253name = "embedded-hal"
254version = "0.2.7"
255source = "registry+https://github.com/rust-lang/crates.io-index"
256checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
257dependencies = [
258 "nb 0.1.3",
259 "void",
260]
261
262[[package]]
263name = "embedded-hal"
264version = "1.0.0"
265source = "registry+https://github.com/rust-lang/crates.io-index"
266checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
267
268[[package]]
269name = "embedded-hal-async"
270version = "1.0.0"
271source = "registry+https://github.com/rust-lang/crates.io-index"
272checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
273dependencies = [
274 "embedded-hal 1.0.0",
275]
276
277[[package]]
278name = "embedded-hal-nb"
279version = "1.0.0"
280source = "registry+https://github.com/rust-lang/crates.io-index"
281checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
282dependencies = [
283 "embedded-hal 1.0.0",
284 "nb 1.1.0",
285]
286
287[[package]]
288name = "embedded-io"
289version = "0.6.1"
290source = "registry+https://github.com/rust-lang/crates.io-index"
291checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
292
293[[package]]
294name = "embedded-io-async"
295version = "0.6.1"
296source = "registry+https://github.com/rust-lang/crates.io-index"
297checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f"
298dependencies = [
299 "embedded-io",
300]
301
302[[package]]
303name = "embedded-storage"
304version = "0.3.1"
305source = "registry+https://github.com/rust-lang/crates.io-index"
306checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032"
307
308[[package]]
309name = "embedded-storage-async"
310version = "0.4.1"
311source = "registry+https://github.com/rust-lang/crates.io-index"
312checksum = "1763775e2323b7d5f0aa6090657f5e21cfa02ede71f5dc40eead06d64dcd15cc"
313dependencies = [
314 "embedded-storage",
315]
316
317[[package]]
318name = "find-msvc-tools"
319version = "0.1.5"
320source = "registry+https://github.com/rust-lang/crates.io-index"
321checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
322
323[[package]]
324name = "futures-core"
325version = "0.3.31"
326source = "registry+https://github.com/rust-lang/crates.io-index"
327checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
328
329[[package]]
330name = "futures-sink"
331version = "0.3.31"
332source = "registry+https://github.com/rust-lang/crates.io-index"
333checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
334
335[[package]]
336name = "generator"
337version = "0.8.7"
338source = "registry+https://github.com/rust-lang/crates.io-index"
339checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2"
340dependencies = [
341 "cc",
342 "cfg-if",
343 "libc",
344 "log",
345 "rustversion",
346 "windows",
347]
348
349[[package]]
350name = "hash32"
351version = "0.3.1"
352source = "registry+https://github.com/rust-lang/crates.io-index"
353checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
354dependencies = [
355 "byteorder",
356]
357
358[[package]]
359name = "heapless"
360version = "0.8.0"
361source = "registry+https://github.com/rust-lang/crates.io-index"
362checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
363dependencies = [
364 "hash32",
365 "stable_deref_trait",
366]
367
368[[package]]
369name = "lazy_static"
370version = "1.5.0"
371source = "registry+https://github.com/rust-lang/crates.io-index"
372checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
373
374[[package]]
375name = "libc"
376version = "0.2.177"
377source = "registry+https://github.com/rust-lang/crates.io-index"
378checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
379
380[[package]]
381name = "litrs"
382version = "1.0.0"
383source = "registry+https://github.com/rust-lang/crates.io-index"
384checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
385
386[[package]]
387name = "log"
388version = "0.4.28"
389source = "registry+https://github.com/rust-lang/crates.io-index"
390checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
391
392[[package]]
393name = "loom"
394version = "0.7.2"
395source = "registry+https://github.com/rust-lang/crates.io-index"
396checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
397dependencies = [
398 "cfg-if",
399 "generator",
400 "scoped-tls",
401 "tracing",
402 "tracing-subscriber",
403]
404
405[[package]]
406name = "maitake-sync"
407version = "0.2.2"
408source = "registry+https://github.com/rust-lang/crates.io-index"
409checksum = "748f86d9befd480b602c3bebc9ef30dbf2f3dfc8acc4a73d07b90f0117e6de3f"
410dependencies = [
411 "cordyceps",
412 "critical-section",
413 "loom",
414 "mutex-traits",
415 "mycelium-bitfield",
416 "pin-project",
417 "portable-atomic",
418 "tracing",
419]
420
421[[package]]
422name = "matchers"
423version = "0.2.0"
424source = "registry+https://github.com/rust-lang/crates.io-index"
425checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
426dependencies = [
427 "regex-automata",
428]
429
430[[package]]
431name = "mcxa-pac"
432version = "0.1.0"
433source = "git+https://github.com/OpenDevicePartnership/mcxa-pac#e18dfb52500ca77b8d8326662b966a80251182ca"
434dependencies = [
435 "cortex-m",
436 "cortex-m-rt",
437 "critical-section",
438 "defmt",
439 "vcell",
440]
441
442[[package]]
443name = "memchr"
444version = "2.7.6"
445source = "registry+https://github.com/rust-lang/crates.io-index"
446checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
447
448[[package]]
449name = "mutex-traits"
450version = "1.0.1"
451source = "registry+https://github.com/rust-lang/crates.io-index"
452checksum = "3929f2b5633d29cf7b6624992e5f3c1e9334f1193423e12d17be4faf678cde3f"
453
454[[package]]
455name = "mycelium-bitfield"
456version = "0.1.5"
457source = "registry+https://github.com/rust-lang/crates.io-index"
458checksum = "24e0cc5e2c585acbd15c5ce911dff71e1f4d5313f43345873311c4f5efd741cc"
459
460[[package]]
461name = "nb"
462version = "0.1.3"
463source = "registry+https://github.com/rust-lang/crates.io-index"
464checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
465dependencies = [
466 "nb 1.1.0",
467]
468
469[[package]]
470name = "nb"
471version = "1.1.0"
472source = "registry+https://github.com/rust-lang/crates.io-index"
473checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
474
475[[package]]
476name = "nu-ansi-term"
477version = "0.50.3"
478source = "registry+https://github.com/rust-lang/crates.io-index"
479checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
480dependencies = [
481 "windows-sys",
482]
483
484[[package]]
485name = "num-traits"
486version = "0.2.19"
487source = "registry+https://github.com/rust-lang/crates.io-index"
488checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
489dependencies = [
490 "autocfg",
491]
492
493[[package]]
494name = "once_cell"
495version = "1.21.3"
496source = "registry+https://github.com/rust-lang/crates.io-index"
497checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
498
499[[package]]
500name = "paste"
501version = "1.0.15"
502source = "registry+https://github.com/rust-lang/crates.io-index"
503checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
504
505[[package]]
506name = "pin-project"
507version = "1.1.10"
508source = "registry+https://github.com/rust-lang/crates.io-index"
509checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
510dependencies = [
511 "pin-project-internal",
512]
513
514[[package]]
515name = "pin-project-internal"
516version = "1.1.10"
517source = "registry+https://github.com/rust-lang/crates.io-index"
518checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
519dependencies = [
520 "proc-macro2",
521 "quote",
522 "syn",
523]
524
525[[package]]
526name = "pin-project-lite"
527version = "0.2.16"
528source = "registry+https://github.com/rust-lang/crates.io-index"
529checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
530
531[[package]]
532name = "portable-atomic"
533version = "1.11.1"
534source = "registry+https://github.com/rust-lang/crates.io-index"
535checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
536dependencies = [
537 "critical-section",
538]
539
540[[package]]
541name = "proc-macro-error-attr2"
542version = "2.0.0"
543source = "registry+https://github.com/rust-lang/crates.io-index"
544checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
545dependencies = [
546 "proc-macro2",
547 "quote",
548]
549
550[[package]]
551name = "proc-macro-error2"
552version = "2.0.1"
553source = "registry+https://github.com/rust-lang/crates.io-index"
554checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
555dependencies = [
556 "proc-macro-error-attr2",
557 "proc-macro2",
558 "quote",
559 "syn",
560]
561
562[[package]]
563name = "proc-macro2"
564version = "1.0.103"
565source = "registry+https://github.com/rust-lang/crates.io-index"
566checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
567dependencies = [
568 "unicode-ident",
569]
570
571[[package]]
572name = "quote"
573version = "1.0.42"
574source = "registry+https://github.com/rust-lang/crates.io-index"
575checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
576dependencies = [
577 "proc-macro2",
578]
579
580[[package]]
581name = "regex-automata"
582version = "0.4.13"
583source = "registry+https://github.com/rust-lang/crates.io-index"
584checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
585dependencies = [
586 "aho-corasick",
587 "memchr",
588 "regex-syntax",
589]
590
591[[package]]
592name = "regex-syntax"
593version = "0.8.8"
594source = "registry+https://github.com/rust-lang/crates.io-index"
595checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
596
597[[package]]
598name = "rustc_version"
599version = "0.2.3"
600source = "registry+https://github.com/rust-lang/crates.io-index"
601checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
602dependencies = [
603 "semver",
604]
605
606[[package]]
607name = "rustversion"
608version = "1.0.22"
609source = "registry+https://github.com/rust-lang/crates.io-index"
610checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
611
612[[package]]
613name = "scoped-tls"
614version = "1.0.1"
615source = "registry+https://github.com/rust-lang/crates.io-index"
616checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
617
618[[package]]
619name = "semver"
620version = "0.9.0"
621source = "registry+https://github.com/rust-lang/crates.io-index"
622checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
623dependencies = [
624 "semver-parser",
625]
626
627[[package]]
628name = "semver-parser"
629version = "0.7.0"
630source = "registry+https://github.com/rust-lang/crates.io-index"
631checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
632
633[[package]]
634name = "sharded-slab"
635version = "0.1.7"
636source = "registry+https://github.com/rust-lang/crates.io-index"
637checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
638dependencies = [
639 "lazy_static",
640]
641
642[[package]]
643name = "shlex"
644version = "1.3.0"
645source = "registry+https://github.com/rust-lang/crates.io-index"
646checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
647
648[[package]]
649name = "smallvec"
650version = "1.15.1"
651source = "registry+https://github.com/rust-lang/crates.io-index"
652checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
653
654[[package]]
655name = "stable_deref_trait"
656version = "1.2.1"
657source = "registry+https://github.com/rust-lang/crates.io-index"
658checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
659
660[[package]]
661name = "syn"
662version = "2.0.110"
663source = "registry+https://github.com/rust-lang/crates.io-index"
664checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
665dependencies = [
666 "proc-macro2",
667 "quote",
668 "unicode-ident",
669]
670
671[[package]]
672name = "thiserror"
673version = "2.0.17"
674source = "registry+https://github.com/rust-lang/crates.io-index"
675checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
676dependencies = [
677 "thiserror-impl",
678]
679
680[[package]]
681name = "thiserror-impl"
682version = "2.0.17"
683source = "registry+https://github.com/rust-lang/crates.io-index"
684checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
685dependencies = [
686 "proc-macro2",
687 "quote",
688 "syn",
689]
690
691[[package]]
692name = "thread_local"
693version = "1.1.9"
694source = "registry+https://github.com/rust-lang/crates.io-index"
695checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
696dependencies = [
697 "cfg-if",
698]
699
700[[package]]
701name = "tracing"
702version = "0.1.41"
703source = "registry+https://github.com/rust-lang/crates.io-index"
704checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
705dependencies = [
706 "pin-project-lite",
707 "tracing-attributes",
708 "tracing-core",
709]
710
711[[package]]
712name = "tracing-attributes"
713version = "0.1.30"
714source = "registry+https://github.com/rust-lang/crates.io-index"
715checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
716dependencies = [
717 "proc-macro2",
718 "quote",
719 "syn",
720]
721
722[[package]]
723name = "tracing-core"
724version = "0.1.34"
725source = "registry+https://github.com/rust-lang/crates.io-index"
726checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
727dependencies = [
728 "once_cell",
729 "valuable",
730]
731
732[[package]]
733name = "tracing-log"
734version = "0.2.0"
735source = "registry+https://github.com/rust-lang/crates.io-index"
736checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
737dependencies = [
738 "log",
739 "once_cell",
740 "tracing-core",
741]
742
743[[package]]
744name = "tracing-subscriber"
745version = "0.3.20"
746source = "registry+https://github.com/rust-lang/crates.io-index"
747checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
748dependencies = [
749 "matchers",
750 "nu-ansi-term",
751 "once_cell",
752 "regex-automata",
753 "sharded-slab",
754 "smallvec",
755 "thread_local",
756 "tracing",
757 "tracing-core",
758 "tracing-log",
759]
760
761[[package]]
762name = "unicode-ident"
763version = "1.0.22"
764source = "registry+https://github.com/rust-lang/crates.io-index"
765checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
766
767[[package]]
768name = "valuable"
769version = "0.1.1"
770source = "registry+https://github.com/rust-lang/crates.io-index"
771checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
772
773[[package]]
774name = "vcell"
775version = "0.1.3"
776source = "registry+https://github.com/rust-lang/crates.io-index"
777checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
778
779[[package]]
780name = "void"
781version = "1.0.2"
782source = "registry+https://github.com/rust-lang/crates.io-index"
783checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
784
785[[package]]
786name = "volatile-register"
787version = "0.2.2"
788source = "registry+https://github.com/rust-lang/crates.io-index"
789checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
790dependencies = [
791 "vcell",
792]
793
794[[package]]
795name = "windows"
796version = "0.61.3"
797source = "registry+https://github.com/rust-lang/crates.io-index"
798checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
799dependencies = [
800 "windows-collections",
801 "windows-core",
802 "windows-future",
803 "windows-link 0.1.3",
804 "windows-numerics",
805]
806
807[[package]]
808name = "windows-collections"
809version = "0.2.0"
810source = "registry+https://github.com/rust-lang/crates.io-index"
811checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
812dependencies = [
813 "windows-core",
814]
815
816[[package]]
817name = "windows-core"
818version = "0.61.2"
819source = "registry+https://github.com/rust-lang/crates.io-index"
820checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
821dependencies = [
822 "windows-implement",
823 "windows-interface",
824 "windows-link 0.1.3",
825 "windows-result",
826 "windows-strings",
827]
828
829[[package]]
830name = "windows-future"
831version = "0.2.1"
832source = "registry+https://github.com/rust-lang/crates.io-index"
833checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
834dependencies = [
835 "windows-core",
836 "windows-link 0.1.3",
837 "windows-threading",
838]
839
840[[package]]
841name = "windows-implement"
842version = "0.60.2"
843source = "registry+https://github.com/rust-lang/crates.io-index"
844checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
845dependencies = [
846 "proc-macro2",
847 "quote",
848 "syn",
849]
850
851[[package]]
852name = "windows-interface"
853version = "0.59.3"
854source = "registry+https://github.com/rust-lang/crates.io-index"
855checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
856dependencies = [
857 "proc-macro2",
858 "quote",
859 "syn",
860]
861
862[[package]]
863name = "windows-link"
864version = "0.1.3"
865source = "registry+https://github.com/rust-lang/crates.io-index"
866checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
867
868[[package]]
869name = "windows-link"
870version = "0.2.1"
871source = "registry+https://github.com/rust-lang/crates.io-index"
872checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
873
874[[package]]
875name = "windows-numerics"
876version = "0.2.0"
877source = "registry+https://github.com/rust-lang/crates.io-index"
878checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
879dependencies = [
880 "windows-core",
881 "windows-link 0.1.3",
882]
883
884[[package]]
885name = "windows-result"
886version = "0.3.4"
887source = "registry+https://github.com/rust-lang/crates.io-index"
888checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
889dependencies = [
890 "windows-link 0.1.3",
891]
892
893[[package]]
894name = "windows-strings"
895version = "0.4.2"
896source = "registry+https://github.com/rust-lang/crates.io-index"
897checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
898dependencies = [
899 "windows-link 0.1.3",
900]
901
902[[package]]
903name = "windows-sys"
904version = "0.61.2"
905source = "registry+https://github.com/rust-lang/crates.io-index"
906checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
907dependencies = [
908 "windows-link 0.2.1",
909]
910
911[[package]]
912name = "windows-threading"
913version = "0.1.0"
914source = "registry+https://github.com/rust-lang/crates.io-index"
915checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
916dependencies = [
917 "windows-link 0.1.3",
918]
diff --git a/embassy-mcxa/Cargo.toml b/embassy-mcxa/Cargo.toml
new file mode 100644
index 000000000..22660bee9
--- /dev/null
+++ b/embassy-mcxa/Cargo.toml
@@ -0,0 +1,53 @@
1[package]
2name = "embassy-mcxa"
3version = "0.1.0"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6description = "Embassy Hardware Abstraction Layer (HAL) for NXP MCXA series of MCUs"
7keywords = ["embedded", "hal", "nxp", "mcxa", "embassy"]
8categories = ["embedded", "hardware-support", "no-std"]
9
10[dependencies]
11cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
12# If you would like "device" to be an optional feature, please open an issue.
13cortex-m-rt = { version = "0.7", features = ["device"] }
14critical-section = "1.2.0"
15defmt = { version = "1.0", optional = true }
16embassy-embedded-hal = "0.5.0"
17embassy-hal-internal = { version = "0.3.0", features = ["cortex-m", "prio-bits-3"] }
18embassy-sync = "0.7.2"
19embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
20embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
21embedded-hal-async = { version = "1.0" }
22embedded-hal-nb = { version = "1.0" }
23embedded-io = "0.6"
24embedded-io-async = { version = "0.6.1" }
25heapless = "0.8"
26mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt", "critical-section"], version = "0.1.0" }
27nb = "1.1.0"
28paste = "1.0.15"
29maitake-sync = { version = "0.2.2", default-features = false, features = ["critical-section", "no-cache-pad"] }
30
31# `time` dependencies
32embassy-time = { version = "0.5.0", optional = true }
33embassy-time-driver = { version = "0.2.1", optional = true }
34
35[features]
36default = ["rt"]
37
38# Base defmt feature enables core + panic handler
39# Use with one logger feature: defmt-rtt (preferred) or defmt-uart (fallback)
40defmt = ["dep:defmt", "mcxa-pac/defmt"]
41
42unstable-pac = []
43
44# dummy feature to silence embassy-hal-internal lint
45#
46# This feature makes no change to embassy-mcxa's operation.
47rt = []
48
49# Embassy time
50time = [
51 "dep:embassy-time",
52 "dep:embassy-time-driver",
53]
diff --git a/embassy-mcxa/Embed.toml b/embassy-mcxa/Embed.toml
new file mode 100644
index 000000000..40218c8bf
--- /dev/null
+++ b/embassy-mcxa/Embed.toml
@@ -0,0 +1,28 @@
1[default.probe]
2# Use the first available probe
3protocol = "Swd"
4speed = 1000
5
6[default.flashing]
7# Attach-only: don't flash (we already loaded to RAM)
8enabled = false
9
10[default.reset]
11# Don't reset; keep running app started by run_jlink_noblock.sh
12enabled = false
13
14[default.general]
15# The chip name of the target
16chip = "MCXA276"
17
18[default.gdb]
19# Whether or not a GDB server should be opened after loading
20enabled = false
21
22[default.rtt]
23# Enable RTT for debugging output
24enabled = true
25
26# Increase timeout for RTT CB discovery
27up_channels = [ { channel = 0, mode = "BlockIfFull" } ]
28timeout = 5000
diff --git a/embassy-mcxa/LICENSE b/embassy-mcxa/LICENSE
new file mode 100644
index 000000000..609bd42da
--- /dev/null
+++ b/embassy-mcxa/LICENSE
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) 2025 Open Device Partnership
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
diff --git a/embassy-mcxa/README.md b/embassy-mcxa/README.md
new file mode 100644
index 000000000..6e7d61c0e
--- /dev/null
+++ b/embassy-mcxa/README.md
@@ -0,0 +1,13 @@
1# Embassy MCXA256 HAL
2
3[![check](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/check.yml/badge.svg)](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/check.yml)
4[![no-std](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/nostd.yml/badge.svg)](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/nostd.yml)
5[![rolling](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/rolling.yml/badge.svg)](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/rolling.yml)
6
7A Hardware Abstraction Layer (HAL) for the NXP MCXA256 microcontroller
8using the Embassy async framework. This HAL provides safe, idiomatic
9Rust interfaces for GPIO, UART, and OSTIMER peripherals.
10
11## License
12
13This project is licensed under MIT OR Apache-2.0.
diff --git a/embassy-mcxa/SECURITY.md b/embassy-mcxa/SECURITY.md
new file mode 100644
index 000000000..5357b8824
--- /dev/null
+++ b/embassy-mcxa/SECURITY.md
@@ -0,0 +1,66 @@
1# Vulnerability Disclosure and Embargo Policy
2
3The Open Device Partnership project welcomes the responsible disclosure of vulnerabilities.
4
5## Initial Contact
6
7All security bugs in Open Device Partnership should be reported to the security team.
8To do so, please reach out in the form of a
9[Github Security Advisory](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities).
10
11You will be invited to join this private area to discuss specifics. Doing so
12allows us to start with a high level of confidentiality and relax it if the
13issue is less critical, moving to work on the fix in the open.
14
15Your initial contact will be acknowledged within 48 hours, and you’ll receive
16a more detailed response within 96 hours indicating the next steps in handling
17your report.
18
19After the initial reply to your report, the security team will endeavor to
20keep you informed of the progress being made towards a fix and full
21announcement. As recommended by
22[RFPolicy](https://dl.packetstormsecurity.net/papers/general/rfpolicy-2.0.txt),
23these updates will be sent at least every five working days.
24
25## Disclosure Policy
26
27The Open Device Partnership project has a 5 step disclosure process.
28
291. Contact is established, a private channel created, and the security report
30 is received and is assigned a primary handler. This person will coordinate
31 the fix and release process.
322. The problem is confirmed and a list of all affected versions is determined.
33 If an embargo is needed (see below), details of the embargo are decided.
343. Code is audited to find any potential similar problems.
354. Fixes are prepared for all releases which are still under maintenance. In
36 case of embargo, these fixes are not committed to the public repository but
37 rather held in a private fork pending the announcement.
385. The changes are pushed to the public repository and new builds are deployed.
39
40This process can take some time, especially when coordination is required
41with maintainers of other projects. Every effort will be made to handle the bug
42in as timely a manner as possible, however it is important that we follow the
43release process above to ensure that the disclosure is handled in a consistent
44manner.
45
46## Embargoes
47
48While the Open Device Partnership project aims to follow the highest standards of
49transparency and openness, handling some security issues may pose such an
50immediate threat to various stakeholders and require coordination between
51various actors that it cannot be made immediately public.
52
53In this case, security issues will fall under an embargo.
54
55An embargo can be called for in various cases:
56
57- when disclosing the issue without simultaneously providing a mitigation
58 would seriously endanger users,
59- when producing a fix requires coordinating between multiple actors (such as
60 upstream or downstream/dependency projects), or simply
61- when proper analysis of the issue and its ramifications demands time.
62
63If we determine that an issue you report requires an embargo, we will discuss
64this with you and try to find a reasonable expiry date (aka “embargo
65completion date”), as well as who should be included in the list of
66need-to-know people.
diff --git a/embassy-mcxa/SW-Content-Register.txt b/embassy-mcxa/SW-Content-Register.txt
new file mode 100644
index 000000000..09f879c2f
--- /dev/null
+++ b/embassy-mcxa/SW-Content-Register.txt
@@ -0,0 +1,78 @@
1Release Name: Embassy MCXA276 HAL
2Release Version: 0.1.0
3Package License: MIT (see ./License.txt)
4Note: The crate is dual-licensed “MIT OR Apache-2.0” per Cargo.toml; choosing MIT satisfies the dual-license terms.
5
6Scope of this Register
7This file documents software content that is part of this repository and, for transparency, lists major non-vendored Rust dependencies that are resolved from crates.io during builds. Components that are not present in the repository (e.g., external crates) are listed under “Non‑vendored Rust dependencies”.
8
9Repository Components (included in this release)
10
11embassy_mcxa276_hal Name: Embassy MCXA276 HAL (library + examples)
12 Version: 0.1.0
13 Outgoing License: MIT
14 License File: ./License.txt
15 Format: Rust source code, examples, scripts, linker scripts
16 Description: Hardware Abstraction Layer for NXP MCXA276 using the Embassy async framework. Implements drivers and helpers for:
17 - LPUART2 (UART), OSTIMER0, RTC0, ADC1, GPIO
18 - Embassy integration (executors, time)
19 Location: ./src, ./examples, ./build.rs, ./memory.x, ./ram.ld, ./defmt.x, ./.cargo/config.toml, ./run.sh, ./tools/
20 Origin: OEMWCSE (MIT)
21 URL: https://bitbucket.sw.nxp.com/scm/oemwcse/mcxa_rust.git
22
23mcxa276_pac Name: MCXA276 Peripheral Access Crate (PAC)
24 Version: 0.1.0
25 Outgoing License: MIT OR Apache-2.0
26 License File: (see crate metadata)
27 Format: Rust source code (auto-generated PAC)
28 Description: Auto-generated register mappings for MCXA276 peripherals (svd2rust). Includes device.x and interrupt vectors.
29 Location: External (git): https://github.com/bogdan-petru/mcxa-pac (pinned rev a9dd3301)
30 Origin: Generated by svdtools + svd2rust from NXP SVD 25.06.00
31 URL: N/A (generated from NXP SVD)
32
33examples Name: Example applications
34 Version: N/A
35 Outgoing License: MIT
36 License File: ./License.txt
37 Format: Rust source code (examples)
38 Description: Demonstrations for HAL peripherals and features:
39 - hello, blink
40 - lpuart_polling, lpuart_buffered, uart_interrupt
41 - rtc_alarm
42 - adc_polling, adc_interrupt
43 - ostimer_alarm, ostimer_async, ostimer_counter, ostimer_race_test
44 Location: ./examples
45 Origin: OEMWCSE (MIT)
46
47Non‑vendored Rust dependencies (resolved from crates.io at build time)
48The following crates are not vendored in this repository. They are fetched during builds via Cargo. See Cargo.lock for exact versions. License summaries below are for convenience; consult each crate for authoritative terms.
49
50cortex-m License: MIT OR Apache-2.0 Purpose: Cortex-M core support
51cortex-m-rt License: MIT OR Apache-2.0 Purpose: Cortex-M runtime (vectors, reset)
52embassy-executor License: MIT OR Apache-2.0 Purpose: Async executor for Cortex-M
53embassy-time (+ driver) License: MIT OR Apache-2.0 Purpose: Time abstraction and drivers
54embassy-sync License: MIT OR Apache-2.0 Purpose: No-std synchronization primitives
55embassy-embedded-hal License: MIT OR Apache-2.0 Purpose: Traits/adapters for embedded-hal
56embassy-hal-internal License: MIT OR Apache-2.0 Purpose: HAL building blocks (peripherals!)
57embedded-hal (1.0) License: MIT OR Apache-2.0 Purpose: Core embedded hardware traits
58embedded-hal (0.2.6) License: MIT OR Apache-2.0 Purpose: Legacy HAL traits ("unproven")
59embedded-hal-async License: MIT OR Apache-2.0 Purpose: Async HAL traits
60embedded-hal-nb License: MIT OR Apache-2.0 Purpose: Non-blocking HAL traits
61embedded-io / embedded-io-async License: MIT OR Apache-2.0 Purpose: IO traits
62critical-section License: MIT OR Apache-2.0 Purpose: Critical-section abstraction
63heapless License: MIT OR Apache-2.0 Purpose: Fixed-capacity data structures
64paste License: MIT OR Apache-2.0 Purpose: Macro hygiene utility
65nb License: MIT OR Apache-2.0 Purpose: Non-blocking result type
66vcell License: MIT OR Apache-2.0 Purpose: Volatile cell (PAC dependency)
67
68defmt, defmt-rtt, rtt-target, panic-probe License: MIT OR Apache-2.0 Purpose: Optional (feature-gated) logging/panic crates
69
70Attribution notes
71- MCXA276 PAC is sourced as an external dependency (see Cargo.toml). SVD 25.06.00 already includes OS_EVENT and WAKETIMER0 interrupts (no local fixes).
72- Examples run from RAM using custom linker setup; see README.txt for platform-specific instructions.
73
74Compliance
75- This repository’s distributed source is covered by MIT (see License.txt). Where crates are pulled from crates.io, those crates retain their own licenses; the dual-license compatibility (MIT OR Apache-2.0) is standard in the Rust embedded ecosystem and is compatible with this project’s selected MIT distribution.
76
77
78
diff --git a/embassy-mcxa/defmt.x b/embassy-mcxa/defmt.x
new file mode 100644
index 000000000..dbd6d0850
--- /dev/null
+++ b/embassy-mcxa/defmt.x
@@ -0,0 +1,6 @@
1/*
2 Dummy defmt.x to satisfy -Tdefmt.x when building without the `defmt` feature.
3 When `defmt` is enabled, the real defmt.x provided by the defmt crate is used via the linker search path.
4 This file intentionally contains no SECTIONS so it won't override ram.ld.
5*/
6
diff --git a/embassy-mcxa/deny.toml b/embassy-mcxa/deny.toml
new file mode 100644
index 000000000..7097f2f55
--- /dev/null
+++ b/embassy-mcxa/deny.toml
@@ -0,0 +1,241 @@
1# This template contains all of the possible sections and their default values
2
3# Note that all fields that take a lint level have these possible values:
4# * deny - An error will be produced and the check will fail
5# * warn - A warning will be produced, but the check will not fail
6# * allow - No warning or error will be produced, though in some cases a note
7# will be
8
9# The values provided in this template are the default values that will be used
10# when any section or field is not specified in your own configuration
11
12# Root options
13
14# The graph table configures how the dependency graph is constructed and thus
15# which crates the checks are performed against
16[graph]
17# If 1 or more target triples (and optionally, target_features) are specified,
18# only the specified targets will be checked when running `cargo deny check`.
19# This means, if a particular package is only ever used as a target specific
20# dependency, such as, for example, the `nix` crate only being used via the
21# `target_family = "unix"` configuration, that only having windows targets in
22# this list would mean the nix crate, as well as any of its exclusive
23# dependencies not shared by any other crates, would be ignored, as the target
24# list here is effectively saying which targets you are building for.
25targets = [
26 # The triple can be any string, but only the target triples built in to
27 # rustc (as of 1.40) can be checked against actual config expressions
28 #"x86_64-unknown-linux-musl",
29 # You can also specify which target_features you promise are enabled for a
30 # particular target. target_features are currently not validated against
31 # the actual valid features supported by the target architecture.
32 #{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
33]
34# When creating the dependency graph used as the source of truth when checks are
35# executed, this field can be used to prune crates from the graph, removing them
36# from the view of cargo-deny. This is an extremely heavy hammer, as if a crate
37# is pruned from the graph, all of its dependencies will also be pruned unless
38# they are connected to another crate in the graph that hasn't been pruned,
39# so it should be used with care. The identifiers are [Package ID Specifications]
40# (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html)
41#exclude = []
42# If true, metadata will be collected with `--all-features`. Note that this can't
43# be toggled off if true, if you want to conditionally enable `--all-features` it
44# is recommended to pass `--all-features` on the cmd line instead
45all-features = false
46# If true, metadata will be collected with `--no-default-features`. The same
47# caveat with `all-features` applies
48no-default-features = false
49# If set, these feature will be enabled when collecting metadata. If `--features`
50# is specified on the cmd line they will take precedence over this option.
51#features = []
52
53# The output table provides options for how/if diagnostics are outputted
54[output]
55# When outputting inclusion graphs in diagnostics that include features, this
56# option can be used to specify the depth at which feature edges will be added.
57# This option is included since the graphs can be quite large and the addition
58# of features from the crate(s) to all of the graph roots can be far too verbose.
59# This option can be overridden via `--feature-depth` on the cmd line
60feature-depth = 1
61
62# This section is considered when running `cargo deny check advisories`
63# More documentation for the advisories section can be found here:
64# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
65[advisories]
66# The path where the advisory databases are cloned/fetched into
67#db-path = "$CARGO_HOME/advisory-dbs"
68# The url(s) of the advisory databases to use
69#db-urls = ["https://github.com/rustsec/advisory-db"]
70# A list of advisory IDs to ignore. Note that ignored advisories will still
71# output a note when they are encountered.
72ignore = [
73 #"RUSTSEC-0000-0000",
74 #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" },
75 #"[email protected]", # you can also ignore yanked crate versions if you wish
76 #{ crate = "[email protected]", reason = "you can specify why you are ignoring the yanked crate" },
77 # { id = "RUSTSEC-2024-0370", reason = "proc-macro-error is unmaintained, no safe upgrade available, need upstream dependencies to migrate away from it." },
78 { id = "RUSTSEC-2024-0436", reason = "there are no suitable replacements for paste right now; paste has been archived as read-only. It only affects compile time concatenation in macros. We will allow it for now" },
79 # { id = "RUSTSEC-2023-0089", reason = "this is a deprecation warning for a dependency of a dependency. https://github.com/jamesmunns/postcard/issues/223 tracks fixing the dependency; until that's resolved, we can accept the deprecated code as it has no known vulnerabilities."}
80]
81# If this is true, then cargo deny will use the git executable to fetch advisory database.
82# If this is false, then it uses a built-in git library.
83# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
84# See Git Authentication for more information about setting up git authentication.
85#git-fetch-with-cli = true
86
87# This section is considered when running `cargo deny check licenses`
88# More documentation for the licenses section can be found here:
89# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
90[licenses]
91# List of explicitly allowed licenses
92# See https://spdx.org/licenses/ for list of possible licenses
93# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
94allow = [
95 "MIT",
96 "Apache-2.0",
97
98 # unicode-ident 1.0.14 switched from Unicode-DFS-2016 to Unicode-3.0 license.
99 "Unicode-3.0",
100 #"Apache-2.0 WITH LLVM-exception",
101]
102# The confidence threshold for detecting a license from license text.
103# The higher the value, the more closely the license text must be to the
104# canonical license text of a valid SPDX license file.
105# [possible values: any between 0.0 and 1.0].
106confidence-threshold = 0.8
107# Allow 1 or more licenses on a per-crate basis, so that particular licenses
108# aren't accepted for every possible crate as with the normal allow list
109exceptions = [
110 # Each entry is the crate and version constraint, and its specific allow
111 # list
112 #{ allow = ["Zlib"], crate = "adler32" },
113]
114
115# Some crates don't have (easily) machine readable licensing information,
116# adding a clarification entry for it allows you to manually specify the
117# licensing information
118#[[licenses.clarify]]
119# The package spec the clarification applies to
120#crate = "ring"
121# The SPDX expression for the license requirements of the crate
122#expression = "MIT AND ISC AND OpenSSL"
123# One or more files in the crate's source used as the "source of truth" for
124# the license expression. If the contents match, the clarification will be used
125# when running the license check, otherwise the clarification will be ignored
126# and the crate will be checked normally, which may produce warnings or errors
127# depending on the rest of your configuration
128#license-files = [
129# Each entry is a crate relative path, and the (opaque) hash of its contents
130#{ path = "LICENSE", hash = 0xbd0eed23 }
131#]
132
133[licenses.private]
134# If true, ignores workspace crates that aren't published, or are only
135# published to private registries.
136# To see how to mark a crate as unpublished (to the official registry),
137# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
138ignore = false
139# One or more private registries that you might publish crates to, if a crate
140# is only published to private registries, and ignore is true, the crate will
141# not have its license(s) checked
142registries = [
143 #"https://sekretz.com/registry
144]
145
146# This section is considered when running `cargo deny check bans`.
147# More documentation about the 'bans' section can be found here:
148# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
149[bans]
150# Lint level for when multiple versions of the same crate are detected
151multiple-versions = "warn"
152# Lint level for when a crate version requirement is `*`
153wildcards = "allow"
154# The graph highlighting used when creating dotgraphs for crates
155# with multiple versions
156# * lowest-version - The path to the lowest versioned duplicate is highlighted
157# * simplest-path - The path to the version with the fewest edges is highlighted
158# * all - Both lowest-version and simplest-path are used
159highlight = "all"
160# The default lint level for `default` features for crates that are members of
161# the workspace that is being checked. This can be overridden by allowing/denying
162# `default` on a crate-by-crate basis if desired.
163workspace-default-features = "allow"
164# The default lint level for `default` features for external crates that are not
165# members of the workspace. This can be overridden by allowing/denying `default`
166# on a crate-by-crate basis if desired.
167external-default-features = "allow"
168# List of crates that are allowed. Use with care!
169allow = [
170 #"[email protected]",
171 #{ crate = "[email protected]", reason = "you can specify a reason it is allowed" },
172]
173# List of crates to deny
174deny = [
175 #"[email protected]",
176 #{ crate = "[email protected]", reason = "you can specify a reason it is banned" },
177 # Wrapper crates can optionally be specified to allow the crate when it
178 # is a direct dependency of the otherwise banned crate
179 #{ crate = "[email protected]", wrappers = ["this-crate-directly-depends-on-ansi_term"] },
180]
181
182# List of features to allow/deny
183# Each entry the name of a crate and a version range. If version is
184# not specified, all versions will be matched.
185#[[bans.features]]
186#crate = "reqwest"
187# Features to not allow
188#deny = ["json"]
189# Features to allow
190#allow = [
191# "rustls",
192# "__rustls",
193# "__tls",
194# "hyper-rustls",
195# "rustls",
196# "rustls-pemfile",
197# "rustls-tls-webpki-roots",
198# "tokio-rustls",
199# "webpki-roots",
200#]
201# If true, the allowed features must exactly match the enabled feature set. If
202# this is set there is no point setting `deny`
203#exact = true
204
205# Certain crates/versions that will be skipped when doing duplicate detection.
206skip = [
207 #"[email protected]",
208 #{ crate = "[email protected]", reason = "you can specify a reason why it can't be updated/removed" },
209]
210# Similarly to `skip` allows you to skip certain crates during duplicate
211# detection. Unlike skip, it also includes the entire tree of transitive
212# dependencies starting at the specified crate, up to a certain depth, which is
213# by default infinite.
214skip-tree = [
215 #"[email protected]", # will be skipped along with _all_ of its direct and transitive dependencies
216 #{ crate = "[email protected]", depth = 20 },
217]
218
219# This section is considered when running `cargo deny check sources`.
220# More documentation about the 'sources' section can be found here:
221# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
222[sources]
223# Lint level for what to happen when a crate from a crate registry that is not
224# in the allow list is encountered
225unknown-registry = "warn"
226# Lint level for what to happen when a crate from a git repository that is not
227# in the allow list is encountered
228unknown-git = "warn"
229# List of URLs for allowed crate registries. Defaults to the crates.io index
230# if not specified. If it is specified but empty, no registries are allowed.
231allow-registry = ["https://github.com/rust-lang/crates.io-index"]
232# List of URLs for allowed Git repositories
233allow-git = []
234
235[sources.allow-org]
236# github.com organizations to allow git sources for
237github = ["OpenDevicePartnership"]
238# gitlab.com organizations to allow git sources for
239gitlab = []
240# bitbucket.org organizations to allow git sources for
241bitbucket = []
diff --git a/embassy-mcxa/ram.ld b/embassy-mcxa/ram.ld
new file mode 100644
index 000000000..816ab6819
--- /dev/null
+++ b/embassy-mcxa/ram.ld
@@ -0,0 +1,109 @@
1/* Simple RAM execution linker script for MCXA276 */
2MEMORY
3{
4 RAM : ORIGIN = 0x20000000, LENGTH = 128K
5}
6
7/* Pull in device default interrupt symbol aliases (e.g., CMC = DefaultHandler) */
8INCLUDE device.x
9
10
11/* Provide core exception weak aliases if not supplied by cortex-m-rt's link.x */
12PROVIDE(NonMaskableInt = DefaultHandler);
13PROVIDE(HardFault = DefaultHandler);
14PROVIDE(MemoryManagement = DefaultHandler);
15PROVIDE(BusFault = DefaultHandler);
16PROVIDE(UsageFault = DefaultHandler);
17PROVIDE(SecureFault = DefaultHandler);
18PROVIDE(SVCall = DefaultHandler);
19PROVIDE(DebugMonitor = DefaultHandler);
20PROVIDE(PendSV = DefaultHandler);
21PROVIDE(SysTick = DefaultHandler);
22
23/* In RAM-run we have no FLASH sidata; copy from sdata */
24__sidata = __sdata;
25
26/* Ensure the PAC interrupt table is kept */
27EXTERN(__INTERRUPTS);
28
29
30/* Pull in defmt's linker script to generate the defmt table that host decoders expect */
31INCLUDE defmt.x
32
33ENTRY(Reset)
34EXTERN(VECTOR_TABLE)
35EXTERN(Reset)
36EXTERN(main)
37
38/* Define _stack_start at end of RAM BEFORE it's used in vector table */
39_stack_start = ORIGIN(RAM) + LENGTH(RAM);
40
41SECTIONS
42{
43 .vector_table ORIGIN(RAM) :
44 {
45 /* Slot 0: Initial stack pointer - use our explicitly set _stack_start */
46 LONG(_stack_start);
47 /* Slot 1: Reset vector - address of Reset function with Thumb bit set */
48 LONG(Reset | 1);
49 /* Cortex-M33 core exceptions (slots 2-14) */
50 KEEP(*(.vector_table.exceptions));
51 /* Peripheral interrupt vectors provided by PAC (slots 16+) */
52 KEEP(*(.vector_table.interrupts));
53 } > RAM
54
55 .text :
56 {
57 KEEP(*(.text.Reset));
58 KEEP(*(.text.main));
59 *(.text .text.*);
60 } > RAM
61
62 /* Keep defmt table and fragments so host decoders can find metadata */
63 .defmt :
64 {
65 KEEP(*(.defmt));
66 KEEP(*(.defmt.*));
67 } > RAM
68
69 .rodata :
70 {
71 *(.rodata .rodata.*);
72 } > RAM
73
74 .data :
75 {
76 . = ALIGN(4);
77 __sdata = .;
78 *(.data .data.*);
79 . = ALIGN(4);
80 __edata = .;
81 } > RAM
82
83 /* Ensure RTT control block with "SEGGER RTT" signature is loaded to RAM */
84 .rtt :
85 {
86 KEEP(*(.rtt));
87 } > RAM
88
89 /* Place uninitialized buffers (like defmt-rtt) in RAM; load is fine for RAM-run */
90 .uninit :
91 {
92 *(.uninit .uninit.*);
93 } > RAM
94
95 .bss :
96 {
97 . = ALIGN(4);
98 __sbss = .;
99 *(.bss .bss.*);
100 . = ALIGN(4);
101 __ebss = .;
102 } > RAM
103
104 /* Discard exception unwinding info */
105 /DISCARD/ :
106 {
107 *(.ARM.exidx .ARM.exidx.*);
108 }
109}
diff --git a/embassy-mcxa/run.sh b/embassy-mcxa/run.sh
new file mode 100644
index 000000000..418dc8a24
--- /dev/null
+++ b/embassy-mcxa/run.sh
@@ -0,0 +1,93 @@
1#!/usr/bin/env bash
2set -euo pipefail
3
4ELF="${1:-}"
5if [[ -z "${ELF}" ]]; then
6 echo "Usage: $0 <elf_file>"
7 exit 1
8fi
9if [[ ! -f "${ELF}" ]]; then
10 echo "ELF not found: ${ELF}"
11 exit 1
12fi
13
14# Configurable via env
15CHIP="${CHIP:-MCXA276}"
16SPEED="${PROBE_SPEED:-1000}" # kHz
17# Default to J-Link if PROBE not provided
18PROBE_OPT=(--probe "${PROBE:-1366:0101:000600110607}")
19PORT="${PROBE_RS_GDB_PORT:-1337}"
20
21cleanup() {
22 if [[ -n "${GDB_SERVER_PID:-}" ]]; then kill "${GDB_SERVER_PID}" 2>/dev/null || true; fi
23 [[ -n "${GDB_SCRIPT:-}" ]] && rm -f "${GDB_SCRIPT}" || true
24 [[ -n "${SERVER_LOG:-}" ]] && rm -f "${SERVER_LOG}" || true
25}
26trap cleanup EXIT
27
28if ! command -v probe-rs >/dev/null 2>&1; then
29 echo "probe-rs not found (cargo install probe-rs --features cli)"
30 exit 1
31fi
32if ! command -v gdb-multiarch >/dev/null 2>&1; then
33 echo "gdb-multiarch not found; install it (e.g., sudo apt install gdb-multiarch)."
34 exit 1
35fi
36
37# Start probe-rs GDB server and capture its output to a log (do not hide errors)
38SERVER_LOG=$(mktemp)
39set +e
40probe-rs gdb --chip "${CHIP}" --protocol swd --speed "${SPEED}" --non-interactive "${ELF}" "${PROBE_OPT[@]}" \
41 >"${SERVER_LOG}" 2>&1 &
42GDB_SERVER_PID=$!
43set -e
44
45# Wait for server readiness without touching the TCP port to avoid corrupting the GDB protocol
46ready=""
47for _ in {1..50}; do
48 if grep -q "Firing up GDB stub" "${SERVER_LOG}"; then ready=1; break; fi
49 if grep -q "Connecting to the chip was unsuccessful" "${SERVER_LOG}"; then
50 echo "probe-rs gdb server failed to connect to target. Log:" >&2
51 echo "----- probe-rs gdb log -----" >&2
52 sed -e 's/^/ /' "${SERVER_LOG}" >&2 || true
53 exit 1
54 fi
55 sleep 0.1
56done
57if [[ -z "${ready}" ]]; then
58 echo "probe-rs gdb server did not report readiness. Log:" >&2
59 echo "----- probe-rs gdb log -----" >&2
60 sed -e 's/^/ /' "${SERVER_LOG}" >&2 || true
61 exit 1
62fi
63
64# GDB script: load to RAM and run, no reset
65GDB_SCRIPT=$(mktemp)
66cat >"${GDB_SCRIPT}" <<EOF
67set pagination off
68set confirm off
69set mem inaccessible-by-default off
70
71# Connect and load without reset
72target remote :${PORT}
73monitor halt
74load
75# Set VTOR to point to our RAM vector table at 0x20000000
76# This ensures the CPU uses the correct initial SP and Reset vector
77set *0xE000ED08 = 0x20000000
78# Now read SP and PC from our vector table and set them
79set \$sp = *(unsigned int*)0x20000000
80set \$pc = *(unsigned int*)0x20000004
81# Run target (blocks here until you Ctrl+C, like before)
82continue
83EOF
84
85# Run gdb against the server
86if ! gdb-multiarch -q -batch -x "${GDB_SCRIPT}" "${ELF}"; then
87 echo "GDB failed to load/run. probe-rs gdb server log:" >&2
88 echo "----- probe-rs gdb log -----" >&2
89 sed -e 's/^/ /' "${SERVER_LOG}" >&2 || true
90 exit 1
91fi
92
93echo "Program loaded and started (no reset)"
diff --git a/embassy-mcxa/rustfmt.toml b/embassy-mcxa/rustfmt.toml
new file mode 100644
index 000000000..9eb3c3b4f
--- /dev/null
+++ b/embassy-mcxa/rustfmt.toml
@@ -0,0 +1,3 @@
1group_imports = "StdExternalCrate"
2imports_granularity = "Module"
3max_width = 120
diff --git a/embassy-mcxa/src/adc.rs b/embassy-mcxa/src/adc.rs
new file mode 100644
index 000000000..b5ec5983f
--- /dev/null
+++ b/embassy-mcxa/src/adc.rs
@@ -0,0 +1,409 @@
1//! ADC driver
2use core::sync::atomic::{AtomicBool, Ordering};
3
4use embassy_hal_internal::{Peri, PeripheralType};
5
6use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4};
7use crate::clocks::{enable_and_reset, Gate, PoweredClock};
8use crate::pac;
9use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres};
10use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts};
11use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode};
12use crate::pac::adc1::ctrl::CalAvgs;
13use crate::pac::adc1::tctrl::{Tcmd, Tpri};
14
15type Regs = pac::adc1::RegisterBlock;
16
17static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false);
18// Token-based instance pattern like embassy-imxrt
19pub trait Instance: Gate<MrccPeriphConfig = AdcConfig> + PeripheralType {
20 fn ptr() -> *const Regs;
21}
22
23/// Token for ADC1
24pub type Adc1 = crate::peripherals::ADC1;
25impl Instance for crate::peripherals::ADC1 {
26 #[inline(always)]
27 fn ptr() -> *const Regs {
28 pac::Adc1::ptr()
29 }
30}
31
32// Also implement Instance for the Peri wrapper type
33// impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> {
34// #[inline(always)]
35// fn ptr() -> *const Regs {
36// pac::Adc1::ptr()
37// }
38// }
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41#[repr(u8)]
42pub enum TriggerPriorityPolicy {
43 ConvPreemptImmediatelyNotAutoResumed = 0,
44 ConvPreemptSoftlyNotAutoResumed = 1,
45 ConvPreemptImmediatelyAutoRestarted = 4,
46 ConvPreemptSoftlyAutoRestarted = 5,
47 ConvPreemptImmediatelyAutoResumed = 12,
48 ConvPreemptSoftlyAutoResumed = 13,
49 ConvPreemptSubsequentlyNotAutoResumed = 2,
50 ConvPreemptSubsequentlyAutoRestarted = 6,
51 ConvPreemptSubsequentlyAutoResumed = 14,
52 TriggerPriorityExceptionDisabled = 16,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub struct LpadcConfig {
57 pub enable_in_doze_mode: bool,
58 pub conversion_average_mode: CalAvgs,
59 pub enable_analog_preliminary: bool,
60 pub power_up_delay: u8,
61 pub reference_voltage_source: Refsel,
62 pub power_level_mode: Pwrsel,
63 pub trigger_priority_policy: TriggerPriorityPolicy,
64 pub enable_conv_pause: bool,
65 pub conv_pause_delay: u16,
66 pub fifo_watermark: u8,
67 pub power: PoweredClock,
68 pub source: AdcClockSel,
69 pub div: Div4,
70}
71
72impl Default for LpadcConfig {
73 fn default() -> Self {
74 LpadcConfig {
75 enable_in_doze_mode: true,
76 conversion_average_mode: CalAvgs::NoAverage,
77 enable_analog_preliminary: false,
78 power_up_delay: 0x80,
79 reference_voltage_source: Refsel::Option1,
80 power_level_mode: Pwrsel::Lowest,
81 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
82 enable_conv_pause: false,
83 conv_pause_delay: 0,
84 fifo_watermark: 0,
85 power: PoweredClock::NormalEnabledDeepSleepDisabled,
86 source: AdcClockSel::FroLfDiv,
87 div: Div4::no_div(),
88 }
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub struct ConvCommandConfig {
94 pub sample_channel_mode: Ctype,
95 pub channel_number: Adch,
96 pub chained_next_command_number: Next,
97 pub enable_auto_channel_increment: bool,
98 pub loop_count: u8,
99 pub hardware_average_mode: Avgs,
100 pub sample_time_mode: Sts,
101 pub hardware_compare_mode: Cmpen,
102 pub hardware_compare_value_high: u32,
103 pub hardware_compare_value_low: u32,
104 pub conversion_resolution_mode: Mode,
105 pub enable_wait_trigger: bool,
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct ConvTriggerConfig {
110 pub target_command_id: Tcmd,
111 pub delay_power: u8,
112 pub priority: Tpri,
113 pub enable_hardware_trigger: bool,
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub struct ConvResult {
118 pub command_id_source: u32,
119 pub loop_count_index: u32,
120 pub trigger_id_source: u32,
121 pub conv_value: u16,
122}
123
124pub struct Adc<'a, I: Instance> {
125 _inst: core::marker::PhantomData<&'a mut I>,
126}
127
128impl<'a, I: Instance> Adc<'a, I> {
129 /// initialize ADC
130 pub fn new(_inst: Peri<'a, I>, config: LpadcConfig) -> Self {
131 let adc = unsafe { &*I::ptr() };
132
133 let _clock_freq = unsafe {
134 enable_and_reset::<I>(&AdcConfig {
135 power: config.power,
136 source: config.source,
137 div: config.div,
138 })
139 .expect("Adc Init should not fail")
140 };
141
142 /* Reset the module. */
143 adc.ctrl().modify(|_, w| w.rst().held_in_reset());
144 adc.ctrl().modify(|_, w| w.rst().released_from_reset());
145
146 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
147
148 /* Disable the module before setting configuration. */
149 adc.ctrl().modify(|_, w| w.adcen().disabled());
150
151 /* Configure the module generally. */
152 if config.enable_in_doze_mode {
153 adc.ctrl().modify(|_, w| w.dozen().enabled());
154 } else {
155 adc.ctrl().modify(|_, w| w.dozen().disabled());
156 }
157
158 /* Set calibration average mode. */
159 adc.ctrl()
160 .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode));
161
162 adc.cfg().write(|w| unsafe {
163 let w = if config.enable_analog_preliminary {
164 w.pwren().pre_enabled()
165 } else {
166 w
167 };
168
169 w.pudly()
170 .bits(config.power_up_delay)
171 .refsel()
172 .variant(config.reference_voltage_source)
173 .pwrsel()
174 .variant(config.power_level_mode)
175 .tprictrl()
176 .variant(match config.trigger_priority_policy {
177 TriggerPriorityPolicy::ConvPreemptSoftlyNotAutoResumed
178 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
179 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed => Tprictrl::FinishCurrentOnPriority,
180 TriggerPriorityPolicy::ConvPreemptSubsequentlyNotAutoResumed
181 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
182 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tprictrl::FinishSequenceOnPriority,
183 _ => Tprictrl::AbortCurrentOnPriority,
184 })
185 .tres()
186 .variant(match config.trigger_priority_policy {
187 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoRestarted
188 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
189 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
190 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
191 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
192 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tres::Enabled,
193 _ => Tres::Disabled,
194 })
195 .tcmdres()
196 .variant(match config.trigger_priority_policy {
197 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
198 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
199 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed
200 | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => Tcmdres::Enabled,
201 _ => Tcmdres::Disabled,
202 })
203 .hpt_exdi()
204 .variant(match config.trigger_priority_policy {
205 TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => HptExdi::Disabled,
206 _ => HptExdi::Enabled,
207 })
208 });
209
210 if config.enable_conv_pause {
211 adc.pause()
212 .modify(|_, w| unsafe { w.pauseen().enabled().pausedly().bits(config.conv_pause_delay) });
213 } else {
214 adc.pause().write(|w| unsafe { w.bits(0) });
215 }
216
217 adc.fctrl0()
218 .write(|w| unsafe { w.fwmark().bits(config.fifo_watermark) });
219
220 // Enable ADC
221 adc.ctrl().modify(|_, w| w.adcen().enabled());
222
223 Self {
224 _inst: core::marker::PhantomData,
225 }
226 }
227
228 pub fn deinit(&self) {
229 let adc = unsafe { &*I::ptr() };
230 adc.ctrl().modify(|_, w| w.adcen().disabled());
231 }
232
233 pub fn do_offset_calibration(&self) {
234 let adc = unsafe { &*I::ptr() };
235 // Enable calibration mode
236 adc.ctrl()
237 .modify(|_, w| w.calofs().offset_calibration_request_pending());
238
239 // Wait for calibration to complete (polling status register)
240 while adc.stat().read().cal_rdy().is_not_set() {}
241 }
242
243 pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 {
244 let mut gcra_array = [0u32; 17];
245 let mut gcalr: u32 = 0;
246
247 for i in (1..=17).rev() {
248 let shift = 16 - (i - 1);
249 let step = 1.0 / (1u32 << shift) as f32;
250 let tmp = (gain_adjustment / step) as u32;
251 gcra_array[i - 1] = tmp;
252 gain_adjustment -= tmp as f32 * step;
253 }
254
255 for i in (1..=17).rev() {
256 gcalr += gcra_array[i - 1] << (i - 1);
257 }
258 gcalr
259 }
260
261 pub fn do_auto_calibration(&self) {
262 let adc = unsafe { &*I::ptr() };
263 adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending());
264
265 while adc.gcc0().read().rdy().is_gain_cal_not_valid() {}
266
267 let mut gcca = adc.gcc0().read().gain_cal().bits() as u32;
268 if gcca & ((0xFFFF + 1) >> 1) != 0 {
269 gcca |= !0xFFFF;
270 }
271
272 let gcra = 131072.0 / (131072.0 - gcca as f32);
273
274 // Write to GCR0
275 adc.gcr0().write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) });
276
277 adc.gcr0().modify(|_, w| w.rdy().set_bit());
278
279 // Wait for calibration to complete (polling status register)
280 while adc.stat().read().cal_rdy().is_not_set() {}
281 }
282
283 pub fn do_software_trigger(&self, trigger_id_mask: u32) {
284 let adc = unsafe { &*I::ptr() };
285 adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) });
286 }
287
288 pub fn get_default_conv_command_config(&self) -> ConvCommandConfig {
289 ConvCommandConfig {
290 sample_channel_mode: Ctype::SingleEndedASideChannel,
291 channel_number: Adch::SelectCh0,
292 chained_next_command_number: Next::NoNextCmdTerminateOnFinish,
293 enable_auto_channel_increment: false,
294 loop_count: 0,
295 hardware_average_mode: Avgs::NoAverage,
296 sample_time_mode: Sts::Sample3p5,
297 hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult,
298 hardware_compare_value_high: 0,
299 hardware_compare_value_low: 0,
300 conversion_resolution_mode: Mode::Data12Bits,
301 enable_wait_trigger: false,
302 }
303 }
304
305 //TBD Need to add cmdlx and cmdhx with x {2..7}
306 pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) {
307 let adc = unsafe { &*I::ptr() };
308
309 match index {
310 1 => {
311 adc.cmdl1().write(|w| {
312 w.adch()
313 .variant(config.channel_number)
314 .mode()
315 .variant(config.conversion_resolution_mode)
316 });
317 adc.cmdh1().write(|w| unsafe {
318 w.next()
319 .variant(config.chained_next_command_number)
320 .loop_()
321 .bits(config.loop_count)
322 .avgs()
323 .variant(config.hardware_average_mode)
324 .sts()
325 .variant(config.sample_time_mode)
326 .cmpen()
327 .variant(config.hardware_compare_mode);
328 if config.enable_wait_trigger {
329 w.wait_trig().enabled();
330 }
331 if config.enable_auto_channel_increment {
332 w.lwi().enabled();
333 }
334 w
335 });
336 }
337 _ => panic!("Invalid command index: must be between 1 and 7"),
338 }
339 }
340
341 pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig {
342 ConvTriggerConfig {
343 target_command_id: Tcmd::NotValid,
344 delay_power: 0,
345 priority: Tpri::HighestPriority,
346 enable_hardware_trigger: false,
347 }
348 }
349
350 pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) {
351 let adc = unsafe { &*I::ptr() };
352 let tctrl = &adc.tctrl(trigger_id);
353
354 tctrl.write(|w| unsafe {
355 let w = w.tcmd().variant(config.target_command_id);
356 let w = w.tdly().bits(config.delay_power);
357 w.tpri().variant(config.priority);
358 if config.enable_hardware_trigger {
359 w.hten().enabled()
360 } else {
361 w
362 }
363 });
364 }
365
366 pub fn do_reset_fifo(&self) {
367 let adc = unsafe { &*I::ptr() };
368 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
369 }
370
371 pub fn enable_interrupt(&self, mask: u32) {
372 let adc = unsafe { &*I::ptr() };
373 adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
374 INTERRUPT_TRIGGERED.store(false, Ordering::SeqCst);
375 }
376
377 pub fn is_interrupt_triggered(&self) -> bool {
378 INTERRUPT_TRIGGERED.load(Ordering::Relaxed)
379 }
380}
381
382pub fn get_conv_result() -> Option<ConvResult> {
383 let adc = unsafe { &*pac::Adc1::ptr() };
384 let fifo = adc.resfifo0().read().bits();
385 const VALID_MASK: u32 = 1 << 31;
386 if fifo & VALID_MASK == 0 {
387 return None;
388 }
389
390 Some(ConvResult {
391 command_id_source: (fifo >> 24) & 0x0F,
392 loop_count_index: (fifo >> 20) & 0x0F,
393 trigger_id_source: (fifo >> 16) & 0x0F,
394 conv_value: (fifo & 0xFFFF) as u16,
395 })
396}
397
398pub fn on_interrupt() {
399 if get_conv_result().is_some() {
400 INTERRUPT_TRIGGERED.store(true, Ordering::SeqCst);
401 }
402}
403
404pub struct AdcHandler;
405impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::ADC1> for AdcHandler {
406 unsafe fn on_interrupt() {
407 on_interrupt();
408 }
409}
diff --git a/embassy-mcxa/src/clkout.rs b/embassy-mcxa/src/clkout.rs
new file mode 100644
index 000000000..88c731df1
--- /dev/null
+++ b/embassy-mcxa/src/clkout.rs
@@ -0,0 +1,169 @@
1//! CLKOUT pseudo-peripheral
2//!
3//! CLKOUT is a part of the clock generation subsystem, and can be used
4//! either to generate arbitrary waveforms, or to debug the state of
5//! internal oscillators.
6
7use core::marker::PhantomData;
8
9use embassy_hal_internal::Peri;
10
11pub use crate::clocks::periph_helpers::Div4;
12use crate::clocks::{with_clocks, ClockError, PoweredClock};
13use crate::pac::mrcc0::mrcc_clkout_clksel::Mux;
14use crate::peripherals::CLKOUT;
15
16/// A peripheral representing the CLKOUT pseudo-peripheral
17pub struct ClockOut<'a> {
18 _p: PhantomData<&'a mut CLKOUT>,
19 freq: u32,
20}
21
22/// Selected clock source to output
23pub enum ClockOutSel {
24 /// 12MHz Internal Oscillator
25 Fro12M,
26 /// FRO180M Internal Oscillator, via divisor
27 FroHfDiv,
28 /// External Oscillator
29 ClkIn,
30 /// 16KHz oscillator
31 Clk16K,
32 /// Output of PLL1
33 Pll1Clk,
34 /// Main System CPU clock, divided by 6
35 SlowClk,
36}
37
38/// Configuration for the ClockOut
39pub struct Config {
40 /// Selected Source Clock
41 pub sel: ClockOutSel,
42 /// Selected division level
43 pub div: Div4,
44 /// Selected power level
45 pub level: PoweredClock,
46}
47
48impl<'a> ClockOut<'a> {
49 /// Create a new ClockOut pin. On success, the clock signal will begin immediately
50 /// on the given pin.
51 pub fn new(
52 _peri: Peri<'a, CLKOUT>,
53 pin: Peri<'a, impl sealed::ClockOutPin>,
54 cfg: Config,
55 ) -> Result<Self, ClockError> {
56 // There's no MRCC enable bit, so we check the validity of the clocks here
57 //
58 // TODO: Should we check that the frequency is suitably low?
59 let (freq, mux) = check_sel(cfg.sel, cfg.level)?;
60
61 // All good! Apply requested config, starting with the pin.
62 pin.mux();
63
64 setup_clkout(mux, cfg.div);
65
66 Ok(Self {
67 _p: PhantomData,
68 freq: freq / cfg.div.into_divisor(),
69 })
70 }
71
72 /// Frequency of the clkout pin
73 #[inline]
74 pub fn frequency(&self) -> u32 {
75 self.freq
76 }
77}
78
79impl Drop for ClockOut<'_> {
80 fn drop(&mut self) {
81 disable_clkout();
82 }
83}
84
85/// Check whether the given clock selection is valid
86fn check_sel(sel: ClockOutSel, level: PoweredClock) -> Result<(u32, Mux), ClockError> {
87 let res = with_clocks(|c| {
88 Ok(match sel {
89 ClockOutSel::Fro12M => (c.ensure_fro_hf_active(&level)?, Mux::Clkroot12m),
90 ClockOutSel::FroHfDiv => (c.ensure_fro_hf_div_active(&level)?, Mux::ClkrootFircDiv),
91 ClockOutSel::ClkIn => (c.ensure_clk_in_active(&level)?, Mux::ClkrootSosc),
92 ClockOutSel::Clk16K => (c.ensure_clk_16k_vdd_core_active(&level)?, Mux::Clkroot16k),
93 ClockOutSel::Pll1Clk => (c.ensure_pll1_clk_active(&level)?, Mux::ClkrootSpll),
94 ClockOutSel::SlowClk => (c.ensure_slow_clk_active(&level)?, Mux::ClkrootSlow),
95 })
96 });
97 let Some(res) = res else {
98 return Err(ClockError::NeverInitialized);
99 };
100 res
101}
102
103/// Set up the clkout pin using the given mux and div settings
104fn setup_clkout(mux: Mux, div: Div4) {
105 let mrcc = unsafe { crate::pac::Mrcc0::steal() };
106
107 mrcc.mrcc_clkout_clksel().write(|w| w.mux().variant(mux));
108
109 // Set up clkdiv
110 mrcc.mrcc_clkout_clkdiv().write(|w| {
111 w.halt().set_bit();
112 w.reset().set_bit();
113 unsafe { w.div().bits(div.into_bits()) };
114 w
115 });
116 mrcc.mrcc_clkout_clkdiv().write(|w| {
117 w.halt().clear_bit();
118 w.reset().clear_bit();
119 unsafe { w.div().bits(div.into_bits()) };
120 w
121 });
122
123 while mrcc.mrcc_clkout_clkdiv().read().unstab().bit_is_set() {}
124}
125
126/// Stop the clkout
127fn disable_clkout() {
128 // Stop the output by selecting the "none" clock
129 //
130 // TODO: restore the pin to hi-z or something?
131 let mrcc = unsafe { crate::pac::Mrcc0::steal() };
132 mrcc.mrcc_clkout_clkdiv().write(|w| {
133 w.reset().set_bit();
134 w.halt().set_bit();
135 unsafe {
136 w.div().bits(0);
137 }
138 w
139 });
140 mrcc.mrcc_clkout_clksel().write(|w| unsafe { w.bits(0b111) });
141}
142
143mod sealed {
144 use embassy_hal_internal::PeripheralType;
145
146 use crate::gpio::{Pull, SealedPin};
147
148 /// Sealed marker trait for clockout pins
149 pub trait ClockOutPin: PeripheralType {
150 /// Set the given pin to the correct muxing state
151 fn mux(&self);
152 }
153
154 macro_rules! impl_pin {
155 ($pin:ident, $func:ident) => {
156 impl ClockOutPin for crate::peripherals::$pin {
157 fn mux(&self) {
158 self.set_function(crate::pac::port0::pcr0::Mux::$func);
159 self.set_pull(Pull::Disabled);
160 }
161 }
162 };
163 }
164
165 impl_pin!(P0_6, Mux12);
166 impl_pin!(P3_6, Mux1);
167 impl_pin!(P3_8, Mux12);
168 impl_pin!(P4_2, Mux1);
169}
diff --git a/embassy-mcxa/src/clocks/config.rs b/embassy-mcxa/src/clocks/config.rs
new file mode 100644
index 000000000..0563b8917
--- /dev/null
+++ b/embassy-mcxa/src/clocks/config.rs
@@ -0,0 +1,204 @@
1//! Clock Configuration
2//!
3//! This module holds configuration types used for the system clocks. For
4//! configuration of individual peripherals, see [`super::periph_helpers`].
5
6use super::PoweredClock;
7
8/// This type represents a divider in the range 1..=256.
9///
10/// At a hardware level, this is an 8-bit register from 0..=255,
11/// which adds one.
12#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13pub struct Div8(pub(super) u8);
14
15impl Div8 {
16 /// Store a "raw" divisor value that will divide the source by
17 /// `(n + 1)`, e.g. `Div8::from_raw(0)` will divide the source
18 /// by 1, and `Div8::from_raw(255)` will divide the source by
19 /// 256.
20 pub const fn from_raw(n: u8) -> Self {
21 Self(n)
22 }
23
24 /// Divide by one, or no division
25 pub const fn no_div() -> Self {
26 Self(0)
27 }
28
29 /// Store a specific divisor value that will divide the source
30 /// by `n`. e.g. `Div8::from_divisor(1)` will divide the source
31 /// by 1, and `Div8::from_divisor(256)` will divide the source
32 /// by 256.
33 ///
34 /// Will return `None` if `n` is not in the range `1..=256`.
35 /// Consider [`Self::from_raw`] for an infallible version.
36 pub const fn from_divisor(n: u16) -> Option<Self> {
37 let Some(n) = n.checked_sub(1) else {
38 return None;
39 };
40 if n > (u8::MAX as u16) {
41 return None;
42 }
43 Some(Self(n as u8))
44 }
45
46 /// Convert into "raw" bits form
47 #[inline(always)]
48 pub const fn into_bits(self) -> u8 {
49 self.0
50 }
51
52 /// Convert into "divisor" form, as a u32 for convenient frequency math
53 #[inline(always)]
54 pub const fn into_divisor(self) -> u32 {
55 self.0 as u32 + 1
56 }
57}
58
59/// ```text
60/// ┌─────────────────────────────────────────────────────────┐
61/// │ │
62/// │ ┌───────────┐ clk_out ┌─────────┐ │
63/// XTAL ──────┼──▷│ System │───────────▷│ │ clk_in │
64/// │ │ OSC │ clkout_byp │ MUX │──────────────────┼──────▷
65/// EXTAL ──────┼──▷│ │───────────▷│ │ │
66/// │ └───────────┘ └─────────┘ │
67/// │ │
68/// │ ┌───────────┐ fro_hf_root ┌────┐ fro_hf │
69/// │ │ FRO180 ├───────┬─────▷│ CG │─────────────────────┼──────▷
70/// │ │ │ │ ├────┤ clk_45m │
71/// │ │ │ └─────▷│ CG │─────────────────────┼──────▷
72/// │ └───────────┘ └────┘ │
73/// │ ┌───────────┐ fro_12m_root ┌────┐ fro_12m │
74/// │ │ FRO12M │────────┬─────▷│ CG │────────────────────┼──────▷
75/// │ │ │ │ ├────┤ clk_1m │
76/// │ │ │ └─────▷│1/12│────────────────────┼──────▷
77/// │ └───────────┘ └────┘ │
78/// │ │
79/// │ ┌──────────┐ │
80/// │ │000 │ │
81/// │ clk_in │ │ │
82/// │ ───────────────▷│001 │ │
83/// │ fro_12m │ │ │
84/// │ ───────────────▷│010 │ │
85/// │ fro_hf_root │ │ │
86/// │ ───────────────▷│011 │ main_clk │
87/// │ │ │───────────────────────────┼──────▷
88/// clk_16k ──────┼─────────────────▷│100 │ │
89/// │ none │ │ │
90/// │ ───────────────▷│101 │ │
91/// │ pll1_clk │ │ │
92/// │ ───────────────▷│110 │ │
93/// │ none │ │ │
94/// │ ───────────────▷│111 │ │
95/// │ └──────────┘ │
96/// │ ▲ │
97/// │ │ │
98/// │ SCG SCS │
99/// │ SCG-Lite │
100/// └─────────────────────────────────────────────────────────┘
101///
102///
103/// clk_in ┌─────┐
104/// ───────────────▷│00 │
105/// clk_45m │ │
106/// ───────────────▷│01 │ ┌───────────┐ pll1_clk
107/// none │ │─────▷│ SPLL │───────────────▷
108/// ───────────────▷│10 │ └───────────┘
109/// fro_12m │ │
110/// ───────────────▷│11 │
111/// └─────┘
112/// ```
113#[non_exhaustive]
114pub struct ClocksConfig {
115 /// FIRC, FRO180, 45/60/90/180M clock source
116 pub firc: Option<FircConfig>,
117 /// SIRC, FRO12M, clk_12m clock source
118 // NOTE: I don't think we *can* disable the SIRC?
119 pub sirc: SircConfig,
120 /// FRO16K clock source
121 pub fro16k: Option<Fro16KConfig>,
122}
123
124// FIRC/FRO180M
125
126/// ```text
127/// ┌───────────┐ fro_hf_root ┌────┐ fro_hf
128/// │ FRO180M ├───────┬─────▷│GATE│──────────▷
129/// │ │ │ ├────┤ clk_45m
130/// │ │ └─────▷│GATE│──────────▷
131/// └───────────┘ └────┘
132/// ```
133#[non_exhaustive]
134pub struct FircConfig {
135 /// Selected clock frequency
136 pub frequency: FircFreqSel,
137 /// Selected power state of the clock
138 pub power: PoweredClock,
139 /// Is the "fro_hf" gated clock enabled?
140 pub fro_hf_enabled: bool,
141 /// Is the "clk_45m" gated clock enabled?
142 pub clk_45m_enabled: bool,
143 /// Is the "fro_hf_div" clock enabled? Requires `fro_hf`!
144 pub fro_hf_div: Option<Div8>,
145}
146
147/// Selected FIRC frequency
148pub enum FircFreqSel {
149 /// 45MHz Output
150 Mhz45,
151 /// 60MHz Output
152 Mhz60,
153 /// 90MHz Output
154 Mhz90,
155 /// 180MHz Output
156 Mhz180,
157}
158
159// SIRC/FRO12M
160
161/// ```text
162/// ┌───────────┐ fro_12m_root ┌────┐ fro_12m
163/// │ FRO12M │────────┬─────▷│ CG │──────────▷
164/// │ │ │ ├────┤ clk_1m
165/// │ │ └─────▷│1/12│──────────▷
166/// └───────────┘ └────┘
167/// ```
168#[non_exhaustive]
169pub struct SircConfig {
170 pub power: PoweredClock,
171 // peripheral output, aka sirc_12mhz
172 pub fro_12m_enabled: bool,
173 /// Is the "fro_lf_div" clock enabled? Requires `fro_12m`!
174 pub fro_lf_div: Option<Div8>,
175}
176
177#[non_exhaustive]
178pub struct Fro16KConfig {
179 pub vsys_domain_active: bool,
180 pub vdd_core_domain_active: bool,
181}
182
183impl Default for ClocksConfig {
184 fn default() -> Self {
185 Self {
186 firc: Some(FircConfig {
187 frequency: FircFreqSel::Mhz45,
188 power: PoweredClock::NormalEnabledDeepSleepDisabled,
189 fro_hf_enabled: true,
190 clk_45m_enabled: true,
191 fro_hf_div: None,
192 }),
193 sirc: SircConfig {
194 power: PoweredClock::AlwaysEnabled,
195 fro_12m_enabled: true,
196 fro_lf_div: None,
197 },
198 fro16k: Some(Fro16KConfig {
199 vsys_domain_active: true,
200 vdd_core_domain_active: true,
201 }),
202 }
203 }
204}
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs
new file mode 100644
index 000000000..9c9e6ef3d
--- /dev/null
+++ b/embassy-mcxa/src/clocks/mod.rs
@@ -0,0 +1,943 @@
1//! # Clock Module
2//!
3//! For the MCX-A, we separate clock and peripheral control into two main stages:
4//!
5//! 1. At startup, e.g. when `embassy_mcxa::init()` is called, we configure the
6//! core system clocks, including external and internal oscillators. This
7//! configuration is then largely static for the duration of the program.
8//! 2. When HAL drivers are created, e.g. `Lpuart::new()` is called, the driver
9//! is responsible for two main things:
10//! * Ensuring that any required "upstream" core system clocks necessary for
11//! clocking the peripheral is active and configured to a reasonable value
12//! * Enabling the clock gates for that peripheral, and resetting the peripheral
13//!
14//! From a user perspective, only step 1 is visible. Step 2 is automatically handled
15//! by HAL drivers, using interfaces defined in this module.
16//!
17//! It is also possible to *view* the state of the clock configuration after [`init()`]
18//! has been called, using the [`with_clocks()`] function, which provides a view of the
19//! [`Clocks`] structure.
20//!
21//! ## For HAL driver implementors
22//!
23//! The majority of peripherals in the MCXA chip are fed from either a "hard-coded" or
24//! configurable clock source, e.g. selecting the FROM12M or `clk_1m` as a source. This
25//! selection, as well as often any pre-scaler division from that source clock, is made
26//! through MRCC registers.
27//!
28//! Any peripheral that is controlled through the MRCC register can automatically implement
29//! the necessary APIs using the `impl_cc_gate!` macro in this module. You will also need
30//! to define the configuration surface and steps necessary to fully configure that peripheral
31//! from a clocks perspective by:
32//!
33//! 1. Defining a configuration type in the [`periph_helpers`] module that contains any selects
34//! or divisions available to the HAL driver
35//! 2. Implementing the [`periph_helpers::SPConfHelper`] trait, which should check that the
36//! necessary input clocks are reasonable
37
38use core::cell::RefCell;
39
40use config::{ClocksConfig, FircConfig, FircFreqSel, Fro16KConfig, SircConfig};
41use mcxa_pac::scg0::firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten};
42use mcxa_pac::scg0::sirccsr::{SircClkPeriphEn, Sircsten};
43use periph_helpers::SPConfHelper;
44
45use crate::pac;
46pub mod config;
47pub mod periph_helpers;
48
49//
50// Statics/Consts
51//
52
53/// The state of system core clocks.
54///
55/// Initialized by [`init()`], and then unchanged for the remainder of the program.
56static CLOCKS: critical_section::Mutex<RefCell<Option<Clocks>>> = critical_section::Mutex::new(RefCell::new(None));
57
58//
59// Free functions
60//
61
62/// Initialize the core system clocks with the given [`ClocksConfig`].
63///
64/// This function should be called EXACTLY once at start-up, usually via a
65/// call to [`embassy_mcxa::init()`](crate::init()). Subsequent calls will
66/// return an error.
67pub fn init(settings: ClocksConfig) -> Result<(), ClockError> {
68 critical_section::with(|cs| {
69 if CLOCKS.borrow_ref(cs).is_some() {
70 Err(ClockError::AlreadyInitialized)
71 } else {
72 Ok(())
73 }
74 })?;
75
76 let mut clocks = Clocks::default();
77 let mut operator = ClockOperator {
78 clocks: &mut clocks,
79 config: &settings,
80
81 _mrcc0: unsafe { pac::Mrcc0::steal() },
82 scg0: unsafe { pac::Scg0::steal() },
83 syscon: unsafe { pac::Syscon::steal() },
84 vbat0: unsafe { pac::Vbat0::steal() },
85 };
86
87 operator.configure_firc_clocks()?;
88 operator.configure_sirc_clocks()?;
89 operator.configure_fro16k_clocks()?;
90
91 // For now, just use FIRC as the main/cpu clock, which should already be
92 // the case on reset
93 assert!(operator.scg0.rccr().read().scs().is_firc());
94 let input = operator.clocks.fro_hf_root.clone().unwrap();
95 operator.clocks.main_clk = Some(input.clone());
96 // We can also assume cpu/system clk == fro_hf because div is /1.
97 assert_eq!(operator.syscon.ahbclkdiv().read().div().bits(), 0);
98 operator.clocks.cpu_system_clk = Some(input);
99
100 critical_section::with(|cs| {
101 let mut clks = CLOCKS.borrow_ref_mut(cs);
102 assert!(clks.is_none(), "Clock setup race!");
103 *clks = Some(clocks);
104 });
105
106 Ok(())
107}
108
109/// Obtain the full clocks structure, calling the given closure in a critical section.
110///
111/// The given closure will be called with read-only access to the state of the system
112/// clocks. This can be used to query and return the state of a given clock.
113///
114/// As the caller's closure will be called in a critical section, care must be taken
115/// not to block or cause any other undue delays while accessing.
116///
117/// Calls to this function will not succeed until after a successful call to `init()`,
118/// and will always return None.
119pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {
120 critical_section::with(|cs| {
121 let c = CLOCKS.borrow_ref(cs);
122 let c = c.as_ref()?;
123 Some(f(c))
124 })
125}
126
127//
128// Structs/Enums
129//
130
131/// The `Clocks` structure contains the initialized state of the core system clocks
132///
133/// These values are configured by providing [`config::ClocksConfig`] to the [`init()`] function
134/// at boot time.
135#[derive(Default, Debug, Clone)]
136#[non_exhaustive]
137pub struct Clocks {
138 /// The `clk_in` is a clock provided by an external oscillator
139 pub clk_in: Option<Clock>,
140
141 // FRO180M stuff
142 //
143 /// `fro_hf_root` is the direct output of the `FRO180M` internal oscillator
144 ///
145 /// It is used to feed downstream clocks, such as `fro_hf`, `clk_45m`,
146 /// and `fro_hf_div`.
147 pub fro_hf_root: Option<Clock>,
148
149 /// `fro_hf` is the same frequency as `fro_hf_root`, but behind a gate.
150 pub fro_hf: Option<Clock>,
151
152 /// `clk_45` is a 45MHz clock, sourced from `fro_hf`.
153 pub clk_45m: Option<Clock>,
154
155 /// `fro_hf_div` is a configurable frequency clock, sourced from `fro_hf`.
156 pub fro_hf_div: Option<Clock>,
157
158 //
159 // End FRO180M
160
161 // FRO12M stuff
162 //
163 /// `fro_12m_root` is the direct output of the `FRO12M` internal oscillator
164 ///
165 /// It is used to feed downstream clocks, such as `fro_12m`, `clk_1m`,
166 /// `and `fro_lf_div`.
167 pub fro_12m_root: Option<Clock>,
168
169 /// `fro_12m` is the same frequency as `fro_12m_root`, but behind a gate.
170 pub fro_12m: Option<Clock>,
171
172 /// `clk_1m` is a 1MHz clock, sourced from `fro_12m`
173 pub clk_1m: Option<Clock>,
174
175 /// `fro_lf_div` is a configurable frequency clock, sourced from `fro_12m`
176 pub fro_lf_div: Option<Clock>,
177 //
178 // End FRO12M stuff
179 /// `clk_16k_vsys` is one of two outputs of the `FRO16K` internal oscillator.
180 ///
181 /// Also referred to as `clk_16k[0]` in the datasheet, it feeds peripherals in
182 /// the system domain, such as the CMP and RTC.
183 pub clk_16k_vsys: Option<Clock>,
184
185 /// `clk_16k_vdd_core` is one of two outputs of the `FRO16K` internal oscillator.
186 ///
187 /// Also referred to as `clk_16k[1]` in the datasheet, it feeds peripherals in
188 /// the VDD Core domain, such as the OSTimer or LPUarts.
189 pub clk_16k_vdd_core: Option<Clock>,
190
191 /// `main_clk` is the main clock used by the CPU, AHB, APB, IPS bus, and some
192 /// peripherals.
193 pub main_clk: Option<Clock>,
194
195 /// `CPU_CLK` or `SYSTEM_CLK` is the output of `main_clk`, run through the `AHBCLKDIV`
196 pub cpu_system_clk: Option<Clock>,
197
198 /// `pll1_clk` is the output of the main system PLL, `pll1`.
199 pub pll1_clk: Option<Clock>,
200}
201
202/// `ClockError` is the main error returned when configuring or checking clock state
203#[derive(Debug, Copy, Clone, Eq, PartialEq)]
204#[cfg_attr(feature = "defmt", derive(defmt::Format))]
205#[non_exhaustive]
206pub enum ClockError {
207 /// The system clocks were never initialized by calling [`init()`]
208 NeverInitialized,
209 /// The [`init()`] function was called more than once
210 AlreadyInitialized,
211 /// The requested configuration was not possible to fulfill, as the system clocks
212 /// were not configured in a compatible way
213 BadConfig { clock: &'static str, reason: &'static str },
214 /// The requested configuration was not possible to fulfill, as the required system
215 /// clocks have not yet been implemented.
216 NotImplemented { clock: &'static str },
217 /// The requested peripheral could not be configured, as the steps necessary to
218 /// enable it have not yet been implemented.
219 UnimplementedConfig,
220}
221
222/// Information regarding a system clock
223#[derive(Debug, Clone)]
224pub struct Clock {
225 /// The frequency, in Hz, of the given clock
226 pub frequency: u32,
227 /// The power state of the clock, e.g. whether it is active in deep sleep mode
228 /// or not.
229 pub power: PoweredClock,
230}
231
232/// The power state of a given clock.
233///
234/// On the MCX-A, when Deep-Sleep is entered, any clock not configured for Deep Sleep
235/// mode will be stopped. This means that any downstream usage, e.g. by peripherals,
236/// will also stop.
237///
238/// In the future, we will provide an API for entering Deep Sleep, and if there are
239/// any peripherals that are NOT using an `AlwaysEnabled` clock active, entry into
240/// Deep Sleep will be prevented, in order to avoid misbehaving peripherals.
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub enum PoweredClock {
243 /// The given clock will NOT continue running in Deep Sleep mode
244 NormalEnabledDeepSleepDisabled,
245 /// The given clock WILL continue running in Deep Sleep mode
246 AlwaysEnabled,
247}
248
249/// The ClockOperator is a private helper type that contains the methods used
250/// during system clock initialization.
251///
252/// # SAFETY
253///
254/// Concurrent access to clock-relevant peripheral registers, such as `MRCC`, `SCG`,
255/// `SYSCON`, and `VBAT` should not be allowed for the duration of the [`init()`] function.
256struct ClockOperator<'a> {
257 /// A mutable reference to the current state of system clocks
258 clocks: &'a mut Clocks,
259 /// A reference to the requested configuration provided by the caller of [`init()`]
260 config: &'a ClocksConfig,
261
262 // We hold on to stolen peripherals
263 _mrcc0: pac::Mrcc0,
264 scg0: pac::Scg0,
265 syscon: pac::Syscon,
266 vbat0: pac::Vbat0,
267}
268
269/// Trait describing an AHB clock gate that can be toggled through MRCC.
270pub trait Gate {
271 type MrccPeriphConfig: SPConfHelper;
272
273 /// Enable the clock gate.
274 ///
275 /// # SAFETY
276 ///
277 /// The current peripheral must be disabled prior to calling this method
278 unsafe fn enable_clock();
279
280 /// Disable the clock gate.
281 ///
282 /// # SAFETY
283 ///
284 /// There must be no active user of this peripheral when calling this method
285 unsafe fn disable_clock();
286
287 /// Drive the peripheral into reset.
288 ///
289 /// # SAFETY
290 ///
291 /// There must be no active user of this peripheral when calling this method
292 unsafe fn assert_reset();
293
294 /// Drive the peripheral out of reset.
295 ///
296 /// # SAFETY
297 ///
298 /// There must be no active user of this peripheral when calling this method
299 unsafe fn release_reset();
300
301 /// Return whether the clock gate for this peripheral is currently enabled.
302 fn is_clock_enabled() -> bool;
303
304 /// Return whether the peripheral is currently held in reset.
305 fn is_reset_released() -> bool;
306}
307
308/// This is the primary helper method HAL drivers are expected to call when creating
309/// an instance of the peripheral.
310///
311/// This method:
312///
313/// 1. Enables the MRCC clock gate for this peripheral
314/// 2. Calls the `G::MrccPeriphConfig::post_enable_config()` method, returning an error
315/// and re-disabling the peripheral if this fails.
316/// 3. Pulses the MRCC reset line, to reset the peripheral to the default state
317/// 4. Returns the frequency, in Hz that is fed into the peripheral, taking into account
318/// the selected upstream clock, as well as any division specified by `cfg`.
319///
320/// NOTE: if a clock is disabled, sourced from an "ambient" clock source, this method
321/// may return `Ok(0)`. In the future, this might be updated to return the correct
322/// "ambient" clock, e.g. the AHB/APB frequency.
323///
324/// # SAFETY
325///
326/// This peripheral must not yet be in use prior to calling `enable_and_reset`.
327#[inline]
328pub unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
329 let freq = enable::<G>(cfg).inspect_err(|_| disable::<G>())?;
330 pulse_reset::<G>();
331 Ok(freq)
332}
333
334/// Enable the clock gate for the given peripheral.
335///
336/// Prefer [`enable_and_reset`] unless you are specifically avoiding a pulse of the reset, or need
337/// to control the duration of the pulse more directly.
338///
339/// # SAFETY
340///
341/// This peripheral must not yet be in use prior to calling `enable`.
342#[inline]
343pub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
344 G::enable_clock();
345 while !G::is_clock_enabled() {}
346 core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags));
347
348 let freq = critical_section::with(|cs| {
349 let clocks = CLOCKS.borrow_ref(cs);
350 let clocks = clocks.as_ref().ok_or(ClockError::NeverInitialized)?;
351 cfg.post_enable_config(clocks)
352 });
353
354 freq.inspect_err(|_e| {
355 G::disable_clock();
356 })
357}
358
359/// Disable the clock gate for the given peripheral.
360///
361/// # SAFETY
362///
363/// This peripheral must no longer be in use prior to calling `enable`.
364#[allow(dead_code)]
365#[inline]
366pub unsafe fn disable<G: Gate>() {
367 G::disable_clock();
368}
369
370/// Check whether a gate is currently enabled.
371#[allow(dead_code)]
372#[inline]
373pub fn is_clock_enabled<G: Gate>() -> bool {
374 G::is_clock_enabled()
375}
376
377/// Release a reset line for the given peripheral set.
378///
379/// Prefer [`enable_and_reset`].
380///
381/// # SAFETY
382///
383/// This peripheral must not yet be in use prior to calling `release_reset`.
384#[inline]
385pub unsafe fn release_reset<G: Gate>() {
386 G::release_reset();
387}
388
389/// Assert a reset line for the given peripheral set.
390///
391/// Prefer [`enable_and_reset`].
392///
393/// # SAFETY
394///
395/// This peripheral must not yet be in use prior to calling `assert_reset`.
396#[inline]
397pub unsafe fn assert_reset<G: Gate>() {
398 G::assert_reset();
399}
400
401/// Check whether the peripheral is held in reset.
402#[inline]
403pub unsafe fn is_reset_released<G: Gate>() -> bool {
404 G::is_reset_released()
405}
406
407/// Pulse a reset line (assert then release) with a short delay.
408///
409/// Prefer [`enable_and_reset`].
410///
411/// # SAFETY
412///
413/// This peripheral must not yet be in use prior to calling `release_reset`.
414#[inline]
415pub unsafe fn pulse_reset<G: Gate>() {
416 G::assert_reset();
417 cortex_m::asm::nop();
418 cortex_m::asm::nop();
419 G::release_reset();
420}
421
422//
423// `impl`s for structs/enums
424//
425
426/// The [`Clocks`] type's methods generally take the form of "ensure X clock is active".
427///
428/// These methods are intended to be used by HAL peripheral implementors to ensure that their
429/// selected clocks are active at a suitable level at time of construction. These methods
430/// return the frequency of the requested clock, in Hertz, or a [`ClockError`].
431impl Clocks {
432 /// Ensure the `fro_lf_div` clock is active and valid at the given power state.
433 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
434 let Some(clk) = self.fro_lf_div.as_ref() else {
435 return Err(ClockError::BadConfig {
436 clock: "fro_lf_div",
437 reason: "required but not active",
438 });
439 };
440 if !clk.power.meets_requirement_of(at_level) {
441 return Err(ClockError::BadConfig {
442 clock: "fro_lf_div",
443 reason: "not low power active",
444 });
445 }
446 Ok(clk.frequency)
447 }
448
449 /// Ensure the `fro_hf` clock is active and valid at the given power state.
450 pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
451 let Some(clk) = self.fro_hf.as_ref() else {
452 return Err(ClockError::BadConfig {
453 clock: "fro_hf",
454 reason: "required but not active",
455 });
456 };
457 if !clk.power.meets_requirement_of(at_level) {
458 return Err(ClockError::BadConfig {
459 clock: "fro_hf",
460 reason: "not low power active",
461 });
462 }
463 Ok(clk.frequency)
464 }
465
466 /// Ensure the `fro_hf_div` clock is active and valid at the given power state.
467 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
468 let Some(clk) = self.fro_hf_div.as_ref() else {
469 return Err(ClockError::BadConfig {
470 clock: "fro_hf_div",
471 reason: "required but not active",
472 });
473 };
474 if !clk.power.meets_requirement_of(at_level) {
475 return Err(ClockError::BadConfig {
476 clock: "fro_hf_div",
477 reason: "not low power active",
478 });
479 }
480 Ok(clk.frequency)
481 }
482
483 /// Ensure the `clk_in` clock is active and valid at the given power state.
484 pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
485 Err(ClockError::NotImplemented { clock: "clk_in" })
486 }
487
488 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state.
489 pub fn ensure_clk_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
490 // NOTE: clk_16k is always active in low power mode
491 Ok(self
492 .clk_16k_vsys
493 .as_ref()
494 .ok_or(ClockError::BadConfig {
495 clock: "clk_16k_vsys",
496 reason: "required but not active",
497 })?
498 .frequency)
499 }
500
501 /// Ensure the `clk_16k_vdd_core` clock is active and valid at the given power state.
502 pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
503 // NOTE: clk_16k is always active in low power mode
504 Ok(self
505 .clk_16k_vdd_core
506 .as_ref()
507 .ok_or(ClockError::BadConfig {
508 clock: "clk_16k_vdd_core",
509 reason: "required but not active",
510 })?
511 .frequency)
512 }
513
514 /// Ensure the `clk_1m` clock is active and valid at the given power state.
515 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
516 let Some(clk) = self.clk_1m.as_ref() else {
517 return Err(ClockError::BadConfig {
518 clock: "clk_1m",
519 reason: "required but not active",
520 });
521 };
522 if !clk.power.meets_requirement_of(at_level) {
523 return Err(ClockError::BadConfig {
524 clock: "clk_1m",
525 reason: "not low power active",
526 });
527 }
528 Ok(clk.frequency)
529 }
530
531 /// Ensure the `pll1_clk` clock is active and valid at the given power state.
532 pub fn ensure_pll1_clk_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
533 Err(ClockError::NotImplemented { clock: "pll1_clk" })
534 }
535
536 /// Ensure the `pll1_clk_div` clock is active and valid at the given power state.
537 pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
538 Err(ClockError::NotImplemented { clock: "pll1_clk_div" })
539 }
540
541 /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active
542 pub fn ensure_cpu_system_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
543 let Some(clk) = self.cpu_system_clk.as_ref() else {
544 return Err(ClockError::BadConfig {
545 clock: "cpu_system_clk",
546 reason: "required but not active",
547 });
548 };
549 // Can the main_clk ever be active in deep sleep? I think it is gated?
550 match at_level {
551 PoweredClock::NormalEnabledDeepSleepDisabled => {}
552 PoweredClock::AlwaysEnabled => {
553 return Err(ClockError::BadConfig {
554 clock: "main_clk",
555 reason: "not low power active",
556 });
557 }
558 }
559
560 Ok(clk.frequency)
561 }
562
563 pub fn ensure_slow_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
564 let freq = self.ensure_cpu_system_clk_active(at_level)?;
565
566 Ok(freq / 6)
567 }
568}
569
570impl PoweredClock {
571 /// Does THIS clock meet the power requirements of the OTHER clock?
572 pub fn meets_requirement_of(&self, other: &Self) -> bool {
573 match (self, other) {
574 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::AlwaysEnabled) => false,
575 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
576 (PoweredClock::AlwaysEnabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
577 (PoweredClock::AlwaysEnabled, PoweredClock::AlwaysEnabled) => true,
578 }
579 }
580}
581
582impl ClockOperator<'_> {
583 /// Configure the FIRC/FRO180M clock family
584 ///
585 /// NOTE: Currently we require this to be a fairly hardcoded value, as this clock is used
586 /// as the main clock used for the CPU, AHB, APB, etc.
587 fn configure_firc_clocks(&mut self) -> Result<(), ClockError> {
588 const HARDCODED_ERR: Result<(), ClockError> = Err(ClockError::BadConfig {
589 clock: "firc",
590 reason: "For now, FIRC must be enabled and in default state!",
591 });
592
593 // Did the user give us a FIRC config?
594 let Some(firc) = self.config.firc.as_ref() else {
595 return HARDCODED_ERR;
596 };
597 // Is the FIRC set to 45MHz (should be reset default)
598 if !matches!(firc.frequency, FircFreqSel::Mhz45) {
599 return HARDCODED_ERR;
600 }
601 let base_freq = 45_000_000;
602
603 // Now, check if the FIRC as expected for our hardcoded value
604 let mut firc_ok = true;
605
606 // Is the hardware currently set to the default 45MHz?
607 //
608 // NOTE: the SVD currently has the wrong(?) values for these:
609 // 45 -> 48
610 // 60 -> 64
611 // 90 -> 96
612 // 180 -> 192
613 // Probably correct-ish, but for a different trim value?
614 firc_ok &= self.scg0.firccfg().read().freq_sel().is_firc_48mhz_192s();
615
616 // Check some values in the CSR
617 let csr = self.scg0.firccsr().read();
618 // Is it enabled?
619 firc_ok &= csr.fircen().is_enabled();
620 // Is it accurate?
621 firc_ok &= csr.fircacc().is_enabled_and_valid();
622 // Is there no error?
623 firc_ok &= csr.fircerr().is_error_not_detected();
624 // Is the FIRC the system clock?
625 firc_ok &= csr.fircsel().is_firc();
626 // Is it valid?
627 firc_ok &= csr.fircvld().is_enabled_and_valid();
628
629 // Are we happy with the current (hardcoded) state?
630 if !firc_ok {
631 return HARDCODED_ERR;
632 }
633
634 // Note that the fro_hf_root is active
635 self.clocks.fro_hf_root = Some(Clock {
636 frequency: base_freq,
637 power: firc.power,
638 });
639
640 // Okay! Now we're past that, let's enable all the downstream clocks.
641 let FircConfig {
642 frequency: _,
643 power,
644 fro_hf_enabled,
645 clk_45m_enabled,
646 fro_hf_div,
647 } = firc;
648
649 // When is the FRO enabled?
650 let pow_set = match power {
651 PoweredClock::NormalEnabledDeepSleepDisabled => Fircsten::DisabledInStopModes,
652 PoweredClock::AlwaysEnabled => Fircsten::EnabledInStopModes,
653 };
654
655 // Do we enable the `fro_hf` output?
656 let fro_hf_set = if *fro_hf_enabled {
657 self.clocks.fro_hf = Some(Clock {
658 frequency: base_freq,
659 power: *power,
660 });
661 FircFclkPeriphEn::Enabled
662 } else {
663 FircFclkPeriphEn::Disabled
664 };
665
666 // Do we enable the `clk_45m` output?
667 let clk_45m_set = if *clk_45m_enabled {
668 self.clocks.clk_45m = Some(Clock {
669 frequency: 45_000_000,
670 power: *power,
671 });
672 FircSclkPeriphEn::Enabled
673 } else {
674 FircSclkPeriphEn::Disabled
675 };
676
677 self.scg0.firccsr().modify(|_r, w| {
678 w.fircsten().variant(pow_set);
679 w.firc_fclk_periph_en().variant(fro_hf_set);
680 w.firc_sclk_periph_en().variant(clk_45m_set);
681 w
682 });
683
684 // Do we enable the `fro_hf_div` output?
685 if let Some(d) = fro_hf_div.as_ref() {
686 // We need `fro_hf` to be enabled
687 if !*fro_hf_enabled {
688 return Err(ClockError::BadConfig {
689 clock: "fro_hf_div",
690 reason: "fro_hf not enabled",
691 });
692 }
693
694 // Halt and reset the div; then set our desired div.
695 self.syscon.frohfdiv().write(|w| {
696 w.halt().halt();
697 w.reset().asserted();
698 unsafe { w.div().bits(d.into_bits()) };
699 w
700 });
701 // Then unhalt it, and reset it
702 self.syscon.frohfdiv().write(|w| {
703 w.halt().run();
704 w.reset().released();
705 w
706 });
707
708 // Wait for clock to stabilize
709 while self.syscon.frohfdiv().read().unstab().is_ongoing() {}
710
711 // Store off the clock info
712 self.clocks.fro_hf_div = Some(Clock {
713 frequency: base_freq / d.into_divisor(),
714 power: *power,
715 });
716 }
717
718 Ok(())
719 }
720
721 /// Configure the SIRC/FRO12M clock family
722 fn configure_sirc_clocks(&mut self) -> Result<(), ClockError> {
723 let SircConfig {
724 power,
725 fro_12m_enabled,
726 fro_lf_div,
727 } = &self.config.sirc;
728 let base_freq = 12_000_000;
729
730 // Allow writes
731 self.scg0.sirccsr().modify(|_r, w| w.lk().write_enabled());
732 self.clocks.fro_12m_root = Some(Clock {
733 frequency: base_freq,
734 power: *power,
735 });
736
737 let deep = match power {
738 PoweredClock::NormalEnabledDeepSleepDisabled => Sircsten::Disabled,
739 PoweredClock::AlwaysEnabled => Sircsten::Enabled,
740 };
741 let pclk = if *fro_12m_enabled {
742 self.clocks.fro_12m = Some(Clock {
743 frequency: base_freq,
744 power: *power,
745 });
746 self.clocks.clk_1m = Some(Clock {
747 frequency: base_freq / 12,
748 power: *power,
749 });
750 SircClkPeriphEn::Enabled
751 } else {
752 SircClkPeriphEn::Disabled
753 };
754
755 // Set sleep/peripheral usage
756 self.scg0.sirccsr().modify(|_r, w| {
757 w.sircsten().variant(deep);
758 w.sirc_clk_periph_en().variant(pclk);
759 w
760 });
761
762 while self.scg0.sirccsr().read().sircvld().is_disabled_or_not_valid() {}
763 if self.scg0.sirccsr().read().sircerr().is_error_detected() {
764 return Err(ClockError::BadConfig {
765 clock: "sirc",
766 reason: "error set",
767 });
768 }
769
770 // reset lock
771 self.scg0.sirccsr().modify(|_r, w| w.lk().write_disabled());
772
773 // Do we enable the `fro_lf_div` output?
774 if let Some(d) = fro_lf_div.as_ref() {
775 // We need `fro_lf` to be enabled
776 if !*fro_12m_enabled {
777 return Err(ClockError::BadConfig {
778 clock: "fro_lf_div",
779 reason: "fro_12m not enabled",
780 });
781 }
782
783 // Halt and reset the div; then set our desired div.
784 self.syscon.frolfdiv().write(|w| {
785 w.halt().halt();
786 w.reset().asserted();
787 unsafe { w.div().bits(d.into_bits()) };
788 w
789 });
790 // Then unhalt it, and reset it
791 self.syscon.frolfdiv().modify(|_r, w| {
792 w.halt().run();
793 w.reset().released();
794 w
795 });
796
797 // Wait for clock to stabilize
798 while self.syscon.frolfdiv().read().unstab().is_ongoing() {}
799
800 // Store off the clock info
801 self.clocks.fro_lf_div = Some(Clock {
802 frequency: base_freq / d.into_divisor(),
803 power: *power,
804 });
805 }
806
807 Ok(())
808 }
809
810 /// Configure the FRO16K/clk_16k clock family
811 fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> {
812 let Some(fro16k) = self.config.fro16k.as_ref() else {
813 return Ok(());
814 };
815 // Enable FRO16K oscillator
816 self.vbat0.froctla().modify(|_, w| w.fro_en().set_bit());
817
818 // Lock the control register
819 self.vbat0.frolcka().modify(|_, w| w.lock().set_bit());
820
821 let Fro16KConfig {
822 vsys_domain_active,
823 vdd_core_domain_active,
824 } = fro16k;
825
826 // Enable clock outputs to both VSYS and VDD_CORE domains
827 // Bit 0: clk_16k0 to VSYS domain
828 // Bit 1: clk_16k1 to VDD_CORE domain
829 //
830 // TODO: Define sub-fields for this register with a PAC patch?
831 let mut bits = 0;
832 if *vsys_domain_active {
833 bits |= 0b01;
834 self.clocks.clk_16k_vsys = Some(Clock {
835 frequency: 16_384,
836 power: PoweredClock::AlwaysEnabled,
837 });
838 }
839 if *vdd_core_domain_active {
840 bits |= 0b10;
841 self.clocks.clk_16k_vdd_core = Some(Clock {
842 frequency: 16_384,
843 power: PoweredClock::AlwaysEnabled,
844 });
845 }
846 self.vbat0.froclke().modify(|_r, w| unsafe { w.clke().bits(bits) });
847
848 Ok(())
849 }
850}
851
852//
853// Macros/macro impls
854//
855
856/// This macro is used to implement the [`Gate`] trait for a given peripheral
857/// that is controlled by the MRCC peripheral.
858macro_rules! impl_cc_gate {
859 ($name:ident, $clk_reg:ident, $rst_reg:ident, $field:ident, $config:ty) => {
860 impl Gate for crate::peripherals::$name {
861 type MrccPeriphConfig = $config;
862
863 #[inline]
864 unsafe fn enable_clock() {
865 let mrcc = unsafe { pac::Mrcc0::steal() };
866 mrcc.$clk_reg().modify(|_, w| w.$field().enabled());
867 }
868
869 #[inline]
870 unsafe fn disable_clock() {
871 let mrcc = unsafe { pac::Mrcc0::steal() };
872 mrcc.$clk_reg().modify(|_r, w| w.$field().disabled());
873 }
874
875 #[inline]
876 fn is_clock_enabled() -> bool {
877 let mrcc = unsafe { pac::Mrcc0::steal() };
878 mrcc.$clk_reg().read().$field().is_enabled()
879 }
880
881 #[inline]
882 unsafe fn release_reset() {
883 let mrcc = unsafe { pac::Mrcc0::steal() };
884 mrcc.$rst_reg().modify(|_, w| w.$field().enabled());
885 }
886
887 #[inline]
888 unsafe fn assert_reset() {
889 let mrcc = unsafe { pac::Mrcc0::steal() };
890 mrcc.$rst_reg().modify(|_, w| w.$field().disabled());
891 }
892
893 #[inline]
894 fn is_reset_released() -> bool {
895 let mrcc = unsafe { pac::Mrcc0::steal() };
896 mrcc.$rst_reg().read().$field().is_enabled()
897 }
898 }
899 };
900}
901
902/// This module contains implementations of MRCC APIs, specifically of the [`Gate`] trait,
903/// for various low level peripherals.
904pub(crate) mod gate {
905 #[cfg(not(feature = "time"))]
906 use super::periph_helpers::OsTimerConfig;
907 use super::periph_helpers::{AdcConfig, Lpi2cConfig, LpuartConfig, NoConfig};
908 use super::*;
909
910 // These peripherals have no additional upstream clocks or configuration required
911 // other than enabling through the MRCC gate. Currently, these peripherals will
912 // ALWAYS return `Ok(0)` when calling [`enable_and_reset()`] and/or
913 // [`SPConfHelper::post_enable_config()`].
914 impl_cc_gate!(PORT0, mrcc_glb_cc1, mrcc_glb_rst1, port0, NoConfig);
915 impl_cc_gate!(PORT1, mrcc_glb_cc1, mrcc_glb_rst1, port1, NoConfig);
916 impl_cc_gate!(PORT2, mrcc_glb_cc1, mrcc_glb_rst1, port2, NoConfig);
917 impl_cc_gate!(PORT3, mrcc_glb_cc1, mrcc_glb_rst1, port3, NoConfig);
918 impl_cc_gate!(PORT4, mrcc_glb_cc1, mrcc_glb_rst1, port4, NoConfig);
919
920 impl_cc_gate!(GPIO0, mrcc_glb_cc2, mrcc_glb_rst2, gpio0, NoConfig);
921 impl_cc_gate!(GPIO1, mrcc_glb_cc2, mrcc_glb_rst2, gpio1, NoConfig);
922 impl_cc_gate!(GPIO2, mrcc_glb_cc2, mrcc_glb_rst2, gpio2, NoConfig);
923 impl_cc_gate!(GPIO3, mrcc_glb_cc2, mrcc_glb_rst2, gpio3, NoConfig);
924 impl_cc_gate!(GPIO4, mrcc_glb_cc2, mrcc_glb_rst2, gpio4, NoConfig);
925
926 // These peripherals DO have meaningful configuration, and could fail if the system
927 // clocks do not match their needs.
928 #[cfg(not(feature = "time"))]
929 impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig);
930
931 impl_cc_gate!(LPI2C0, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c0, Lpi2cConfig);
932 impl_cc_gate!(LPI2C1, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c1, Lpi2cConfig);
933 impl_cc_gate!(LPI2C2, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c2, Lpi2cConfig);
934 impl_cc_gate!(LPI2C3, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c3, Lpi2cConfig);
935
936 impl_cc_gate!(LPUART0, mrcc_glb_cc0, mrcc_glb_rst0, lpuart0, LpuartConfig);
937 impl_cc_gate!(LPUART1, mrcc_glb_cc0, mrcc_glb_rst0, lpuart1, LpuartConfig);
938 impl_cc_gate!(LPUART2, mrcc_glb_cc0, mrcc_glb_rst0, lpuart2, LpuartConfig);
939 impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig);
940 impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig);
941 impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig);
942 impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig);
943}
diff --git a/embassy-mcxa/src/clocks/periph_helpers.rs b/embassy-mcxa/src/clocks/periph_helpers.rs
new file mode 100644
index 000000000..1ea7a99ed
--- /dev/null
+++ b/embassy-mcxa/src/clocks/periph_helpers.rs
@@ -0,0 +1,502 @@
1//! Peripheral Helpers
2//!
3//! The purpose of this module is to define the per-peripheral special handling
4//! required from a clocking perspective. Different peripherals have different
5//! selectable source clocks, and some peripherals have additional pre-dividers
6//! that can be used.
7//!
8//! See the docs of [`SPConfHelper`] for more details.
9
10use super::{ClockError, Clocks, PoweredClock};
11use crate::pac;
12
13/// Sealed Peripheral Configuration Helper
14///
15/// NOTE: the name "sealed" doesn't *totally* make sense because its not sealed yet in the
16/// embassy-mcxa project, but it derives from embassy-imxrt where it is. We should
17/// fix the name, or actually do the sealing of peripherals.
18///
19/// This trait serves to act as a per-peripheral customization for clocking behavior.
20///
21/// This trait should be implemented on a configuration type for a given peripheral, and
22/// provide the methods that will be called by the higher level operations like
23/// `embassy_mcxa::clocks::enable_and_reset()`.
24pub trait SPConfHelper {
25 /// This method is called AFTER a given MRCC peripheral has been enabled (e.g. un-gated),
26 /// but BEFORE the peripheral reset line is reset.
27 ///
28 /// This function should check that any relevant upstream clocks are enabled, are in a
29 /// reasonable power state, and that the requested configuration can be made. If any of
30 /// these checks fail, an `Err(ClockError)` should be returned, likely `ClockError::BadConfig`.
31 ///
32 /// This function SHOULD NOT make any changes to the system clock configuration, even
33 /// unsafely, as this should remain static for the duration of the program.
34 ///
35 /// This function WILL be called in a critical section, care should be taken not to delay
36 /// for an unreasonable amount of time.
37 ///
38 /// On success, this function MUST return an `Ok(freq)`, where `freq` is the frequency
39 /// fed into the peripheral, taking into account the selected source clock, as well as
40 /// any pre-divisors.
41 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>;
42}
43
44/// Copy and paste macro that:
45///
46/// * Sets the clocksel mux to `$selvar`
47/// * Resets and halts the div, and applies the calculated div4 bits
48/// * Releases reset + halt
49/// * Waits for the div to stabilize
50/// * Returns `Ok($freq / $conf.div.into_divisor())`
51///
52/// Assumes:
53///
54/// * self is a configuration struct that has a field called `div`, which
55/// is a `Div4`
56///
57/// usage:
58///
59/// ```rust
60/// apply_div4!(self, clksel, clkdiv, variant, freq)
61/// ```
62///
63/// In the future if we make all the clksel+clkdiv pairs into commonly derivedFrom
64/// registers, or if we put some kind of simple trait around those regs, we could
65/// do this with something other than a macro, but for now, this is harm-reduction
66/// to avoid incorrect copy + paste
67macro_rules! apply_div4 {
68 ($conf:ident, $selreg:ident, $divreg:ident, $selvar:ident, $freq:ident) => {{
69 // set clksel
70 $selreg.modify(|_r, w| w.mux().variant($selvar));
71
72 // Set up clkdiv
73 $divreg.modify(|_r, w| {
74 unsafe { w.div().bits($conf.div.into_bits()) }
75 .halt()
76 .asserted()
77 .reset()
78 .asserted()
79 });
80 $divreg.modify(|_r, w| w.halt().deasserted().reset().deasserted());
81
82 while $divreg.read().unstab().is_unstable() {}
83
84 Ok($freq / $conf.div.into_divisor())
85 }};
86}
87
88// config types
89
90/// This type represents a divider in the range 1..=16.
91///
92/// At a hardware level, this is an 8-bit register from 0..=15,
93/// which adds one.
94///
95/// While the *clock* domain seems to use 8-bit dividers, the *peripheral* domain
96/// seems to use 4 bit dividers!
97#[derive(Copy, Clone, Debug, PartialEq, Eq)]
98pub struct Div4(pub(super) u8);
99
100impl Div4 {
101 /// Divide by one, or no division
102 pub const fn no_div() -> Self {
103 Self(0)
104 }
105
106 /// Store a "raw" divisor value that will divide the source by
107 /// `(n + 1)`, e.g. `Div4::from_raw(0)` will divide the source
108 /// by 1, and `Div4::from_raw(15)` will divide the source by
109 /// 16.
110 pub const fn from_raw(n: u8) -> Option<Self> {
111 if n > 0b1111 {
112 None
113 } else {
114 Some(Self(n))
115 }
116 }
117
118 /// Store a specific divisor value that will divide the source
119 /// by `n`. e.g. `Div4::from_divisor(1)` will divide the source
120 /// by 1, and `Div4::from_divisor(16)` will divide the source
121 /// by 16.
122 ///
123 /// Will return `None` if `n` is not in the range `1..=16`.
124 /// Consider [`Self::from_raw`] for an infallible version.
125 pub const fn from_divisor(n: u8) -> Option<Self> {
126 let Some(n) = n.checked_sub(1) else {
127 return None;
128 };
129 if n > 0b1111 {
130 return None;
131 }
132 Some(Self(n))
133 }
134
135 /// Convert into "raw" bits form
136 #[inline(always)]
137 pub const fn into_bits(self) -> u8 {
138 self.0
139 }
140
141 /// Convert into "divisor" form, as a u32 for convenient frequency math
142 #[inline(always)]
143 pub const fn into_divisor(self) -> u32 {
144 self.0 as u32 + 1
145 }
146}
147
148/// A basic type that always returns an error when `post_enable_config` is called.
149///
150/// Should only be used as a placeholder.
151pub struct UnimplementedConfig;
152
153impl SPConfHelper for UnimplementedConfig {
154 fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> {
155 Err(ClockError::UnimplementedConfig)
156 }
157}
158
159/// A basic type that always returns `Ok(0)` when `post_enable_config` is called.
160///
161/// This should only be used for peripherals that are "ambiently" clocked, like `PORTn`
162/// peripherals, which have no selectable/configurable source clock.
163pub struct NoConfig;
164impl SPConfHelper for NoConfig {
165 fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> {
166 Ok(0)
167 }
168}
169
170//
171// LPI2c
172//
173
174/// Selectable clocks for `Lpi2c` peripherals
175#[derive(Debug, Clone, Copy)]
176pub enum Lpi2cClockSel {
177 /// FRO12M/FRO_LF/SIRC clock source, passed through divider
178 /// "fro_lf_div"
179 FroLfDiv,
180 /// FRO180M/FRO_HF/FIRC clock source, passed through divider
181 /// "fro_hf_div"
182 FroHfDiv,
183 /// SOSC/XTAL/EXTAL clock source
184 ClkIn,
185 /// clk_1m/FRO_LF divided by 12
186 Clk1M,
187 /// Output of PLL1, passed through clock divider,
188 /// "pll1_clk_div", maybe "pll1_lf_div"?
189 Pll1ClkDiv,
190 /// Disabled
191 None,
192}
193
194/// Which instance of the `Lpi2c` is this?
195///
196/// Should not be directly selectable by end-users.
197#[derive(Copy, Clone, Debug, PartialEq, Eq)]
198pub enum Lpi2cInstance {
199 /// Instance 0
200 Lpi2c0,
201 /// Instance 1
202 Lpi2c1,
203 /// Instance 2
204 Lpi2c2,
205 /// Instance 3
206 Lpi2c3,
207}
208
209/// Top level configuration for `Lpi2c` instances.
210pub struct Lpi2cConfig {
211 /// Power state required for this peripheral
212 pub power: PoweredClock,
213 /// Clock source
214 pub source: Lpi2cClockSel,
215 /// Clock divisor
216 pub div: Div4,
217 /// Which instance is this?
218 // NOTE: should not be user settable
219 pub(crate) instance: Lpi2cInstance,
220}
221
222impl SPConfHelper for Lpi2cConfig {
223 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
224 // check that source is suitable
225 let mrcc0 = unsafe { pac::Mrcc0::steal() };
226 use mcxa_pac::mrcc0::mrcc_lpi2c0_clksel::Mux;
227
228 let (clkdiv, clksel) = match self.instance {
229 Lpi2cInstance::Lpi2c0 => (mrcc0.mrcc_lpi2c0_clkdiv(), mrcc0.mrcc_lpi2c0_clksel()),
230 Lpi2cInstance::Lpi2c1 => (mrcc0.mrcc_lpi2c1_clkdiv(), mrcc0.mrcc_lpi2c1_clksel()),
231 Lpi2cInstance::Lpi2c2 => (mrcc0.mrcc_lpi2c2_clkdiv(), mrcc0.mrcc_lpi2c2_clksel()),
232 Lpi2cInstance::Lpi2c3 => (mrcc0.mrcc_lpi2c3_clkdiv(), mrcc0.mrcc_lpi2c3_clksel()),
233 };
234
235 let (freq, variant) = match self.source {
236 Lpi2cClockSel::FroLfDiv => {
237 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
238 (freq, Mux::ClkrootFunc0)
239 }
240 Lpi2cClockSel::FroHfDiv => {
241 let freq = clocks.ensure_fro_hf_div_active(&self.power)?;
242 (freq, Mux::ClkrootFunc2)
243 }
244 Lpi2cClockSel::ClkIn => {
245 let freq = clocks.ensure_clk_in_active(&self.power)?;
246 (freq, Mux::ClkrootFunc3)
247 }
248 Lpi2cClockSel::Clk1M => {
249 let freq = clocks.ensure_clk_1m_active(&self.power)?;
250 (freq, Mux::ClkrootFunc5)
251 }
252 Lpi2cClockSel::Pll1ClkDiv => {
253 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
254 (freq, Mux::ClkrootFunc6)
255 }
256 Lpi2cClockSel::None => unsafe {
257 // no ClkrootFunc7, just write manually for now
258 clksel.write(|w| w.bits(0b111));
259 clkdiv.modify(|_r, w| w.reset().asserted().halt().asserted());
260 return Ok(0);
261 },
262 };
263
264 apply_div4!(self, clksel, clkdiv, variant, freq)
265 }
266}
267
268//
269// LPUart
270//
271
272/// Selectable clocks for Lpuart peripherals
273#[derive(Debug, Clone, Copy)]
274pub enum LpuartClockSel {
275 /// FRO12M/FRO_LF/SIRC clock source, passed through divider
276 /// "fro_lf_div"
277 FroLfDiv,
278 /// FRO180M/FRO_HF/FIRC clock source, passed through divider
279 /// "fro_hf_div"
280 FroHfDiv,
281 /// SOSC/XTAL/EXTAL clock source
282 ClkIn,
283 /// FRO16K/clk_16k source
284 Clk16K,
285 /// clk_1m/FRO_LF divided by 12
286 Clk1M,
287 /// Output of PLL1, passed through clock divider,
288 /// "pll1_clk_div", maybe "pll1_lf_div"?
289 Pll1ClkDiv,
290 /// Disabled
291 None,
292}
293
294/// Which instance of the Lpuart is this?
295///
296/// Should not be directly selectable by end-users.
297#[derive(Copy, Clone, Debug, PartialEq, Eq)]
298pub enum LpuartInstance {
299 /// Instance 0
300 Lpuart0,
301 /// Instance 1
302 Lpuart1,
303 /// Instance 2
304 Lpuart2,
305 /// Instance 3
306 Lpuart3,
307 /// Instance 4
308 Lpuart4,
309 /// Instance 5
310 Lpuart5,
311}
312
313/// Top level configuration for `Lpuart` instances.
314pub struct LpuartConfig {
315 /// Power state required for this peripheral
316 pub power: PoweredClock,
317 /// Clock source
318 pub source: LpuartClockSel,
319 /// Clock divisor
320 pub div: Div4,
321 /// Which instance is this?
322 // NOTE: should not be user settable
323 pub(crate) instance: LpuartInstance,
324}
325
326impl SPConfHelper for LpuartConfig {
327 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
328 // check that source is suitable
329 let mrcc0 = unsafe { pac::Mrcc0::steal() };
330 use mcxa_pac::mrcc0::mrcc_lpuart0_clksel::Mux;
331
332 let (clkdiv, clksel) = match self.instance {
333 LpuartInstance::Lpuart0 => (mrcc0.mrcc_lpuart0_clkdiv(), mrcc0.mrcc_lpuart0_clksel()),
334 LpuartInstance::Lpuart1 => (mrcc0.mrcc_lpuart1_clkdiv(), mrcc0.mrcc_lpuart1_clksel()),
335 LpuartInstance::Lpuart2 => (mrcc0.mrcc_lpuart2_clkdiv(), mrcc0.mrcc_lpuart2_clksel()),
336 LpuartInstance::Lpuart3 => (mrcc0.mrcc_lpuart3_clkdiv(), mrcc0.mrcc_lpuart3_clksel()),
337 LpuartInstance::Lpuart4 => (mrcc0.mrcc_lpuart4_clkdiv(), mrcc0.mrcc_lpuart4_clksel()),
338 LpuartInstance::Lpuart5 => (mrcc0.mrcc_lpuart5_clkdiv(), mrcc0.mrcc_lpuart5_clksel()),
339 };
340
341 let (freq, variant) = match self.source {
342 LpuartClockSel::FroLfDiv => {
343 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
344 (freq, Mux::ClkrootFunc0)
345 }
346 LpuartClockSel::FroHfDiv => {
347 let freq = clocks.ensure_fro_hf_div_active(&self.power)?;
348 (freq, Mux::ClkrootFunc2)
349 }
350 LpuartClockSel::ClkIn => {
351 let freq = clocks.ensure_clk_in_active(&self.power)?;
352 (freq, Mux::ClkrootFunc3)
353 }
354 LpuartClockSel::Clk16K => {
355 let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?;
356 (freq, Mux::ClkrootFunc4)
357 }
358 LpuartClockSel::Clk1M => {
359 let freq = clocks.ensure_clk_1m_active(&self.power)?;
360 (freq, Mux::ClkrootFunc5)
361 }
362 LpuartClockSel::Pll1ClkDiv => {
363 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
364 (freq, Mux::ClkrootFunc6)
365 }
366 LpuartClockSel::None => unsafe {
367 // no ClkrootFunc7, just write manually for now
368 clksel.write(|w| w.bits(0b111));
369 clkdiv.modify(|_r, w| {
370 w.reset().asserted();
371 w.halt().asserted();
372 w
373 });
374 return Ok(0);
375 },
376 };
377
378 // set clksel
379 apply_div4!(self, clksel, clkdiv, variant, freq)
380 }
381}
382
383//
384// OSTimer
385//
386
387/// Selectable clocks for the OSTimer peripheral
388#[derive(Copy, Clone, Debug, PartialEq, Eq)]
389pub enum OstimerClockSel {
390 /// 16k clock, sourced from FRO16K (Vdd Core)
391 Clk16kVddCore,
392 /// 1 MHz Clock sourced from FRO12M
393 Clk1M,
394 /// Disabled
395 None,
396}
397
398/// Top level configuration for the `OSTimer` peripheral
399pub struct OsTimerConfig {
400 /// Power state required for this peripheral
401 pub power: PoweredClock,
402 /// Selected clock source for this peripheral
403 pub source: OstimerClockSel,
404}
405
406impl SPConfHelper for OsTimerConfig {
407 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
408 let mrcc0 = unsafe { pac::Mrcc0::steal() };
409 Ok(match self.source {
410 OstimerClockSel::Clk16kVddCore => {
411 let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?;
412 mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_16k());
413 freq
414 }
415 OstimerClockSel::Clk1M => {
416 let freq = clocks.ensure_clk_1m_active(&self.power)?;
417 mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m());
418 freq
419 }
420 OstimerClockSel::None => {
421 mrcc0.mrcc_ostimer0_clksel().write(|w| unsafe { w.mux().bits(0b11) });
422 0
423 }
424 })
425 }
426}
427
428//
429// Adc
430//
431
432/// Selectable clocks for the ADC peripheral
433#[derive(Copy, Clone, Debug, PartialEq, Eq)]
434pub enum AdcClockSel {
435 /// Divided `fro_lf`/`clk_12m`/FRO12M source
436 FroLfDiv,
437 /// Gated `fro_hf`/`FRO180M` source
438 FroHf,
439 /// External Clock Source
440 ClkIn,
441 /// 1MHz clock sourced by a divided `fro_lf`/`clk_12m`
442 Clk1M,
443 /// Internal PLL output, with configurable divisor
444 Pll1ClkDiv,
445 /// No clock/disabled
446 None,
447}
448
449/// Top level configuration for the ADC peripheral
450pub struct AdcConfig {
451 /// Power state required for this peripheral
452 pub power: PoweredClock,
453 /// Selected clock-source for this peripheral
454 pub source: AdcClockSel,
455 /// Pre-divisor, applied to the upstream clock output
456 pub div: Div4,
457}
458
459impl SPConfHelper for AdcConfig {
460 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
461 use mcxa_pac::mrcc0::mrcc_adc_clksel::Mux;
462 let mrcc0 = unsafe { pac::Mrcc0::steal() };
463 let (freq, variant) = match self.source {
464 AdcClockSel::FroLfDiv => {
465 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
466 (freq, Mux::ClkrootFunc0)
467 }
468 AdcClockSel::FroHf => {
469 let freq = clocks.ensure_fro_hf_active(&self.power)?;
470 (freq, Mux::ClkrootFunc1)
471 }
472 AdcClockSel::ClkIn => {
473 let freq = clocks.ensure_clk_in_active(&self.power)?;
474 (freq, Mux::ClkrootFunc3)
475 }
476 AdcClockSel::Clk1M => {
477 let freq = clocks.ensure_clk_1m_active(&self.power)?;
478 (freq, Mux::ClkrootFunc5)
479 }
480 AdcClockSel::Pll1ClkDiv => {
481 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
482 (freq, Mux::ClkrootFunc6)
483 }
484 AdcClockSel::None => {
485 mrcc0.mrcc_adc_clksel().write(|w| unsafe {
486 // no ClkrootFunc7, just write manually for now
487 w.mux().bits(0b111)
488 });
489 mrcc0.mrcc_adc_clkdiv().modify(|_r, w| {
490 w.reset().asserted();
491 w.halt().asserted();
492 w
493 });
494 return Ok(0);
495 }
496 };
497 let clksel = mrcc0.mrcc_adc_clksel();
498 let clkdiv = mrcc0.mrcc_adc_clkdiv();
499
500 apply_div4!(self, clksel, clkdiv, variant, freq)
501 }
502}
diff --git a/embassy-mcxa/src/config.rs b/embassy-mcxa/src/config.rs
new file mode 100644
index 000000000..8d52a1d44
--- /dev/null
+++ b/embassy-mcxa/src/config.rs
@@ -0,0 +1,27 @@
1// HAL configuration (minimal), mirroring embassy-imxrt style
2
3use crate::clocks::config::ClocksConfig;
4use crate::interrupt::Priority;
5
6#[non_exhaustive]
7pub struct Config {
8 #[cfg(feature = "time")]
9 pub time_interrupt_priority: Priority,
10 pub rtc_interrupt_priority: Priority,
11 pub adc_interrupt_priority: Priority,
12 pub gpio_interrupt_priority: Priority,
13 pub clock_cfg: ClocksConfig,
14}
15
16impl Default for Config {
17 fn default() -> Self {
18 Self {
19 #[cfg(feature = "time")]
20 time_interrupt_priority: Priority::from(0),
21 rtc_interrupt_priority: Priority::from(0),
22 adc_interrupt_priority: Priority::from(0),
23 gpio_interrupt_priority: Priority::from(0),
24 clock_cfg: ClocksConfig::default(),
25 }
26 }
27}
diff --git a/embassy-mcxa/src/gpio.rs b/embassy-mcxa/src/gpio.rs
new file mode 100644
index 000000000..65f8df985
--- /dev/null
+++ b/embassy-mcxa/src/gpio.rs
@@ -0,0 +1,1062 @@
1//! GPIO driver built around a type-erased `Flex` pin, similar to other Embassy HALs.
2//! The exported `Output`/`Input` drivers own a `Flex` so they no longer depend on the
3//! concrete pin type.
4
5use core::convert::Infallible;
6use core::future::Future;
7use core::marker::PhantomData;
8use core::pin::pin;
9
10use embassy_hal_internal::{Peri, PeripheralType};
11use maitake_sync::WaitMap;
12use paste::paste;
13
14use crate::pac::interrupt;
15use crate::pac::port0::pcr0::{Dse, Inv, Mux, Pe, Ps, Sre};
16
17struct BitIter(u32);
18
19impl Iterator for BitIter {
20 type Item = usize;
21
22 fn next(&mut self) -> Option<Self::Item> {
23 match self.0.trailing_zeros() {
24 32 => None,
25 b => {
26 self.0 &= !(1 << b);
27 Some(b as usize)
28 }
29 }
30 }
31}
32
33const PORT_COUNT: usize = 5;
34
35static PORT_WAIT_MAPS: [WaitMap<usize, ()>; PORT_COUNT] = [
36 WaitMap::new(),
37 WaitMap::new(),
38 WaitMap::new(),
39 WaitMap::new(),
40 WaitMap::new(),
41];
42
43fn irq_handler(port_index: usize, gpio_base: *const crate::pac::gpio0::RegisterBlock) {
44 let gpio = unsafe { &*gpio_base };
45 let isfr = gpio.isfr0().read().bits();
46
47 for pin in BitIter(isfr) {
48 // Clear all pending interrupts
49 gpio.isfr0().write(|w| unsafe { w.bits(1 << pin) });
50 gpio.icr(pin).modify(|_, w| w.irqc().irqc0()); // Disable interrupt
51
52 // Wake the corresponding port waker
53 if let Some(w) = PORT_WAIT_MAPS.get(port_index) {
54 w.wake(&pin, ());
55 }
56 }
57}
58
59#[interrupt]
60fn GPIO0() {
61 irq_handler(0, crate::pac::Gpio0::ptr());
62}
63
64#[interrupt]
65fn GPIO1() {
66 irq_handler(1, crate::pac::Gpio1::ptr());
67}
68
69#[interrupt]
70fn GPIO2() {
71 irq_handler(2, crate::pac::Gpio2::ptr());
72}
73
74#[interrupt]
75fn GPIO3() {
76 irq_handler(3, crate::pac::Gpio3::ptr());
77}
78
79#[interrupt]
80fn GPIO4() {
81 irq_handler(4, crate::pac::Gpio4::ptr());
82}
83
84pub(crate) unsafe fn init() {
85 use embassy_hal_internal::interrupt::InterruptExt;
86
87 crate::pac::interrupt::GPIO0.enable();
88 crate::pac::interrupt::GPIO1.enable();
89 crate::pac::interrupt::GPIO2.enable();
90 crate::pac::interrupt::GPIO3.enable();
91 crate::pac::interrupt::GPIO4.enable();
92
93 cortex_m::interrupt::enable();
94}
95
96/// Logical level for GPIO pins.
97#[derive(Copy, Clone, Eq, PartialEq, Debug)]
98#[cfg_attr(feature = "defmt", derive(defmt::Format))]
99pub enum Level {
100 Low,
101 High,
102}
103
104impl From<bool> for Level {
105 fn from(val: bool) -> Self {
106 match val {
107 true => Self::High,
108 false => Self::Low,
109 }
110 }
111}
112
113#[derive(Copy, Clone, Eq, PartialEq, Debug)]
114pub enum Pull {
115 Disabled,
116 Up,
117 Down,
118}
119
120impl From<Pull> for (Pe, Ps) {
121 fn from(pull: Pull) -> Self {
122 match pull {
123 Pull::Disabled => (Pe::Pe0, Ps::Ps0),
124 Pull::Up => (Pe::Pe1, Ps::Ps1),
125 Pull::Down => (Pe::Pe1, Ps::Ps0),
126 }
127 }
128}
129
130#[derive(Copy, Clone, Eq, PartialEq, Debug)]
131pub enum SlewRate {
132 Fast,
133 Slow,
134}
135
136impl From<SlewRate> for Sre {
137 fn from(slew_rate: SlewRate) -> Self {
138 match slew_rate {
139 SlewRate::Fast => Sre::Sre0,
140 SlewRate::Slow => Sre::Sre1,
141 }
142 }
143}
144
145#[derive(Copy, Clone, Eq, PartialEq, Debug)]
146pub enum DriveStrength {
147 Normal,
148 Double,
149}
150
151impl From<DriveStrength> for Dse {
152 fn from(strength: DriveStrength) -> Self {
153 match strength {
154 DriveStrength::Normal => Dse::Dse0,
155 DriveStrength::Double => Dse::Dse1,
156 }
157 }
158}
159
160#[derive(Copy, Clone, Eq, PartialEq, Debug)]
161pub enum Inverter {
162 Disabled,
163 Enabled,
164}
165
166impl From<Inverter> for Inv {
167 fn from(strength: Inverter) -> Self {
168 match strength {
169 Inverter::Disabled => Inv::Inv0,
170 Inverter::Enabled => Inv::Inv1,
171 }
172 }
173}
174
175pub type Gpio = crate::peripherals::GPIO0;
176
177/// Type-erased representation of a GPIO pin.
178pub struct AnyPin {
179 port: usize,
180 pin: usize,
181 gpio: &'static crate::pac::gpio0::RegisterBlock,
182 port_reg: &'static crate::pac::port0::RegisterBlock,
183 pcr_reg: &'static crate::pac::port0::Pcr0,
184}
185
186impl AnyPin {
187 /// Create an `AnyPin` from raw components.
188 fn new(
189 port: usize,
190 pin: usize,
191 gpio: &'static crate::pac::gpio0::RegisterBlock,
192 port_reg: &'static crate::pac::port0::RegisterBlock,
193 pcr_reg: &'static crate::pac::port0::Pcr0,
194 ) -> Self {
195 Self {
196 port,
197 pin,
198 gpio,
199 port_reg,
200 pcr_reg,
201 }
202 }
203
204 #[inline(always)]
205 fn mask(&self) -> u32 {
206 1 << self.pin
207 }
208
209 #[inline(always)]
210 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
211 self.gpio
212 }
213
214 #[inline(always)]
215 pub fn port_index(&self) -> usize {
216 self.port
217 }
218
219 #[inline(always)]
220 pub fn pin_index(&self) -> usize {
221 self.pin
222 }
223
224 #[inline(always)]
225 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock {
226 self.port_reg
227 }
228
229 #[inline(always)]
230 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 {
231 self.pcr_reg
232 }
233}
234
235embassy_hal_internal::impl_peripheral!(AnyPin);
236
237pub(crate) trait SealedPin {
238 fn pin_port(&self) -> usize;
239
240 fn port(&self) -> usize {
241 self.pin_port() / 32
242 }
243
244 fn pin(&self) -> usize {
245 self.pin_port() % 32
246 }
247
248 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock;
249
250 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock;
251
252 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0;
253
254 fn set_function(&self, function: Mux);
255
256 fn set_pull(&self, pull: Pull);
257
258 fn set_drive_strength(&self, strength: Dse);
259
260 fn set_slew_rate(&self, slew_rate: Sre);
261
262 fn set_enable_input_buffer(&self);
263}
264
265/// GPIO pin trait.
266#[allow(private_bounds)]
267pub trait GpioPin: SealedPin + Sized + PeripheralType + Into<AnyPin> + 'static {
268 /// Type-erase the pin.
269 fn degrade(self) -> AnyPin {
270 // SAFETY: This is only called within the GpioPin trait, which is only
271 // implemented within this module on valid pin peripherals and thus
272 // has been verified to be correct.
273 AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg())
274 }
275}
276
277impl SealedPin for AnyPin {
278 fn pin_port(&self) -> usize {
279 self.port * 32 + self.pin
280 }
281
282 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
283 self.gpio()
284 }
285
286 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock {
287 self.port_reg()
288 }
289
290 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 {
291 self.pcr_reg()
292 }
293
294 fn set_function(&self, function: Mux) {
295 self.pcr_reg().modify(|_, w| w.mux().variant(function));
296 }
297
298 fn set_pull(&self, pull: Pull) {
299 let (pull_enable, pull_select) = pull.into();
300 self.pcr_reg().modify(|_, w| {
301 w.pe().variant(pull_enable);
302 w.ps().variant(pull_select)
303 });
304 }
305
306 fn set_drive_strength(&self, strength: Dse) {
307 self.pcr_reg().modify(|_, w| w.dse().variant(strength));
308 }
309
310 fn set_slew_rate(&self, slew_rate: Sre) {
311 self.pcr_reg().modify(|_, w| w.sre().variant(slew_rate));
312 }
313
314 fn set_enable_input_buffer(&self) {
315 self.pcr_reg().modify(|_, w| w.ibe().ibe1());
316 }
317}
318
319impl GpioPin for AnyPin {}
320
321macro_rules! impl_pin {
322 ($peri:ident, $port:expr, $pin:expr, $block:ident) => {
323 paste! {
324 impl SealedPin for crate::peripherals::$peri {
325 fn pin_port(&self) -> usize {
326 $port * 32 + $pin
327 }
328
329 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
330 unsafe { &*crate::pac::$block::ptr() }
331 }
332
333 fn port_reg(&self) -> &'static crate::pac::port0::RegisterBlock {
334 unsafe { &*crate::pac::[<Port $port>]::ptr() }
335 }
336
337 fn pcr_reg(&self) -> &'static crate::pac::port0::Pcr0 {
338 self.port_reg().[<pcr $pin>]()
339 }
340
341 fn set_function(&self, function: Mux) {
342 unsafe {
343 let port_reg = &*crate::pac::[<Port $port>]::ptr();
344 port_reg.[<pcr $pin>]().modify(|_, w| {
345 w.mux().variant(function)
346 });
347 }
348 }
349
350 fn set_pull(&self, pull: Pull) {
351 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
352 let (pull_enable, pull_select) = pull.into();
353 port_reg.[<pcr $pin>]().modify(|_, w| {
354 w.pe().variant(pull_enable);
355 w.ps().variant(pull_select)
356 });
357 }
358
359 fn set_drive_strength(&self, strength: Dse) {
360 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
361 port_reg.[<pcr $pin>]().modify(|_, w| w.dse().variant(strength));
362 }
363
364 fn set_slew_rate(&self, slew_rate: Sre) {
365 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
366 port_reg.[<pcr $pin>]().modify(|_, w| w.sre().variant(slew_rate));
367 }
368
369 fn set_enable_input_buffer(&self) {
370 let port_reg = unsafe {&*crate::pac::[<Port $port>]::ptr()};
371 port_reg.[<pcr $pin>]().modify(|_, w| w.ibe().ibe1());
372 }
373 }
374
375 impl GpioPin for crate::peripherals::$peri {}
376
377 impl From<crate::peripherals::$peri> for AnyPin {
378 fn from(value: crate::peripherals::$peri) -> Self {
379 value.degrade()
380 }
381 }
382
383 impl crate::peripherals::$peri {
384 /// Convenience helper to obtain a type-erased handle to this pin.
385 pub fn degrade(&self) -> AnyPin {
386 AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg())
387 }
388 }
389 }
390 };
391}
392
393impl_pin!(P0_0, 0, 0, Gpio0);
394impl_pin!(P0_1, 0, 1, Gpio0);
395impl_pin!(P0_2, 0, 2, Gpio0);
396impl_pin!(P0_3, 0, 3, Gpio0);
397impl_pin!(P0_4, 0, 4, Gpio0);
398impl_pin!(P0_5, 0, 5, Gpio0);
399impl_pin!(P0_6, 0, 6, Gpio0);
400impl_pin!(P0_7, 0, 7, Gpio0);
401impl_pin!(P0_8, 0, 8, Gpio0);
402impl_pin!(P0_9, 0, 9, Gpio0);
403impl_pin!(P0_10, 0, 10, Gpio0);
404impl_pin!(P0_11, 0, 11, Gpio0);
405impl_pin!(P0_12, 0, 12, Gpio0);
406impl_pin!(P0_13, 0, 13, Gpio0);
407impl_pin!(P0_14, 0, 14, Gpio0);
408impl_pin!(P0_15, 0, 15, Gpio0);
409impl_pin!(P0_16, 0, 16, Gpio0);
410impl_pin!(P0_17, 0, 17, Gpio0);
411impl_pin!(P0_18, 0, 18, Gpio0);
412impl_pin!(P0_19, 0, 19, Gpio0);
413impl_pin!(P0_20, 0, 20, Gpio0);
414impl_pin!(P0_21, 0, 21, Gpio0);
415impl_pin!(P0_22, 0, 22, Gpio0);
416impl_pin!(P0_23, 0, 23, Gpio0);
417impl_pin!(P0_24, 0, 24, Gpio0);
418impl_pin!(P0_25, 0, 25, Gpio0);
419impl_pin!(P0_26, 0, 26, Gpio0);
420impl_pin!(P0_27, 0, 27, Gpio0);
421impl_pin!(P0_28, 0, 28, Gpio0);
422impl_pin!(P0_29, 0, 29, Gpio0);
423impl_pin!(P0_30, 0, 30, Gpio0);
424impl_pin!(P0_31, 0, 31, Gpio0);
425
426impl_pin!(P1_0, 1, 0, Gpio1);
427impl_pin!(P1_1, 1, 1, Gpio1);
428impl_pin!(P1_2, 1, 2, Gpio1);
429impl_pin!(P1_3, 1, 3, Gpio1);
430impl_pin!(P1_4, 1, 4, Gpio1);
431impl_pin!(P1_5, 1, 5, Gpio1);
432impl_pin!(P1_6, 1, 6, Gpio1);
433impl_pin!(P1_7, 1, 7, Gpio1);
434impl_pin!(P1_8, 1, 8, Gpio1);
435impl_pin!(P1_9, 1, 9, Gpio1);
436impl_pin!(P1_10, 1, 10, Gpio1);
437impl_pin!(P1_11, 1, 11, Gpio1);
438impl_pin!(P1_12, 1, 12, Gpio1);
439impl_pin!(P1_13, 1, 13, Gpio1);
440impl_pin!(P1_14, 1, 14, Gpio1);
441impl_pin!(P1_15, 1, 15, Gpio1);
442impl_pin!(P1_16, 1, 16, Gpio1);
443impl_pin!(P1_17, 1, 17, Gpio1);
444impl_pin!(P1_18, 1, 18, Gpio1);
445impl_pin!(P1_19, 1, 19, Gpio1);
446impl_pin!(P1_20, 1, 20, Gpio1);
447impl_pin!(P1_21, 1, 21, Gpio1);
448impl_pin!(P1_22, 1, 22, Gpio1);
449impl_pin!(P1_23, 1, 23, Gpio1);
450impl_pin!(P1_24, 1, 24, Gpio1);
451impl_pin!(P1_25, 1, 25, Gpio1);
452impl_pin!(P1_26, 1, 26, Gpio1);
453impl_pin!(P1_27, 1, 27, Gpio1);
454impl_pin!(P1_28, 1, 28, Gpio1);
455impl_pin!(P1_29, 1, 29, Gpio1);
456impl_pin!(P1_30, 1, 30, Gpio1);
457impl_pin!(P1_31, 1, 31, Gpio1);
458
459impl_pin!(P2_0, 2, 0, Gpio2);
460impl_pin!(P2_1, 2, 1, Gpio2);
461impl_pin!(P2_2, 2, 2, Gpio2);
462impl_pin!(P2_3, 2, 3, Gpio2);
463impl_pin!(P2_4, 2, 4, Gpio2);
464impl_pin!(P2_5, 2, 5, Gpio2);
465impl_pin!(P2_6, 2, 6, Gpio2);
466impl_pin!(P2_7, 2, 7, Gpio2);
467impl_pin!(P2_8, 2, 8, Gpio2);
468impl_pin!(P2_9, 2, 9, Gpio2);
469impl_pin!(P2_10, 2, 10, Gpio2);
470impl_pin!(P2_11, 2, 11, Gpio2);
471impl_pin!(P2_12, 2, 12, Gpio2);
472impl_pin!(P2_13, 2, 13, Gpio2);
473impl_pin!(P2_14, 2, 14, Gpio2);
474impl_pin!(P2_15, 2, 15, Gpio2);
475impl_pin!(P2_16, 2, 16, Gpio2);
476impl_pin!(P2_17, 2, 17, Gpio2);
477impl_pin!(P2_18, 2, 18, Gpio2);
478impl_pin!(P2_19, 2, 19, Gpio2);
479impl_pin!(P2_20, 2, 20, Gpio2);
480impl_pin!(P2_21, 2, 21, Gpio2);
481impl_pin!(P2_22, 2, 22, Gpio2);
482impl_pin!(P2_23, 2, 23, Gpio2);
483impl_pin!(P2_24, 2, 24, Gpio2);
484impl_pin!(P2_25, 2, 25, Gpio2);
485impl_pin!(P2_26, 2, 26, Gpio2);
486impl_pin!(P2_27, 2, 27, Gpio2);
487impl_pin!(P2_28, 2, 28, Gpio2);
488impl_pin!(P2_29, 2, 29, Gpio2);
489impl_pin!(P2_30, 2, 30, Gpio2);
490impl_pin!(P2_31, 2, 31, Gpio2);
491
492impl_pin!(P3_0, 3, 0, Gpio3);
493impl_pin!(P3_1, 3, 1, Gpio3);
494impl_pin!(P3_2, 3, 2, Gpio3);
495impl_pin!(P3_3, 3, 3, Gpio3);
496impl_pin!(P3_4, 3, 4, Gpio3);
497impl_pin!(P3_5, 3, 5, Gpio3);
498impl_pin!(P3_6, 3, 6, Gpio3);
499impl_pin!(P3_7, 3, 7, Gpio3);
500impl_pin!(P3_8, 3, 8, Gpio3);
501impl_pin!(P3_9, 3, 9, Gpio3);
502impl_pin!(P3_10, 3, 10, Gpio3);
503impl_pin!(P3_11, 3, 11, Gpio3);
504impl_pin!(P3_12, 3, 12, Gpio3);
505impl_pin!(P3_13, 3, 13, Gpio3);
506impl_pin!(P3_14, 3, 14, Gpio3);
507impl_pin!(P3_15, 3, 15, Gpio3);
508impl_pin!(P3_16, 3, 16, Gpio3);
509impl_pin!(P3_17, 3, 17, Gpio3);
510impl_pin!(P3_18, 3, 18, Gpio3);
511impl_pin!(P3_19, 3, 19, Gpio3);
512impl_pin!(P3_20, 3, 20, Gpio3);
513impl_pin!(P3_21, 3, 21, Gpio3);
514impl_pin!(P3_22, 3, 22, Gpio3);
515impl_pin!(P3_23, 3, 23, Gpio3);
516impl_pin!(P3_24, 3, 24, Gpio3);
517impl_pin!(P3_25, 3, 25, Gpio3);
518impl_pin!(P3_26, 3, 26, Gpio3);
519impl_pin!(P3_27, 3, 27, Gpio3);
520impl_pin!(P3_28, 3, 28, Gpio3);
521impl_pin!(P3_29, 3, 29, Gpio3);
522impl_pin!(P3_30, 3, 30, Gpio3);
523impl_pin!(P3_31, 3, 31, Gpio3);
524
525impl_pin!(P4_0, 4, 0, Gpio4);
526impl_pin!(P4_1, 4, 1, Gpio4);
527impl_pin!(P4_2, 4, 2, Gpio4);
528impl_pin!(P4_3, 4, 3, Gpio4);
529impl_pin!(P4_4, 4, 4, Gpio4);
530impl_pin!(P4_5, 4, 5, Gpio4);
531impl_pin!(P4_6, 4, 6, Gpio4);
532impl_pin!(P4_7, 4, 7, Gpio4);
533impl_pin!(P4_8, 4, 8, Gpio4);
534impl_pin!(P4_9, 4, 9, Gpio4);
535impl_pin!(P4_10, 4, 10, Gpio4);
536impl_pin!(P4_11, 4, 11, Gpio4);
537impl_pin!(P4_12, 4, 12, Gpio4);
538impl_pin!(P4_13, 4, 13, Gpio4);
539impl_pin!(P4_14, 4, 14, Gpio4);
540impl_pin!(P4_15, 4, 15, Gpio4);
541impl_pin!(P4_16, 4, 16, Gpio4);
542impl_pin!(P4_17, 4, 17, Gpio4);
543impl_pin!(P4_18, 4, 18, Gpio4);
544impl_pin!(P4_19, 4, 19, Gpio4);
545impl_pin!(P4_20, 4, 20, Gpio4);
546impl_pin!(P4_21, 4, 21, Gpio4);
547impl_pin!(P4_22, 4, 22, Gpio4);
548impl_pin!(P4_23, 4, 23, Gpio4);
549impl_pin!(P4_24, 4, 24, Gpio4);
550impl_pin!(P4_25, 4, 25, Gpio4);
551impl_pin!(P4_26, 4, 26, Gpio4);
552impl_pin!(P4_27, 4, 27, Gpio4);
553impl_pin!(P4_28, 4, 28, Gpio4);
554impl_pin!(P4_29, 4, 29, Gpio4);
555impl_pin!(P4_30, 4, 30, Gpio4);
556impl_pin!(P4_31, 4, 31, Gpio4);
557
558/// A flexible pin that can be configured as input or output.
559pub struct Flex<'d> {
560 pin: Peri<'d, AnyPin>,
561 _marker: PhantomData<&'d mut ()>,
562}
563
564impl<'d> Flex<'d> {
565 /// Wrap the pin in a `Flex`.
566 ///
567 /// The pin remains unmodified. The initial output level is unspecified, but
568 /// can be changed before the pin is put into output mode.
569 pub fn new(pin: Peri<'d, impl GpioPin>) -> Self {
570 pin.set_function(Mux::Mux0);
571 Self {
572 pin: pin.into(),
573 _marker: PhantomData,
574 }
575 }
576
577 #[inline]
578 fn gpio(&self) -> &'static crate::pac::gpio0::RegisterBlock {
579 self.pin.gpio()
580 }
581
582 #[inline]
583 fn mask(&self) -> u32 {
584 self.pin.mask()
585 }
586
587 /// Put the pin into input mode.
588 pub fn set_as_input(&mut self) {
589 let mask = self.mask();
590 let gpio = self.gpio();
591
592 self.set_enable_input_buffer();
593
594 gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() & !mask) });
595 }
596
597 /// Put the pin into output mode.
598 pub fn set_as_output(&mut self) {
599 let mask = self.mask();
600 let gpio = self.gpio();
601
602 self.set_pull(Pull::Disabled);
603
604 gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
605 }
606
607 /// Set output level to High.
608 #[inline]
609 pub fn set_high(&mut self) {
610 self.gpio().psor().write(|w| unsafe { w.bits(self.mask()) });
611 }
612
613 /// Set output level to Low.
614 #[inline]
615 pub fn set_low(&mut self) {
616 self.gpio().pcor().write(|w| unsafe { w.bits(self.mask()) });
617 }
618
619 /// Set output level to the given `Level`.
620 #[inline]
621 pub fn set_level(&mut self, level: Level) {
622 match level {
623 Level::High => self.set_high(),
624 Level::Low => self.set_low(),
625 }
626 }
627
628 /// Toggle output level.
629 #[inline]
630 pub fn toggle(&mut self) {
631 self.gpio().ptor().write(|w| unsafe { w.bits(self.mask()) });
632 }
633
634 /// Get whether the pin input level is high.
635 #[inline]
636 pub fn is_high(&self) -> bool {
637 (self.gpio().pdir().read().bits() & self.mask()) != 0
638 }
639
640 /// Get whether the pin input level is low.
641 #[inline]
642 pub fn is_low(&self) -> bool {
643 !self.is_high()
644 }
645
646 /// Is the output pin set as high?
647 #[inline]
648 pub fn is_set_high(&self) -> bool {
649 self.is_high()
650 }
651
652 /// Is the output pin set as low?
653 #[inline]
654 pub fn is_set_low(&self) -> bool {
655 !self.is_set_high()
656 }
657
658 /// Configure the pin pull up/down level.
659 pub fn set_pull(&mut self, pull_select: Pull) {
660 self.pin.set_pull(pull_select);
661 }
662
663 /// Configure the pin drive strength.
664 pub fn set_drive_strength(&mut self, strength: DriveStrength) {
665 self.pin.set_drive_strength(strength.into());
666 }
667
668 /// Configure the pin slew rate.
669 pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {
670 self.pin.set_slew_rate(slew_rate.into());
671 }
672
673 /// Enable input buffer for the pin.
674 pub fn set_enable_input_buffer(&mut self) {
675 self.pin.set_enable_input_buffer();
676 }
677
678 /// Get pin level.
679 pub fn get_level(&self) -> Level {
680 self.is_high().into()
681 }
682}
683
684/// Async methods
685impl<'d> Flex<'d> {
686 /// Helper function that waits for a given interrupt trigger
687 async fn wait_for_inner(&mut self, level: crate::pac::gpio0::icr::Irqc) {
688 // First, ensure that we have a waker that is ready for this port+pin
689 let w = PORT_WAIT_MAPS[self.pin.port].wait(self.pin.pin);
690 let mut w = pin!(w);
691 // Wait for the subscription to occur, which requires polling at least once
692 //
693 // This function returns a result, but can only be an Err if:
694 //
695 // * We call `.close()` on a WaitMap, which we never do
696 // * We have a duplicate key, which can't happen because `wait_for_*` methods
697 // take an &mut ref of their unique port+pin combo
698 //
699 // So we wait for it to complete, but ignore the result.
700 _ = w.as_mut().subscribe().await;
701
702 // Now that our waker is in the map, we can enable the appropriate interrupt
703 //
704 // Clear any existing pending interrupt on this pin
705 self.pin
706 .gpio()
707 .isfr0()
708 .write(|w| unsafe { w.bits(1 << self.pin.pin()) });
709 self.pin.gpio().icr(self.pin.pin()).write(|w| w.isf().isf1());
710
711 // Pin interrupt configuration
712 self.pin
713 .gpio()
714 .icr(self.pin.pin())
715 .modify(|_, w| w.irqc().variant(level));
716
717 // Finally, we can await the matching call to `.wake()` from the interrupt.
718 //
719 // Again, technically, this could return a result, but for the same reasons
720 // as above, this can't be an error in our case, so just wait for it to complete
721 _ = w.await;
722 }
723
724 /// Wait until the pin is high. If it is already high, return immediately.
725 #[inline]
726 pub fn wait_for_high(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
727 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc12)
728 }
729
730 /// Wait until the pin is low. If it is already low, return immediately.
731 #[inline]
732 pub fn wait_for_low(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
733 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc8)
734 }
735
736 /// Wait for the pin to undergo a transition from low to high.
737 #[inline]
738 pub fn wait_for_rising_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
739 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc9)
740 }
741
742 /// Wait for the pin to undergo a transition from high to low.
743 #[inline]
744 pub fn wait_for_falling_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
745 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc10)
746 }
747
748 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
749 #[inline]
750 pub fn wait_for_any_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
751 self.wait_for_inner(crate::pac::gpio0::icr::Irqc::Irqc11)
752 }
753}
754
755/// GPIO output driver that owns a `Flex` pin.
756pub struct Output<'d> {
757 flex: Flex<'d>,
758}
759
760impl<'d> Output<'d> {
761 /// Create a GPIO output driver for a [GpioPin] with the provided [Level].
762 pub fn new(pin: Peri<'d, impl GpioPin>, initial: Level, strength: DriveStrength, slew_rate: SlewRate) -> Self {
763 let mut flex = Flex::new(pin);
764 flex.set_level(initial);
765 flex.set_as_output();
766 flex.set_drive_strength(strength);
767 flex.set_slew_rate(slew_rate);
768 Self { flex }
769 }
770
771 /// Set the output as high.
772 #[inline]
773 pub fn set_high(&mut self) {
774 self.flex.set_high();
775 }
776
777 /// Set the output as low.
778 #[inline]
779 pub fn set_low(&mut self) {
780 self.flex.set_low();
781 }
782
783 /// Set the output level.
784 #[inline]
785 pub fn set_level(&mut self, level: Level) {
786 self.flex.set_level(level);
787 }
788
789 /// Toggle the output level.
790 #[inline]
791 pub fn toggle(&mut self) {
792 self.flex.toggle();
793 }
794
795 /// Is the output pin set as high?
796 #[inline]
797 pub fn is_set_high(&self) -> bool {
798 self.flex.is_high()
799 }
800
801 /// Is the output pin set as low?
802 #[inline]
803 pub fn is_set_low(&self) -> bool {
804 !self.is_set_high()
805 }
806
807 /// Expose the inner `Flex` if callers need to reconfigure the pin.
808 #[inline]
809 pub fn into_flex(self) -> Flex<'d> {
810 self.flex
811 }
812}
813
814/// GPIO input driver that owns a `Flex` pin.
815pub struct Input<'d> {
816 flex: Flex<'d>,
817}
818
819impl<'d> Input<'d> {
820 /// Create a GPIO input driver for a [GpioPin].
821 ///
822 pub fn new(pin: Peri<'d, impl GpioPin>, pull_select: Pull) -> Self {
823 let mut flex = Flex::new(pin);
824 flex.set_as_input();
825 flex.set_pull(pull_select);
826 Self { flex }
827 }
828
829 /// Get whether the pin input level is high.
830 #[inline]
831 pub fn is_high(&self) -> bool {
832 self.flex.is_high()
833 }
834
835 /// Get whether the pin input level is low.
836 #[inline]
837 pub fn is_low(&self) -> bool {
838 self.flex.is_low()
839 }
840
841 /// Expose the inner `Flex` if callers need to reconfigure the pin.
842 ///
843 /// Since Drive Strength and Slew Rate are not set when creating the Input
844 /// pin, they need to be set when converting
845 #[inline]
846 pub fn into_flex(mut self, strength: DriveStrength, slew_rate: SlewRate) -> Flex<'d> {
847 self.flex.set_drive_strength(strength);
848 self.flex.set_slew_rate(slew_rate);
849 self.flex
850 }
851
852 /// Get the pin level.
853 pub fn get_level(&self) -> Level {
854 self.flex.get_level()
855 }
856}
857
858/// Async methods
859impl<'d> Input<'d> {
860 /// Wait until the pin is high. If it is already high, return immediately.
861 #[inline]
862 pub fn wait_for_high(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
863 self.flex.wait_for_high()
864 }
865
866 /// Wait until the pin is low. If it is already low, return immediately.
867 #[inline]
868 pub fn wait_for_low(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
869 self.flex.wait_for_low()
870 }
871
872 /// Wait for the pin to undergo a transition from low to high.
873 #[inline]
874 pub fn wait_for_rising_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
875 self.flex.wait_for_rising_edge()
876 }
877
878 /// Wait for the pin to undergo a transition from high to low.
879 #[inline]
880 pub fn wait_for_falling_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
881 self.flex.wait_for_falling_edge()
882 }
883
884 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
885 #[inline]
886 pub fn wait_for_any_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {
887 self.flex.wait_for_any_edge()
888 }
889}
890
891impl embedded_hal_async::digital::Wait for Input<'_> {
892 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
893 self.wait_for_high().await;
894 Ok(())
895 }
896
897 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
898 self.wait_for_low().await;
899 Ok(())
900 }
901
902 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
903 self.wait_for_rising_edge().await;
904 Ok(())
905 }
906
907 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
908 self.wait_for_falling_edge().await;
909 Ok(())
910 }
911
912 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
913 self.wait_for_any_edge().await;
914 Ok(())
915 }
916}
917
918impl embedded_hal_async::digital::Wait for Flex<'_> {
919 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
920 self.wait_for_high().await;
921 Ok(())
922 }
923
924 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
925 self.wait_for_low().await;
926 Ok(())
927 }
928
929 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
930 self.wait_for_rising_edge().await;
931 Ok(())
932 }
933
934 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
935 self.wait_for_falling_edge().await;
936 Ok(())
937 }
938
939 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
940 self.wait_for_any_edge().await;
941 Ok(())
942 }
943}
944
945// Both embedded_hal 0.2 and 1.0 must be supported by embassy HALs.
946impl embedded_hal_02::digital::v2::InputPin for Flex<'_> {
947 // GPIO operations on this block cannot fail, therefor we set the error type
948 // to Infallible to guarantee that we can only produce Ok variants.
949 type Error = Infallible;
950
951 #[inline]
952 fn is_high(&self) -> Result<bool, Self::Error> {
953 Ok(self.is_high())
954 }
955
956 #[inline]
957 fn is_low(&self) -> Result<bool, Self::Error> {
958 Ok(self.is_low())
959 }
960}
961
962impl embedded_hal_02::digital::v2::InputPin for Input<'_> {
963 type Error = Infallible;
964
965 #[inline]
966 fn is_high(&self) -> Result<bool, Self::Error> {
967 Ok(self.is_high())
968 }
969
970 #[inline]
971 fn is_low(&self) -> Result<bool, Self::Error> {
972 Ok(self.is_low())
973 }
974}
975
976impl embedded_hal_02::digital::v2::OutputPin for Flex<'_> {
977 type Error = Infallible;
978
979 #[inline]
980 fn set_high(&mut self) -> Result<(), Self::Error> {
981 self.set_high();
982 Ok(())
983 }
984
985 #[inline]
986 fn set_low(&mut self) -> Result<(), Self::Error> {
987 self.set_low();
988 Ok(())
989 }
990}
991
992impl embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'_> {
993 #[inline]
994 fn is_set_high(&self) -> Result<bool, Self::Error> {
995 Ok(self.is_set_high())
996 }
997
998 #[inline]
999 fn is_set_low(&self) -> Result<bool, Self::Error> {
1000 Ok(self.is_set_low())
1001 }
1002}
1003
1004impl embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'_> {
1005 type Error = Infallible;
1006
1007 #[inline]
1008 fn toggle(&mut self) -> Result<(), Self::Error> {
1009 self.toggle();
1010 Ok(())
1011 }
1012}
1013
1014impl embedded_hal_1::digital::ErrorType for Flex<'_> {
1015 type Error = Infallible;
1016}
1017
1018impl embedded_hal_1::digital::ErrorType for Input<'_> {
1019 type Error = Infallible;
1020}
1021
1022impl embedded_hal_1::digital::ErrorType for Output<'_> {
1023 type Error = Infallible;
1024}
1025
1026impl embedded_hal_1::digital::InputPin for Input<'_> {
1027 #[inline]
1028 fn is_high(&mut self) -> Result<bool, Self::Error> {
1029 Ok((*self).is_high())
1030 }
1031
1032 #[inline]
1033 fn is_low(&mut self) -> Result<bool, Self::Error> {
1034 Ok((*self).is_low())
1035 }
1036}
1037
1038impl embedded_hal_1::digital::OutputPin for Flex<'_> {
1039 #[inline]
1040 fn set_high(&mut self) -> Result<(), Self::Error> {
1041 self.set_high();
1042 Ok(())
1043 }
1044
1045 #[inline]
1046 fn set_low(&mut self) -> Result<(), Self::Error> {
1047 self.set_low();
1048 Ok(())
1049 }
1050}
1051
1052impl embedded_hal_1::digital::StatefulOutputPin for Flex<'_> {
1053 #[inline]
1054 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
1055 Ok((*self).is_set_high())
1056 }
1057
1058 #[inline]
1059 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
1060 Ok((*self).is_set_low())
1061 }
1062}
diff --git a/embassy-mcxa/src/i2c/controller.rs b/embassy-mcxa/src/i2c/controller.rs
new file mode 100644
index 000000000..182a53aea
--- /dev/null
+++ b/embassy-mcxa/src/i2c/controller.rs
@@ -0,0 +1,742 @@
1//! LPI2C controller driver
2
3use core::future::Future;
4use core::marker::PhantomData;
5
6use embassy_hal_internal::drop::OnDrop;
7use embassy_hal_internal::Peri;
8use mcxa_pac::lpi2c0::mtdr::Cmd;
9
10use super::{Async, Blocking, Error, Instance, InterruptHandler, Mode, Result, SclPin, SdaPin};
11use crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig};
12use crate::clocks::{enable_and_reset, PoweredClock};
13use crate::interrupt::typelevel::Interrupt;
14use crate::AnyPin;
15
16/// Bus speed (nominal SCL, no clock stretching)
17#[derive(Clone, Copy, Default, PartialEq)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub enum Speed {
20 #[default]
21 /// 100 kbit/sec
22 Standard,
23 /// 400 kbit/sec
24 Fast,
25 /// 1 Mbit/sec
26 FastPlus,
27 /// 3.4 Mbit/sec
28 UltraFast,
29}
30
31impl From<Speed> for (u8, u8, u8, u8) {
32 fn from(value: Speed) -> (u8, u8, u8, u8) {
33 match value {
34 Speed::Standard => (0x3d, 0x37, 0x3b, 0x1d),
35 Speed::Fast => (0x0e, 0x0c, 0x0d, 0x06),
36 Speed::FastPlus => (0x04, 0x03, 0x03, 0x02),
37
38 // UltraFast is "special". Leaving it unimplemented until
39 // the driver and the clock API is further stabilized.
40 Speed::UltraFast => todo!(),
41 }
42 }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
47enum SendStop {
48 No,
49 Yes,
50}
51
52/// I2C controller configuration
53#[derive(Clone, Copy, Default)]
54#[non_exhaustive]
55pub struct Config {
56 /// Bus speed
57 pub speed: Speed,
58}
59
60/// I2C Controller Driver.
61pub struct I2c<'d, T: Instance, M: Mode> {
62 _peri: Peri<'d, T>,
63 _scl: Peri<'d, AnyPin>,
64 _sda: Peri<'d, AnyPin>,
65 _phantom: PhantomData<M>,
66 is_hs: bool,
67}
68
69impl<'d, T: Instance> I2c<'d, T, Blocking> {
70 /// Create a new blocking instance of the I2C Controller bus driver.
71 pub fn new_blocking(
72 peri: Peri<'d, T>,
73 scl: Peri<'d, impl SclPin<T>>,
74 sda: Peri<'d, impl SdaPin<T>>,
75 config: Config,
76 ) -> Result<Self> {
77 Self::new_inner(peri, scl, sda, config)
78 }
79}
80
81impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
82 fn new_inner(
83 _peri: Peri<'d, T>,
84 scl: Peri<'d, impl SclPin<T>>,
85 sda: Peri<'d, impl SdaPin<T>>,
86 config: Config,
87 ) -> Result<Self> {
88 let (power, source, div) = Self::clock_config(config.speed);
89
90 // Enable clocks
91 let conf = Lpi2cConfig {
92 power,
93 source,
94 div,
95 instance: T::CLOCK_INSTANCE,
96 };
97
98 _ = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
99
100 scl.mux();
101 sda.mux();
102
103 let _scl = scl.into();
104 let _sda = sda.into();
105
106 Self::set_config(&config)?;
107
108 Ok(Self {
109 _peri,
110 _scl,
111 _sda,
112 _phantom: PhantomData,
113 is_hs: config.speed == Speed::UltraFast,
114 })
115 }
116
117 fn set_config(config: &Config) -> Result<()> {
118 // Disable the controller.
119 critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().disabled()));
120
121 // Soft-reset the controller, read and write FIFOs.
122 Self::reset_fifos();
123 critical_section::with(|_| {
124 T::regs().mcr().modify(|_, w| w.rst().reset());
125 // According to Reference Manual section 40.7.1.4, "There
126 // is no minimum delay required before clearing the
127 // software reset", therefore we clear it immediately.
128 T::regs().mcr().modify(|_, w| w.rst().not_reset());
129
130 T::regs().mcr().modify(|_, w| w.dozen().clear_bit().dbgen().clear_bit());
131 });
132
133 let (clklo, clkhi, sethold, datavd) = config.speed.into();
134
135 critical_section::with(|_| {
136 T::regs().mccr0().modify(|_, w| unsafe {
137 w.clklo()
138 .bits(clklo)
139 .clkhi()
140 .bits(clkhi)
141 .sethold()
142 .bits(sethold)
143 .datavd()
144 .bits(datavd)
145 })
146 });
147
148 // Enable the controller.
149 critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().enabled()));
150
151 // Clear all flags
152 T::regs().msr().write(|w| {
153 w.epf()
154 .clear_bit_by_one()
155 .sdf()
156 .clear_bit_by_one()
157 .ndf()
158 .clear_bit_by_one()
159 .alf()
160 .clear_bit_by_one()
161 .fef()
162 .clear_bit_by_one()
163 .pltf()
164 .clear_bit_by_one()
165 .dmf()
166 .clear_bit_by_one()
167 .stf()
168 .clear_bit_by_one()
169 });
170
171 Ok(())
172 }
173
174 // REVISIT: turn this into a function of the speed parameter
175 fn clock_config(speed: Speed) -> (PoweredClock, Lpi2cClockSel, Div4) {
176 match speed {
177 Speed::Standard | Speed::Fast | Speed::FastPlus => (
178 PoweredClock::NormalEnabledDeepSleepDisabled,
179 Lpi2cClockSel::FroLfDiv,
180 const { Div4::no_div() },
181 ),
182 Speed::UltraFast => (
183 PoweredClock::NormalEnabledDeepSleepDisabled,
184 Lpi2cClockSel::FroHfDiv,
185 const { Div4::no_div() },
186 ),
187 }
188 }
189
190 /// Resets both TX and RX FIFOs dropping their contents.
191 fn reset_fifos() {
192 critical_section::with(|_| {
193 T::regs().mcr().modify(|_, w| w.rtf().reset().rrf().reset());
194 });
195 }
196
197 /// Checks whether the TX FIFO is full
198 fn is_tx_fifo_full() -> bool {
199 let txfifo_size = 1 << T::regs().param().read().mtxfifo().bits();
200 T::regs().mfsr().read().txcount().bits() == txfifo_size
201 }
202
203 /// Checks whether the TX FIFO is empty
204 fn is_tx_fifo_empty() -> bool {
205 T::regs().mfsr().read().txcount() == 0
206 }
207
208 /// Checks whether the RX FIFO is empty.
209 fn is_rx_fifo_empty() -> bool {
210 T::regs().mfsr().read().rxcount() == 0
211 }
212
213 /// Reads and parses the controller status producing an
214 /// appropriate `Result<(), Error>` variant.
215 fn status() -> Result<()> {
216 let msr = T::regs().msr().read();
217 T::regs().msr().write(|w| {
218 w.epf()
219 .clear_bit_by_one()
220 .sdf()
221 .clear_bit_by_one()
222 .ndf()
223 .clear_bit_by_one()
224 .alf()
225 .clear_bit_by_one()
226 .fef()
227 .clear_bit_by_one()
228 .fef()
229 .clear_bit_by_one()
230 .pltf()
231 .clear_bit_by_one()
232 .dmf()
233 .clear_bit_by_one()
234 .stf()
235 .clear_bit_by_one()
236 });
237
238 if msr.ndf().bit_is_set() {
239 Err(Error::AddressNack)
240 } else if msr.alf().bit_is_set() {
241 Err(Error::ArbitrationLoss)
242 } else if msr.fef().bit_is_set() {
243 Err(Error::FifoError)
244 } else {
245 Ok(())
246 }
247 }
248
249 /// Inserts the given command into the outgoing FIFO.
250 ///
251 /// Caller must ensure there is space in the FIFO for the new
252 /// command.
253 fn send_cmd(cmd: Cmd, data: u8) {
254 #[cfg(feature = "defmt")]
255 defmt::trace!(
256 "Sending cmd '{}' ({}) with data '{:08x}' MSR: {:08x}",
257 cmd,
258 cmd as u8,
259 data,
260 T::regs().msr().read().bits()
261 );
262
263 T::regs()
264 .mtdr()
265 .write(|w| unsafe { w.data().bits(data) }.cmd().variant(cmd));
266 }
267
268 /// Prepares an appropriate Start condition on bus by issuing a
269 /// `Start` command together with the device address and R/w bit.
270 ///
271 /// Blocks waiting for space in the FIFO to become available, then
272 /// sends the command and blocks waiting for the FIFO to become
273 /// empty ensuring the command was sent.
274 fn start(&mut self, address: u8, read: bool) -> Result<()> {
275 if address >= 0x80 {
276 return Err(Error::AddressOutOfRange(address));
277 }
278
279 // Wait until we have space in the TxFIFO
280 while Self::is_tx_fifo_full() {}
281
282 let addr_rw = address << 1 | if read { 1 } else { 0 };
283 Self::send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw);
284
285 // Wait for TxFIFO to be drained
286 while !Self::is_tx_fifo_empty() {}
287
288 // Check controller status
289 Self::status()
290 }
291
292 /// Prepares a Stop condition on the bus.
293 ///
294 /// Analogous to `start`, this blocks waiting for space in the
295 /// FIFO to become available, then sends the command and blocks
296 /// waiting for the FIFO to become empty ensuring the command was
297 /// sent.
298 fn stop() -> Result<()> {
299 // Wait until we have space in the TxFIFO
300 while Self::is_tx_fifo_full() {}
301
302 Self::send_cmd(Cmd::Stop, 0);
303
304 // Wait for TxFIFO to be drained
305 while !Self::is_tx_fifo_empty() {}
306
307 Self::status()
308 }
309
310 fn blocking_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> {
311 if read.is_empty() {
312 return Err(Error::InvalidReadBufferLength);
313 }
314
315 for chunk in read.chunks_mut(256) {
316 self.start(address, true)?;
317
318 // Wait until we have space in the TxFIFO
319 while Self::is_tx_fifo_full() {}
320
321 Self::send_cmd(Cmd::Receive, (chunk.len() - 1) as u8);
322
323 for byte in chunk.iter_mut() {
324 // Wait until there's data in the RxFIFO
325 while Self::is_rx_fifo_empty() {}
326
327 *byte = T::regs().mrdr().read().data().bits();
328 }
329 }
330
331 if send_stop == SendStop::Yes {
332 Self::stop()?;
333 }
334
335 Ok(())
336 }
337
338 fn blocking_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> {
339 self.start(address, false)?;
340
341 // Usually, embassy HALs error out with an empty write,
342 // however empty writes are useful for writing I2C scanning
343 // logic through write probing. That is, we send a start with
344 // R/w bit cleared, but instead of writing any data, just send
345 // the stop onto the bus. This has the effect of checking if
346 // the resulting address got an ACK but causing no
347 // side-effects to the device on the other end.
348 //
349 // Because of this, we are not going to error out in case of
350 // empty writes.
351 #[cfg(feature = "defmt")]
352 if write.is_empty() {
353 defmt::trace!("Empty write, write probing?");
354 }
355
356 for byte in write {
357 // Wait until we have space in the TxFIFO
358 while Self::is_tx_fifo_full() {}
359
360 Self::send_cmd(Cmd::Transmit, *byte);
361 }
362
363 if send_stop == SendStop::Yes {
364 Self::stop()?;
365 }
366
367 Ok(())
368 }
369
370 // Public API: Blocking
371
372 /// Read from address into buffer blocking caller until done.
373 pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<()> {
374 self.blocking_read_internal(address, read, SendStop::Yes)
375 }
376
377 /// Write to address from buffer blocking caller until done.
378 pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<()> {
379 self.blocking_write_internal(address, write, SendStop::Yes)
380 }
381
382 /// Write to address from bytes and read from address into buffer blocking caller until done.
383 pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> {
384 self.blocking_write_internal(address, write, SendStop::No)?;
385 self.blocking_read_internal(address, read, SendStop::Yes)
386 }
387}
388
389impl<'d, T: Instance> I2c<'d, T, Async> {
390 /// Create a new async instance of the I2C Controller bus driver.
391 pub fn new_async(
392 peri: Peri<'d, T>,
393 scl: Peri<'d, impl SclPin<T>>,
394 sda: Peri<'d, impl SdaPin<T>>,
395 _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
396 config: Config,
397 ) -> Result<Self> {
398 T::Interrupt::unpend();
399
400 // Safety: `_irq` ensures an Interrupt Handler exists.
401 unsafe { T::Interrupt::enable() };
402
403 Self::new_inner(peri, scl, sda, config)
404 }
405
406 fn remediation() {
407 #[cfg(feature = "defmt")]
408 defmt::trace!("Future dropped, issuing stop",);
409
410 // if the FIFO is not empty, drop its contents.
411 if !Self::is_tx_fifo_empty() {
412 Self::reset_fifos();
413 }
414
415 // send a stop command
416 let _ = Self::stop();
417 }
418
419 fn enable_rx_ints(&mut self) {
420 T::regs().mier().write(|w| {
421 w.rdie()
422 .enabled()
423 .ndie()
424 .enabled()
425 .alie()
426 .enabled()
427 .feie()
428 .enabled()
429 .pltie()
430 .enabled()
431 });
432 }
433
434 fn enable_tx_ints(&mut self) {
435 T::regs().mier().write(|w| {
436 w.tdie()
437 .enabled()
438 .ndie()
439 .enabled()
440 .alie()
441 .enabled()
442 .feie()
443 .enabled()
444 .pltie()
445 .enabled()
446 });
447 }
448
449 async fn async_start(&mut self, address: u8, read: bool) -> Result<()> {
450 if address >= 0x80 {
451 return Err(Error::AddressOutOfRange(address));
452 }
453
454 // send the start command
455 let addr_rw = address << 1 | if read { 1 } else { 0 };
456 Self::send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw);
457
458 T::wait_cell()
459 .wait_for(|| {
460 // enable interrupts
461 self.enable_tx_ints();
462 // if the command FIFO is empty, we're done sending start
463 Self::is_tx_fifo_empty()
464 })
465 .await
466 .map_err(|_| Error::Other)?;
467
468 Self::status()
469 }
470
471 async fn async_stop(&mut self) -> Result<()> {
472 // send the stop command
473 Self::send_cmd(Cmd::Stop, 0);
474
475 T::wait_cell()
476 .wait_for(|| {
477 // enable interrupts
478 self.enable_tx_ints();
479 // if the command FIFO is empty, we're done sending stop
480 Self::is_tx_fifo_empty()
481 })
482 .await
483 .map_err(|_| Error::Other)?;
484
485 Self::status()
486 }
487
488 async fn async_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> {
489 if read.is_empty() {
490 return Err(Error::InvalidReadBufferLength);
491 }
492
493 // perform corrective action if the future is dropped
494 let on_drop = OnDrop::new(|| Self::remediation());
495
496 for chunk in read.chunks_mut(256) {
497 self.async_start(address, true).await?;
498
499 // send receive command
500 Self::send_cmd(Cmd::Receive, (chunk.len() - 1) as u8);
501
502 T::wait_cell()
503 .wait_for(|| {
504 // enable interrupts
505 self.enable_tx_ints();
506 // if the command FIFO is empty, we're done sending start
507 Self::is_tx_fifo_empty()
508 })
509 .await
510 .map_err(|_| Error::Other)?;
511
512 for byte in chunk.iter_mut() {
513 T::wait_cell()
514 .wait_for(|| {
515 // enable interrupts
516 self.enable_rx_ints();
517 // if the rx FIFO is not empty, we need to read a byte
518 !Self::is_rx_fifo_empty()
519 })
520 .await
521 .map_err(|_| Error::ReadFail)?;
522
523 *byte = T::regs().mrdr().read().data().bits();
524 }
525 }
526
527 if send_stop == SendStop::Yes {
528 self.async_stop().await?;
529 }
530
531 // defuse it if the future is not dropped
532 on_drop.defuse();
533
534 Ok(())
535 }
536
537 async fn async_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> {
538 self.async_start(address, false).await?;
539
540 // perform corrective action if the future is dropped
541 let on_drop = OnDrop::new(|| Self::remediation());
542
543 // Usually, embassy HALs error out with an empty write,
544 // however empty writes are useful for writing I2C scanning
545 // logic through write probing. That is, we send a start with
546 // R/w bit cleared, but instead of writing any data, just send
547 // the stop onto the bus. This has the effect of checking if
548 // the resulting address got an ACK but causing no
549 // side-effects to the device on the other end.
550 //
551 // Because of this, we are not going to error out in case of
552 // empty writes.
553 #[cfg(feature = "defmt")]
554 if write.is_empty() {
555 defmt::trace!("Empty write, write probing?");
556 }
557
558 for byte in write {
559 T::wait_cell()
560 .wait_for(|| {
561 // enable interrupts
562 self.enable_tx_ints();
563 // initiate transmit
564 Self::send_cmd(Cmd::Transmit, *byte);
565 // if the tx FIFO is empty, we're done transmiting
566 Self::is_tx_fifo_empty()
567 })
568 .await
569 .map_err(|_| Error::WriteFail)?;
570 }
571
572 if send_stop == SendStop::Yes {
573 self.async_stop().await?;
574 }
575
576 // defuse it if the future is not dropped
577 on_drop.defuse();
578
579 Ok(())
580 }
581
582 // Public API: Async
583
584 /// Read from address into buffer asynchronously.
585 pub fn async_read<'a>(
586 &mut self,
587 address: u8,
588 read: &'a mut [u8],
589 ) -> impl Future<Output = Result<()>> + use<'_, 'a, 'd, T> {
590 self.async_read_internal(address, read, SendStop::Yes)
591 }
592
593 /// Write to address from buffer asynchronously.
594 pub fn async_write<'a>(
595 &mut self,
596 address: u8,
597 write: &'a [u8],
598 ) -> impl Future<Output = Result<()>> + use<'_, 'a, 'd, T> {
599 self.async_write_internal(address, write, SendStop::Yes)
600 }
601
602 /// Write to address from bytes and read from address into buffer asynchronously.
603 pub async fn async_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> {
604 self.async_write_internal(address, write, SendStop::No).await?;
605 self.async_read_internal(address, read, SendStop::Yes).await
606 }
607}
608
609impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> {
610 type Error = Error;
611
612 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<()> {
613 self.blocking_read(address, buffer)
614 }
615}
616
617impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> {
618 type Error = Error;
619
620 fn write(&mut self, address: u8, bytes: &[u8]) -> Result<()> {
621 self.blocking_write(address, bytes)
622 }
623}
624
625impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> {
626 type Error = Error;
627
628 fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<()> {
629 self.blocking_write_read(address, bytes, buffer)
630 }
631}
632
633impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, T, M> {
634 type Error = Error;
635
636 fn exec(&mut self, address: u8, operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>]) -> Result<()> {
637 if let Some((last, rest)) = operations.split_last_mut() {
638 for op in rest {
639 match op {
640 embedded_hal_02::blocking::i2c::Operation::Read(buf) => {
641 self.blocking_read_internal(address, buf, SendStop::No)?
642 }
643 embedded_hal_02::blocking::i2c::Operation::Write(buf) => {
644 self.blocking_write_internal(address, buf, SendStop::No)?
645 }
646 }
647 }
648
649 match last {
650 embedded_hal_02::blocking::i2c::Operation::Read(buf) => {
651 self.blocking_read_internal(address, buf, SendStop::Yes)
652 }
653 embedded_hal_02::blocking::i2c::Operation::Write(buf) => {
654 self.blocking_write_internal(address, buf, SendStop::Yes)
655 }
656 }
657 } else {
658 Ok(())
659 }
660 }
661}
662
663impl embedded_hal_1::i2c::Error for Error {
664 fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
665 match *self {
666 Self::ArbitrationLoss => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
667 Self::AddressNack => {
668 embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
669 }
670 _ => embedded_hal_1::i2c::ErrorKind::Other,
671 }
672 }
673}
674
675impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> {
676 type Error = Error;
677}
678
679impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {
680 fn transaction(&mut self, address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>]) -> Result<()> {
681 if let Some((last, rest)) = operations.split_last_mut() {
682 for op in rest {
683 match op {
684 embedded_hal_1::i2c::Operation::Read(buf) => {
685 self.blocking_read_internal(address, buf, SendStop::No)?
686 }
687 embedded_hal_1::i2c::Operation::Write(buf) => {
688 self.blocking_write_internal(address, buf, SendStop::No)?
689 }
690 }
691 }
692
693 match last {
694 embedded_hal_1::i2c::Operation::Read(buf) => self.blocking_read_internal(address, buf, SendStop::Yes),
695 embedded_hal_1::i2c::Operation::Write(buf) => self.blocking_write_internal(address, buf, SendStop::Yes),
696 }
697 } else {
698 Ok(())
699 }
700 }
701}
702
703impl<'d, T: Instance> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> {
704 async fn transaction(
705 &mut self,
706 address: u8,
707 operations: &mut [embedded_hal_async::i2c::Operation<'_>],
708 ) -> Result<()> {
709 if let Some((last, rest)) = operations.split_last_mut() {
710 for op in rest {
711 match op {
712 embedded_hal_async::i2c::Operation::Read(buf) => {
713 self.async_read_internal(address, buf, SendStop::No).await?
714 }
715 embedded_hal_async::i2c::Operation::Write(buf) => {
716 self.async_write_internal(address, buf, SendStop::No).await?
717 }
718 }
719 }
720
721 match last {
722 embedded_hal_async::i2c::Operation::Read(buf) => {
723 self.async_read_internal(address, buf, SendStop::Yes).await
724 }
725 embedded_hal_async::i2c::Operation::Write(buf) => {
726 self.async_write_internal(address, buf, SendStop::Yes).await
727 }
728 }
729 } else {
730 Ok(())
731 }
732 }
733}
734
735impl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> {
736 type Config = Config;
737 type ConfigError = Error;
738
739 fn set_config(&mut self, config: &Self::Config) -> Result<()> {
740 Self::set_config(config)
741 }
742}
diff --git a/embassy-mcxa/src/i2c/mod.rs b/embassy-mcxa/src/i2c/mod.rs
new file mode 100644
index 000000000..9a014224a
--- /dev/null
+++ b/embassy-mcxa/src/i2c/mod.rs
@@ -0,0 +1,194 @@
1//! I2C Support
2
3use core::marker::PhantomData;
4
5use embassy_hal_internal::PeripheralType;
6use maitake_sync::WaitCell;
7use paste::paste;
8
9use crate::clocks::periph_helpers::Lpi2cConfig;
10use crate::clocks::{ClockError, Gate};
11use crate::gpio::{GpioPin, SealedPin};
12use crate::{interrupt, pac};
13
14/// Shorthand for `Result<T>`.
15pub type Result<T> = core::result::Result<T, Error>;
16
17pub mod controller;
18
19/// Error information type
20#[derive(Debug, Copy, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub enum Error {
23 /// Clock configuration error.
24 ClockSetup(ClockError),
25 /// FIFO Error
26 FifoError,
27 /// Reading for I2C failed.
28 ReadFail,
29 /// Writing to I2C failed.
30 WriteFail,
31 /// I2C address NAK condition.
32 AddressNack,
33 /// Bus level arbitration loss.
34 ArbitrationLoss,
35 /// Address out of range.
36 AddressOutOfRange(u8),
37 /// Invalid write buffer length.
38 InvalidWriteBufferLength,
39 /// Invalid read buffer length.
40 InvalidReadBufferLength,
41 /// Other internal errors or unexpected state.
42 Other,
43}
44
45/// I2C interrupt handler.
46pub struct InterruptHandler<T: Instance> {
47 _phantom: PhantomData<T>,
48}
49
50impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
51 unsafe fn on_interrupt() {
52 if T::regs().mier().read().bits() != 0 {
53 T::regs().mier().write(|w| {
54 w.tdie()
55 .disabled()
56 .rdie()
57 .disabled()
58 .epie()
59 .disabled()
60 .sdie()
61 .disabled()
62 .ndie()
63 .disabled()
64 .alie()
65 .disabled()
66 .feie()
67 .disabled()
68 .pltie()
69 .disabled()
70 .dmie()
71 .disabled()
72 .stie()
73 .disabled()
74 });
75
76 T::wait_cell().wake();
77 }
78 }
79}
80
81mod sealed {
82 /// Seal a trait
83 pub trait Sealed {}
84}
85
86impl<T: GpioPin> sealed::Sealed for T {}
87
88trait SealedInstance {
89 fn regs() -> &'static pac::lpi2c0::RegisterBlock;
90 fn wait_cell() -> &'static WaitCell;
91}
92
93/// I2C Instance
94#[allow(private_bounds)]
95pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = Lpi2cConfig> {
96 /// Interrupt for this I2C instance.
97 type Interrupt: interrupt::typelevel::Interrupt;
98 /// Clock instance
99 const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance;
100}
101
102macro_rules! impl_instance {
103 ($($n:expr),*) => {
104 $(
105 paste!{
106 impl SealedInstance for crate::peripherals::[<LPI2C $n>] {
107 fn regs() -> &'static pac::lpi2c0::RegisterBlock {
108 unsafe { &*pac::[<Lpi2c $n>]::ptr() }
109 }
110
111 fn wait_cell() -> &'static WaitCell {
112 static WAIT_CELL: WaitCell = WaitCell::new();
113 &WAIT_CELL
114 }
115 }
116
117 impl Instance for crate::peripherals::[<LPI2C $n>] {
118 type Interrupt = crate::interrupt::typelevel::[<LPI2C $n>];
119 const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance
120 = crate::clocks::periph_helpers::Lpi2cInstance::[<Lpi2c $n>];
121 }
122 }
123 )*
124 };
125}
126
127impl_instance!(0, 1, 2, 3);
128
129/// SCL pin trait.
130pub trait SclPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {
131 fn mux(&self);
132}
133
134/// SDA pin trait.
135pub trait SdaPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {
136 fn mux(&self);
137}
138
139/// Driver mode.
140#[allow(private_bounds)]
141pub trait Mode: sealed::Sealed {}
142
143/// Blocking mode.
144pub struct Blocking;
145impl sealed::Sealed for Blocking {}
146impl Mode for Blocking {}
147
148/// Async mode.
149pub struct Async;
150impl sealed::Sealed for Async {}
151impl Mode for Async {}
152
153macro_rules! impl_pin {
154 ($pin:ident, $peri:ident, $fn:ident, $trait:ident) => {
155 impl $trait<crate::peripherals::$peri> for crate::peripherals::$pin {
156 fn mux(&self) {
157 self.set_pull(crate::gpio::Pull::Disabled);
158 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
159 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
160 self.set_function(crate::pac::port0::pcr0::Mux::$fn);
161 self.set_enable_input_buffer();
162 }
163 }
164 };
165}
166
167impl_pin!(P0_16, LPI2C0, Mux2, SdaPin);
168impl_pin!(P0_17, LPI2C0, Mux2, SclPin);
169impl_pin!(P0_18, LPI2C0, Mux2, SclPin);
170impl_pin!(P0_19, LPI2C0, Mux2, SdaPin);
171impl_pin!(P1_0, LPI2C1, Mux3, SdaPin);
172impl_pin!(P1_1, LPI2C1, Mux3, SclPin);
173impl_pin!(P1_2, LPI2C1, Mux3, SdaPin);
174impl_pin!(P1_3, LPI2C1, Mux3, SclPin);
175impl_pin!(P1_8, LPI2C2, Mux3, SdaPin);
176impl_pin!(P1_9, LPI2C2, Mux3, SclPin);
177impl_pin!(P1_10, LPI2C2, Mux3, SdaPin);
178impl_pin!(P1_11, LPI2C2, Mux3, SclPin);
179impl_pin!(P1_12, LPI2C1, Mux2, SdaPin);
180impl_pin!(P1_13, LPI2C1, Mux2, SclPin);
181impl_pin!(P1_14, LPI2C1, Mux2, SclPin);
182impl_pin!(P1_15, LPI2C1, Mux2, SdaPin);
183impl_pin!(P1_30, LPI2C0, Mux3, SdaPin);
184impl_pin!(P1_31, LPI2C0, Mux3, SclPin);
185impl_pin!(P3_27, LPI2C3, Mux2, SclPin);
186impl_pin!(P3_28, LPI2C3, Mux2, SdaPin);
187// impl_pin!(P3_29, LPI2C3, Mux2, HreqPin); What is this HREQ pin?
188impl_pin!(P3_30, LPI2C3, Mux2, SclPin);
189impl_pin!(P3_31, LPI2C3, Mux2, SdaPin);
190impl_pin!(P4_2, LPI2C2, Mux2, SdaPin);
191impl_pin!(P4_3, LPI2C0, Mux2, SclPin);
192impl_pin!(P4_4, LPI2C2, Mux2, SdaPin);
193impl_pin!(P4_5, LPI2C0, Mux2, SclPin);
194// impl_pin!(P4_6, LPI2C0, Mux2, HreqPin); What is this HREQ pin?
diff --git a/embassy-mcxa/src/interrupt.rs b/embassy-mcxa/src/interrupt.rs
new file mode 100644
index 000000000..1d10e3b2d
--- /dev/null
+++ b/embassy-mcxa/src/interrupt.rs
@@ -0,0 +1,563 @@
1//! Minimal interrupt helpers mirroring embassy-imxrt style for OS_EVENT and LPUART2.
2//! Type-level interrupt traits and bindings are provided by the
3//! `embassy_hal_internal::interrupt_mod!` macro via the generated module below.
4
5// TODO(AJM): As of 2025-11-13, we need to do a pass to ensure safety docs
6// are complete prior to release.
7#![allow(clippy::missing_safety_doc)]
8
9mod generated {
10 #[rustfmt::skip]
11 embassy_hal_internal::interrupt_mod!(
12 ADC1,
13 GPIO0,
14 GPIO1,
15 GPIO2,
16 GPIO3,
17 GPIO4,
18 LPI2C0,
19 LPI2C1,
20 LPI2C2,
21 LPI2C3,
22 LPUART0,
23 LPUART1,
24 LPUART2,
25 LPUART3,
26 LPUART4,
27 LPUART5,
28 OS_EVENT,
29 RTC,
30 );
31}
32
33use core::sync::atomic::{AtomicU16, AtomicU32, Ordering};
34
35pub use generated::interrupt::{typelevel, Priority};
36
37use crate::pac::Interrupt;
38
39/// Trait for configuring and controlling interrupts.
40///
41/// This trait provides a consistent interface for interrupt management across
42/// different interrupt sources, similar to embassy-imxrt's InterruptExt.
43pub trait InterruptExt {
44 /// Clear any pending interrupt in NVIC.
45 fn unpend(&self);
46
47 /// Set NVIC priority for this interrupt.
48 fn set_priority(&self, priority: Priority);
49
50 /// Enable this interrupt in NVIC.
51 ///
52 /// # Safety
53 /// This function is unsafe because it can enable interrupts that may not be
54 /// properly configured, potentially leading to undefined behavior.
55 unsafe fn enable(&self);
56
57 /// Disable this interrupt in NVIC.
58 ///
59 /// # Safety
60 /// This function is unsafe because disabling interrupts may leave the system
61 /// in an inconsistent state if the interrupt was expected to fire.
62 unsafe fn disable(&self);
63
64 /// Check if the interrupt is pending in NVIC.
65 fn is_pending(&self) -> bool;
66}
67
68#[derive(Clone, Copy, Debug, Default)]
69pub struct DefaultHandlerSnapshot {
70 pub vector: u16,
71 pub count: u32,
72 pub cfsr: u32,
73 pub hfsr: u32,
74 pub stacked_pc: u32,
75 pub stacked_lr: u32,
76 pub stacked_sp: u32,
77}
78
79static LAST_DEFAULT_VECTOR: AtomicU16 = AtomicU16::new(0);
80static LAST_DEFAULT_COUNT: AtomicU32 = AtomicU32::new(0);
81static LAST_DEFAULT_CFSR: AtomicU32 = AtomicU32::new(0);
82static LAST_DEFAULT_HFSR: AtomicU32 = AtomicU32::new(0);
83static LAST_DEFAULT_PC: AtomicU32 = AtomicU32::new(0);
84static LAST_DEFAULT_LR: AtomicU32 = AtomicU32::new(0);
85static LAST_DEFAULT_SP: AtomicU32 = AtomicU32::new(0);
86
87#[inline]
88pub fn default_handler_snapshot() -> DefaultHandlerSnapshot {
89 DefaultHandlerSnapshot {
90 vector: LAST_DEFAULT_VECTOR.load(Ordering::Relaxed),
91 count: LAST_DEFAULT_COUNT.load(Ordering::Relaxed),
92 cfsr: LAST_DEFAULT_CFSR.load(Ordering::Relaxed),
93 hfsr: LAST_DEFAULT_HFSR.load(Ordering::Relaxed),
94 stacked_pc: LAST_DEFAULT_PC.load(Ordering::Relaxed),
95 stacked_lr: LAST_DEFAULT_LR.load(Ordering::Relaxed),
96 stacked_sp: LAST_DEFAULT_SP.load(Ordering::Relaxed),
97 }
98}
99
100#[inline]
101pub fn clear_default_handler_snapshot() {
102 LAST_DEFAULT_VECTOR.store(0, Ordering::Relaxed);
103 LAST_DEFAULT_COUNT.store(0, Ordering::Relaxed);
104 LAST_DEFAULT_CFSR.store(0, Ordering::Relaxed);
105 LAST_DEFAULT_HFSR.store(0, Ordering::Relaxed);
106 LAST_DEFAULT_PC.store(0, Ordering::Relaxed);
107 LAST_DEFAULT_LR.store(0, Ordering::Relaxed);
108 LAST_DEFAULT_SP.store(0, Ordering::Relaxed);
109}
110
111/// OS_EVENT interrupt helper with methods similar to embassy-imxrt's InterruptExt.
112pub struct OsEvent;
113pub const OS_EVENT: OsEvent = OsEvent;
114
115impl InterruptExt for OsEvent {
116 /// Clear any pending OS_EVENT in NVIC.
117 #[inline]
118 fn unpend(&self) {
119 cortex_m::peripheral::NVIC::unpend(Interrupt::OS_EVENT);
120 }
121
122 /// Set NVIC priority for OS_EVENT.
123 #[inline]
124 fn set_priority(&self, priority: Priority) {
125 unsafe {
126 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
127 nvic.set_priority(Interrupt::OS_EVENT, u8::from(priority));
128 }
129 }
130
131 /// Enable OS_EVENT in NVIC.
132 #[inline]
133 unsafe fn enable(&self) {
134 cortex_m::peripheral::NVIC::unmask(Interrupt::OS_EVENT);
135 }
136
137 /// Disable OS_EVENT in NVIC.
138 #[inline]
139 unsafe fn disable(&self) {
140 cortex_m::peripheral::NVIC::mask(Interrupt::OS_EVENT);
141 }
142
143 /// Check if OS_EVENT is pending in NVIC.
144 #[inline]
145 fn is_pending(&self) -> bool {
146 cortex_m::peripheral::NVIC::is_pending(Interrupt::OS_EVENT)
147 }
148}
149
150impl OsEvent {
151 /// Configure OS_EVENT interrupt for timer operation.
152 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
153 /// Also performs a software event to wake any pending WFE.
154 pub fn configure_for_timer(&self, priority: Priority) {
155 // Configure NVIC
156 self.unpend();
157 self.set_priority(priority);
158 unsafe {
159 self.enable();
160 }
161
162 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
163 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
164 unsafe {
165 cortex_m::interrupt::enable();
166 }
167
168 // Wake any executor WFE that might be sleeping when we armed the first deadline
169 cortex_m::asm::sev();
170 }
171}
172
173/// LPUART2 interrupt helper with methods similar to embassy-imxrt's InterruptExt.
174pub struct Lpuart2;
175pub const LPUART2: Lpuart2 = Lpuart2;
176
177impl InterruptExt for Lpuart2 {
178 /// Clear any pending LPUART2 in NVIC.
179 #[inline]
180 fn unpend(&self) {
181 cortex_m::peripheral::NVIC::unpend(Interrupt::LPUART2);
182 }
183
184 /// Set NVIC priority for LPUART2.
185 #[inline]
186 fn set_priority(&self, priority: Priority) {
187 unsafe {
188 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
189 nvic.set_priority(Interrupt::LPUART2, u8::from(priority));
190 }
191 }
192
193 /// Enable LPUART2 in NVIC.
194 #[inline]
195 unsafe fn enable(&self) {
196 cortex_m::peripheral::NVIC::unmask(Interrupt::LPUART2);
197 }
198
199 /// Disable LPUART2 in NVIC.
200 #[inline]
201 unsafe fn disable(&self) {
202 cortex_m::peripheral::NVIC::mask(Interrupt::LPUART2);
203 }
204
205 /// Check if LPUART2 is pending in NVIC.
206 #[inline]
207 fn is_pending(&self) -> bool {
208 cortex_m::peripheral::NVIC::is_pending(Interrupt::LPUART2)
209 }
210}
211
212impl Lpuart2 {
213 /// Configure LPUART2 interrupt for UART operation.
214 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
215 pub fn configure_for_uart(&self, priority: Priority) {
216 // Configure NVIC
217 self.unpend();
218 self.set_priority(priority);
219 unsafe {
220 self.enable();
221 }
222
223 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
224 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
225 unsafe {
226 cortex_m::interrupt::enable();
227 }
228 }
229
230 /// Install LPUART2 handler into the RAM vector table.
231 /// Safety: See `install_irq_handler`.
232 pub unsafe fn install_handler(&self, handler: unsafe extern "C" fn()) {
233 install_irq_handler(Interrupt::LPUART2, handler);
234 }
235}
236
237pub struct Rtc;
238pub const RTC: Rtc = Rtc;
239
240impl InterruptExt for Rtc {
241 /// Clear any pending RTC in NVIC.
242 #[inline]
243 fn unpend(&self) {
244 cortex_m::peripheral::NVIC::unpend(Interrupt::RTC);
245 }
246
247 /// Set NVIC priority for RTC.
248 #[inline]
249 fn set_priority(&self, priority: Priority) {
250 unsafe {
251 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
252 nvic.set_priority(Interrupt::RTC, u8::from(priority));
253 }
254 }
255
256 /// Enable RTC in NVIC.
257 #[inline]
258 unsafe fn enable(&self) {
259 cortex_m::peripheral::NVIC::unmask(Interrupt::RTC);
260 }
261
262 /// Disable RTC in NVIC.
263 #[inline]
264 unsafe fn disable(&self) {
265 cortex_m::peripheral::NVIC::mask(Interrupt::RTC);
266 }
267
268 /// Check if RTC is pending in NVIC.
269 #[inline]
270 fn is_pending(&self) -> bool {
271 cortex_m::peripheral::NVIC::is_pending(Interrupt::RTC)
272 }
273}
274
275pub struct Adc;
276pub const ADC1: Adc = Adc;
277
278impl InterruptExt for Adc {
279 /// Clear any pending ADC1 in NVIC.
280 #[inline]
281 fn unpend(&self) {
282 cortex_m::peripheral::NVIC::unpend(Interrupt::ADC1);
283 }
284
285 /// Set NVIC priority for ADC1.
286 #[inline]
287 fn set_priority(&self, priority: Priority) {
288 unsafe {
289 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
290 nvic.set_priority(Interrupt::ADC1, u8::from(priority));
291 }
292 }
293
294 /// Enable ADC1 in NVIC.
295 #[inline]
296 unsafe fn enable(&self) {
297 cortex_m::peripheral::NVIC::unmask(Interrupt::ADC1);
298 }
299
300 /// Disable ADC1 in NVIC.
301 #[inline]
302 unsafe fn disable(&self) {
303 cortex_m::peripheral::NVIC::mask(Interrupt::ADC1);
304 }
305
306 /// Check if ADC1 is pending in NVIC.
307 #[inline]
308 fn is_pending(&self) -> bool {
309 cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC1)
310 }
311}
312
313pub struct Gpio0;
314pub const GPIO0: Gpio0 = Gpio0;
315
316impl InterruptExt for Gpio0 {
317 /// Clear any pending GPIO0 in NVIC.
318 #[inline]
319 fn unpend(&self) {
320 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO0);
321 }
322
323 /// Set NVIC priority for GPIO0.
324 #[inline]
325 fn set_priority(&self, priority: Priority) {
326 unsafe {
327 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
328 nvic.set_priority(Interrupt::GPIO0, u8::from(priority));
329 }
330 }
331
332 /// Enable GPIO0 in NVIC.
333 #[inline]
334 unsafe fn enable(&self) {
335 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO0);
336 }
337
338 /// Disable GPIO0 in NVIC.
339 #[inline]
340 unsafe fn disable(&self) {
341 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO0);
342 }
343
344 /// Check if GPIO0 is pending in NVIC.
345 #[inline]
346 fn is_pending(&self) -> bool {
347 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO0)
348 }
349}
350
351pub struct Gpio1;
352pub const GPIO1: Gpio1 = Gpio1;
353
354impl InterruptExt for Gpio1 {
355 /// Clear any pending GPIO1 in NVIC.
356 #[inline]
357 fn unpend(&self) {
358 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO1);
359 }
360
361 /// Set NVIC priority for GPIO1.
362 #[inline]
363 fn set_priority(&self, priority: Priority) {
364 unsafe {
365 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
366 nvic.set_priority(Interrupt::GPIO1, u8::from(priority));
367 }
368 }
369
370 /// Enable GPIO1 in NVIC.
371 #[inline]
372 unsafe fn enable(&self) {
373 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO1);
374 }
375
376 /// Disable GPIO1 in NVIC.
377 #[inline]
378 unsafe fn disable(&self) {
379 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO1);
380 }
381
382 /// Check if GPIO1 is pending in NVIC.
383 #[inline]
384 fn is_pending(&self) -> bool {
385 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO1)
386 }
387}
388
389pub struct Gpio2;
390pub const GPIO2: Gpio2 = Gpio2;
391
392impl InterruptExt for Gpio2 {
393 /// Clear any pending GPIO2 in NVIC.
394 #[inline]
395 fn unpend(&self) {
396 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO2);
397 }
398
399 /// Set NVIC priority for GPIO2.
400 #[inline]
401 fn set_priority(&self, priority: Priority) {
402 unsafe {
403 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
404 nvic.set_priority(Interrupt::GPIO2, u8::from(priority));
405 }
406 }
407
408 /// Enable GPIO2 in NVIC.
409 #[inline]
410 unsafe fn enable(&self) {
411 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO2);
412 }
413
414 /// Disable GPIO2 in NVIC.
415 #[inline]
416 unsafe fn disable(&self) {
417 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO2);
418 }
419
420 /// Check if GPIO2 is pending in NVIC.
421 #[inline]
422 fn is_pending(&self) -> bool {
423 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO2)
424 }
425}
426
427pub struct Gpio3;
428pub const GPIO3: Gpio3 = Gpio3;
429
430impl InterruptExt for Gpio3 {
431 /// Clear any pending GPIO3 in NVIC.
432 #[inline]
433 fn unpend(&self) {
434 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO3);
435 }
436
437 /// Set NVIC priority for GPIO3.
438 #[inline]
439 fn set_priority(&self, priority: Priority) {
440 unsafe {
441 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
442 nvic.set_priority(Interrupt::GPIO3, u8::from(priority));
443 }
444 }
445
446 /// Enable GPIO3 in NVIC.
447 #[inline]
448 unsafe fn enable(&self) {
449 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO3);
450 }
451
452 /// Disable GPIO3 in NVIC.
453 #[inline]
454 unsafe fn disable(&self) {
455 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO3);
456 }
457
458 /// Check if GPIO3 is pending in NVIC.
459 #[inline]
460 fn is_pending(&self) -> bool {
461 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO3)
462 }
463}
464
465pub struct Gpio4;
466pub const GPIO4: Gpio4 = Gpio4;
467
468impl InterruptExt for Gpio4 {
469 /// Clear any pending GPIO4 in NVIC.
470 #[inline]
471 fn unpend(&self) {
472 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO4);
473 }
474
475 /// Set NVIC priority for GPIO4.
476 #[inline]
477 fn set_priority(&self, priority: Priority) {
478 unsafe {
479 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
480 nvic.set_priority(Interrupt::GPIO4, u8::from(priority));
481 }
482 }
483
484 /// Enable GPIO4 in NVIC.
485 #[inline]
486 unsafe fn enable(&self) {
487 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO4);
488 }
489
490 /// Disable GPIO4 in NVIC.
491 #[inline]
492 unsafe fn disable(&self) {
493 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO4);
494 }
495
496 /// Check if GPIO4 is pending in NVIC.
497 #[inline]
498 fn is_pending(&self) -> bool {
499 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO4)
500 }
501}
502
503/// Set VTOR (Vector Table Offset) to a RAM-based vector table.
504/// Pass a pointer to the first word in the RAM table (stack pointer slot 0).
505/// Safety: Caller must ensure the RAM table is valid and aligned as required by the core.
506pub unsafe fn vtor_set_ram_vector_base(base: *const u32) {
507 core::ptr::write_volatile(0xE000_ED08 as *mut u32, base as u32);
508}
509
510/// Install an interrupt handler into the current VTOR-based vector table.
511/// This writes the function pointer at index 16 + irq number.
512/// Safety: Caller must ensure VTOR points at a writable RAM table and that `handler`
513/// has the correct ABI and lifetime.
514pub unsafe fn install_irq_handler(irq: Interrupt, handler: unsafe extern "C" fn()) {
515 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
516 let idx = 16 + (irq as isize as usize);
517 core::ptr::write_volatile(vtor_base.add(idx), handler as usize as u32);
518}
519
520impl OsEvent {
521 /// Convenience to install the OS_EVENT handler into the RAM vector table.
522 /// Safety: See `install_irq_handler`.
523 pub unsafe fn install_handler(&self, handler: extern "C" fn()) {
524 install_irq_handler(Interrupt::OS_EVENT, handler);
525 }
526}
527
528/// Install OS_EVENT handler by raw address. Useful to avoid fn pointer type mismatches.
529/// Safety: Caller must ensure the address is a valid `extern "C" fn()` handler.
530pub unsafe fn os_event_install_handler_raw(handler_addr: usize) {
531 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
532 let idx = 16 + (Interrupt::OS_EVENT as isize as usize);
533 core::ptr::write_volatile(vtor_base.add(idx), handler_addr as u32);
534}
535
536/// Provide a conservative default IRQ handler that avoids wedging the system.
537/// It clears all NVIC pending bits and returns, so spurious or reserved IRQs
538/// don’t trap the core in an infinite WFI loop during bring-up.
539#[no_mangle]
540pub unsafe extern "C" fn DefaultHandler() {
541 let active = core::ptr::read_volatile(0xE000_ED04 as *const u32) & 0x1FF;
542 let cfsr = core::ptr::read_volatile(0xE000_ED28 as *const u32);
543 let hfsr = core::ptr::read_volatile(0xE000_ED2C as *const u32);
544
545 let sp = cortex_m::register::msp::read();
546 let stacked = sp as *const u32;
547 // Stacked registers follow ARMv8-M procedure call standard order
548 let stacked_pc = unsafe { stacked.add(6).read() };
549 let stacked_lr = unsafe { stacked.add(5).read() };
550
551 LAST_DEFAULT_VECTOR.store(active as u16, Ordering::Relaxed);
552 LAST_DEFAULT_CFSR.store(cfsr, Ordering::Relaxed);
553 LAST_DEFAULT_HFSR.store(hfsr, Ordering::Relaxed);
554 LAST_DEFAULT_COUNT.fetch_add(1, Ordering::Relaxed);
555 LAST_DEFAULT_PC.store(stacked_pc, Ordering::Relaxed);
556 LAST_DEFAULT_LR.store(stacked_lr, Ordering::Relaxed);
557 LAST_DEFAULT_SP.store(sp, Ordering::Relaxed);
558
559 // Do nothing here: on some MCUs/TrustZone setups, writing NVIC from a spurious
560 // handler can fault if targeting the Secure bank. Just return.
561 cortex_m::asm::dsb();
562 cortex_m::asm::isb();
563}
diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs
new file mode 100644
index 000000000..c6d8adc8f
--- /dev/null
+++ b/embassy-mcxa/src/lib.rs
@@ -0,0 +1,471 @@
1#![no_std]
2#![allow(async_fn_in_trait)]
3#![doc = include_str!("../README.md")]
4
5// //! ## Feature flags
6// #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
7
8pub mod clocks; // still provide clock helpers
9pub mod gpio;
10pub mod pins; // pin mux helpers
11
12pub mod adc;
13pub mod clkout;
14pub mod config;
15pub mod i2c;
16pub mod interrupt;
17pub mod lpuart;
18pub mod ostimer;
19pub mod rtc;
20
21pub use crate::pac::NVIC_PRIO_BITS;
22
23#[rustfmt::skip]
24embassy_hal_internal::peripherals!(
25 ADC0,
26 ADC1,
27
28 AOI0,
29 AOI1,
30
31 CAN0,
32 CAN1,
33
34 CDOG0,
35 CDOG1,
36
37 // CLKOUT is not specifically a peripheral (it's part of SYSCON),
38 // but we still want it to be a singleton.
39 CLKOUT,
40
41 CMC,
42 CMP0,
43 CMP1,
44 CRC0,
45
46 CTIMER0,
47 CTIMER1,
48 CTIMER2,
49 CTIMER3,
50 CTIMER4,
51
52 DBGMAILBOX,
53 DMA0,
54 EDMA0_TCD0,
55 EIM0,
56 EQDC0,
57 EQDC1,
58 ERM0,
59 FLEXIO0,
60 FLEXPWM0,
61 FLEXPWM1,
62 FMC0,
63 FMU0,
64 FREQME0,
65 GLIKEY0,
66
67 GPIO0,
68 GPIO1,
69 GPIO2,
70 GPIO3,
71 GPIO4,
72
73 I3C0,
74 INPUTMUX0,
75
76 LPI2C0,
77 LPI2C1,
78 LPI2C2,
79 LPI2C3,
80
81 LPSPI0,
82 LPSPI1,
83
84 LPTMR0,
85
86 LPUART0,
87 LPUART1,
88 LPUART2,
89 LPUART3,
90 LPUART4,
91 LPUART5,
92
93 MAU0,
94 MBC0,
95 MRCC0,
96 OPAMP0,
97
98 #[cfg(not(feature = "time"))]
99 OSTIMER0,
100
101 P0_0,
102 P0_1,
103 P0_2,
104 P0_3,
105 P0_4,
106 P0_5,
107 P0_6,
108 P0_7,
109 P0_8,
110 P0_9,
111 P0_10,
112 P0_11,
113 P0_12,
114 P0_13,
115 P0_14,
116 P0_15,
117 P0_16,
118 P0_17,
119 P0_18,
120 P0_19,
121 P0_20,
122 P0_21,
123 P0_22,
124 P0_23,
125 P0_24,
126 P0_25,
127 P0_26,
128 P0_27,
129 P0_28,
130 P0_29,
131 P0_30,
132 P0_31,
133
134 P1_0,
135 P1_1,
136 P1_2,
137 P1_3,
138 P1_4,
139 P1_5,
140 P1_6,
141 P1_7,
142 P1_8,
143 P1_9,
144 P1_10,
145 P1_11,
146 P1_12,
147 P1_13,
148 P1_14,
149 P1_15,
150 P1_16,
151 P1_17,
152 P1_18,
153 P1_19,
154 P1_20,
155 P1_21,
156 P1_22,
157 P1_23,
158 P1_24,
159 P1_25,
160 P1_26,
161 P1_27,
162 P1_28,
163 P1_29,
164 P1_30,
165 P1_31,
166
167 P2_0,
168 P2_1,
169 P2_2,
170 P2_3,
171 P2_4,
172 P2_5,
173 P2_6,
174 P2_7,
175 P2_8,
176 P2_9,
177 P2_10,
178 P2_11,
179 P2_12,
180 P2_13,
181 P2_14,
182 P2_15,
183 P2_16,
184 P2_17,
185 P2_18,
186 P2_19,
187 P2_20,
188 P2_21,
189 P2_22,
190 P2_23,
191 P2_24,
192 P2_25,
193 P2_26,
194 P2_27,
195 P2_28,
196 P2_29,
197 P2_30,
198 P2_31,
199
200 P3_0,
201 P3_1,
202 P3_2,
203 P3_3,
204 P3_4,
205 P3_5,
206 P3_6,
207 P3_7,
208 P3_8,
209 P3_9,
210 P3_10,
211 P3_11,
212 P3_12,
213 P3_13,
214 P3_14,
215 P3_15,
216 P3_16,
217 P3_17,
218 P3_18,
219 P3_19,
220 P3_20,
221 P3_21,
222 P3_22,
223 P3_23,
224 P3_24,
225 P3_25,
226 P3_26,
227 P3_27,
228 P3_28,
229 P3_29,
230 P3_30,
231 P3_31,
232
233 P4_0,
234 P4_1,
235 P4_2,
236 P4_3,
237 P4_4,
238 P4_5,
239 P4_6,
240 P4_7,
241 P4_8,
242 P4_9,
243 P4_10,
244 P4_11,
245 P4_12,
246 P4_13,
247 P4_14,
248 P4_15,
249 P4_16,
250 P4_17,
251 P4_18,
252 P4_19,
253 P4_20,
254 P4_21,
255 P4_22,
256 P4_23,
257 P4_24,
258 P4_25,
259 P4_26,
260 P4_27,
261 P4_28,
262 P4_29,
263 P4_30,
264 P4_31,
265
266 P5_0,
267 P5_1,
268 P5_2,
269 P5_3,
270 P5_4,
271 P5_5,
272 P5_6,
273 P5_7,
274 P5_8,
275 P5_9,
276 P5_10,
277 P5_11,
278 P5_12,
279 P5_13,
280 P5_14,
281 P5_15,
282 P5_16,
283 P5_17,
284 P5_18,
285 P5_19,
286 P5_20,
287 P5_21,
288 P5_22,
289 P5_23,
290 P5_24,
291 P5_25,
292 P5_26,
293 P5_27,
294 P5_28,
295 P5_29,
296 P5_30,
297 P5_31,
298
299 PKC0,
300
301 PORT0,
302 PORT1,
303 PORT2,
304 PORT3,
305 PORT4,
306
307 RTC0,
308 SAU,
309 SCG0,
310 SCN_SCB,
311 SGI0,
312 SMARTDMA0,
313 SPC0,
314 SYSCON,
315 TDET0,
316 TRNG0,
317 UDF0,
318 USB0,
319 UTICK0,
320 VBAT0,
321 WAKETIMER0,
322 WUU0,
323 WWDT0,
324);
325
326// Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it.
327
328// Re-export interrupt traits and types
329pub use adc::Adc1 as Adc1Token;
330pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output};
331pub use interrupt::InterruptExt;
332#[cfg(feature = "unstable-pac")]
333pub use mcxa_pac as pac;
334#[cfg(not(feature = "unstable-pac"))]
335pub(crate) use mcxa_pac as pac;
336
337/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals.
338/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling).
339pub fn init(cfg: crate::config::Config) -> Peripherals {
340 let peripherals = Peripherals::take();
341 // Apply user-configured priority early; enabling is left to examples/apps
342 #[cfg(feature = "time")]
343 crate::interrupt::OS_EVENT.set_priority(cfg.time_interrupt_priority);
344 // Apply user-configured priority early; enabling is left to examples/apps
345 crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority);
346 // Apply user-configured priority early; enabling is left to examples/apps
347 crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority);
348 // Apply user-configured priority early; enabling is left to examples/apps
349 crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority);
350 // Apply user-configured priority early; enabling is left to examples/apps
351 crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority);
352 // Apply user-configured priority early; enabling is left to examples/apps
353 crate::interrupt::GPIO2.set_priority(cfg.gpio_interrupt_priority);
354 // Apply user-configured priority early; enabling is left to examples/apps
355 crate::interrupt::GPIO3.set_priority(cfg.gpio_interrupt_priority);
356 // Apply user-configured priority early; enabling is left to examples/apps
357 crate::interrupt::GPIO4.set_priority(cfg.gpio_interrupt_priority);
358
359 // Configure clocks
360 crate::clocks::init(cfg.clock_cfg).unwrap();
361
362 unsafe {
363 crate::gpio::init();
364 }
365
366 // Initialize embassy-time global driver backed by OSTIMER0
367 #[cfg(feature = "time")]
368 crate::ostimer::time_driver::init(crate::config::Config::default().time_interrupt_priority, 1_000_000);
369
370 // Enable GPIO clocks
371 unsafe {
372 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT0>(&crate::clocks::periph_helpers::NoConfig);
373 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO0>(&crate::clocks::periph_helpers::NoConfig);
374
375 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT1>(&crate::clocks::periph_helpers::NoConfig);
376 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO1>(&crate::clocks::periph_helpers::NoConfig);
377
378 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT2>(&crate::clocks::periph_helpers::NoConfig);
379 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO2>(&crate::clocks::periph_helpers::NoConfig);
380
381 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT3>(&crate::clocks::periph_helpers::NoConfig);
382 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO3>(&crate::clocks::periph_helpers::NoConfig);
383
384 _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT4>(&crate::clocks::periph_helpers::NoConfig);
385 _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO4>(&crate::clocks::periph_helpers::NoConfig);
386 }
387
388 peripherals
389}
390
391// /// Optional hook called by cortex-m-rt before RAM init.
392// /// We proactively mask and clear all NVIC IRQs to avoid wedges from stale state
393// /// left by soft resets/debug sessions.
394// ///
395// /// NOTE: Manual VTOR setup is required for RAM execution. The cortex-m-rt 'set-vtor'
396// /// feature is incompatible with our setup because it expects __vector_table to be
397// /// defined differently than how our RAM-based linker script arranges it.
398// #[no_mangle]
399// pub unsafe extern "C" fn __pre_init() {
400// // Set the VTOR to point to the interrupt vector table in RAM
401// // This is required since code runs from RAM on this MCU
402// crate::interrupt::vtor_set_ram_vector_base(0x2000_0000 as *const u32);
403
404// // Mask and clear pending for all NVIC lines (0..127) to avoid stale state across runs.
405// let nvic = &*cortex_m::peripheral::NVIC::PTR;
406// for i in 0..4 {
407// // 4 words x 32 = 128 IRQs
408// nvic.icer[i].write(0xFFFF_FFFF);
409// nvic.icpr[i].write(0xFFFF_FFFF);
410// }
411// // Do NOT touch peripheral registers here: clocks may be off and accesses can fault.
412// crate::interrupt::clear_default_handler_snapshot();
413// }
414
415/// Internal helper to dispatch a type-level interrupt handler.
416#[inline(always)]
417#[doc(hidden)]
418pub unsafe fn __handle_interrupt<T, H>()
419where
420 T: crate::interrupt::typelevel::Interrupt,
421 H: crate::interrupt::typelevel::Handler<T>,
422{
423 H::on_interrupt();
424}
425
426/// Macro to bind interrupts to handlers, similar to embassy-imxrt.
427///
428/// Example:
429/// - Bind OS_EVENT to the OSTIMER time-driver handler
430/// bind_interrupts!(struct Irqs { OS_EVENT => crate::ostimer::time_driver::OsEventHandler; });
431#[macro_export]
432macro_rules! bind_interrupts {
433 ($(#[$attr:meta])* $vis:vis struct $name:ident {
434 $(
435 $(#[cfg($cond_irq:meta)])?
436 $irq:ident => $(
437 $(#[cfg($cond_handler:meta)])?
438 $handler:ty
439 ),*;
440 )*
441 }) => {
442 #[derive(Copy, Clone)]
443 $(#[$attr])*
444 $vis struct $name;
445
446 $(
447 #[allow(non_snake_case)]
448 #[no_mangle]
449 $(#[cfg($cond_irq)])?
450 unsafe extern "C" fn $irq() {
451 unsafe {
452 $(
453 $(#[cfg($cond_handler)])?
454 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
455 )*
456 }
457 }
458
459 $(#[cfg($cond_irq)])?
460 $crate::bind_interrupts!(@inner
461 $(
462 $(#[cfg($cond_handler)])?
463 unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
464 )*
465 );
466 )*
467 };
468 (@inner $($t:tt)*) => {
469 $($t)*
470 }
471}
diff --git a/embassy-mcxa/src/lpuart/buffered.rs b/embassy-mcxa/src/lpuart/buffered.rs
new file mode 100644
index 000000000..8eb443ca7
--- /dev/null
+++ b/embassy-mcxa/src/lpuart/buffered.rs
@@ -0,0 +1,780 @@
1use core::future::poll_fn;
2use core::marker::PhantomData;
3use core::sync::atomic::{AtomicBool, Ordering};
4use core::task::Poll;
5
6use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
7use embassy_hal_internal::Peri;
8use embassy_sync::waitqueue::AtomicWaker;
9
10use super::*;
11use crate::interrupt;
12
13// ============================================================================
14// STATIC STATE MANAGEMENT
15// ============================================================================
16
17/// State for buffered LPUART operations
18pub struct State {
19 tx_waker: AtomicWaker,
20 tx_buf: RingBuffer,
21 tx_done: AtomicBool,
22 rx_waker: AtomicWaker,
23 rx_buf: RingBuffer,
24 initialized: AtomicBool,
25}
26
27impl Default for State {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl State {
34 /// Create a new state instance
35 pub const fn new() -> Self {
36 Self {
37 tx_waker: AtomicWaker::new(),
38 tx_buf: RingBuffer::new(),
39 tx_done: AtomicBool::new(true),
40 rx_waker: AtomicWaker::new(),
41 rx_buf: RingBuffer::new(),
42 initialized: AtomicBool::new(false),
43 }
44 }
45}
46// ============================================================================
47// BUFFERED DRIVER STRUCTURES
48// ============================================================================
49
50/// Buffered LPUART driver
51pub struct BufferedLpuart<'a> {
52 tx: BufferedLpuartTx<'a>,
53 rx: BufferedLpuartRx<'a>,
54}
55
56/// Buffered LPUART TX driver
57pub struct BufferedLpuartTx<'a> {
58 info: Info,
59 state: &'static State,
60 _tx_pin: Peri<'a, AnyPin>,
61 _cts_pin: Option<Peri<'a, AnyPin>>,
62}
63
64/// Buffered LPUART RX driver
65pub struct BufferedLpuartRx<'a> {
66 info: Info,
67 state: &'static State,
68 _rx_pin: Peri<'a, AnyPin>,
69 _rts_pin: Option<Peri<'a, AnyPin>>,
70}
71
72// ============================================================================
73// BUFFERED LPUART IMPLEMENTATION
74// ============================================================================
75
76impl<'a> BufferedLpuart<'a> {
77 /// Common initialization logic
78 fn init_common<T: Instance>(
79 _inner: &Peri<'a, T>,
80 tx_buffer: Option<&'a mut [u8]>,
81 rx_buffer: Option<&'a mut [u8]>,
82 config: &Config,
83 enable_tx: bool,
84 enable_rx: bool,
85 enable_rts: bool,
86 enable_cts: bool,
87 ) -> Result<&'static State> {
88 let state = T::buffered_state();
89
90 if state.initialized.load(Ordering::Relaxed) {
91 return Err(Error::InvalidArgument);
92 }
93
94 // Initialize buffers
95 if let Some(tx_buffer) = tx_buffer {
96 if tx_buffer.is_empty() {
97 return Err(Error::InvalidArgument);
98 }
99 unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), tx_buffer.len()) };
100 }
101
102 if let Some(rx_buffer) = rx_buffer {
103 if rx_buffer.is_empty() {
104 return Err(Error::InvalidArgument);
105 }
106 unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_buffer.len()) };
107 }
108
109 state.initialized.store(true, Ordering::Relaxed);
110
111 // Enable clocks and initialize hardware
112 let conf = LpuartConfig {
113 power: config.power,
114 source: config.source,
115 div: config.div,
116 instance: T::CLOCK_INSTANCE,
117 };
118 let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
119
120 Self::init_hardware(
121 T::info().regs,
122 *config,
123 clock_freq,
124 enable_tx,
125 enable_rx,
126 enable_rts,
127 enable_cts,
128 )?;
129
130 Ok(state)
131 }
132
133 /// Helper for full-duplex initialization
134 fn new_inner<T: Instance>(
135 inner: Peri<'a, T>,
136 tx_pin: Peri<'a, AnyPin>,
137 rx_pin: Peri<'a, AnyPin>,
138 rts_pin: Option<Peri<'a, AnyPin>>,
139 cts_pin: Option<Peri<'a, AnyPin>>,
140 tx_buffer: &'a mut [u8],
141 rx_buffer: &'a mut [u8],
142 config: Config,
143 ) -> Result<(BufferedLpuartTx<'a>, BufferedLpuartRx<'a>)> {
144 let state = Self::init_common::<T>(
145 &inner,
146 Some(tx_buffer),
147 Some(rx_buffer),
148 &config,
149 true,
150 true,
151 rts_pin.is_some(),
152 cts_pin.is_some(),
153 )?;
154
155 let tx = BufferedLpuartTx {
156 info: T::info(),
157 state,
158 _tx_pin: tx_pin,
159 _cts_pin: cts_pin,
160 };
161
162 let rx = BufferedLpuartRx {
163 info: T::info(),
164 state,
165 _rx_pin: rx_pin,
166 _rts_pin: rts_pin,
167 };
168
169 Ok((tx, rx))
170 }
171
172 /// Common hardware initialization logic
173 fn init_hardware(
174 regs: &'static mcxa_pac::lpuart0::RegisterBlock,
175 config: Config,
176 clock_freq: u32,
177 enable_tx: bool,
178 enable_rx: bool,
179 enable_rts: bool,
180 enable_cts: bool,
181 ) -> Result<()> {
182 // Perform standard initialization
183 perform_software_reset(regs);
184 disable_transceiver(regs);
185 configure_baudrate(regs, config.baudrate_bps, clock_freq)?;
186 configure_frame_format(regs, &config);
187 configure_control_settings(regs, &config);
188 configure_fifo(regs, &config);
189 clear_all_status_flags(regs);
190 configure_flow_control(regs, enable_rts, enable_cts, &config);
191 configure_bit_order(regs, config.msb_first);
192
193 // Enable interrupts for buffered operation
194 cortex_m::interrupt::free(|_| {
195 regs.ctrl().modify(|_, w| {
196 w.rie()
197 .enabled() // RX interrupt
198 .orie()
199 .enabled() // Overrun interrupt
200 .peie()
201 .enabled() // Parity error interrupt
202 .feie()
203 .enabled() // Framing error interrupt
204 .neie()
205 .enabled() // Noise error interrupt
206 });
207 });
208
209 // Enable the transceiver
210 enable_transceiver(regs, enable_rx, enable_tx);
211
212 Ok(())
213 }
214
215 /// Create a new full duplex buffered LPUART
216 pub fn new<T: Instance>(
217 inner: Peri<'a, T>,
218 tx_pin: Peri<'a, impl TxPin<T>>,
219 rx_pin: Peri<'a, impl RxPin<T>>,
220 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
221 tx_buffer: &'a mut [u8],
222 rx_buffer: &'a mut [u8],
223 config: Config,
224 ) -> Result<Self> {
225 tx_pin.as_tx();
226 rx_pin.as_rx();
227
228 let (tx, rx) = Self::new_inner::<T>(
229 inner,
230 tx_pin.into(),
231 rx_pin.into(),
232 None,
233 None,
234 tx_buffer,
235 rx_buffer,
236 config,
237 )?;
238
239 Ok(Self { tx, rx })
240 }
241
242 /// Create a new buffered LPUART instance with RTS/CTS flow control
243 pub fn new_with_rtscts<T: Instance>(
244 inner: Peri<'a, T>,
245 tx_pin: Peri<'a, impl TxPin<T>>,
246 rx_pin: Peri<'a, impl RxPin<T>>,
247 rts_pin: Peri<'a, impl RtsPin<T>>,
248 cts_pin: Peri<'a, impl CtsPin<T>>,
249 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
250 tx_buffer: &'a mut [u8],
251 rx_buffer: &'a mut [u8],
252 config: Config,
253 ) -> Result<Self> {
254 tx_pin.as_tx();
255 rx_pin.as_rx();
256 rts_pin.as_rts();
257 cts_pin.as_cts();
258
259 let (tx, rx) = Self::new_inner::<T>(
260 inner,
261 tx_pin.into(),
262 rx_pin.into(),
263 Some(rts_pin.into()),
264 Some(cts_pin.into()),
265 tx_buffer,
266 rx_buffer,
267 config,
268 )?;
269
270 Ok(Self { tx, rx })
271 }
272
273 /// Create a new buffered LPUART with only RTS flow control (RX flow control)
274 pub fn new_with_rts<T: Instance>(
275 inner: Peri<'a, T>,
276 tx_pin: Peri<'a, impl TxPin<T>>,
277 rx_pin: Peri<'a, impl RxPin<T>>,
278 rts_pin: Peri<'a, impl RtsPin<T>>,
279 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
280 tx_buffer: &'a mut [u8],
281 rx_buffer: &'a mut [u8],
282 config: Config,
283 ) -> Result<Self> {
284 tx_pin.as_tx();
285 rx_pin.as_rx();
286 rts_pin.as_rts();
287
288 let (tx, rx) = Self::new_inner::<T>(
289 inner,
290 tx_pin.into(),
291 rx_pin.into(),
292 Some(rts_pin.into()),
293 None,
294 tx_buffer,
295 rx_buffer,
296 config,
297 )?;
298
299 Ok(Self { tx, rx })
300 }
301
302 /// Create a new buffered LPUART with only CTS flow control (TX flow control)
303 pub fn new_with_cts<T: Instance>(
304 inner: Peri<'a, T>,
305 tx_pin: Peri<'a, impl TxPin<T>>,
306 rx_pin: Peri<'a, impl RxPin<T>>,
307 cts_pin: Peri<'a, impl CtsPin<T>>,
308 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
309 tx_buffer: &'a mut [u8],
310 rx_buffer: &'a mut [u8],
311 config: Config,
312 ) -> Result<Self> {
313 tx_pin.as_tx();
314 rx_pin.as_rx();
315 cts_pin.as_cts();
316
317 let (tx, rx) = Self::new_inner::<T>(
318 inner,
319 tx_pin.into(),
320 rx_pin.into(),
321 None,
322 Some(cts_pin.into()),
323 tx_buffer,
324 rx_buffer,
325 config,
326 )?;
327
328 Ok(Self { tx, rx })
329 }
330
331 /// Split the buffered LPUART into separate TX and RX parts
332 pub fn split(self) -> (BufferedLpuartTx<'a>, BufferedLpuartRx<'a>) {
333 (self.tx, self.rx)
334 }
335
336 /// Get mutable references to TX and RX parts
337 pub fn split_ref(&mut self) -> (&mut BufferedLpuartTx<'a>, &mut BufferedLpuartRx<'a>) {
338 (&mut self.tx, &mut self.rx)
339 }
340}
341
342// ============================================================================
343// BUFFERED TX IMPLEMENTATION
344// ============================================================================
345
346impl<'a> BufferedLpuartTx<'a> {
347 /// Helper for TX-only initialization
348 fn new_inner<T: Instance>(
349 inner: Peri<'a, T>,
350 tx_pin: Peri<'a, AnyPin>,
351 cts_pin: Option<Peri<'a, AnyPin>>,
352 tx_buffer: &'a mut [u8],
353 config: Config,
354 ) -> Result<BufferedLpuartTx<'a>> {
355 let state = BufferedLpuart::init_common::<T>(
356 &inner,
357 Some(tx_buffer),
358 None,
359 &config,
360 true,
361 false,
362 false,
363 cts_pin.is_some(),
364 )?;
365
366 Ok(BufferedLpuartTx {
367 info: T::info(),
368 state,
369 _tx_pin: tx_pin,
370 _cts_pin: cts_pin,
371 })
372 }
373
374 pub fn new<T: Instance>(
375 inner: Peri<'a, T>,
376 tx_pin: Peri<'a, impl TxPin<T>>,
377 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
378 tx_buffer: &'a mut [u8],
379 config: Config,
380 ) -> Result<Self> {
381 tx_pin.as_tx();
382
383 Self::new_inner::<T>(inner, tx_pin.into(), None, tx_buffer, config)
384 }
385
386 /// Create a new TX-only buffered LPUART with CTS flow control
387 pub fn new_with_cts<T: Instance>(
388 inner: Peri<'a, T>,
389 tx_pin: Peri<'a, impl TxPin<T>>,
390 cts_pin: Peri<'a, impl CtsPin<T>>,
391 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
392 tx_buffer: &'a mut [u8],
393 config: Config,
394 ) -> Result<Self> {
395 tx_pin.as_tx();
396 cts_pin.as_cts();
397
398 Self::new_inner::<T>(inner, tx_pin.into(), Some(cts_pin.into()), tx_buffer, config)
399 }
400}
401
402impl<'a> BufferedLpuartTx<'a> {
403 /// Write data asynchronously
404 pub async fn write(&mut self, buf: &[u8]) -> Result<usize> {
405 let mut written = 0;
406
407 for &byte in buf {
408 // Wait for space in the buffer
409 poll_fn(|cx| {
410 self.state.tx_waker.register(cx.waker());
411
412 let mut writer = unsafe { self.state.tx_buf.writer() };
413 if writer.push_one(byte) {
414 // Enable TX interrupt to start transmission
415 cortex_m::interrupt::free(|_| {
416 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
417 });
418 Poll::Ready(Ok(()))
419 } else {
420 Poll::Pending
421 }
422 })
423 .await?;
424
425 written += 1;
426 }
427
428 Ok(written)
429 }
430
431 /// Flush the TX buffer and wait for transmission to complete
432 pub async fn flush(&mut self) -> Result<()> {
433 // Wait for TX buffer to empty and transmission to complete
434 poll_fn(|cx| {
435 self.state.tx_waker.register(cx.waker());
436
437 let tx_empty = self.state.tx_buf.is_empty();
438 let fifo_empty = self.info.regs.water().read().txcount().bits() == 0;
439 let tc_complete = self.info.regs.stat().read().tc().is_complete();
440
441 if tx_empty && fifo_empty && tc_complete {
442 Poll::Ready(Ok(()))
443 } else {
444 // Enable appropriate interrupt
445 cortex_m::interrupt::free(|_| {
446 if !tx_empty {
447 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
448 } else {
449 self.info.regs.ctrl().modify(|_, w| w.tcie().enabled());
450 }
451 });
452 Poll::Pending
453 }
454 })
455 .await
456 }
457
458 /// Try to write without blocking
459 pub fn try_write(&mut self, buf: &[u8]) -> Result<usize> {
460 let mut writer = unsafe { self.state.tx_buf.writer() };
461 let mut written = 0;
462
463 for &byte in buf {
464 if writer.push_one(byte) {
465 written += 1;
466 } else {
467 break;
468 }
469 }
470
471 if written > 0 {
472 // Enable TX interrupt to start transmission
473 cortex_m::interrupt::free(|_| {
474 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
475 });
476 }
477
478 Ok(written)
479 }
480}
481
482// ============================================================================
483// BUFFERED RX IMPLEMENTATION
484// ============================================================================
485
486impl<'a> BufferedLpuartRx<'a> {
487 /// Helper for RX-only initialization
488 fn new_inner<T: Instance>(
489 inner: Peri<'a, T>,
490 rx_pin: Peri<'a, AnyPin>,
491 rts_pin: Option<Peri<'a, AnyPin>>,
492 rx_buffer: &'a mut [u8],
493 config: Config,
494 ) -> Result<BufferedLpuartRx<'a>> {
495 let state = BufferedLpuart::init_common::<T>(
496 &inner,
497 None,
498 Some(rx_buffer),
499 &config,
500 false,
501 true,
502 rts_pin.is_some(),
503 false,
504 )?;
505
506 Ok(BufferedLpuartRx {
507 info: T::info(),
508 state,
509 _rx_pin: rx_pin,
510 _rts_pin: rts_pin,
511 })
512 }
513
514 /// Create a new RX-only buffered LPUART
515 pub fn new<T: Instance>(
516 inner: Peri<'a, T>,
517 rx_pin: Peri<'a, impl RxPin<T>>,
518 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
519 rx_buffer: &'a mut [u8],
520 config: Config,
521 ) -> Result<Self> {
522 rx_pin.as_rx();
523
524 Self::new_inner::<T>(inner, rx_pin.into(), None, rx_buffer, config)
525 }
526
527 /// Create a new RX-only buffered LPUART with RTS flow control
528 pub fn new_with_rts<T: Instance>(
529 inner: Peri<'a, T>,
530 rx_pin: Peri<'a, impl RxPin<T>>,
531 rts_pin: Peri<'a, impl RtsPin<T>>,
532 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
533 rx_buffer: &'a mut [u8],
534 config: Config,
535 ) -> Result<Self> {
536 rx_pin.as_rx();
537 rts_pin.as_rts();
538
539 Self::new_inner::<T>(inner, rx_pin.into(), Some(rts_pin.into()), rx_buffer, config)
540 }
541}
542
543impl<'a> BufferedLpuartRx<'a> {
544 /// Read data asynchronously
545 pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
546 if buf.is_empty() {
547 return Ok(0);
548 }
549
550 let mut read = 0;
551
552 // Try to read available data
553 poll_fn(|cx| {
554 self.state.rx_waker.register(cx.waker());
555
556 // Disable RX interrupt while reading from buffer
557 cortex_m::interrupt::free(|_| {
558 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
559 });
560
561 let mut reader = unsafe { self.state.rx_buf.reader() };
562 let available = reader.pop(|data| {
563 let to_copy = core::cmp::min(data.len(), buf.len() - read);
564 if to_copy > 0 {
565 buf[read..read + to_copy].copy_from_slice(&data[..to_copy]);
566 read += to_copy;
567 }
568 to_copy
569 });
570
571 // Re-enable RX interrupt
572 cortex_m::interrupt::free(|_| {
573 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
574 });
575
576 if read > 0 {
577 Poll::Ready(Ok(read))
578 } else if available == 0 {
579 Poll::Pending
580 } else {
581 Poll::Ready(Ok(0))
582 }
583 })
584 .await
585 }
586
587 /// Try to read without blocking
588 pub fn try_read(&mut self, buf: &mut [u8]) -> Result<usize> {
589 if buf.is_empty() {
590 return Ok(0);
591 }
592
593 // Disable RX interrupt while reading from buffer
594 cortex_m::interrupt::free(|_| {
595 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
596 });
597
598 let mut reader = unsafe { self.state.rx_buf.reader() };
599 let read = reader.pop(|data| {
600 let to_copy = core::cmp::min(data.len(), buf.len());
601 if to_copy > 0 {
602 buf[..to_copy].copy_from_slice(&data[..to_copy]);
603 }
604 to_copy
605 });
606
607 // Re-enable RX interrupt
608 cortex_m::interrupt::free(|_| {
609 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
610 });
611
612 Ok(read)
613 }
614}
615
616// ============================================================================
617// INTERRUPT HANDLER
618// ============================================================================
619
620/// Buffered UART interrupt handler
621pub struct BufferedInterruptHandler<T: Instance> {
622 _phantom: PhantomData<T>,
623}
624
625impl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> {
626 unsafe fn on_interrupt() {
627 let regs = T::info().regs;
628 let state = T::buffered_state();
629
630 // Check if this instance is initialized
631 if !state.initialized.load(Ordering::Relaxed) {
632 return;
633 }
634
635 let ctrl = regs.ctrl().read();
636 let stat = regs.stat().read();
637 let has_fifo = regs.param().read().rxfifo().bits() > 0;
638
639 // Handle overrun error
640 if stat.or().is_overrun() {
641 regs.stat().write(|w| w.or().clear_bit_by_one());
642 state.rx_waker.wake();
643 return;
644 }
645
646 // Clear other error flags
647 if stat.pf().is_parity() {
648 regs.stat().write(|w| w.pf().clear_bit_by_one());
649 }
650 if stat.fe().is_error() {
651 regs.stat().write(|w| w.fe().clear_bit_by_one());
652 }
653 if stat.nf().is_noise() {
654 regs.stat().write(|w| w.nf().clear_bit_by_one());
655 }
656
657 // Handle RX data
658 if ctrl.rie().is_enabled() && (has_data(regs) || stat.idle().is_idle()) {
659 let mut pushed_any = false;
660 let mut writer = state.rx_buf.writer();
661
662 if has_fifo {
663 // Read from FIFO
664 while regs.water().read().rxcount().bits() > 0 {
665 let byte = (regs.data().read().bits() & 0xFF) as u8;
666 if writer.push_one(byte) {
667 pushed_any = true;
668 } else {
669 // Buffer full, stop reading
670 break;
671 }
672 }
673 } else {
674 // Read single byte
675 if regs.stat().read().rdrf().is_rxdata() {
676 let byte = (regs.data().read().bits() & 0xFF) as u8;
677 if writer.push_one(byte) {
678 pushed_any = true;
679 }
680 }
681 }
682
683 if pushed_any {
684 state.rx_waker.wake();
685 }
686
687 // Clear idle flag if set
688 if stat.idle().is_idle() {
689 regs.stat().write(|w| w.idle().clear_bit_by_one());
690 }
691 }
692
693 // Handle TX data
694 if ctrl.tie().is_enabled() {
695 let mut sent_any = false;
696 let mut reader = state.tx_buf.reader();
697
698 // Send data while TX buffer is ready and we have data
699 while regs.stat().read().tdre().is_no_txdata() {
700 if let Some(byte) = reader.pop_one() {
701 regs.data().write(|w| w.bits(u32::from(byte)));
702 sent_any = true;
703 } else {
704 // No more data to send
705 break;
706 }
707 }
708
709 if sent_any {
710 state.tx_waker.wake();
711 }
712
713 // If buffer is empty, switch to TC interrupt or disable
714 if state.tx_buf.is_empty() {
715 cortex_m::interrupt::free(|_| {
716 regs.ctrl().modify(|_, w| w.tie().disabled().tcie().enabled());
717 });
718 }
719 }
720
721 // Handle transmission complete
722 if ctrl.tcie().is_enabled() && regs.stat().read().tc().is_complete() {
723 state.tx_done.store(true, Ordering::Release);
724 state.tx_waker.wake();
725
726 // Disable TC interrupt
727 cortex_m::interrupt::free(|_| {
728 regs.ctrl().modify(|_, w| w.tcie().disabled());
729 });
730 }
731 }
732}
733
734// ============================================================================
735// EMBEDDED-IO ASYNC TRAIT IMPLEMENTATIONS
736// ============================================================================
737
738impl embedded_io_async::ErrorType for BufferedLpuartTx<'_> {
739 type Error = Error;
740}
741
742impl embedded_io_async::ErrorType for BufferedLpuartRx<'_> {
743 type Error = Error;
744}
745
746impl embedded_io_async::ErrorType for BufferedLpuart<'_> {
747 type Error = Error;
748}
749
750impl embedded_io_async::Write for BufferedLpuartTx<'_> {
751 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
752 self.write(buf).await
753 }
754
755 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
756 self.flush().await
757 }
758}
759
760impl embedded_io_async::Read for BufferedLpuartRx<'_> {
761 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
762 self.read(buf).await
763 }
764}
765
766impl embedded_io_async::Write for BufferedLpuart<'_> {
767 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
768 self.tx.write(buf).await
769 }
770
771 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
772 self.tx.flush().await
773 }
774}
775
776impl embedded_io_async::Read for BufferedLpuart<'_> {
777 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
778 self.rx.read(buf).await
779 }
780}
diff --git a/embassy-mcxa/src/lpuart/mod.rs b/embassy-mcxa/src/lpuart/mod.rs
new file mode 100644
index 000000000..2d8308ec8
--- /dev/null
+++ b/embassy-mcxa/src/lpuart/mod.rs
@@ -0,0 +1,1292 @@
1use core::marker::PhantomData;
2
3use embassy_hal_internal::{Peri, PeripheralType};
4use paste::paste;
5
6use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig};
7use crate::clocks::{enable_and_reset, ClockError, Gate, PoweredClock};
8use crate::gpio::SealedPin;
9use crate::pac::lpuart0::baud::Sbns as StopBits;
10use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, Pt as Parity, M as DataBits};
11use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource};
12use crate::pac::lpuart0::stat::Msbf as MsbFirst;
13use crate::{interrupt, pac, AnyPin};
14
15pub mod buffered;
16
17// ============================================================================
18// STUB IMPLEMENTATION
19// ============================================================================
20
21// Stub implementation for LIB (Peripherals), GPIO, DMA and CLOCK until stable API
22// Pin and Clock initialization is currently done at the examples level.
23
24// --- START DMA ---
25mod dma {
26 pub struct Channel<'d> {
27 pub(super) _lifetime: core::marker::PhantomData<&'d ()>,
28 }
29}
30
31use dma::Channel;
32
33// --- END DMA ---
34
35// ============================================================================
36// MISC
37// ============================================================================
38
39mod sealed {
40 /// Simply seal a trait to prevent external implementations
41 pub trait Sealed {}
42}
43
44// ============================================================================
45// INSTANCE TRAIT
46// ============================================================================
47
48pub type Regs = &'static crate::pac::lpuart0::RegisterBlock;
49
50pub trait SealedInstance {
51 fn info() -> Info;
52 fn index() -> usize;
53 fn buffered_state() -> &'static buffered::State;
54}
55
56pub struct Info {
57 pub regs: Regs,
58}
59
60/// Trait for LPUART peripheral instances
61#[allow(private_bounds)]
62pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = LpuartConfig> {
63 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance;
64 type Interrupt: interrupt::typelevel::Interrupt;
65}
66
67macro_rules! impl_instance {
68 ($($n:expr),*) => {
69 $(
70 paste!{
71 impl SealedInstance for crate::peripherals::[<LPUART $n>] {
72 fn info() -> Info {
73 Info {
74 regs: unsafe { &*pac::[<Lpuart $n>]::ptr() },
75 }
76 }
77
78 #[inline]
79 fn index() -> usize {
80 $n
81 }
82
83 fn buffered_state() -> &'static buffered::State {
84 static BUFFERED_STATE: buffered::State = buffered::State::new();
85 &BUFFERED_STATE
86 }
87 }
88
89 impl Instance for crate::peripherals::[<LPUART $n>] {
90 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance
91 = crate::clocks::periph_helpers::LpuartInstance::[<Lpuart $n>];
92 type Interrupt = crate::interrupt::typelevel::[<LPUART $n>];
93 }
94 }
95 )*
96 };
97}
98
99impl_instance!(0, 1, 2, 3, 4, 5);
100
101// ============================================================================
102// INSTANCE HELPER FUNCTIONS
103// ============================================================================
104
105/// Perform software reset on the LPUART peripheral
106pub fn perform_software_reset(regs: Regs) {
107 // Software reset - set and clear RST bit (Global register)
108 regs.global().write(|w| w.rst().reset());
109 regs.global().write(|w| w.rst().no_effect());
110}
111
112/// Disable both transmitter and receiver
113pub fn disable_transceiver(regs: Regs) {
114 regs.ctrl().modify(|_, w| w.te().disabled().re().disabled());
115}
116
117/// Calculate and configure baudrate settings
118pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock_freq: u32) -> Result<()> {
119 let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?;
120
121 // Configure BAUD register
122 regs.baud().modify(|_, w| unsafe {
123 // Clear and set OSR
124 w.osr().bits(osr - 1);
125 // Clear and set SBR
126 w.sbr().bits(sbr);
127 // Set BOTHEDGE if OSR is between 4 and 7
128 if osr > 3 && osr < 8 {
129 w.bothedge().enabled()
130 } else {
131 w.bothedge().disabled()
132 }
133 });
134
135 Ok(())
136}
137
138/// Configure frame format (stop bits, data bits)
139pub fn configure_frame_format(regs: Regs, config: &Config) {
140 // Configure stop bits
141 regs.baud().modify(|_, w| w.sbns().variant(config.stop_bits_count));
142
143 // Clear M10 for now (10-bit mode)
144 regs.baud().modify(|_, w| w.m10().disabled());
145}
146
147/// Configure control settings (parity, data bits, idle config, pin swap)
148pub fn configure_control_settings(regs: Regs, config: &Config) {
149 regs.ctrl().modify(|_, w| {
150 // Parity configuration
151 let mut w = if let Some(parity) = config.parity_mode {
152 w.pe().enabled().pt().variant(parity)
153 } else {
154 w.pe().disabled()
155 };
156
157 // Data bits configuration
158 w = match config.data_bits_count {
159 DataBits::Data8 => {
160 if config.parity_mode.is_some() {
161 w.m().data9() // 8 data + 1 parity = 9 bits
162 } else {
163 w.m().data8() // 8 data bits only
164 }
165 }
166 DataBits::Data9 => w.m().data9(),
167 };
168
169 // Idle configuration
170 w = w.idlecfg().variant(config.rx_idle_config);
171 w = w.ilt().variant(config.rx_idle_type);
172
173 // Swap TXD/RXD if configured
174 if config.swap_txd_rxd {
175 w.swap().swap()
176 } else {
177 w.swap().standard()
178 }
179 });
180}
181
182/// Configure FIFO settings and watermarks
183pub fn configure_fifo(regs: Regs, config: &Config) {
184 // Configure WATER register for FIFO watermarks
185 regs.water().write(|w| unsafe {
186 w.rxwater()
187 .bits(config.rx_fifo_watermark)
188 .txwater()
189 .bits(config.tx_fifo_watermark)
190 });
191
192 // Enable TX/RX FIFOs
193 regs.fifo().modify(|_, w| w.txfe().enabled().rxfe().enabled());
194
195 // Flush FIFOs
196 regs.fifo()
197 .modify(|_, w| w.txflush().txfifo_rst().rxflush().rxfifo_rst());
198}
199
200/// Clear all status flags
201pub fn clear_all_status_flags(regs: Regs) {
202 regs.stat().reset();
203}
204
205/// Configure hardware flow control if enabled
206pub fn configure_flow_control(regs: Regs, enable_tx_cts: bool, enable_rx_rts: bool, config: &Config) {
207 if enable_rx_rts || enable_tx_cts {
208 regs.modir().modify(|_, w| {
209 let mut w = w;
210
211 // Configure TX CTS
212 w = w.txctsc().variant(config.tx_cts_config);
213 w = w.txctssrc().variant(config.tx_cts_source);
214
215 if enable_rx_rts {
216 w = w.rxrtse().enabled();
217 } else {
218 w = w.rxrtse().disabled();
219 }
220
221 if enable_tx_cts {
222 w = w.txctse().enabled();
223 } else {
224 w = w.txctse().disabled();
225 }
226
227 w
228 });
229 }
230}
231
232/// Configure bit order (MSB first or LSB first)
233pub fn configure_bit_order(regs: Regs, msb_first: MsbFirst) {
234 regs.stat().modify(|_, w| w.msbf().variant(msb_first));
235}
236
237/// Enable transmitter and/or receiver based on configuration
238pub fn enable_transceiver(regs: Regs, enable_tx: bool, enable_rx: bool) {
239 regs.ctrl().modify(|_, w| {
240 let mut w = w;
241 if enable_tx {
242 w = w.te().enabled();
243 }
244 if enable_rx {
245 w = w.re().enabled();
246 }
247 w
248 });
249}
250
251pub fn calculate_baudrate(baudrate: u32, src_clock_hz: u32) -> Result<(u8, u16)> {
252 let mut baud_diff = baudrate;
253 let mut osr = 0u8;
254 let mut sbr = 0u16;
255
256 // Try OSR values from 4 to 32
257 for osr_temp in 4u8..=32u8 {
258 // Calculate SBR: (srcClock_Hz * 2 / (baudRate * osr) + 1) / 2
259 let sbr_calc = ((src_clock_hz * 2) / (baudrate * osr_temp as u32)).div_ceil(2);
260
261 let sbr_temp = if sbr_calc == 0 {
262 1
263 } else if sbr_calc > 0x1FFF {
264 0x1FFF
265 } else {
266 sbr_calc as u16
267 };
268
269 // Calculate actual baud rate
270 let calculated_baud = src_clock_hz / (osr_temp as u32 * sbr_temp as u32);
271
272 let temp_diff = calculated_baud.abs_diff(baudrate);
273
274 if temp_diff <= baud_diff {
275 baud_diff = temp_diff;
276 osr = osr_temp;
277 sbr = sbr_temp;
278 }
279 }
280
281 // Check if baud rate difference is within 3%
282 if baud_diff > (baudrate / 100) * 3 {
283 return Err(Error::UnsupportedBaudrate);
284 }
285
286 Ok((osr, sbr))
287}
288
289/// Wait for all transmit operations to complete
290pub fn wait_for_tx_complete(regs: Regs) {
291 // Wait for TX FIFO to empty
292 while regs.water().read().txcount().bits() != 0 {
293 // Wait for TX FIFO to drain
294 }
295
296 // Wait for last character to shift out (TC = Transmission Complete)
297 while regs.stat().read().tc().is_active() {
298 // Wait for transmission to complete
299 }
300}
301
302pub fn check_and_clear_rx_errors(regs: Regs) -> Result<()> {
303 let stat = regs.stat().read();
304 let mut status = Ok(());
305
306 // Check for overrun first - other error flags are prevented when OR is set
307 if stat.or().is_overrun() {
308 regs.stat().write(|w| w.or().clear_bit_by_one());
309
310 return Err(Error::Overrun);
311 }
312
313 if stat.pf().is_parity() {
314 regs.stat().write(|w| w.pf().clear_bit_by_one());
315 status = Err(Error::Parity);
316 }
317
318 if stat.fe().is_error() {
319 regs.stat().write(|w| w.fe().clear_bit_by_one());
320 status = Err(Error::Framing);
321 }
322
323 if stat.nf().is_noise() {
324 regs.stat().write(|w| w.nf().clear_bit_by_one());
325 status = Err(Error::Noise);
326 }
327
328 status
329}
330
331pub fn has_data(regs: Regs) -> bool {
332 if regs.param().read().rxfifo().bits() > 0 {
333 // FIFO is available - check RXCOUNT in WATER register
334 regs.water().read().rxcount().bits() > 0
335 } else {
336 // No FIFO - check RDRF flag in STAT register
337 regs.stat().read().rdrf().is_rxdata()
338 }
339}
340
341// ============================================================================
342// PIN TRAITS FOR LPUART FUNCTIONALITY
343// ============================================================================
344
345impl<T: SealedPin> sealed::Sealed for T {}
346
347/// io configuration trait for Lpuart Tx configuration
348pub trait TxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
349 /// convert the pin to appropriate function for Lpuart Tx usage
350 fn as_tx(&self);
351}
352
353/// io configuration trait for Lpuart Rx configuration
354pub trait RxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
355 /// convert the pin to appropriate function for Lpuart Rx usage
356 fn as_rx(&self);
357}
358
359/// io configuration trait for Lpuart Cts
360pub trait CtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
361 /// convert the pin to appropriate function for Lpuart Cts usage
362 fn as_cts(&self);
363}
364
365/// io configuration trait for Lpuart Rts
366pub trait RtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {
367 /// convert the pin to appropriate function for Lpuart Rts usage
368 fn as_rts(&self);
369}
370
371macro_rules! impl_tx_pin {
372 ($inst:ident, $pin:ident, $alt:ident) => {
373 impl TxPin<crate::peripherals::$inst> for crate::peripherals::$pin {
374 fn as_tx(&self) {
375 // TODO: Check these are right
376 self.set_pull(crate::gpio::Pull::Up);
377 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
378 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
379 self.set_function(crate::pac::port0::pcr0::Mux::$alt);
380 self.set_enable_input_buffer();
381 }
382 }
383 };
384}
385
386macro_rules! impl_rx_pin {
387 ($inst:ident, $pin:ident, $alt:ident) => {
388 impl RxPin<crate::peripherals::$inst> for crate::peripherals::$pin {
389 fn as_rx(&self) {
390 // TODO: Check these are right
391 self.set_pull(crate::gpio::Pull::Up);
392 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
393 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
394 self.set_function(crate::pac::port0::pcr0::Mux::$alt);
395 self.set_enable_input_buffer();
396 }
397 }
398 };
399}
400
401// TODO: Macro and impls for CTS/RTS pins
402macro_rules! impl_cts_pin {
403 ($inst:ident, $pin:ident, $alt:ident) => {
404 impl CtsPin<crate::peripherals::$inst> for crate::peripherals::$pin {
405 fn as_cts(&self) {
406 todo!()
407 }
408 }
409 };
410}
411
412macro_rules! impl_rts_pin {
413 ($inst:ident, $pin:ident, $alt:ident) => {
414 impl RtsPin<crate::peripherals::$inst> for crate::peripherals::$pin {
415 fn as_rts(&self) {
416 todo!()
417 }
418 }
419 };
420}
421
422// LPUART 0
423impl_tx_pin!(LPUART0, P0_3, Mux2);
424impl_tx_pin!(LPUART0, P0_21, Mux3);
425impl_tx_pin!(LPUART0, P2_1, Mux2);
426
427impl_rx_pin!(LPUART0, P0_2, Mux2);
428impl_rx_pin!(LPUART0, P0_20, Mux3);
429impl_rx_pin!(LPUART0, P2_0, Mux2);
430
431impl_cts_pin!(LPUART0, P0_1, Mux2);
432impl_cts_pin!(LPUART0, P0_23, Mux3);
433impl_cts_pin!(LPUART0, P2_3, Mux2);
434
435impl_rts_pin!(LPUART0, P0_0, Mux2);
436impl_rts_pin!(LPUART0, P0_22, Mux3);
437impl_rts_pin!(LPUART0, P2_2, Mux2);
438
439// LPUART 1
440impl_tx_pin!(LPUART1, P1_9, Mux2);
441impl_tx_pin!(LPUART1, P2_13, Mux3);
442impl_tx_pin!(LPUART1, P3_9, Mux3);
443impl_tx_pin!(LPUART1, P3_21, Mux3);
444
445impl_rx_pin!(LPUART1, P1_8, Mux2);
446impl_rx_pin!(LPUART1, P2_12, Mux3);
447impl_rx_pin!(LPUART1, P3_8, Mux3);
448impl_rx_pin!(LPUART1, P3_20, Mux3);
449
450impl_cts_pin!(LPUART1, P1_11, Mux2);
451impl_cts_pin!(LPUART1, P2_17, Mux3);
452impl_cts_pin!(LPUART1, P3_11, Mux3);
453impl_cts_pin!(LPUART1, P3_23, Mux3);
454
455impl_rts_pin!(LPUART1, P1_10, Mux2);
456impl_rts_pin!(LPUART1, P2_15, Mux3);
457impl_rts_pin!(LPUART1, P2_16, Mux3);
458impl_rts_pin!(LPUART1, P3_10, Mux3);
459
460// LPUART 2
461impl_tx_pin!(LPUART2, P1_5, Mux3);
462impl_tx_pin!(LPUART2, P1_13, Mux3);
463impl_tx_pin!(LPUART2, P2_2, Mux3);
464impl_tx_pin!(LPUART2, P2_10, Mux3);
465impl_tx_pin!(LPUART2, P3_15, Mux2);
466
467impl_rx_pin!(LPUART2, P1_4, Mux3);
468impl_rx_pin!(LPUART2, P1_12, Mux3);
469impl_rx_pin!(LPUART2, P2_3, Mux3);
470impl_rx_pin!(LPUART2, P2_11, Mux3);
471impl_rx_pin!(LPUART2, P3_14, Mux2);
472
473impl_cts_pin!(LPUART2, P1_7, Mux3);
474impl_cts_pin!(LPUART2, P1_15, Mux3);
475impl_cts_pin!(LPUART2, P2_4, Mux3);
476impl_cts_pin!(LPUART2, P3_13, Mux2);
477
478impl_rts_pin!(LPUART2, P1_6, Mux3);
479impl_rts_pin!(LPUART2, P1_14, Mux3);
480impl_rts_pin!(LPUART2, P2_5, Mux3);
481impl_rts_pin!(LPUART2, P3_12, Mux2);
482
483// LPUART 3
484impl_tx_pin!(LPUART3, P3_1, Mux3);
485impl_tx_pin!(LPUART3, P3_12, Mux3);
486impl_tx_pin!(LPUART3, P4_5, Mux3);
487
488impl_rx_pin!(LPUART3, P3_0, Mux3);
489impl_rx_pin!(LPUART3, P3_13, Mux3);
490impl_rx_pin!(LPUART3, P4_2, Mux3);
491
492impl_cts_pin!(LPUART3, P3_7, Mux3);
493impl_cts_pin!(LPUART3, P3_14, Mux3);
494impl_cts_pin!(LPUART3, P4_6, Mux3);
495
496impl_rts_pin!(LPUART3, P3_6, Mux3);
497impl_rts_pin!(LPUART3, P3_15, Mux3);
498impl_rts_pin!(LPUART3, P4_7, Mux3);
499
500// LPUART 4
501impl_tx_pin!(LPUART4, P2_7, Mux3);
502impl_tx_pin!(LPUART4, P3_19, Mux2);
503impl_tx_pin!(LPUART4, P3_27, Mux3);
504impl_tx_pin!(LPUART4, P4_3, Mux3);
505
506impl_rx_pin!(LPUART4, P2_6, Mux3);
507impl_rx_pin!(LPUART4, P3_18, Mux2);
508impl_rx_pin!(LPUART4, P3_28, Mux3);
509impl_rx_pin!(LPUART4, P4_4, Mux3);
510
511impl_cts_pin!(LPUART4, P2_0, Mux3);
512impl_cts_pin!(LPUART4, P3_17, Mux2);
513impl_cts_pin!(LPUART4, P3_31, Mux3);
514
515impl_rts_pin!(LPUART4, P2_1, Mux3);
516impl_rts_pin!(LPUART4, P3_16, Mux2);
517impl_rts_pin!(LPUART4, P3_30, Mux3);
518
519// LPUART 5
520impl_tx_pin!(LPUART5, P1_10, Mux8);
521impl_tx_pin!(LPUART5, P1_17, Mux8);
522
523impl_rx_pin!(LPUART5, P1_11, Mux8);
524impl_rx_pin!(LPUART5, P1_16, Mux8);
525
526impl_cts_pin!(LPUART5, P1_12, Mux8);
527impl_cts_pin!(LPUART5, P1_19, Mux8);
528
529impl_rts_pin!(LPUART5, P1_13, Mux8);
530impl_rts_pin!(LPUART5, P1_18, Mux8);
531
532// ============================================================================
533// ERROR TYPES AND RESULTS
534// ============================================================================
535
536/// LPUART error types
537#[derive(Debug, Copy, Clone, Eq, PartialEq)]
538#[cfg_attr(feature = "defmt", derive(defmt::Format))]
539pub enum Error {
540 /// Read error
541 Read,
542 /// Buffer overflow
543 Overrun,
544 /// Noise error
545 Noise,
546 /// Framing error
547 Framing,
548 /// Parity error
549 Parity,
550 /// Failure
551 Fail,
552 /// Invalid argument
553 InvalidArgument,
554 /// Lpuart baud rate cannot be supported with the given clock
555 UnsupportedBaudrate,
556 /// RX FIFO Empty
557 RxFifoEmpty,
558 /// TX FIFO Full
559 TxFifoFull,
560 /// TX Busy
561 TxBusy,
562 /// Clock Error
563 ClockSetup(ClockError),
564}
565
566/// A specialized Result type for LPUART operations
567pub type Result<T> = core::result::Result<T, Error>;
568
569// ============================================================================
570// CONFIGURATION STRUCTURES
571// ============================================================================
572
573/// Lpuart config
574#[derive(Debug, Clone, Copy)]
575pub struct Config {
576 /// Power state required for this peripheral
577 pub power: PoweredClock,
578 /// Clock source
579 pub source: LpuartClockSel,
580 /// Clock divisor
581 pub div: Div4,
582 /// Baud rate in bits per second
583 pub baudrate_bps: u32,
584 /// Parity configuration
585 pub parity_mode: Option<Parity>,
586 /// Number of data bits
587 pub data_bits_count: DataBits,
588 /// MSB First or LSB First configuration
589 pub msb_first: MsbFirst,
590 /// Number of stop bits
591 pub stop_bits_count: StopBits,
592 /// TX FIFO watermark
593 pub tx_fifo_watermark: u8,
594 /// RX FIFO watermark
595 pub rx_fifo_watermark: u8,
596 /// TX CTS source
597 pub tx_cts_source: TxCtsSource,
598 /// TX CTS configure
599 pub tx_cts_config: TxCtsConfig,
600 /// RX IDLE type
601 pub rx_idle_type: IdleType,
602 /// RX IDLE configuration
603 pub rx_idle_config: IdleConfig,
604 /// Swap TXD and RXD pins
605 pub swap_txd_rxd: bool,
606}
607
608impl Default for Config {
609 fn default() -> Self {
610 Self {
611 baudrate_bps: 115_200u32,
612 parity_mode: None,
613 data_bits_count: DataBits::Data8,
614 msb_first: MsbFirst::LsbFirst,
615 stop_bits_count: StopBits::One,
616 tx_fifo_watermark: 0,
617 rx_fifo_watermark: 1,
618 tx_cts_source: TxCtsSource::Cts,
619 tx_cts_config: TxCtsConfig::Start,
620 rx_idle_type: IdleType::FromStart,
621 rx_idle_config: IdleConfig::Idle1,
622 swap_txd_rxd: false,
623 power: PoweredClock::NormalEnabledDeepSleepDisabled,
624 source: LpuartClockSel::FroLfDiv,
625 div: Div4::no_div(),
626 }
627 }
628}
629
630/// LPUART status flags
631#[derive(Debug, Clone, Copy)]
632#[cfg_attr(feature = "defmt", derive(defmt::Format))]
633pub struct Status {
634 /// Transmit data register empty
635 pub tx_empty: bool,
636 /// Transmission complete
637 pub tx_complete: bool,
638 /// Receive data register full
639 pub rx_full: bool,
640 /// Idle line detected
641 pub idle: bool,
642 /// Receiver overrun
643 pub overrun: bool,
644 /// Noise error
645 pub noise: bool,
646 /// Framing error
647 pub framing: bool,
648 /// Parity error
649 pub parity: bool,
650}
651
652// ============================================================================
653// MODE TRAITS (BLOCKING/ASYNC)
654// ============================================================================
655
656/// Driver move trait.
657#[allow(private_bounds)]
658pub trait Mode: sealed::Sealed {}
659
660/// Blocking mode.
661pub struct Blocking;
662impl sealed::Sealed for Blocking {}
663impl Mode for Blocking {}
664
665/// Async mode.
666pub struct Async;
667impl sealed::Sealed for Async {}
668impl Mode for Async {}
669
670// ============================================================================
671// CORE DRIVER STRUCTURES
672// ============================================================================
673
674/// Lpuart driver.
675pub struct Lpuart<'a, M: Mode> {
676 info: Info,
677 tx: LpuartTx<'a, M>,
678 rx: LpuartRx<'a, M>,
679}
680
681/// Lpuart TX driver.
682pub struct LpuartTx<'a, M: Mode> {
683 info: Info,
684 _tx_pin: Peri<'a, AnyPin>,
685 _cts_pin: Option<Peri<'a, AnyPin>>,
686 _tx_dma: Option<Channel<'a>>,
687 mode: PhantomData<(&'a (), M)>,
688}
689
690/// Lpuart Rx driver.
691pub struct LpuartRx<'a, M: Mode> {
692 info: Info,
693 _rx_pin: Peri<'a, AnyPin>,
694 _rts_pin: Option<Peri<'a, AnyPin>>,
695 _rx_dma: Option<Channel<'a>>,
696 mode: PhantomData<(&'a (), M)>,
697}
698
699// ============================================================================
700// LPUART CORE IMPLEMENTATION
701// ============================================================================
702
703impl<'a, M: Mode> Lpuart<'a, M> {
704 fn init<T: Instance>(
705 enable_tx: bool,
706 enable_rx: bool,
707 enable_tx_cts: bool,
708 enable_rx_rts: bool,
709 config: Config,
710 ) -> Result<()> {
711 let regs = T::info().regs;
712
713 // Enable clocks
714 let conf = LpuartConfig {
715 power: config.power,
716 source: config.source,
717 div: config.div,
718 instance: T::CLOCK_INSTANCE,
719 };
720 let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
721
722 // Perform initialization sequence
723 perform_software_reset(regs);
724 disable_transceiver(regs);
725 configure_baudrate(regs, config.baudrate_bps, clock_freq)?;
726 configure_frame_format(regs, &config);
727 configure_control_settings(regs, &config);
728 configure_fifo(regs, &config);
729 clear_all_status_flags(regs);
730 configure_flow_control(regs, enable_tx_cts, enable_rx_rts, &config);
731 configure_bit_order(regs, config.msb_first);
732 enable_transceiver(regs, enable_rx, enable_tx);
733
734 Ok(())
735 }
736
737 /// Deinitialize the LPUART peripheral
738 pub fn deinit(&self) -> Result<()> {
739 let regs = self.info.regs;
740
741 // Wait for TX operations to complete
742 wait_for_tx_complete(regs);
743
744 // Clear all status flags
745 clear_all_status_flags(regs);
746
747 // Disable the module - clear all CTRL register bits
748 regs.ctrl().reset();
749
750 Ok(())
751 }
752
753 /// Split the Lpuart into a transmitter and receiver
754 pub fn split(self) -> (LpuartTx<'a, M>, LpuartRx<'a, M>) {
755 (self.tx, self.rx)
756 }
757
758 /// Split the Lpuart into a transmitter and receiver by mutable reference
759 pub fn split_ref(&mut self) -> (&mut LpuartTx<'a, M>, &mut LpuartRx<'a, M>) {
760 (&mut self.tx, &mut self.rx)
761 }
762}
763
764// ============================================================================
765// BLOCKING MODE IMPLEMENTATIONS
766// ============================================================================
767
768impl<'a> Lpuart<'a, Blocking> {
769 /// Create a new blocking LPUART instance with RX/TX pins.
770 pub fn new_blocking<T: Instance>(
771 _inner: Peri<'a, T>,
772 tx_pin: Peri<'a, impl TxPin<T>>,
773 rx_pin: Peri<'a, impl RxPin<T>>,
774 config: Config,
775 ) -> Result<Self> {
776 // Configure the pins for LPUART usage
777 tx_pin.as_tx();
778 rx_pin.as_rx();
779
780 // Initialize the peripheral
781 Self::init::<T>(true, true, false, false, config)?;
782
783 Ok(Self {
784 info: T::info(),
785 tx: LpuartTx::new_inner(T::info(), tx_pin.into(), None, None),
786 rx: LpuartRx::new_inner(T::info(), rx_pin.into(), None, None),
787 })
788 }
789
790 /// Create a new blocking LPUART instance with RX, TX and RTS/CTS flow control pins
791 pub fn new_blocking_with_rtscts<T: Instance>(
792 _inner: Peri<'a, T>,
793 tx_pin: Peri<'a, impl TxPin<T>>,
794 rx_pin: Peri<'a, impl RxPin<T>>,
795 cts_pin: Peri<'a, impl CtsPin<T>>,
796 rts_pin: Peri<'a, impl RtsPin<T>>,
797 config: Config,
798 ) -> Result<Self> {
799 // Configure the pins for LPUART usage
800 rx_pin.as_rx();
801 tx_pin.as_tx();
802 rts_pin.as_rts();
803 cts_pin.as_cts();
804
805 // Initialize the peripheral with flow control
806 Self::init::<T>(true, true, true, true, config)?;
807
808 Ok(Self {
809 info: T::info(),
810 rx: LpuartRx::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into()), None),
811 tx: LpuartTx::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into()), None),
812 })
813 }
814}
815
816// ----------------------------------------------------------------------------
817// Blocking TX Implementation
818// ----------------------------------------------------------------------------
819
820impl<'a, M: Mode> LpuartTx<'a, M> {
821 fn new_inner(
822 info: Info,
823 tx_pin: Peri<'a, AnyPin>,
824 cts_pin: Option<Peri<'a, AnyPin>>,
825 tx_dma: Option<Channel<'a>>,
826 ) -> Self {
827 Self {
828 info,
829 _tx_pin: tx_pin,
830 _cts_pin: cts_pin,
831 _tx_dma: tx_dma,
832 mode: PhantomData,
833 }
834 }
835}
836
837impl<'a> LpuartTx<'a, Blocking> {
838 /// Create a new blocking LPUART transmitter instance
839 pub fn new_blocking<T: Instance>(
840 _inner: Peri<'a, T>,
841 tx_pin: Peri<'a, impl TxPin<T>>,
842 config: Config,
843 ) -> Result<Self> {
844 // Configure the pins for LPUART usage
845 tx_pin.as_tx();
846
847 // Initialize the peripheral
848 Lpuart::<Blocking>::init::<T>(true, false, false, false, config)?;
849
850 Ok(Self::new_inner(T::info(), tx_pin.into(), None, None))
851 }
852
853 /// Create a new blocking LPUART transmitter instance with CTS flow control
854 pub fn new_blocking_with_cts<T: Instance>(
855 _inner: Peri<'a, T>,
856 tx_pin: Peri<'a, impl TxPin<T>>,
857 cts_pin: Peri<'a, impl CtsPin<T>>,
858 config: Config,
859 ) -> Result<Self> {
860 tx_pin.as_tx();
861 cts_pin.as_cts();
862
863 Lpuart::<Blocking>::init::<T>(true, false, true, false, config)?;
864
865 Ok(Self::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into()), None))
866 }
867
868 fn write_byte_internal(&mut self, byte: u8) -> Result<()> {
869 self.info.regs.data().modify(|_, w| unsafe { w.bits(u32::from(byte)) });
870
871 Ok(())
872 }
873
874 fn blocking_write_byte(&mut self, byte: u8) -> Result<()> {
875 while self.info.regs.stat().read().tdre().is_txdata() {}
876 self.write_byte_internal(byte)
877 }
878
879 fn write_byte(&mut self, byte: u8) -> Result<()> {
880 if self.info.regs.stat().read().tdre().is_txdata() {
881 Err(Error::TxFifoFull)
882 } else {
883 self.write_byte_internal(byte)
884 }
885 }
886
887 /// Write data to LPUART TX blocking execution until all data is sent.
888 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
889 for x in buf {
890 self.blocking_write_byte(*x)?;
891 }
892
893 Ok(())
894 }
895
896 pub fn write_str_blocking(&mut self, buf: &str) {
897 let _ = self.blocking_write(buf.as_bytes());
898 }
899
900 /// Write data to LPUART TX without blocking.
901 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
902 for x in buf {
903 self.write_byte(*x)?;
904 }
905
906 Ok(())
907 }
908
909 /// Flush LPUART TX blocking execution until all data has been transmitted.
910 pub fn blocking_flush(&mut self) -> Result<()> {
911 while self.info.regs.water().read().txcount().bits() != 0 {
912 // Wait for TX FIFO to drain
913 }
914
915 // Wait for last character to shift out
916 while self.info.regs.stat().read().tc().is_active() {
917 // Wait for transmission to complete
918 }
919
920 Ok(())
921 }
922
923 /// Flush LPUART TX.
924 pub fn flush(&mut self) -> Result<()> {
925 // Check if TX FIFO is empty
926 if self.info.regs.water().read().txcount().bits() != 0 {
927 return Err(Error::TxBusy);
928 }
929
930 // Check if transmission is complete
931 if self.info.regs.stat().read().tc().is_active() {
932 return Err(Error::TxBusy);
933 }
934
935 Ok(())
936 }
937}
938
939// ----------------------------------------------------------------------------
940// Blocking RX Implementation
941// ----------------------------------------------------------------------------
942
943impl<'a, M: Mode> LpuartRx<'a, M> {
944 fn new_inner(
945 info: Info,
946 rx_pin: Peri<'a, AnyPin>,
947 rts_pin: Option<Peri<'a, AnyPin>>,
948 rx_dma: Option<Channel<'a>>,
949 ) -> Self {
950 Self {
951 info,
952 _rx_pin: rx_pin,
953 _rts_pin: rts_pin,
954 _rx_dma: rx_dma,
955 mode: PhantomData,
956 }
957 }
958}
959
960impl<'a> LpuartRx<'a, Blocking> {
961 /// Create a new blocking LPUART Receiver instance
962 pub fn new_blocking<T: Instance>(
963 _inner: Peri<'a, T>,
964 rx_pin: Peri<'a, impl RxPin<T>>,
965 config: Config,
966 ) -> Result<Self> {
967 rx_pin.as_rx();
968
969 Lpuart::<Blocking>::init::<T>(false, true, false, false, config)?;
970
971 Ok(Self::new_inner(T::info(), rx_pin.into(), None, None))
972 }
973
974 /// Create a new blocking LPUART Receiver instance with RTS flow control
975 pub fn new_blocking_with_rts<T: Instance>(
976 _inner: Peri<'a, T>,
977 rx_pin: Peri<'a, impl RxPin<T>>,
978 rts_pin: Peri<'a, impl RtsPin<T>>,
979 config: Config,
980 ) -> Result<Self> {
981 rx_pin.as_rx();
982 rts_pin.as_rts();
983
984 Lpuart::<Blocking>::init::<T>(false, true, false, true, config)?;
985
986 Ok(Self::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into()), None))
987 }
988
989 fn read_byte_internal(&mut self) -> Result<u8> {
990 let data = self.info.regs.data().read();
991
992 Ok((data.bits() & 0xFF) as u8)
993 }
994
995 fn read_byte(&mut self) -> Result<u8> {
996 check_and_clear_rx_errors(self.info.regs)?;
997
998 if !has_data(self.info.regs) {
999 return Err(Error::RxFifoEmpty);
1000 }
1001
1002 self.read_byte_internal()
1003 }
1004
1005 fn blocking_read_byte(&mut self) -> Result<u8> {
1006 loop {
1007 if has_data(self.info.regs) {
1008 return self.read_byte_internal();
1009 }
1010
1011 check_and_clear_rx_errors(self.info.regs)?;
1012 }
1013 }
1014
1015 /// Read data from LPUART RX without blocking.
1016 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
1017 for byte in buf.iter_mut() {
1018 *byte = self.read_byte()?;
1019 }
1020 Ok(())
1021 }
1022
1023 /// Read data from LPUART RX blocking execution until the buffer is filled.
1024 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
1025 for byte in buf.iter_mut() {
1026 *byte = self.blocking_read_byte()?;
1027 }
1028 Ok(())
1029 }
1030}
1031
1032impl<'a> Lpuart<'a, Blocking> {
1033 /// Read data from LPUART RX blocking execution until the buffer is filled
1034 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
1035 self.rx.blocking_read(buf)
1036 }
1037
1038 /// Read data from LPUART RX without blocking
1039 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
1040 self.rx.read(buf)
1041 }
1042
1043 /// Write data to LPUART TX blocking execution until all data is sent
1044 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
1045 self.tx.blocking_write(buf)
1046 }
1047
1048 pub fn write_byte(&mut self, byte: u8) -> Result<()> {
1049 self.tx.write_byte(byte)
1050 }
1051
1052 pub fn read_byte_blocking(&mut self) -> u8 {
1053 loop {
1054 if let Ok(b) = self.rx.read_byte() {
1055 return b;
1056 }
1057 }
1058 }
1059
1060 pub fn write_str_blocking(&mut self, buf: &str) {
1061 self.tx.write_str_blocking(buf);
1062 }
1063
1064 /// Write data to LPUART TX without blocking
1065 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
1066 self.tx.write(buf)
1067 }
1068
1069 /// Flush LPUART TX blocking execution until all data has been transmitted
1070 pub fn blocking_flush(&mut self) -> Result<()> {
1071 self.tx.blocking_flush()
1072 }
1073
1074 /// Flush LPUART TX without blocking
1075 pub fn flush(&mut self) -> Result<()> {
1076 self.tx.flush()
1077 }
1078}
1079
1080// ============================================================================
1081// ASYNC MODE IMPLEMENTATIONS
1082// ============================================================================
1083
1084// TODO: Implement async mode for LPUART
1085
1086// ============================================================================
1087// EMBEDDED-HAL 0.2 TRAIT IMPLEMENTATIONS
1088// ============================================================================
1089
1090impl embedded_hal_02::serial::Read<u8> for LpuartRx<'_, Blocking> {
1091 type Error = Error;
1092
1093 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1094 let mut buf = [0; 1];
1095 match self.read(&mut buf) {
1096 Ok(_) => Ok(buf[0]),
1097 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1098 Err(e) => Err(nb::Error::Other(e)),
1099 }
1100 }
1101}
1102
1103impl embedded_hal_02::serial::Write<u8> for LpuartTx<'_, Blocking> {
1104 type Error = Error;
1105
1106 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1107 match self.write(&[word]) {
1108 Ok(_) => Ok(()),
1109 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1110 Err(e) => Err(nb::Error::Other(e)),
1111 }
1112 }
1113
1114 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1115 match self.flush() {
1116 Ok(_) => Ok(()),
1117 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1118 Err(e) => Err(nb::Error::Other(e)),
1119 }
1120 }
1121}
1122
1123impl embedded_hal_02::blocking::serial::Write<u8> for LpuartTx<'_, Blocking> {
1124 type Error = Error;
1125
1126 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1127 self.blocking_write(buffer)
1128 }
1129
1130 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1131 self.blocking_flush()
1132 }
1133}
1134
1135impl embedded_hal_02::serial::Read<u8> for Lpuart<'_, Blocking> {
1136 type Error = Error;
1137
1138 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1139 embedded_hal_02::serial::Read::read(&mut self.rx)
1140 }
1141}
1142
1143impl embedded_hal_02::serial::Write<u8> for Lpuart<'_, Blocking> {
1144 type Error = Error;
1145
1146 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1147 embedded_hal_02::serial::Write::write(&mut self.tx, word)
1148 }
1149
1150 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1151 embedded_hal_02::serial::Write::flush(&mut self.tx)
1152 }
1153}
1154
1155impl embedded_hal_02::blocking::serial::Write<u8> for Lpuart<'_, Blocking> {
1156 type Error = Error;
1157
1158 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1159 self.blocking_write(buffer)
1160 }
1161
1162 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1163 self.blocking_flush()
1164 }
1165}
1166
1167// ============================================================================
1168// EMBEDDED-HAL-NB TRAIT IMPLEMENTATIONS
1169// ============================================================================
1170
1171impl embedded_hal_nb::serial::Error for Error {
1172 fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
1173 match *self {
1174 Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
1175 Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
1176 Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
1177 Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise,
1178 _ => embedded_hal_nb::serial::ErrorKind::Other,
1179 }
1180 }
1181}
1182
1183impl embedded_hal_nb::serial::ErrorType for LpuartRx<'_, Blocking> {
1184 type Error = Error;
1185}
1186
1187impl embedded_hal_nb::serial::ErrorType for LpuartTx<'_, Blocking> {
1188 type Error = Error;
1189}
1190
1191impl embedded_hal_nb::serial::ErrorType for Lpuart<'_, Blocking> {
1192 type Error = Error;
1193}
1194
1195impl embedded_hal_nb::serial::Read for LpuartRx<'_, Blocking> {
1196 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1197 let mut buf = [0; 1];
1198 match self.read(&mut buf) {
1199 Ok(_) => Ok(buf[0]),
1200 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1201 Err(e) => Err(nb::Error::Other(e)),
1202 }
1203 }
1204}
1205
1206impl embedded_hal_nb::serial::Write for LpuartTx<'_, Blocking> {
1207 fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
1208 match self.write(&[word]) {
1209 Ok(_) => Ok(()),
1210 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1211 Err(e) => Err(nb::Error::Other(e)),
1212 }
1213 }
1214
1215 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1216 match self.flush() {
1217 Ok(_) => Ok(()),
1218 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1219 Err(e) => Err(nb::Error::Other(e)),
1220 }
1221 }
1222}
1223
1224impl embedded_hal_nb::serial::Read for Lpuart<'_, Blocking> {
1225 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1226 embedded_hal_nb::serial::Read::read(&mut self.rx)
1227 }
1228}
1229
1230impl embedded_hal_nb::serial::Write for Lpuart<'_, Blocking> {
1231 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
1232 embedded_hal_nb::serial::Write::write(&mut self.tx, char)
1233 }
1234
1235 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1236 embedded_hal_nb::serial::Write::flush(&mut self.tx)
1237 }
1238}
1239
1240// ============================================================================
1241// EMBEDDED-IO TRAIT IMPLEMENTATIONS
1242// ============================================================================
1243
1244impl embedded_io::Error for Error {
1245 fn kind(&self) -> embedded_io::ErrorKind {
1246 embedded_io::ErrorKind::Other
1247 }
1248}
1249
1250impl embedded_io::ErrorType for LpuartRx<'_, Blocking> {
1251 type Error = Error;
1252}
1253
1254impl embedded_io::ErrorType for LpuartTx<'_, Blocking> {
1255 type Error = Error;
1256}
1257
1258impl embedded_io::ErrorType for Lpuart<'_, Blocking> {
1259 type Error = Error;
1260}
1261
1262impl embedded_io::Read for LpuartRx<'_, Blocking> {
1263 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1264 self.blocking_read(buf).map(|_| buf.len())
1265 }
1266}
1267
1268impl embedded_io::Write for LpuartTx<'_, Blocking> {
1269 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1270 self.blocking_write(buf).map(|_| buf.len())
1271 }
1272
1273 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1274 self.blocking_flush()
1275 }
1276}
1277
1278impl embedded_io::Read for Lpuart<'_, Blocking> {
1279 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1280 embedded_io::Read::read(&mut self.rx, buf)
1281 }
1282}
1283
1284impl embedded_io::Write for Lpuart<'_, Blocking> {
1285 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1286 embedded_io::Write::write(&mut self.tx, buf)
1287 }
1288
1289 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1290 embedded_io::Write::flush(&mut self.tx)
1291 }
1292}
diff --git a/embassy-mcxa/src/ostimer.rs b/embassy-mcxa/src/ostimer.rs
new file mode 100644
index 000000000..c51812e3d
--- /dev/null
+++ b/embassy-mcxa/src/ostimer.rs
@@ -0,0 +1,745 @@
1//! # OSTIMER Driver with Robustness Features
2//!
3//! This module provides an async timer driver for the NXP MCXA276 OSTIMER peripheral
4//! with protection against race conditions and timer rollover issues.
5//!
6//! ## Features
7//!
8//! - Async timing with embassy-time integration
9//! - Gray code counter handling (42-bit counter)
10//! - Interrupt-driven wakeups
11//! - Configurable interrupt priority
12//! - **Race condition protection**: Critical sections and atomic operations
13//! - **Timer rollover handling**: Bounds checking and rollover prevention
14//!
15//! ## Clock Frequency Configuration
16//!
17//! The OSTIMER frequency depends on your system's clock configuration. You must provide
18//! the actual frequency when calling `time_driver::init()`.
19//!
20//! ## Race Condition Protection
21//! - Critical sections in interrupt handlers prevent concurrent access
22//! - Atomic register operations with memory barriers
23//! - Proper interrupt flag clearing and validation
24//!
25//! ## Timer Rollover Handling
26//! - Bounds checking prevents scheduling beyond timer capacity
27//! - Immediate wake for timestamps that would cause rollover issues
28#![allow(dead_code)]
29
30use core::sync::atomic::{AtomicBool, Ordering};
31
32use embassy_hal_internal::{Peri, PeripheralType};
33
34use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel};
35use crate::clocks::{assert_reset, enable_and_reset, is_reset_released, release_reset, Gate, PoweredClock};
36use crate::interrupt::InterruptExt;
37use crate::pac;
38
39// PAC defines the shared RegisterBlock under `ostimer0`.
40type Regs = pac::ostimer0::RegisterBlock;
41
42// OSTIMER EVTIMER register layout constants
43/// Total width of the EVTIMER counter in bits (42 bits total)
44const EVTIMER_TOTAL_BITS: u32 = 42;
45/// Width of the low part of EVTIMER (bits 31:0)
46const EVTIMER_LO_BITS: u32 = 32;
47/// Width of the high part of EVTIMER (bits 41:32)
48const EVTIMER_HI_BITS: u32 = 10;
49/// Bit position where high part starts in the combined 64-bit value
50const EVTIMER_HI_SHIFT: u32 = 32;
51
52/// Bit mask for the high part of EVTIMER
53const EVTIMER_HI_MASK: u16 = (1 << EVTIMER_HI_BITS) - 1;
54
55/// Maximum value for MATCH_L register (32-bit)
56const MATCH_L_MAX: u32 = u32::MAX;
57/// Maximum value for MATCH_H register (10-bit)
58const MATCH_H_MAX: u16 = EVTIMER_HI_MASK;
59
60/// Bit mask for extracting the low 32 bits from a 64-bit value
61const LOW_32_BIT_MASK: u64 = u32::MAX as u64;
62
63/// Gray code conversion bit shifts (most significant to least)
64const GRAY_CONVERSION_SHIFTS: [u32; 6] = [32, 16, 8, 4, 2, 1];
65
66/// Maximum timer value before rollover (2^42 - 1 ticks)
67/// Actual rollover time depends on the configured clock frequency
68const TIMER_MAX_VALUE: u64 = (1u64 << EVTIMER_TOTAL_BITS) - 1;
69
70/// Threshold for detecting timer rollover in comparisons (1 second at 1MHz)
71const TIMER_ROLLOVER_THRESHOLD: u64 = 1_000_000;
72
73/// Common default interrupt priority for OSTIMER
74const DEFAULT_INTERRUPT_PRIORITY: u8 = 3;
75
76// Global alarm state for interrupt handling
77static ALARM_ACTIVE: AtomicBool = AtomicBool::new(false);
78static mut ALARM_CALLBACK: Option<fn()> = None;
79static mut ALARM_FLAG: Option<*const AtomicBool> = None;
80static mut ALARM_TARGET_TIME: u64 = 0;
81
82/// Number of tight spin iterations between elapsed time checks while waiting for MATCH writes to return to the idle (0) state.
83const MATCH_WRITE_READY_SPINS: usize = 512;
84/// Maximum time (in OSTIMER ticks) to wait for MATCH registers to become writable (~5 ms at 1 MHz).
85const MATCH_WRITE_READY_TIMEOUT_TICKS: u64 = 5_000;
86/// Short stabilization delay executed after toggling the MRCC reset line to let the OSTIMER bus interface settle.
87const RESET_STABILIZE_SPINS: usize = 512;
88
89pub(super) fn wait_for_match_write_ready(r: &Regs) -> bool {
90 let start = now_ticks_read();
91 let mut spin_budget = 0usize;
92
93 loop {
94 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
95 return true;
96 }
97
98 cortex_m::asm::nop();
99 spin_budget += 1;
100
101 if spin_budget >= MATCH_WRITE_READY_SPINS {
102 spin_budget = 0;
103
104 let elapsed = now_ticks_read().wrapping_sub(start);
105 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
106 return false;
107 }
108 }
109 }
110}
111
112pub(super) fn wait_for_match_write_complete(r: &Regs) -> bool {
113 let start = now_ticks_read();
114 let mut spin_budget = 0usize;
115
116 loop {
117 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
118 return true;
119 }
120
121 cortex_m::asm::nop();
122 spin_budget += 1;
123
124 if spin_budget >= MATCH_WRITE_READY_SPINS {
125 spin_budget = 0;
126
127 let elapsed = now_ticks_read().wrapping_sub(start);
128 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
129 return false;
130 }
131 }
132 }
133}
134
135fn prime_match_registers(r: &Regs) {
136 // Disable the interrupt, clear any pending flag, then wait until the MATCH registers are writable.
137 r.osevent_ctrl()
138 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
139
140 if wait_for_match_write_ready(r) {
141 r.match_l().write(|w| unsafe { w.match_value().bits(MATCH_L_MAX) });
142 r.match_h().write(|w| unsafe { w.match_value().bits(MATCH_H_MAX) });
143 let _ = wait_for_match_write_complete(r);
144 }
145}
146
147/// Single-shot alarm functionality for OSTIMER
148pub struct Alarm<'d> {
149 /// Whether the alarm is currently active
150 active: AtomicBool,
151 /// Callback to execute when alarm expires (optional)
152 callback: Option<fn()>,
153 /// Flag that gets set when alarm expires (optional)
154 flag: Option<&'d AtomicBool>,
155 _phantom: core::marker::PhantomData<&'d mut ()>,
156}
157
158impl<'d> Default for Alarm<'d> {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl<'d> Alarm<'d> {
165 /// Create a new alarm instance
166 pub fn new() -> Self {
167 Self {
168 active: AtomicBool::new(false),
169 callback: None,
170 flag: None,
171 _phantom: core::marker::PhantomData,
172 }
173 }
174
175 /// Set a callback that will be executed when the alarm expires
176 /// Note: Due to interrupt handler constraints, callbacks must be static function pointers
177 pub fn with_callback(mut self, callback: fn()) -> Self {
178 self.callback = Some(callback);
179 self
180 }
181
182 /// Set a flag that will be set to true when the alarm expires
183 pub fn with_flag(mut self, flag: &'d AtomicBool) -> Self {
184 self.flag = Some(flag);
185 self
186 }
187
188 /// Check if the alarm is currently active
189 pub fn is_active(&self) -> bool {
190 self.active.load(Ordering::Acquire)
191 }
192
193 /// Cancel the alarm if it's active
194 pub fn cancel(&self) {
195 self.active.store(false, Ordering::Release);
196 }
197}
198
199/// Configuration for Ostimer::new()
200#[derive(Copy, Clone)]
201pub struct Config {
202 /// Initialize MATCH registers to their max values and mask/clear the interrupt flag.
203 pub init_match_max: bool,
204 pub power: PoweredClock,
205 pub source: OstimerClockSel,
206}
207
208impl Default for Config {
209 fn default() -> Self {
210 Self {
211 init_match_max: true,
212 power: PoweredClock::NormalEnabledDeepSleepDisabled,
213 source: OstimerClockSel::Clk1M,
214 }
215 }
216}
217
218/// OSTIMER peripheral instance
219pub struct Ostimer<'d, I: Instance> {
220 _inst: core::marker::PhantomData<I>,
221 clock_frequency_hz: u64,
222 _phantom: core::marker::PhantomData<&'d mut ()>,
223}
224
225impl<'d, I: Instance> Ostimer<'d, I> {
226 /// Construct OSTIMER handle.
227 /// Requires clocks for the instance to be enabled by the board before calling.
228 /// Does not enable NVIC or INTENA; use time_driver::init() for async operation.
229 pub fn new(_inst: Peri<'d, I>, cfg: Config) -> Self {
230 let clock_freq = unsafe {
231 enable_and_reset::<I>(&OsTimerConfig {
232 power: cfg.power,
233 source: cfg.source,
234 })
235 .expect("Enabling OsTimer clock should not fail")
236 };
237
238 assert!(clock_freq > 0, "OSTIMER frequency must be greater than 0");
239
240 if cfg.init_match_max {
241 let r: &Regs = unsafe { &*I::ptr() };
242 // Mask INTENA, clear pending flag, and set MATCH to max so no spurious IRQ fires.
243 prime_match_registers(r);
244 }
245
246 Self {
247 _inst: core::marker::PhantomData,
248 clock_frequency_hz: clock_freq as u64,
249 _phantom: core::marker::PhantomData,
250 }
251 }
252
253 /// Get the configured clock frequency in Hz
254 pub fn clock_frequency_hz(&self) -> u64 {
255 self.clock_frequency_hz
256 }
257
258 /// Read the current timer counter value in timer ticks
259 ///
260 /// # Returns
261 /// Current timer counter value as a 64-bit unsigned integer
262 pub fn now(&self) -> u64 {
263 now_ticks_read()
264 }
265
266 /// Reset the timer counter to zero
267 ///
268 /// This performs a hardware reset of the OSTIMER peripheral, which will reset
269 /// the counter to zero and clear any pending interrupts. Note that this will
270 /// affect all timer operations including embassy-time.
271 ///
272 /// # Safety
273 /// This operation will reset the entire OSTIMER peripheral. Any active alarms
274 /// or time_driver operations will be disrupted. Use with caution.
275 pub fn reset(&self, _peripherals: &crate::pac::Peripherals) {
276 critical_section::with(|_| {
277 let r: &Regs = unsafe { &*I::ptr() };
278
279 // Mask the peripheral interrupt flag before we toggle the reset line so that
280 // no new NVIC activity races with the reset sequence.
281 r.osevent_ctrl()
282 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
283
284 unsafe {
285 assert_reset::<I>();
286
287 for _ in 0..RESET_STABILIZE_SPINS {
288 cortex_m::asm::nop();
289 }
290
291 release_reset::<I>();
292
293 while !is_reset_released::<I>() {
294 cortex_m::asm::nop();
295 }
296 }
297
298 for _ in 0..RESET_STABILIZE_SPINS {
299 cortex_m::asm::nop();
300 }
301
302 // Clear alarm bookkeeping before re-arming MATCH registers.
303 ALARM_ACTIVE.store(false, Ordering::Release);
304 unsafe {
305 ALARM_TARGET_TIME = 0;
306 ALARM_CALLBACK = None;
307 ALARM_FLAG = None;
308 }
309
310 prime_match_registers(r);
311 });
312
313 // Ensure no stale OS_EVENT request remains pending after the reset sequence.
314 crate::interrupt::OS_EVENT.unpend();
315 }
316
317 /// Schedule a single-shot alarm to expire after the specified delay in microseconds
318 ///
319 /// # Parameters
320 /// * `alarm` - The alarm instance to schedule
321 /// * `delay_us` - Delay in microseconds from now
322 ///
323 /// # Returns
324 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
325 pub fn schedule_alarm_delay(&self, alarm: &Alarm, delay_us: u64) -> bool {
326 let delay_ticks = (delay_us * self.clock_frequency_hz) / 1_000_000;
327 let target_time = now_ticks_read() + delay_ticks;
328 self.schedule_alarm_at(alarm, target_time)
329 }
330
331 /// Schedule a single-shot alarm to expire at the specified absolute time in timer ticks
332 ///
333 /// # Parameters
334 /// * `alarm` - The alarm instance to schedule
335 /// * `target_ticks` - Absolute time in timer ticks when the alarm should expire
336 ///
337 /// # Returns
338 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
339 pub fn schedule_alarm_at(&self, alarm: &Alarm, target_ticks: u64) -> bool {
340 let now = now_ticks_read();
341
342 // Check if target time is in the past
343 if target_ticks <= now {
344 // Execute callback immediately if alarm was supposed to be active
345 if alarm.active.load(Ordering::Acquire) {
346 alarm.active.store(false, Ordering::Release);
347 if let Some(callback) = alarm.callback {
348 callback();
349 }
350 if let Some(flag) = &alarm.flag {
351 flag.store(true, Ordering::Release);
352 }
353 }
354 return true;
355 }
356
357 // Check for timer rollover
358 let max_future = now + TIMER_MAX_VALUE;
359 if target_ticks > max_future {
360 return false; // Would exceed timer capacity
361 }
362
363 // Program the timer
364 let r: &Regs = unsafe { &*I::ptr() };
365
366 critical_section::with(|_| {
367 // Disable interrupt and clear flag
368 r.osevent_ctrl()
369 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
370
371 if !wait_for_match_write_ready(r) {
372 prime_match_registers(r);
373
374 if !wait_for_match_write_ready(r) {
375 alarm.active.store(false, Ordering::Release);
376 ALARM_ACTIVE.store(false, Ordering::Release);
377 unsafe {
378 ALARM_TARGET_TIME = 0;
379 ALARM_CALLBACK = None;
380 ALARM_FLAG = None;
381 }
382 return false;
383 }
384 }
385
386 // Mark alarm as active now that we know the MATCH registers are writable
387 alarm.active.store(true, Ordering::Release);
388
389 // Set global alarm state for interrupt handler
390 ALARM_ACTIVE.store(true, Ordering::Release);
391 unsafe {
392 ALARM_TARGET_TIME = target_ticks;
393 ALARM_CALLBACK = alarm.callback;
394 ALARM_FLAG = alarm.flag.map(|f| f as *const AtomicBool);
395 }
396
397 // Program MATCH registers (Gray-coded)
398 let gray = bin_to_gray(target_ticks);
399 let l = (gray & LOW_32_BIT_MASK) as u32;
400 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
401
402 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
403 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
404
405 if !wait_for_match_write_complete(r) {
406 alarm.active.store(false, Ordering::Release);
407 ALARM_ACTIVE.store(false, Ordering::Release);
408 unsafe {
409 ALARM_TARGET_TIME = 0;
410 ALARM_CALLBACK = None;
411 ALARM_FLAG = None;
412 }
413 return false;
414 }
415
416 let now_after_program = now_ticks_read();
417 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
418 if now_after_program >= target_ticks && !intrflag_set {
419 alarm.active.store(false, Ordering::Release);
420 ALARM_ACTIVE.store(false, Ordering::Release);
421 unsafe {
422 ALARM_TARGET_TIME = 0;
423 ALARM_CALLBACK = None;
424 ALARM_FLAG = None;
425 }
426 return false;
427 }
428
429 // Enable interrupt
430 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
431
432 true
433 })
434 }
435
436 /// Cancel any active alarm
437 pub fn cancel_alarm(&self, alarm: &Alarm) {
438 critical_section::with(|_| {
439 alarm.cancel();
440
441 // Clear global alarm state
442 ALARM_ACTIVE.store(false, Ordering::Release);
443 unsafe { ALARM_TARGET_TIME = 0 };
444
445 // Reset MATCH registers to maximum values to prevent spurious interrupts
446 let r: &Regs = unsafe { &*I::ptr() };
447 prime_match_registers(r);
448 });
449 }
450
451 /// Check if an alarm has expired (call this from your interrupt handler)
452 /// Returns true if the alarm was active and has now expired
453 pub fn check_alarm_expired(&self, alarm: &Alarm) -> bool {
454 if alarm.active.load(Ordering::Acquire) {
455 alarm.active.store(false, Ordering::Release);
456
457 // Execute callback
458 if let Some(callback) = alarm.callback {
459 callback();
460 }
461
462 // Set flag
463 if let Some(flag) = &alarm.flag {
464 flag.store(true, Ordering::Release);
465 }
466
467 true
468 } else {
469 false
470 }
471 }
472}
473
474/// Read current EVTIMER (Gray-coded) and convert to binary ticks.
475#[inline(always)]
476fn now_ticks_read() -> u64 {
477 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
478
479 // Read high then low to minimize incoherent snapshots
480 let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64);
481 let lo = r.evtimerl().read().evtimer_count_value().bits() as u64;
482 // Combine and convert from Gray code to binary
483 let gray = lo | (hi << EVTIMER_HI_SHIFT);
484 gray_to_bin(gray)
485}
486
487// Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance
488pub trait Instance: Gate<MrccPeriphConfig = OsTimerConfig> + PeripheralType {
489 fn ptr() -> *const Regs;
490}
491
492#[cfg(not(feature = "time"))]
493impl Instance for crate::peripherals::OSTIMER0 {
494 #[inline(always)]
495 fn ptr() -> *const Regs {
496 pac::Ostimer0::ptr()
497 }
498}
499
500#[inline(always)]
501fn bin_to_gray(x: u64) -> u64 {
502 x ^ (x >> 1)
503}
504
505#[inline(always)]
506fn gray_to_bin(gray: u64) -> u64 {
507 // More efficient iterative conversion using predefined shifts
508 let mut bin = gray;
509 for &shift in &GRAY_CONVERSION_SHIFTS {
510 bin ^= bin >> shift;
511 }
512 bin
513}
514
515#[cfg(feature = "time")]
516pub mod time_driver {
517 use core::sync::atomic::Ordering;
518 use core::task::Waker;
519
520 use embassy_sync::waitqueue::AtomicWaker;
521 use embassy_time_driver as etd;
522
523 use super::{
524 bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME,
525 EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK,
526 };
527 use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel};
528 use crate::clocks::{enable_and_reset, PoweredClock};
529 use crate::pac;
530
531 #[allow(non_camel_case_types)]
532 pub(crate) struct _OSTIMER0_TIME_DRIVER {
533 _x: (),
534 }
535
536 // #[cfg(feature = "time")]
537 // impl_cc_gate!(_OSTIMER0_TIME_DRIVER, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig);
538
539 impl crate::clocks::Gate for _OSTIMER0_TIME_DRIVER {
540 type MrccPeriphConfig = crate::clocks::periph_helpers::OsTimerConfig;
541
542 #[inline]
543 unsafe fn enable_clock() {
544 let mrcc = unsafe { pac::Mrcc0::steal() };
545 mrcc.mrcc_glb_cc1().modify(|_, w| w.ostimer0().enabled());
546 }
547
548 #[inline]
549 unsafe fn disable_clock() {
550 let mrcc = unsafe { pac::Mrcc0::steal() };
551 mrcc.mrcc_glb_cc1().modify(|_r, w| w.ostimer0().disabled());
552 }
553
554 #[inline]
555 fn is_clock_enabled() -> bool {
556 let mrcc = unsafe { pac::Mrcc0::steal() };
557 mrcc.mrcc_glb_cc1().read().ostimer0().is_enabled()
558 }
559
560 #[inline]
561 unsafe fn release_reset() {
562 let mrcc = unsafe { pac::Mrcc0::steal() };
563 mrcc.mrcc_glb_rst1().modify(|_, w| w.ostimer0().enabled());
564 }
565
566 #[inline]
567 unsafe fn assert_reset() {
568 let mrcc = unsafe { pac::Mrcc0::steal() };
569 mrcc.mrcc_glb_rst1().modify(|_, w| w.ostimer0().disabled());
570 }
571
572 #[inline]
573 fn is_reset_released() -> bool {
574 let mrcc = unsafe { pac::Mrcc0::steal() };
575 mrcc.mrcc_glb_rst1().read().ostimer0().is_enabled()
576 }
577 }
578
579 pub struct Driver;
580 static TIMER_WAKER: AtomicWaker = AtomicWaker::new();
581
582 impl etd::Driver for Driver {
583 fn now(&self) -> u64 {
584 // Use the hardware counter (frequency configured in init)
585 super::now_ticks_read()
586 }
587
588 fn schedule_wake(&self, timestamp: u64, waker: &Waker) {
589 let now = self.now();
590
591 // If timestamp is in the past or very close to now, wake immediately
592 if timestamp <= now {
593 waker.wake_by_ref();
594 return;
595 }
596
597 // Prevent scheduling too far in the future (beyond timer rollover)
598 // This prevents wraparound issues
599 let max_future = now + super::TIMER_MAX_VALUE;
600 if timestamp > max_future {
601 // For very long timeouts, wake immediately to avoid rollover issues
602 waker.wake_by_ref();
603 return;
604 }
605
606 // Register the waker first so any immediate wake below is observed by the executor.
607 TIMER_WAKER.register(waker);
608
609 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
610
611 critical_section::with(|_| {
612 // Mask INTENA and clear flag
613 r.osevent_ctrl()
614 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
615
616 // Read back to ensure W1C took effect on hardware
617 let _ = r.osevent_ctrl().read().ostimer_intrflag().bit();
618
619 if !super::wait_for_match_write_ready(r) {
620 super::prime_match_registers(r);
621
622 if !super::wait_for_match_write_ready(r) {
623 // If we can't safely program MATCH, wake immediately and leave INTENA masked.
624 waker.wake_by_ref();
625 return;
626 }
627 }
628
629 // Program MATCH (Gray-coded). Write low then high, then fence.
630 let gray = bin_to_gray(timestamp);
631 let l = (gray & LOW_32_BIT_MASK) as u32;
632
633 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
634
635 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
636 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
637
638 if !super::wait_for_match_write_complete(r) {
639 waker.wake_by_ref();
640 return;
641 }
642
643 let now_after_program = super::now_ticks_read();
644 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
645 if now_after_program >= timestamp && !intrflag_set {
646 waker.wake_by_ref();
647 return;
648 }
649
650 // Enable peripheral interrupt
651 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
652 });
653 }
654 }
655
656 /// Install the global embassy-time driver and configure NVIC priority for OS_EVENT.
657 ///
658 /// # Parameters
659 /// * `priority` - Interrupt priority for the OSTIMER interrupt
660 /// * `frequency_hz` - Actual OSTIMER clock frequency in Hz (stored for future use)
661 ///
662 /// Note: The frequency parameter is currently accepted for API compatibility.
663 /// The embassy_time_driver macro handles driver registration automatically.
664 pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) {
665 let _clock_freq = unsafe {
666 enable_and_reset::<_OSTIMER0_TIME_DRIVER>(&OsTimerConfig {
667 power: PoweredClock::AlwaysEnabled,
668 source: OstimerClockSel::Clk1M,
669 })
670 .expect("Enabling OsTimer clock should not fail")
671 };
672
673 // Mask/clear at peripheral and set default MATCH
674 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
675 super::prime_match_registers(r);
676
677 // Configure NVIC for timer operation
678 crate::interrupt::OS_EVENT.configure_for_timer(priority);
679
680 // Note: The embassy_time_driver macro automatically registers the driver
681 // The frequency parameter is accepted for future compatibility
682 let _ = frequency_hz; // Suppress unused parameter warning
683 }
684
685 // Export the global time driver expected by embassy-time
686 embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver);
687
688 /// To be called from the OS_EVENT IRQ.
689 pub fn on_interrupt() {
690 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
691
692 // Critical section to prevent races with schedule_wake
693 critical_section::with(|_| {
694 // Check if interrupt is actually pending and handle it atomically
695 if r.osevent_ctrl().read().ostimer_intrflag().bit_is_set() {
696 // Clear flag and disable interrupt atomically
697 r.osevent_ctrl().write(|w| {
698 w.ostimer_intrflag()
699 .clear_bit_by_one() // Write-1-to-clear using safe helper
700 .ostimer_intena()
701 .clear_bit()
702 });
703
704 // Wake the waiting task
705 TIMER_WAKER.wake();
706
707 // Handle alarm callback if active and this interrupt is for the alarm
708 if ALARM_ACTIVE.load(Ordering::SeqCst) {
709 let current_time = now_ticks_read();
710 let target_time = unsafe { ALARM_TARGET_TIME };
711
712 // Check if current time is close to alarm target time (within 1000 ticks for timing variations)
713 if current_time >= target_time && current_time <= target_time + 1000 {
714 ALARM_ACTIVE.store(false, Ordering::SeqCst);
715 unsafe { ALARM_TARGET_TIME = 0 };
716
717 // Execute callback if set
718 unsafe {
719 if let Some(callback) = ALARM_CALLBACK {
720 callback();
721 }
722 }
723
724 // Set flag if provided
725 unsafe {
726 if let Some(flag) = ALARM_FLAG {
727 (*flag).store(true, Ordering::SeqCst);
728 }
729 }
730 }
731 }
732 }
733 });
734 }
735}
736
737#[cfg(feature = "time")]
738use crate::pac::interrupt;
739
740#[cfg(feature = "time")]
741#[allow(non_snake_case)]
742#[interrupt]
743fn OS_EVENT() {
744 time_driver::on_interrupt()
745}
diff --git a/embassy-mcxa/src/pins.rs b/embassy-mcxa/src/pins.rs
new file mode 100644
index 000000000..fdf1b0a86
--- /dev/null
+++ b/embassy-mcxa/src/pins.rs
@@ -0,0 +1,28 @@
1//! Pin configuration helpers (separate from peripheral drivers).
2use crate::pac;
3
4pub unsafe fn configure_adc_pins() {
5 // P1_10 = ADC1_A8
6 let port1 = &*pac::Port1::ptr();
7 port1.pcr10().write(|w| {
8 w.ps()
9 .ps0()
10 .pe()
11 .pe0()
12 .sre()
13 .sre0()
14 .ode()
15 .ode0()
16 .dse()
17 .dse0()
18 .mux()
19 .mux0()
20 .ibe()
21 .ibe0()
22 .inv()
23 .inv0()
24 .lk()
25 .lk0()
26 });
27 core::arch::asm!("dsb sy; isb sy");
28}
diff --git a/embassy-mcxa/src/rtc.rs b/embassy-mcxa/src/rtc.rs
new file mode 100644
index 000000000..f975d9c9f
--- /dev/null
+++ b/embassy-mcxa/src/rtc.rs
@@ -0,0 +1,453 @@
1//! RTC DateTime driver.
2use core::marker::PhantomData;
3
4use embassy_hal_internal::{Peri, PeripheralType};
5use maitake_sync::WaitCell;
6
7use crate::clocks::with_clocks;
8use crate::interrupt::typelevel::{Handler, Interrupt};
9use crate::pac;
10use crate::pac::rtc0::cr::Um;
11
12type Regs = pac::rtc0::RegisterBlock;
13
14/// Global wait cell for alarm notifications
15static WAKER: WaitCell = WaitCell::new();
16
17/// RTC interrupt handler.
18pub struct InterruptHandler<I: Instance> {
19 _phantom: PhantomData<I>,
20}
21
22/// Trait for RTC peripheral instances
23pub trait Instance: PeripheralType {
24 type Interrupt: Interrupt;
25 fn ptr() -> *const Regs;
26}
27
28/// Token for RTC0
29pub type Rtc0 = crate::peripherals::RTC0;
30impl Instance for crate::peripherals::RTC0 {
31 type Interrupt = crate::interrupt::typelevel::RTC;
32 #[inline(always)]
33 fn ptr() -> *const Regs {
34 pac::Rtc0::ptr()
35 }
36}
37
38/// Number of days in a standard year
39const DAYS_IN_A_YEAR: u32 = 365;
40/// Number of seconds in a day
41const SECONDS_IN_A_DAY: u32 = 86400;
42/// Number of seconds in an hour
43const SECONDS_IN_A_HOUR: u32 = 3600;
44/// Number of seconds in a minute
45const SECONDS_IN_A_MINUTE: u32 = 60;
46/// Unix epoch start year
47const YEAR_RANGE_START: u16 = 1970;
48
49/// Date and time structure for RTC operations
50#[derive(Debug, Clone, Copy)]
51pub struct RtcDateTime {
52 pub year: u16,
53 pub month: u8,
54 pub day: u8,
55 pub hour: u8,
56 pub minute: u8,
57 pub second: u8,
58}
59#[derive(Copy, Clone)]
60pub struct RtcConfig {
61 #[allow(dead_code)]
62 wakeup_select: bool,
63 update_mode: Um,
64 #[allow(dead_code)]
65 supervisor_access: bool,
66 compensation_interval: u8,
67 compensation_time: u8,
68}
69
70/// RTC interrupt enable flags
71#[derive(Copy, Clone)]
72pub struct RtcInterruptEnable;
73impl RtcInterruptEnable {
74 pub const RTC_TIME_INVALID_INTERRUPT_ENABLE: u32 = 1 << 0;
75 pub const RTC_TIME_OVERFLOW_INTERRUPT_ENABLE: u32 = 1 << 1;
76 pub const RTC_ALARM_INTERRUPT_ENABLE: u32 = 1 << 2;
77 pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4;
78}
79
80/// Converts a DateTime structure to Unix timestamp (seconds since 1970-01-01)
81///
82/// # Arguments
83///
84/// * `datetime` - The date and time to convert
85///
86/// # Returns
87///
88/// Unix timestamp as u32
89///
90/// # Note
91///
92/// This function handles leap years correctly.
93pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 {
94 let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
95
96 let mut seconds = (datetime.year as u32 - 1970) * DAYS_IN_A_YEAR;
97 seconds += (datetime.year as u32 / 4) - (1970 / 4);
98 seconds += month_days[datetime.month as usize] as u32;
99 seconds += datetime.day as u32 - 1;
100
101 if (datetime.year & 3 == 0) && (datetime.month <= 2) {
102 seconds -= 1;
103 }
104
105 seconds = seconds * SECONDS_IN_A_DAY
106 + (datetime.hour as u32 * SECONDS_IN_A_HOUR)
107 + (datetime.minute as u32 * SECONDS_IN_A_MINUTE)
108 + datetime.second as u32;
109
110 seconds
111}
112
113/// Converts Unix timestamp to DateTime structure
114///
115/// # Arguments
116///
117/// * `seconds` - Unix timestamp (seconds since 1970-01-01)
118///
119/// # Returns
120///
121/// RtcDateTime structure with the converted date and time
122///
123/// # Note
124///
125/// This function handles leap years correctly.
126pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime {
127 let mut seconds_remaining = seconds;
128 let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1;
129 seconds_remaining %= SECONDS_IN_A_DAY;
130
131 let hour = (seconds_remaining / SECONDS_IN_A_HOUR) as u8;
132 seconds_remaining %= SECONDS_IN_A_HOUR;
133 let minute = (seconds_remaining / SECONDS_IN_A_MINUTE) as u8;
134 let second = (seconds_remaining % SECONDS_IN_A_MINUTE) as u8;
135
136 let mut year = YEAR_RANGE_START;
137 let mut days_in_year = DAYS_IN_A_YEAR;
138
139 while days > days_in_year {
140 days -= days_in_year;
141 year += 1;
142
143 days_in_year = if year.is_multiple_of(4) {
144 DAYS_IN_A_YEAR + 1
145 } else {
146 DAYS_IN_A_YEAR
147 };
148 }
149
150 let days_per_month = [
151 31,
152 if year.is_multiple_of(4) { 29 } else { 28 },
153 31,
154 30,
155 31,
156 30,
157 31,
158 31,
159 30,
160 31,
161 30,
162 31,
163 ];
164
165 let mut month = 1;
166 for (m, month_days) in days_per_month.iter().enumerate() {
167 let m = m + 1;
168 if days <= *month_days as u32 {
169 month = m;
170 break;
171 } else {
172 days -= *month_days as u32;
173 }
174 }
175
176 let day = days as u8;
177
178 RtcDateTime {
179 year,
180 month: month as u8,
181 day,
182 hour,
183 minute,
184 second,
185 }
186}
187
188/// Returns default RTC configuration
189///
190/// # Returns
191///
192/// RtcConfig with sensible default values:
193/// - No wakeup selection
194/// - Update mode 0 (immediate updates)
195/// - No supervisor access restriction
196/// - No compensation
197pub fn get_default_config() -> RtcConfig {
198 RtcConfig {
199 wakeup_select: false,
200 update_mode: Um::Um0,
201 supervisor_access: false,
202 compensation_interval: 0,
203 compensation_time: 0,
204 }
205}
206/// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy)
207pub struct Rtc<'a, I: Instance> {
208 _inst: core::marker::PhantomData<&'a mut I>,
209}
210
211impl<'a, I: Instance> Rtc<'a, I> {
212 /// Create a new instance of the real time clock.
213 pub fn new(
214 _inst: Peri<'a, I>,
215 _irq: impl crate::interrupt::typelevel::Binding<I::Interrupt, InterruptHandler<I>> + 'a,
216 config: RtcConfig,
217 ) -> Self {
218 let rtc = unsafe { &*I::ptr() };
219
220 // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock
221 // on the vsys domain is active
222 let clocks = with_clocks(|c| c.clk_16k_vsys.clone());
223 match clocks {
224 None => panic!("Clocks have not been initialized"),
225 Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"),
226 Some(Some(_)) => {}
227 }
228
229 // RTC reset
230 rtc.cr().modify(|_, w| w.swr().set_bit());
231 rtc.cr().modify(|_, w| w.swr().clear_bit());
232 rtc.tsr().write(|w| unsafe { w.bits(1) });
233
234 rtc.cr().modify(|_, w| w.um().variant(config.update_mode));
235
236 rtc.tcr().modify(|_, w| unsafe {
237 w.cir()
238 .bits(config.compensation_interval)
239 .tcr()
240 .bits(config.compensation_time)
241 });
242
243 // Enable RTC interrupt
244 I::Interrupt::unpend();
245 unsafe { I::Interrupt::enable() };
246
247 Self {
248 _inst: core::marker::PhantomData,
249 }
250 }
251
252 /// Set the current date and time
253 ///
254 /// # Arguments
255 ///
256 /// * `datetime` - The date and time to set
257 ///
258 /// # Note
259 ///
260 /// The datetime is converted to Unix timestamp and written to the time seconds register.
261 pub fn set_datetime(&self, datetime: RtcDateTime) {
262 let rtc = unsafe { &*I::ptr() };
263 let seconds = convert_datetime_to_seconds(&datetime);
264 rtc.tsr().write(|w| unsafe { w.bits(seconds) });
265 }
266
267 /// Get the current date and time
268 ///
269 /// # Returns
270 ///
271 /// Current date and time as RtcDateTime
272 ///
273 /// # Note
274 ///
275 /// Reads the current Unix timestamp from the time seconds register and converts it.
276 pub fn get_datetime(&self) -> RtcDateTime {
277 let rtc = unsafe { &*I::ptr() };
278 let seconds = rtc.tsr().read().bits();
279 convert_seconds_to_datetime(seconds)
280 }
281
282 /// Set the alarm date and time
283 ///
284 /// # Arguments
285 ///
286 /// * `alarm` - The date and time when the alarm should trigger
287 ///
288 /// # Note
289 ///
290 /// This function:
291 /// - Clears any existing alarm by writing 0 to the alarm register
292 /// - Waits for the clear operation to complete
293 /// - Sets the new alarm time
294 /// - Waits for the write operation to complete
295 /// - Uses timeouts to prevent infinite loops
296 /// - Enables the alarm interrupt after setting
297 pub fn set_alarm(&self, alarm: RtcDateTime) {
298 let rtc = unsafe { &*I::ptr() };
299 let seconds = convert_datetime_to_seconds(&alarm);
300
301 rtc.tar().write(|w| unsafe { w.bits(0) });
302 let mut timeout = 10000;
303 while rtc.tar().read().bits() != 0 && timeout > 0 {
304 timeout -= 1;
305 }
306
307 rtc.tar().write(|w| unsafe { w.bits(seconds) });
308
309 let mut timeout = 10000;
310 while rtc.tar().read().bits() != seconds && timeout > 0 {
311 timeout -= 1;
312 }
313
314 self.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
315 }
316
317 /// Get the current alarm date and time
318 ///
319 /// # Returns
320 ///
321 /// Alarm date and time as RtcDateTime
322 ///
323 /// # Note
324 ///
325 /// Reads the alarm timestamp from the time alarm register and converts it.
326 pub fn get_alarm(&self) -> RtcDateTime {
327 let rtc = unsafe { &*I::ptr() };
328 let alarm_seconds = rtc.tar().read().bits();
329 convert_seconds_to_datetime(alarm_seconds)
330 }
331
332 /// Start the RTC time counter
333 ///
334 /// # Note
335 ///
336 /// Sets the Time Counter Enable (TCE) bit in the status register.
337 pub fn start(&self) {
338 let rtc = unsafe { &*I::ptr() };
339 rtc.sr().modify(|_, w| w.tce().set_bit());
340 }
341
342 /// Stop the RTC time counter
343 ///
344 /// # Note
345 ///
346 /// Clears the Time Counter Enable (TCE) bit in the status register.
347 pub fn stop(&self) {
348 let rtc = unsafe { &*I::ptr() };
349 rtc.sr().modify(|_, w| w.tce().clear_bit());
350 }
351
352 /// Enable specific RTC interrupts
353 ///
354 /// # Arguments
355 ///
356 /// * `mask` - Bitmask of interrupts to enable (use RtcInterruptEnable constants)
357 ///
358 /// # Note
359 ///
360 /// This function enables the specified interrupt types and resets the alarm occurred flag.
361 /// Available interrupts:
362 /// - Time Invalid Interrupt
363 /// - Time Overflow Interrupt
364 /// - Alarm Interrupt
365 /// - Seconds Interrupt
366 pub fn set_interrupt(&self, mask: u32) {
367 let rtc = unsafe { &*I::ptr() };
368
369 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
370 rtc.ier().modify(|_, w| w.tiie().tiie_1());
371 }
372 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
373 rtc.ier().modify(|_, w| w.toie().toie_1());
374 }
375 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
376 rtc.ier().modify(|_, w| w.taie().taie_1());
377 }
378 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
379 rtc.ier().modify(|_, w| w.tsie().tsie_1());
380 }
381 }
382
383 /// Disable specific RTC interrupts
384 ///
385 /// # Arguments
386 ///
387 /// * `mask` - Bitmask of interrupts to disable (use RtcInterruptEnable constants)
388 ///
389 /// # Note
390 ///
391 /// This function disables the specified interrupt types.
392 pub fn disable_interrupt(&self, mask: u32) {
393 let rtc = unsafe { &*I::ptr() };
394
395 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
396 rtc.ier().modify(|_, w| w.tiie().tiie_0());
397 }
398 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
399 rtc.ier().modify(|_, w| w.toie().toie_0());
400 }
401 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
402 rtc.ier().modify(|_, w| w.taie().taie_0());
403 }
404 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
405 rtc.ier().modify(|_, w| w.tsie().tsie_0());
406 }
407 }
408
409 /// Clear the alarm interrupt flag
410 ///
411 /// # Note
412 ///
413 /// This function clears the Time Alarm Interrupt Enable bit.
414 pub fn clear_alarm_flag(&self) {
415 let rtc = unsafe { &*I::ptr() };
416 rtc.ier().modify(|_, w| w.taie().clear_bit());
417 }
418
419 /// Wait for an RTC alarm to trigger.
420 ///
421 /// # Arguments
422 ///
423 /// * `alarm` - The date and time when the alarm should trigger
424 /// This function will wait until the RTC alarm is triggered.
425 /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered.
426 pub async fn wait_for_alarm(&mut self, alarm: RtcDateTime) {
427 let wait = WAKER.subscribe().await;
428
429 self.set_alarm(alarm);
430 self.start();
431
432 // REVISIT: propagate error?
433 let _ = wait.await;
434
435 // Clear the interrupt and disable the alarm after waking up
436 self.disable_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
437 }
438}
439
440/// RTC interrupt handler
441///
442/// This struct implements the interrupt handler for RTC events.
443impl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {
444 unsafe fn on_interrupt() {
445 let rtc = &*pac::Rtc0::ptr();
446 // Check if this is actually a time alarm interrupt
447 let sr = rtc.sr().read();
448 if sr.taf().bit_is_set() {
449 rtc.ier().modify(|_, w| w.taie().clear_bit());
450 WAKER.wake();
451 }
452 }
453}
diff --git a/embassy-mcxa/supply-chain/README.md b/embassy-mcxa/supply-chain/README.md
new file mode 100644
index 000000000..1f2f6c69e
--- /dev/null
+++ b/embassy-mcxa/supply-chain/README.md
@@ -0,0 +1,149 @@
1# Working with cargo vet
2
3## Introduction
4
5`cargo vet` is a tool to help ensure that third-party Rust dependencies have been audited by a trusted entity.
6It matches all dependencies against a set of audits conducted by the authors of the project or entities they trust.
7To learn more, visit [mozilla/cargo-vet](https://github.com/mozilla/cargo-vet)
8
9---
10
11## Adding a new dependency
12
13When updating or adding a new dependency, we need to ensure it's audited before being merged into main.
14For our repositories, we have designated experts who are responsible for vetting any new dependencies being added to their repository.
15_It is the shared responsibility of the developer creating the PR and the auditors to conduct a successful audit._
16Follow the process below to ensure compliance:
17
18### For Developers
191. **Respond to `cargo vet` failures**:
20 - If your PR fails the `cargo vet` step, the cargo-vet workflow will add a comment to the PR with a template questionnaire
21 - Copy the questionnaire, fill it out and paste it as a new comment on the PR. This greatly helps the auditors get some context of the changes requiring the new dependencies
22
232. **Engage with auditors**:
24 - Respond to any questions that the auditors might have regarding the need of any new dependencies
25
263. **Rebase and verify**:
27 - At their discretion, auditors will check in their audits into either [rust-crate-audits](https://github.com/OpenDevicePartnership/rust-crate-audits) or into the same repository
28 - Once the new audits have been merged, rebase your branch on main and verify it passes `cargo vet`
29 ```bash
30 git fetch upstream
31 git rebase upstream/main
32 cargo vet
33 ```
34
354. **Update PR**:
36 - If the audits were checked into rust-crate-audits, they will show up in _imports.lock_ on running `cargo vet`. In this case add the updated _imports.lock_ to your PR
37 - If the audits were checked into the same repository, they will be present in _audits.toml_ after rebase and you can simply force push to your PR after rebase
38 ```bash
39 git push -f
40 ```
41
425. **Check PR status**:
43 - The existing PR comment from the previous failure will be updated with a success message once the check passes
44
45### For Auditors
46
471. **Review the questionnaire**:
48 - Check the filled questionnaire on the PR once the developer responds to the `cargo vet` failure
49 - Respond to the developer comment in case more information is needed
50
512. **Audit new dependencies**:
52 - Inspect the `cargo vet` failures using your preferred method
53 - Use [gh pr checkout](https://cli.github.com/manual/gh_pr_checkout) to checkout the PR and run `cargo vet --locked`
54 - Use [Github Pull Requests for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) to checkout the PR and run `cargo vet --locked`
55 - For more suggestions: [Checking out pull requests locally](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally)
56
573. **Follow `cargo vet` recommendations**:
58 - Follow the recommendations of the `cargo vet` command output, either `cargo vet diff` for version update or `cargo vet inspect` for new dependencies
59
604. **Record audits**:
61 - Use `cargo vet certify` to add new audits to _audits.toml_
62 - Verify all dependencies pass using `cargo vet`
63
645. **Decide audit location**:
65 - **Shared audits**: New audits should ideally be shared across ODP repositories to reduce the overhead of multiple audits for the same dependencies. To facilitate this, it's recommended to cut and paste the new audits and submit as a separate PR to the _audits.toml_ in [rust-crate-audits](https://github.com/OpenDevicePartnership/rust-crate-audits)
66 - If due to business reasons, the audits are not to be shared across repositories, copy the updated _audits.toml_ to a new branch off main in the same repository and submit the PR to update the audits
67
686. **Communicate successful audit**:
69 - Communicate to the PR developer via a PR comment so they can update the PR and get `cargo vet` to pass
70
71---
72
73## Audit criteria
74`cargo vet` comes pre-equipped with two built-in criteria but supports adding new criteria to suit our needs.
75As defined [here](https://mozilla.github.io/cargo-vet/built-in-criteria.html), the default criteria are:
76
77- **safe-to-run**
78 This crate can be compiled, run, and tested on a local workstation or in
79 controlled automation without surprising consequences, such as:
80 * Reading or writing data from sensitive or unrelated parts of the filesystem.
81 * Installing software or reconfiguring the device.
82 * Connecting to untrusted network endpoints.
83 * Misuse of system resources (e.g. cryptocurrency mining).
84
85- **safe-to-deploy**
86 This crate will not introduce a serious security vulnerability to production
87 software exposed to untrusted input.
88
89 Auditors are not required to perform a full logic review of the entire crate.
90 Rather, they must review enough to fully reason about the behavior of all unsafe
91 blocks and usage of powerful imports. For any reasonable usage of the crate in
92 real-world software, an attacker must not be able to manipulate the runtime
93 behavior of these sections in an exploitable or surprising way.
94
95 Ideally, all unsafe code is fully sound, and ambient capabilities (e.g.
96 filesystem access) are hardened against manipulation and consistent with the
97 advertised behavior of the crate. However, some discretion is permitted. In such
98 cases, the nature of the discretion should be recorded in the `notes` field of
99 the audit record.
100
101 For crates which generate deployed code (e.g. build dependencies or procedural
102 macros), reasonable usage of the crate should output code which meets the above
103 criteria.
104
105 **Note: `safe-to-deploy` implies `safe-to-run`**
106
107---
108
109## Conducting an audit
110
111When performing an audit for a new or updated dependency, auditors may consider the following criteria to ensure the safety, reliability, and suitability of the crate for use in our projects:
112
113- **Security**:
114 - Review the crate for known vulnerabilities or security advisories.
115 - Check for unsafe code usage and ensure it is justified and well-documented.
116 - Evaluate the crate’s history of security issues and responsiveness to reported problems.
117
118- **Maintenance and Activity**:
119 - Assess the frequency of updates and the responsiveness of maintainers to issues and pull requests.
120 - Prefer crates that are actively maintained and have a healthy contributor base.
121
122- **License Compliance**:
123 - Verify that the crate’s license is compatible with our project’s licensing requirements.
124
125- **Community Trust and Adoption**:
126 - Consider the crate’s adoption in the wider Rust ecosystem.
127 - Prefer crates that are widely used and trusted by the community.
128
129- **Functionality and Suitability**:
130 - Confirm that the crate provides the required functionality without unnecessary features or bloat.
131 - Evaluate whether the crate’s API is stable and unlikely to introduce breaking changes unexpectedly.
132
133- **Audit Trail**:
134 - Record the audit decision, including any concerns, mitigations, or recommendations for future updates.
135 - If exemptions are granted, document the justification and any follow-up actions required.
136
137---
138
139## Tips for using `cargo vet`:
140
141- **Update _imports.lock_**:
142 - Import trusted third party audits to reduce the number of new audits to be performed. Running `cargo vet` without `--locked` fetches new imports and updates _imports.lock_ with any audits that are helpful for our project.
143
144- **Add exemptions**:
145 - If an audit cannot be performed for some dependency due to time sensitivity or business justified reasons, use `cargo vet add-exemption <PACKAGE> <VERSION>` to add the dependency to exemptions in _config.toml_
146 - To add all remaining audits to exemptions at once, use `cargo vet regenerate exemptions`
147
148- **Prune unnecessary entries**:
149 - Remove unnecessary exemptions and imports using `cargo vet prune` \ No newline at end of file
diff --git a/embassy-mcxa/supply-chain/audits.toml b/embassy-mcxa/supply-chain/audits.toml
new file mode 100644
index 000000000..871109648
--- /dev/null
+++ b/embassy-mcxa/supply-chain/audits.toml
@@ -0,0 +1,349 @@
1
2# cargo-vet audits file
3
4[[audits.autocfg]]
5who = "Felipe Balbi <[email protected]>"
6criteria = "safe-to-deploy"
7version = "1.5.0"
8
9[[audits.cc]]
10who = "Felipe Balbi <[email protected]>"
11criteria = "safe-to-deploy"
12version = "1.2.47"
13
14[[audits.cfg-if]]
15who = "Felipe Balbi <[email protected]>"
16criteria = "safe-to-deploy"
17version = "1.0.4"
18
19[[audits.cordyceps]]
20who = "Felipe Balbi <[email protected]>"
21criteria = "safe-to-deploy"
22version = "0.3.4"
23
24[[audits.darling]]
25who = "Felipe Balbi <[email protected]>"
26criteria = "safe-to-deploy"
27version = "0.20.11"
28
29[[audits.darling_core]]
30who = "Felipe Balbi <[email protected]>"
31criteria = "safe-to-deploy"
32version = "0.20.11"
33
34[[audits.darling_macro]]
35who = "Felipe Balbi <[email protected]>"
36criteria = "safe-to-deploy"
37version = "0.20.11"
38
39[[audits.defmt-rtt]]
40who = "Felipe Balbi <[email protected]>"
41criteria = "safe-to-deploy"
42version = "1.0.0"
43notes = "defmt-rtt is used for all our logging purposes. Version 1.0.0 merely stabilizes what was already available previously."
44
45[[audits.defmt-rtt]]
46who = "Felipe Balbi <[email protected]>"
47criteria = "safe-to-deploy"
48delta = "1.0.0 -> 1.1.0"
49
50[[audits.document-features]]
51who = "Felipe Balbi <[email protected]>"
52criteria = "safe-to-deploy"
53version = "0.2.12"
54
55[[audits.document-features]]
56who = "Felipe Balbi <[email protected]>"
57criteria = "safe-to-run"
58version = "0.2.12"
59
60[[audits.embassy-executor]]
61who = "Felipe Balbi <[email protected]>"
62criteria = "safe-to-deploy"
63version = "0.9.1"
64
65[[audits.embassy-executor-macros]]
66who = "Felipe Balbi <[email protected]>"
67criteria = "safe-to-deploy"
68version = "0.7.0"
69
70[[audits.embassy-executor-timer-queue]]
71who = "Felipe Balbi <[email protected]>"
72criteria = "safe-to-deploy"
73version = "0.1.0"
74
75[[audits.embassy-executor-timer-queue]]
76who = "Felipe Balbi <[email protected]>"
77criteria = "safe-to-deploy"
78version = "0.1.0"
79
80[[audits.embassy-time-queue-utils]]
81who = "Felipe Balbi <[email protected]>"
82criteria = "safe-to-deploy"
83version = "0.3.0"
84
85[[audits.find-msvc-tools]]
86who = "Felipe Balbi <[email protected]>"
87criteria = "safe-to-deploy"
88version = "0.1.5"
89
90[[audits.generator]]
91who = "Felipe Balbi <[email protected]>"
92criteria = "safe-to-deploy"
93version = "0.8.7"
94
95[[audits.ident_case]]
96who = "Felipe Balbi <[email protected]>"
97criteria = "safe-to-deploy"
98version = "1.0.1"
99
100[[audits.litrs]]
101who = "Felipe Balbi <[email protected]>"
102criteria = "safe-to-deploy"
103version = "1.0.0"
104
105[[audits.maitake-sync]]
106who = "Felipe Balbi <[email protected]>"
107criteria = "safe-to-deploy"
108version = "0.2.2"
109
110[[audits.mutex-traits]]
111who = "Felipe Balbi <[email protected]>"
112criteria = "safe-to-deploy"
113version = "1.0.1"
114
115[[audits.mycelium-bitfield]]
116who = "Felipe Balbi <[email protected]>"
117criteria = "safe-to-deploy"
118version = "0.1.5"
119
120[[audits.once_cell]]
121who = "Felipe Balbi <[email protected]>"
122criteria = "safe-to-deploy"
123version = "1.20.1"
124
125[[audits.panic-probe]]
126who = "Felipe Balbi <[email protected]>"
127criteria = "safe-to-deploy"
128version = "1.0.0"
129
130[[audits.pin-project]]
131who = "Felipe Balbi <[email protected]>"
132criteria = "safe-to-deploy"
133version = "1.1.10"
134
135[[audits.pin-project-internal]]
136who = "Felipe Balbi <[email protected]>"
137criteria = "safe-to-deploy"
138version = "1.1.10"
139
140[[audits.portable-atomic]]
141who = "Felipe Balbi <[email protected]>"
142criteria = "safe-to-deploy"
143version = "1.11.1"
144
145[[audits.proc-macro2]]
146who = "Felipe Balbi <[email protected]>"
147criteria = "safe-to-deploy"
148version = "1.0.103"
149
150[[audits.quote]]
151who = "Felipe Balbi <[email protected]>"
152criteria = "safe-to-deploy"
153version = "1.0.42"
154
155[[audits.stable_deref_trait]]
156who = "Felipe Balbi <[email protected]>"
157criteria = "safe-to-deploy"
158version = "1.2.1"
159
160[[audits.static_cell]]
161who = "jerrysxie <[email protected]>"
162criteria = "safe-to-run"
163delta = "2.1.0 -> 2.1.1"
164
165[[audits.syn]]
166who = "Felipe Balbi <[email protected]>"
167criteria = "safe-to-deploy"
168version = "2.0.110"
169
170[[audits.syn]]
171who = "Felipe Balbi <[email protected]>"
172criteria = "safe-to-deploy"
173delta = "2.0.100 -> 2.0.109"
174
175[[audits.thiserror]]
176who = "Felipe Balbi <[email protected]>"
177criteria = "safe-to-deploy"
178version = "2.0.17"
179
180[[audits.thiserror-impl]]
181who = "Felipe Balbi <[email protected]>"
182criteria = "safe-to-deploy"
183version = "2.0.17"
184
185[[audits.unicode-ident]]
186who = "Felipe Balbi <[email protected]>"
187criteria = "safe-to-deploy"
188version = "1.0.22"
189
190[[audits.valuable]]
191who = "Felipe Balbi <[email protected]>"
192criteria = "safe-to-deploy"
193version = "0.1.1"
194
195[[trusted.aho-corasick]]
196criteria = "safe-to-deploy"
197user-id = 189 # Andrew Gallant (BurntSushi)
198start = "2019-03-28"
199end = "2026-11-26"
200
201[[trusted.cc]]
202criteria = "safe-to-deploy"
203user-id = 55123 # rust-lang-owner
204start = "2022-10-29"
205end = "2026-11-26"
206
207[[trusted.find-msvc-tools]]
208criteria = "safe-to-deploy"
209user-id = 539
210start = "2025-08-29"
211end = "2026-11-26"
212
213[[trusted.libc]]
214criteria = "safe-to-deploy"
215user-id = 55123 # rust-lang-owner
216start = "2024-08-15"
217end = "2026-11-26"
218
219[[trusted.loom]]
220criteria = "safe-to-deploy"
221user-id = 6741 # Alice Ryhl (Darksonn)
222start = "2021-04-12"
223end = "2026-11-26"
224
225[[trusted.memchr]]
226criteria = "safe-to-deploy"
227user-id = 189 # Andrew Gallant (BurntSushi)
228start = "2019-07-07"
229end = "2026-11-26"
230
231[[trusted.paste]]
232criteria = "safe-to-deploy"
233user-id = 3618 # David Tolnay (dtolnay)
234start = "2019-03-19"
235end = "2026-11-26"
236
237[[trusted.regex-automata]]
238criteria = "safe-to-deploy"
239user-id = 189 # Andrew Gallant (BurntSushi)
240start = "2019-02-25"
241end = "2026-11-26"
242
243[[trusted.regex-syntax]]
244criteria = "safe-to-deploy"
245user-id = 189 # Andrew Gallant (BurntSushi)
246start = "2019-03-30"
247end = "2026-11-26"
248
249[[trusted.rustversion]]
250criteria = "safe-to-deploy"
251user-id = 3618 # David Tolnay (dtolnay)
252start = "2019-07-08"
253end = "2026-11-26"
254
255[[trusted.scoped-tls]]
256criteria = "safe-to-deploy"
257user-id = 1 # Alex Crichton (alexcrichton)
258start = "2019-02-26"
259end = "2026-11-26"
260
261[[trusted.thread_local]]
262criteria = "safe-to-deploy"
263user-id = 2915 # Amanieu d'Antras (Amanieu)
264start = "2019-09-07"
265end = "2026-11-26"
266
267[[trusted.tracing-subscriber]]
268criteria = "safe-to-deploy"
269user-id = 10 # Carl Lerche (carllerche)
270start = "2025-08-29"
271end = "2026-11-26"
272
273[[trusted.valuable]]
274criteria = "safe-to-deploy"
275user-id = 10 # Carl Lerche (carllerche)
276start = "2022-01-03"
277end = "2026-11-26"
278
279[[trusted.windows]]
280criteria = "safe-to-deploy"
281user-id = 64539 # Kenny Kerr (kennykerr)
282start = "2021-01-15"
283end = "2026-11-26"
284
285[[trusted.windows-collections]]
286criteria = "safe-to-deploy"
287user-id = 64539 # Kenny Kerr (kennykerr)
288start = "2025-02-06"
289end = "2026-11-26"
290
291[[trusted.windows-core]]
292criteria = "safe-to-deploy"
293user-id = 64539 # Kenny Kerr (kennykerr)
294start = "2021-11-15"
295end = "2026-11-26"
296
297[[trusted.windows-future]]
298criteria = "safe-to-deploy"
299user-id = 64539 # Kenny Kerr (kennykerr)
300start = "2025-02-10"
301end = "2026-11-26"
302
303[[trusted.windows-implement]]
304criteria = "safe-to-deploy"
305user-id = 64539 # Kenny Kerr (kennykerr)
306start = "2022-01-27"
307end = "2026-11-26"
308
309[[trusted.windows-interface]]
310criteria = "safe-to-deploy"
311user-id = 64539 # Kenny Kerr (kennykerr)
312start = "2022-02-18"
313end = "2026-11-26"
314
315[[trusted.windows-link]]
316criteria = "safe-to-deploy"
317user-id = 64539 # Kenny Kerr (kennykerr)
318start = "2024-07-17"
319end = "2026-11-26"
320
321[[trusted.windows-numerics]]
322criteria = "safe-to-deploy"
323user-id = 64539 # Kenny Kerr (kennykerr)
324start = "2023-05-15"
325end = "2026-11-26"
326
327[[trusted.windows-result]]
328criteria = "safe-to-deploy"
329user-id = 64539 # Kenny Kerr (kennykerr)
330start = "2024-02-02"
331end = "2026-11-26"
332
333[[trusted.windows-strings]]
334criteria = "safe-to-deploy"
335user-id = 64539 # Kenny Kerr (kennykerr)
336start = "2024-02-02"
337end = "2026-11-26"
338
339[[trusted.windows-sys]]
340criteria = "safe-to-deploy"
341user-id = 64539 # Kenny Kerr (kennykerr)
342start = "2021-11-15"
343end = "2026-11-26"
344
345[[trusted.windows-threading]]
346criteria = "safe-to-deploy"
347user-id = 64539 # Kenny Kerr (kennykerr)
348start = "2025-04-29"
349end = "2026-11-26"
diff --git a/embassy-mcxa/supply-chain/config.toml b/embassy-mcxa/supply-chain/config.toml
new file mode 100644
index 000000000..5682db9ea
--- /dev/null
+++ b/embassy-mcxa/supply-chain/config.toml
@@ -0,0 +1,141 @@
1
2# cargo-vet config file
3
4[cargo-vet]
5version = "0.10"
6
7[imports.OpenDevicePartnership]
8url = "https://raw.githubusercontent.com/OpenDevicePartnership/rust-crate-audits/main/audits.toml"
9
10[imports.bytecode-alliance]
11url = "https://raw.githubusercontent.com/bytecodealliance/wasmtime/main/supply-chain/audits.toml"
12
13[imports.google]
14url = "https://raw.githubusercontent.com/google/rust-crate-audits/main/audits.toml"
15
16[imports.mozilla]
17url = "https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml"
18
19[[exemptions.bare-metal]]
20version = "0.2.5"
21criteria = "safe-to-deploy"
22
23[[exemptions.bitfield]]
24version = "0.13.2"
25criteria = "safe-to-deploy"
26
27[[exemptions.cortex-m]]
28version = "0.7.7"
29criteria = "safe-to-deploy"
30
31[[exemptions.cortex-m-rt]]
32version = "0.7.5"
33criteria = "safe-to-deploy"
34
35[[exemptions.cortex-m-rt-macros]]
36version = "0.7.5"
37criteria = "safe-to-deploy"
38
39[[exemptions.critical-section]]
40version = "1.2.0"
41criteria = "safe-to-deploy"
42
43[[exemptions.defmt]]
44version = "1.0.1"
45criteria = "safe-to-deploy"
46
47[[exemptions.defmt-macros]]
48version = "1.0.1"
49criteria = "safe-to-deploy"
50
51[[exemptions.defmt-parser]]
52version = "1.0.0"
53criteria = "safe-to-deploy"
54
55[[exemptions.embassy-embedded-hal]]
56version = "0.5.0"
57criteria = "safe-to-deploy"
58
59[[exemptions.embassy-futures]]
60version = "0.1.2"
61criteria = "safe-to-deploy"
62
63[[exemptions.embassy-hal-internal]]
64version = "0.3.0"
65criteria = "safe-to-deploy"
66
67[[exemptions.embassy-sync]]
68version = "0.7.2"
69criteria = "safe-to-deploy"
70
71[[exemptions.embassy-time]]
72version = "0.5.0"
73criteria = "safe-to-deploy"
74
75[[exemptions.embassy-time-driver]]
76version = "0.2.1"
77criteria = "safe-to-deploy"
78
79[[exemptions.embedded-hal]]
80version = "0.2.7"
81criteria = "safe-to-deploy"
82
83[[exemptions.embedded-hal]]
84version = "1.0.0"
85criteria = "safe-to-deploy"
86
87[[exemptions.embedded-hal-async]]
88version = "1.0.0"
89criteria = "safe-to-deploy"
90
91[[exemptions.embedded-hal-nb]]
92version = "1.0.0"
93criteria = "safe-to-deploy"
94
95[[exemptions.embedded-io-async]]
96version = "0.6.1"
97criteria = "safe-to-deploy"
98
99[[exemptions.embedded-storage]]
100version = "0.3.1"
101criteria = "safe-to-deploy"
102
103[[exemptions.embedded-storage-async]]
104version = "0.4.1"
105criteria = "safe-to-deploy"
106
107[[exemptions.hash32]]
108version = "0.3.1"
109criteria = "safe-to-deploy"
110
111[[exemptions.heapless]]
112version = "0.8.0"
113criteria = "safe-to-deploy"
114
115[[exemptions.proc-macro-error-attr2]]
116version = "2.0.0"
117criteria = "safe-to-deploy"
118
119[[exemptions.proc-macro-error2]]
120version = "2.0.1"
121criteria = "safe-to-deploy"
122
123[[exemptions.rustc_version]]
124version = "0.2.3"
125criteria = "safe-to-deploy"
126
127[[exemptions.semver]]
128version = "0.9.0"
129criteria = "safe-to-deploy"
130
131[[exemptions.semver-parser]]
132version = "0.7.0"
133criteria = "safe-to-deploy"
134
135[[exemptions.vcell]]
136version = "0.1.3"
137criteria = "safe-to-deploy"
138
139[[exemptions.volatile-register]]
140version = "0.2.2"
141criteria = "safe-to-deploy"
diff --git a/embassy-mcxa/supply-chain/imports.lock b/embassy-mcxa/supply-chain/imports.lock
new file mode 100644
index 000000000..dbd1235b0
--- /dev/null
+++ b/embassy-mcxa/supply-chain/imports.lock
@@ -0,0 +1,523 @@
1
2# cargo-vet imports lock
3
4[[publisher.aho-corasick]]
5version = "1.1.4"
6when = "2025-10-28"
7user-id = 189
8user-login = "BurntSushi"
9user-name = "Andrew Gallant"
10
11[[publisher.libc]]
12version = "0.2.177"
13when = "2025-10-09"
14user-id = 55123
15user-login = "rust-lang-owner"
16
17[[publisher.loom]]
18version = "0.7.2"
19when = "2024-04-23"
20user-id = 6741
21user-login = "Darksonn"
22user-name = "Alice Ryhl"
23
24[[publisher.memchr]]
25version = "2.7.6"
26when = "2025-09-25"
27user-id = 189
28user-login = "BurntSushi"
29user-name = "Andrew Gallant"
30
31[[publisher.paste]]
32version = "1.0.15"
33when = "2024-05-07"
34user-id = 3618
35user-login = "dtolnay"
36user-name = "David Tolnay"
37
38[[publisher.regex-automata]]
39version = "0.4.13"
40when = "2025-10-13"
41user-id = 189
42user-login = "BurntSushi"
43user-name = "Andrew Gallant"
44
45[[publisher.regex-syntax]]
46version = "0.8.8"
47when = "2025-10-13"
48user-id = 189
49user-login = "BurntSushi"
50user-name = "Andrew Gallant"
51
52[[publisher.rustversion]]
53version = "1.0.22"
54when = "2025-08-08"
55user-id = 3618
56user-login = "dtolnay"
57user-name = "David Tolnay"
58
59[[publisher.scoped-tls]]
60version = "1.0.1"
61when = "2022-10-31"
62user-id = 1
63user-login = "alexcrichton"
64user-name = "Alex Crichton"
65
66[[publisher.thread_local]]
67version = "1.1.9"
68when = "2025-06-12"
69user-id = 2915
70user-login = "Amanieu"
71user-name = "Amanieu d'Antras"
72
73[[publisher.tracing-subscriber]]
74version = "0.3.20"
75when = "2025-08-29"
76user-id = 10
77user-login = "carllerche"
78user-name = "Carl Lerche"
79
80[[publisher.windows]]
81version = "0.61.3"
82when = "2025-06-12"
83user-id = 64539
84user-login = "kennykerr"
85user-name = "Kenny Kerr"
86
87[[publisher.windows-collections]]
88version = "0.2.0"
89when = "2025-03-18"
90user-id = 64539
91user-login = "kennykerr"
92user-name = "Kenny Kerr"
93
94[[publisher.windows-core]]
95version = "0.61.2"
96when = "2025-05-19"
97user-id = 64539
98user-login = "kennykerr"
99user-name = "Kenny Kerr"
100
101[[publisher.windows-future]]
102version = "0.2.1"
103when = "2025-05-15"
104user-id = 64539
105user-login = "kennykerr"
106user-name = "Kenny Kerr"
107
108[[publisher.windows-implement]]
109version = "0.60.2"
110when = "2025-10-06"
111user-id = 64539
112user-login = "kennykerr"
113user-name = "Kenny Kerr"
114
115[[publisher.windows-interface]]
116version = "0.59.3"
117when = "2025-10-06"
118user-id = 64539
119user-login = "kennykerr"
120user-name = "Kenny Kerr"
121
122[[publisher.windows-link]]
123version = "0.1.3"
124when = "2025-06-12"
125user-id = 64539
126user-login = "kennykerr"
127user-name = "Kenny Kerr"
128
129[[publisher.windows-link]]
130version = "0.2.1"
131when = "2025-10-06"
132user-id = 64539
133user-login = "kennykerr"
134user-name = "Kenny Kerr"
135
136[[publisher.windows-numerics]]
137version = "0.2.0"
138when = "2025-03-18"
139user-id = 64539
140user-login = "kennykerr"
141user-name = "Kenny Kerr"
142
143[[publisher.windows-result]]
144version = "0.3.4"
145when = "2025-05-19"
146user-id = 64539
147user-login = "kennykerr"
148user-name = "Kenny Kerr"
149
150[[publisher.windows-strings]]
151version = "0.4.2"
152when = "2025-05-19"
153user-id = 64539
154user-login = "kennykerr"
155user-name = "Kenny Kerr"
156
157[[publisher.windows-sys]]
158version = "0.61.2"
159when = "2025-10-06"
160user-id = 64539
161user-login = "kennykerr"
162user-name = "Kenny Kerr"
163
164[[publisher.windows-threading]]
165version = "0.1.0"
166when = "2025-05-15"
167user-id = 64539
168user-login = "kennykerr"
169user-name = "Kenny Kerr"
170
171[audits.OpenDevicePartnership.audits]
172
173[[audits.bytecode-alliance.audits.embedded-io]]
174who = "Alex Crichton <[email protected]>"
175criteria = "safe-to-deploy"
176version = "0.4.0"
177notes = "No `unsafe` code and only uses `std` in ways one would expect the crate to do so."
178
179[[audits.bytecode-alliance.audits.embedded-io]]
180who = "Alex Crichton <[email protected]>"
181criteria = "safe-to-deploy"
182delta = "0.4.0 -> 0.6.1"
183notes = "Major updates, but almost all safe code. Lots of pruning/deletions, nothing out of the ordrinary."
184
185[[audits.bytecode-alliance.audits.futures-core]]
186who = "Pat Hickey <[email protected]>"
187criteria = "safe-to-deploy"
188version = "0.3.27"
189notes = "Unsafe used to implement a concurrency primitive AtomicWaker. Well-commented and not obviously incorrect. Like my other audits of these concurrency primitives inside the futures family, I couldn't certify that it is correct without formal methods, but that is out of scope for this vetting."
190
191[[audits.bytecode-alliance.audits.futures-core]]
192who = "Pat Hickey <[email protected]>"
193criteria = "safe-to-deploy"
194delta = "0.3.28 -> 0.3.31"
195
196[[audits.bytecode-alliance.audits.futures-sink]]
197who = "Pat Hickey <[email protected]>"
198criteria = "safe-to-deploy"
199version = "0.3.27"
200
201[[audits.bytecode-alliance.audits.futures-sink]]
202who = "Pat Hickey <[email protected]>"
203criteria = "safe-to-deploy"
204delta = "0.3.28 -> 0.3.31"
205
206[[audits.bytecode-alliance.audits.log]]
207who = "Alex Crichton <[email protected]>"
208criteria = "safe-to-deploy"
209delta = "0.4.22 -> 0.4.27"
210notes = "Lots of minor updates to macros and such, nothing touching `unsafe`"
211
212[[audits.bytecode-alliance.audits.log]]
213who = "Alex Crichton <[email protected]>"
214criteria = "safe-to-deploy"
215delta = "0.4.27 -> 0.4.28"
216notes = "Minor doc updates and lots new tests, nothing out of the ordinary."
217
218[[audits.bytecode-alliance.audits.matchers]]
219who = "Pat Hickey <[email protected]>"
220criteria = "safe-to-deploy"
221version = "0.1.0"
222
223[[audits.bytecode-alliance.audits.matchers]]
224who = "Alex Crichton <[email protected]>"
225criteria = "safe-to-deploy"
226delta = "0.1.0 -> 0.2.0"
227notes = "Some unsafe code, but not more than before. Nothing awry."
228
229[[audits.bytecode-alliance.audits.nu-ansi-term]]
230who = "Pat Hickey <[email protected]>"
231criteria = "safe-to-deploy"
232version = "0.46.0"
233notes = "one use of unsafe to call windows specific api to get console handle."
234
235[[audits.bytecode-alliance.audits.nu-ansi-term]]
236who = "Alex Crichton <[email protected]>"
237criteria = "safe-to-deploy"
238delta = "0.46.0 -> 0.50.1"
239notes = "Lots of stylistic/rust-related chanegs, plus new features, but nothing out of the ordrinary."
240
241[[audits.bytecode-alliance.audits.nu-ansi-term]]
242who = "Alex Crichton <[email protected]>"
243criteria = "safe-to-deploy"
244delta = "0.50.1 -> 0.50.3"
245notes = "CI changes, Rust changes, nothing out of the ordinary."
246
247[[audits.bytecode-alliance.audits.sharded-slab]]
248who = "Pat Hickey <[email protected]>"
249criteria = "safe-to-deploy"
250version = "0.1.4"
251notes = "I always really enjoy reading eliza's code, she left perfect comments at every use of unsafe."
252
253[[audits.bytecode-alliance.audits.shlex]]
254who = "Alex Crichton <[email protected]>"
255criteria = "safe-to-deploy"
256version = "1.1.0"
257notes = "Only minor `unsafe` code blocks which look valid and otherwise does what it says on the tin."
258
259[[audits.bytecode-alliance.audits.tracing-attributes]]
260who = "Alex Crichton <[email protected]>"
261criteria = "safe-to-deploy"
262delta = "0.1.28 -> 0.1.30"
263notes = "Few code changes, a pretty minor update."
264
265[[audits.bytecode-alliance.audits.tracing-core]]
266who = "Alex Crichton <[email protected]>"
267criteria = "safe-to-deploy"
268delta = "0.1.33 -> 0.1.34"
269notes = "Mostly just an update with Rust stylistic conventions changing. Nothing awry."
270
271[[audits.bytecode-alliance.audits.tracing-log]]
272who = "Alex Crichton <[email protected]>"
273criteria = "safe-to-deploy"
274version = "0.1.3"
275notes = """
276This is a standard adapter between the `log` ecosystem and the `tracing`
277ecosystem. There's one `unsafe` block in this crate and it's well-scoped.
278"""
279
280[[audits.bytecode-alliance.audits.tracing-log]]
281who = "Alex Crichton <[email protected]>"
282criteria = "safe-to-deploy"
283delta = "0.1.3 -> 0.2.0"
284notes = "Nothing out of the ordinary, a typical major version update and nothing awry."
285
286[[audits.google.audits.bitflags]]
287who = "Lukasz Anforowicz <[email protected]>"
288criteria = "safe-to-deploy"
289version = "1.3.2"
290notes = """
291Security review of earlier versions of the crate can be found at
292(Google-internal, sorry): go/image-crate-chromium-security-review
293
294The crate exposes a function marked as `unsafe`, but doesn't use any
295`unsafe` blocks (except for tests of the single `unsafe` function). I
296think this justifies marking this crate as `ub-risk-1`.
297
298Additional review comments can be found at https://crrev.com/c/4723145/31
299"""
300aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
301
302[[audits.google.audits.byteorder]]
303who = "danakj <[email protected]>"
304criteria = "safe-to-deploy"
305version = "1.5.0"
306notes = "Unsafe review in https://crrev.com/c/5838022"
307aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
308
309[[audits.google.audits.lazy_static]]
310who = "Lukasz Anforowicz <[email protected]>"
311criteria = "safe-to-deploy"
312version = "1.4.0"
313notes = '''
314I grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits.
315
316There are two places where `unsafe` is used. Unsafe review notes can be found
317in https://crrev.com/c/5347418.
318
319This crate has been added to Chromium in https://crrev.com/c/3321895.
320'''
321aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
322
323[[audits.google.audits.lazy_static]]
324who = "Lukasz Anforowicz <[email protected]>"
325criteria = "safe-to-deploy"
326delta = "1.4.0 -> 1.5.0"
327notes = "Unsafe review notes: https://crrev.com/c/5650836"
328aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
329
330[[audits.google.audits.log]]
331who = "danakj <[email protected]>"
332criteria = "safe-to-deploy"
333version = "0.4.22"
334notes = """
335Unsafe review in https://docs.google.com/document/d/1IXQbD1GhTRqNHIGxq6yy7qHqxeO4CwN5noMFXnqyDIM/edit?usp=sharing
336
337Unsafety is generally very well-documented, with one exception, which we
338describe in the review doc.
339"""
340aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
341
342[[audits.google.audits.nb]]
343who = "George Burgess IV <[email protected]>"
344criteria = "safe-to-deploy"
345version = "1.0.0"
346aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
347
348[[audits.google.audits.nb]]
349who = "George Burgess IV <[email protected]>"
350criteria = "safe-to-deploy"
351delta = "1.0.0 -> 0.1.3"
352aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
353
354[[audits.google.audits.nb]]
355who = "George Burgess IV <[email protected]>"
356criteria = "safe-to-deploy"
357delta = "1.0.0 -> 1.1.0"
358aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
359
360[[audits.google.audits.num-traits]]
361who = "Manish Goregaokar <[email protected]>"
362criteria = "safe-to-deploy"
363version = "0.2.19"
364notes = "Contains a single line of float-to-int unsafe with decent safety comments"
365aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
366
367[[audits.google.audits.pin-project-lite]]
368who = "David Koloski <[email protected]>"
369criteria = "safe-to-deploy"
370version = "0.2.9"
371notes = "Reviewed on https://fxrev.dev/824504"
372aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT"
373
374[[audits.google.audits.pin-project-lite]]
375who = "David Koloski <[email protected]>"
376criteria = "safe-to-deploy"
377delta = "0.2.9 -> 0.2.13"
378notes = "Audited at https://fxrev.dev/946396"
379aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT"
380
381[[audits.google.audits.smallvec]]
382who = "Manish Goregaokar <[email protected]>"
383criteria = "safe-to-deploy"
384version = "1.13.2"
385aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
386
387[[audits.google.audits.smallvec]]
388who = "Jonathan Hao <[email protected]>"
389criteria = "safe-to-deploy"
390delta = "1.13.2 -> 1.14.0"
391notes = """
392WARNING: This certification is a result of a **partial** audit. The
393`malloc_size_of` feature has **not** been audited. This feature does
394not explicitly document its safety requirements.
395See also https://chromium-review.googlesource.com/c/chromium/src/+/6275133/comment/ea0d7a93_98051a2e/
396and https://github.com/servo/malloc_size_of/issues/8.
397This feature is banned in gnrt_config.toml.
398"""
399aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
400
401[[audits.google.audits.void]]
402who = "George Burgess IV <[email protected]>"
403criteria = "safe-to-deploy"
404version = "1.0.2"
405aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
406
407[[audits.mozilla.audits.futures-core]]
408who = "Mike Hommey <[email protected]>"
409criteria = "safe-to-deploy"
410delta = "0.3.27 -> 0.3.28"
411aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
412
413[[audits.mozilla.audits.futures-sink]]
414who = "Mike Hommey <[email protected]>"
415criteria = "safe-to-deploy"
416delta = "0.3.27 -> 0.3.28"
417aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
418
419[[audits.mozilla.audits.once_cell]]
420who = "Erich Gubler <[email protected]>"
421criteria = "safe-to-deploy"
422delta = "1.20.1 -> 1.20.2"
423notes = "This update works around a Cargo bug that forces the addition of `portable-atomic` into a lockfile, which we have never needed to use."
424aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
425
426[[audits.mozilla.audits.once_cell]]
427who = "Erich Gubler <[email protected]>"
428criteria = "safe-to-deploy"
429delta = "1.20.2 -> 1.20.3"
430aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
431
432[[audits.mozilla.audits.once_cell]]
433who = "Erich Gubler <[email protected]>"
434criteria = "safe-to-deploy"
435delta = "1.20.3 -> 1.21.1"
436aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
437
438[[audits.mozilla.audits.once_cell]]
439who = "Erich Gubler <[email protected]>"
440criteria = "safe-to-deploy"
441delta = "1.21.1 -> 1.21.3"
442aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
443
444[[audits.mozilla.audits.pin-project-lite]]
445who = "Mike Hommey <[email protected]>"
446criteria = "safe-to-deploy"
447delta = "0.2.13 -> 0.2.14"
448aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
449
450[[audits.mozilla.audits.pin-project-lite]]
451who = "Nika Layzell <[email protected]>"
452criteria = "safe-to-deploy"
453delta = "0.2.14 -> 0.2.16"
454notes = """
455Only functional change is to work around a bug in the negative_impls feature
456(https://github.com/taiki-e/pin-project/issues/340#issuecomment-2432146009)
457"""
458aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml"
459
460[[audits.mozilla.audits.sharded-slab]]
461who = "Mark Hammond <[email protected]>"
462criteria = "safe-to-deploy"
463delta = "0.1.4 -> 0.1.7"
464aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
465
466[[audits.mozilla.audits.shlex]]
467who = "Max Inden <[email protected]>"
468criteria = "safe-to-deploy"
469delta = "1.1.0 -> 1.3.0"
470aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
471
472[[audits.mozilla.audits.smallvec]]
473who = "Erich Gubler <[email protected]>"
474criteria = "safe-to-deploy"
475delta = "1.14.0 -> 1.15.1"
476aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
477
478[[audits.mozilla.audits.tracing]]
479who = "Alex Franchuk <[email protected]>"
480criteria = "safe-to-deploy"
481version = "0.1.37"
482notes = """
483There's only one unsafe impl, and its purpose is to ensure correct behavior by
484creating a non-Send marker type (it has nothing to do with soundness). All
485dependencies make sense, and no side-effectful std functions are used.
486"""
487aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
488
489[[audits.mozilla.audits.tracing]]
490who = "Mark Hammond <[email protected]>"
491criteria = "safe-to-deploy"
492delta = "0.1.37 -> 0.1.41"
493aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
494
495[[audits.mozilla.audits.tracing-attributes]]
496who = "Alex Franchuk <[email protected]>"
497criteria = "safe-to-deploy"
498version = "0.1.24"
499notes = "No unsafe code, macros extensively tested and produce reasonable code."
500aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
501
502[[audits.mozilla.audits.tracing-attributes]]
503who = "Mark Hammond <[email protected]>"
504criteria = "safe-to-deploy"
505delta = "0.1.24 -> 0.1.28"
506aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
507
508[[audits.mozilla.audits.tracing-core]]
509who = "Alex Franchuk <[email protected]>"
510criteria = "safe-to-deploy"
511version = "0.1.30"
512notes = """
513Most unsafe code is in implementing non-std sync primitives. Unsafe impls are
514logically correct and justified in comments, and unsafe code is sound and
515justified in comments.
516"""
517aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
518
519[[audits.mozilla.audits.tracing-core]]
520who = "Mark Hammond <[email protected]>"
521criteria = "safe-to-deploy"
522delta = "0.1.30 -> 0.1.33"
523aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
diff --git a/embassy-mcxa/tools/run_and_attach_rtt.sh b/embassy-mcxa/tools/run_and_attach_rtt.sh
new file mode 100644
index 000000000..13041d06b
--- /dev/null
+++ b/embassy-mcxa/tools/run_and_attach_rtt.sh
@@ -0,0 +1,24 @@
1#!/usr/bin/env bash
2set -euo pipefail
3
4ELF="${1:-target/thumbv8m.main-none-eabihf/debug/examples/hello}"
5PROBE_ID="${2:-1fc9:0143:H3AYDQVQMTROB}"
6CHIP="${3:-MCXA276}"
7SPEED="${4:-1000}"
8
9# 1) Flash & run using the existing run.sh (probe is in use only during this step)
10./run.sh "$ELF"
11
12# 2) Give target a short moment to boot and set up RTT CB in RAM
13sleep 0.5
14
15# 3) Attach RTT/defmt using probe-rs (no flashing)
16exec probe-rs attach \
17 --chip "$CHIP" \
18 --probe "$PROBE_ID" \
19 --protocol swd \
20 --speed "$SPEED" \
21 "$ELF" \
22 --rtt-scan-memory \
23 --log-format oneline
24
diff --git a/embassy-mcxa/tools/run_jlink_noblock.sh b/embassy-mcxa/tools/run_jlink_noblock.sh
new file mode 100644
index 000000000..3ea1f2b4b
--- /dev/null
+++ b/embassy-mcxa/tools/run_jlink_noblock.sh
@@ -0,0 +1,76 @@
1#!/usr/bin/env bash
2set -euo pipefail
3
4ELF="${1:-}"
5PROBE_ID="${2:-1366:0101:000600110607}" # default to your J-Link
6CHIP="${3:-MCXA276}"
7SPEED="${4:-1000}"
8PORT="${PROBE_RS_GDB_PORT:-1337}"
9
10if [[ -z "${ELF}" || ! -f "${ELF}" ]]; then
11 echo "Usage: $0 <elf> [probe-id] [chip] [speed-khz]" >&2
12 exit 1
13fi
14
15if ! command -v probe-rs >/dev/null 2>&1; then
16 echo "probe-rs not found (cargo install probe-rs --features cli)" >&2
17 exit 1
18fi
19if ! command -v gdb-multiarch >/dev/null 2>&1; then
20 echo "gdb-multiarch not found; install it (e.g., sudo apt install gdb-multiarch)." >&2
21 exit 1
22fi
23
24# Start probe-rs GDB server
25SERVER_LOG=$(mktemp)
26probe-rs gdb --chip "${CHIP}" --protocol swd --speed "${SPEED}" --non-interactive "${ELF}" --probe "${PROBE_ID}" \
27 >"${SERVER_LOG}" 2>&1 &
28GDB_SERVER_PID=$!
29
30# Wait for readiness
31for _ in {1..50}; do
32 if grep -q "Firing up GDB stub" "${SERVER_LOG}"; then break; fi
33 if grep -q "Connecting to the chip was unsuccessful" "${SERVER_LOG}"; then
34 echo "probe-rs gdb server failed. Log:" >&2
35 sed -e 's/^/ /' "${SERVER_LOG}" >&2 || true
36 kill "${GDB_SERVER_PID}" 2>/dev/null || true
37 exit 1
38 fi
39 sleep 0.1
40done
41
42# GDB script: load, resume, detach
43GDB_SCRIPT=$(mktemp)
44cat >"${GDB_SCRIPT}" <<EOF
45set pagination off
46set confirm off
47set mem inaccessible-by-default off
48
49target remote :${PORT}
50monitor halt
51load
52set language c
53# Start clean from Reset vector in RAM
54set {int}0xE000ED08 = 0x20000000
55set \$xpsr = 0x01000000
56set \$sp = 0x20020000
57set \$pc = Reset
58monitor resume
59detach
60quit
61EOF
62
63# Run GDB to program and resume target, then exit (probe released)
64if ! gdb-multiarch -q -x "${GDB_SCRIPT}" "${ELF}"; then
65 echo "GDB failed; server log:" >&2
66 sed -e 's/^/ /' "${SERVER_LOG}" >&2 || true
67 kill "${GDB_SERVER_PID}" 2>/dev/null || true
68 exit 1
69fi
70
71# Stop server now that we've detached
72kill "${GDB_SERVER_PID}" 2>/dev/null || true
73rm -f "${GDB_SCRIPT}" "${SERVER_LOG}" || true
74
75echo "Flashed, resumed, and detached (probe free)."
76
diff --git a/examples/mcxa/.cargo/config.toml b/examples/mcxa/.cargo/config.toml
new file mode 100644
index 000000000..aedc55b06
--- /dev/null
+++ b/examples/mcxa/.cargo/config.toml
@@ -0,0 +1,17 @@
1[target.thumbv8m.main-none-eabihf]
2runner = 'probe-rs run --chip MCXA276 --preverify --verify --protocol swd --speed 12000'
3
4rustflags = [
5 "-C", "linker=flip-link",
6 "-C", "link-arg=-Tlink.x",
7 "-C", "link-arg=-Tdefmt.x",
8 # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
9 # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
10 "-C", "link-arg=--nmagic",
11]
12
13[build]
14target = "thumbv8m.main-none-eabihf" # Cortex-M33
15
16[env]
17DEFMT_LOG = "trace"
diff --git a/examples/mcxa/.gitignore b/examples/mcxa/.gitignore
new file mode 100644
index 000000000..2f7896d1d
--- /dev/null
+++ b/examples/mcxa/.gitignore
@@ -0,0 +1 @@
target/
diff --git a/examples/mcxa/Cargo.lock b/examples/mcxa/Cargo.lock
new file mode 100644
index 000000000..c6e864df2
--- /dev/null
+++ b/examples/mcxa/Cargo.lock
@@ -0,0 +1,1555 @@
1# This file is automatically @generated by Cargo.
2# It is not intended for manual editing.
3version = 4
4
5[[package]]
6name = "aho-corasick"
7version = "1.1.4"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
10dependencies = [
11 "memchr",
12]
13
14[[package]]
15name = "anyhow"
16version = "1.0.100"
17source = "registry+https://github.com/rust-lang/crates.io-index"
18checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
19
20[[package]]
21name = "askama"
22version = "0.14.0"
23source = "registry+https://github.com/rust-lang/crates.io-index"
24checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4"
25dependencies = [
26 "askama_derive",
27 "itoa",
28 "percent-encoding",
29 "serde",
30 "serde_json",
31]
32
33[[package]]
34name = "askama_derive"
35version = "0.14.0"
36source = "registry+https://github.com/rust-lang/crates.io-index"
37checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f"
38dependencies = [
39 "askama_parser",
40 "memchr",
41 "proc-macro2",
42 "quote",
43 "rustc-hash",
44 "syn 2.0.110",
45]
46
47[[package]]
48name = "askama_parser"
49version = "0.14.0"
50source = "registry+https://github.com/rust-lang/crates.io-index"
51checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358"
52dependencies = [
53 "memchr",
54 "winnow 0.7.13",
55]
56
57[[package]]
58name = "autocfg"
59version = "1.5.0"
60source = "registry+https://github.com/rust-lang/crates.io-index"
61checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
62
63[[package]]
64name = "bare-metal"
65version = "0.2.5"
66source = "registry+https://github.com/rust-lang/crates.io-index"
67checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
68dependencies = [
69 "rustc_version",
70]
71
72[[package]]
73name = "bitfield"
74version = "0.13.2"
75source = "registry+https://github.com/rust-lang/crates.io-index"
76checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
77
78[[package]]
79name = "bitflags"
80version = "1.3.2"
81source = "registry+https://github.com/rust-lang/crates.io-index"
82checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
83
84[[package]]
85name = "bitflags"
86version = "2.10.0"
87source = "registry+https://github.com/rust-lang/crates.io-index"
88checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
89
90[[package]]
91name = "bitvec"
92version = "1.0.1"
93source = "registry+https://github.com/rust-lang/crates.io-index"
94checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
95dependencies = [
96 "funty",
97 "radium",
98 "tap",
99 "wyz",
100]
101
102[[package]]
103name = "byteorder"
104version = "1.5.0"
105source = "registry+https://github.com/rust-lang/crates.io-index"
106checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
107
108[[package]]
109name = "cc"
110version = "1.2.47"
111source = "registry+https://github.com/rust-lang/crates.io-index"
112checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07"
113dependencies = [
114 "find-msvc-tools",
115 "shlex",
116]
117
118[[package]]
119name = "cfg-if"
120version = "1.0.4"
121source = "registry+https://github.com/rust-lang/crates.io-index"
122checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
123
124[[package]]
125name = "convert_case"
126version = "0.6.0"
127source = "registry+https://github.com/rust-lang/crates.io-index"
128checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
129dependencies = [
130 "unicode-segmentation",
131]
132
133[[package]]
134name = "cordyceps"
135version = "0.3.4"
136source = "registry+https://github.com/rust-lang/crates.io-index"
137checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a"
138dependencies = [
139 "loom",
140 "tracing",
141]
142
143[[package]]
144name = "cortex-m"
145version = "0.7.7"
146source = "registry+https://github.com/rust-lang/crates.io-index"
147checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
148dependencies = [
149 "bare-metal",
150 "bitfield",
151 "critical-section",
152 "embedded-hal 0.2.7",
153 "volatile-register",
154]
155
156[[package]]
157name = "cortex-m-rt"
158version = "0.7.5"
159source = "registry+https://github.com/rust-lang/crates.io-index"
160checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6"
161dependencies = [
162 "cortex-m-rt-macros",
163]
164
165[[package]]
166name = "cortex-m-rt-macros"
167version = "0.7.5"
168source = "registry+https://github.com/rust-lang/crates.io-index"
169checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
170dependencies = [
171 "proc-macro2",
172 "quote",
173 "syn 2.0.110",
174]
175
176[[package]]
177name = "critical-section"
178version = "1.2.0"
179source = "registry+https://github.com/rust-lang/crates.io-index"
180checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
181
182[[package]]
183name = "darling"
184version = "0.20.11"
185source = "registry+https://github.com/rust-lang/crates.io-index"
186checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
187dependencies = [
188 "darling_core",
189 "darling_macro",
190]
191
192[[package]]
193name = "darling_core"
194version = "0.20.11"
195source = "registry+https://github.com/rust-lang/crates.io-index"
196checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
197dependencies = [
198 "fnv",
199 "ident_case",
200 "proc-macro2",
201 "quote",
202 "strsim",
203 "syn 2.0.110",
204]
205
206[[package]]
207name = "darling_macro"
208version = "0.20.11"
209source = "registry+https://github.com/rust-lang/crates.io-index"
210checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
211dependencies = [
212 "darling_core",
213 "quote",
214 "syn 2.0.110",
215]
216
217[[package]]
218name = "dd-manifest-tree"
219version = "1.0.0"
220source = "registry+https://github.com/rust-lang/crates.io-index"
221checksum = "5793572036e0a6638977c7370c6afc423eac848ee8495f079b8fd3964de7b9f9"
222dependencies = [
223 "toml",
224]
225
226[[package]]
227name = "defmt"
228version = "1.0.1"
229source = "registry+https://github.com/rust-lang/crates.io-index"
230checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78"
231dependencies = [
232 "bitflags 1.3.2",
233 "defmt-macros",
234]
235
236[[package]]
237name = "defmt-macros"
238version = "1.0.1"
239source = "registry+https://github.com/rust-lang/crates.io-index"
240checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e"
241dependencies = [
242 "defmt-parser",
243 "proc-macro-error2",
244 "proc-macro2",
245 "quote",
246 "syn 2.0.110",
247]
248
249[[package]]
250name = "defmt-parser"
251version = "1.0.0"
252source = "registry+https://github.com/rust-lang/crates.io-index"
253checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
254dependencies = [
255 "thiserror",
256]
257
258[[package]]
259name = "defmt-rtt"
260version = "1.1.0"
261source = "registry+https://github.com/rust-lang/crates.io-index"
262checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e"
263dependencies = [
264 "critical-section",
265 "defmt",
266]
267
268[[package]]
269name = "device-driver"
270version = "1.0.7"
271source = "registry+https://github.com/rust-lang/crates.io-index"
272checksum = "af0e43acfcbb0bb3b7435cc1b1dbb33596cacfec1eb243336b74a398e0bd6cbf"
273dependencies = [
274 "device-driver-macros",
275 "embedded-io",
276 "embedded-io-async",
277]
278
279[[package]]
280name = "device-driver-generation"
281version = "1.0.7"
282source = "registry+https://github.com/rust-lang/crates.io-index"
283checksum = "3935aec9cf5bb2ab927f59ca69faecf976190390b0ce34c6023889e9041040c0"
284dependencies = [
285 "anyhow",
286 "askama",
287 "bitvec",
288 "convert_case",
289 "dd-manifest-tree",
290 "itertools",
291 "kdl",
292 "proc-macro2",
293 "quote",
294 "syn 2.0.110",
295]
296
297[[package]]
298name = "device-driver-macros"
299version = "1.0.7"
300source = "registry+https://github.com/rust-lang/crates.io-index"
301checksum = "4fdc68ed515c4eddff2e95371185b4becba066085bf36d50f07f09782af98e17"
302dependencies = [
303 "device-driver-generation",
304 "proc-macro2",
305 "syn 2.0.110",
306]
307
308[[package]]
309name = "document-features"
310version = "0.2.12"
311source = "registry+https://github.com/rust-lang/crates.io-index"
312checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
313dependencies = [
314 "litrs",
315]
316
317[[package]]
318name = "either"
319version = "1.15.0"
320source = "registry+https://github.com/rust-lang/crates.io-index"
321checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
322
323[[package]]
324name = "embassy-embedded-hal"
325version = "0.5.0"
326source = "registry+https://github.com/rust-lang/crates.io-index"
327checksum = "554e3e840696f54b4c9afcf28a0f24da431c927f4151040020416e7393d6d0d8"
328dependencies = [
329 "embassy-futures",
330 "embassy-hal-internal",
331 "embassy-sync",
332 "embedded-hal 0.2.7",
333 "embedded-hal 1.0.0",
334 "embedded-hal-async",
335 "embedded-storage",
336 "embedded-storage-async",
337 "nb 1.1.0",
338]
339
340[[package]]
341name = "embassy-executor"
342version = "0.9.1"
343source = "registry+https://github.com/rust-lang/crates.io-index"
344checksum = "06070468370195e0e86f241c8e5004356d696590a678d47d6676795b2e439c6b"
345dependencies = [
346 "cortex-m",
347 "critical-section",
348 "document-features",
349 "embassy-executor-macros",
350 "embassy-executor-timer-queue",
351]
352
353[[package]]
354name = "embassy-executor-macros"
355version = "0.7.0"
356source = "registry+https://github.com/rust-lang/crates.io-index"
357checksum = "dfdddc3a04226828316bf31393b6903ee162238576b1584ee2669af215d55472"
358dependencies = [
359 "darling",
360 "proc-macro2",
361 "quote",
362 "syn 2.0.110",
363]
364
365[[package]]
366name = "embassy-executor-timer-queue"
367version = "0.1.0"
368source = "registry+https://github.com/rust-lang/crates.io-index"
369checksum = "2fc328bf943af66b80b98755db9106bf7e7471b0cf47dc8559cd9a6be504cc9c"
370
371[[package]]
372name = "embassy-futures"
373version = "0.1.2"
374source = "registry+https://github.com/rust-lang/crates.io-index"
375checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01"
376
377[[package]]
378name = "embassy-hal-internal"
379version = "0.3.0"
380source = "registry+https://github.com/rust-lang/crates.io-index"
381checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a"
382dependencies = [
383 "cortex-m",
384 "critical-section",
385 "num-traits",
386]
387
388[[package]]
389name = "embassy-mcxa"
390version = "0.1.0"
391dependencies = [
392 "cortex-m",
393 "cortex-m-rt",
394 "critical-section",
395 "defmt",
396 "embassy-embedded-hal",
397 "embassy-hal-internal",
398 "embassy-sync",
399 "embassy-time",
400 "embassy-time-driver",
401 "embedded-hal 0.2.7",
402 "embedded-hal 1.0.0",
403 "embedded-hal-async",
404 "embedded-hal-nb",
405 "embedded-io",
406 "embedded-io-async",
407 "heapless 0.8.0",
408 "maitake-sync",
409 "mcxa-pac",
410 "nb 1.1.0",
411 "paste",
412]
413
414[[package]]
415name = "embassy-mcxa-examples"
416version = "0.1.0"
417dependencies = [
418 "cortex-m",
419 "cortex-m-rt",
420 "critical-section",
421 "defmt",
422 "defmt-rtt",
423 "embassy-embedded-hal",
424 "embassy-executor",
425 "embassy-mcxa",
426 "embassy-sync",
427 "embassy-time",
428 "embassy-time-driver",
429 "embedded-io-async",
430 "heapless 0.9.2",
431 "panic-probe",
432 "tmp108",
433]
434
435[[package]]
436name = "embassy-sync"
437version = "0.7.2"
438source = "registry+https://github.com/rust-lang/crates.io-index"
439checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b"
440dependencies = [
441 "cfg-if",
442 "critical-section",
443 "embedded-io-async",
444 "futures-core",
445 "futures-sink",
446 "heapless 0.8.0",
447]
448
449[[package]]
450name = "embassy-time"
451version = "0.5.0"
452source = "registry+https://github.com/rust-lang/crates.io-index"
453checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65"
454dependencies = [
455 "cfg-if",
456 "critical-section",
457 "document-features",
458 "embassy-time-driver",
459 "embedded-hal 0.2.7",
460 "embedded-hal 1.0.0",
461 "embedded-hal-async",
462 "futures-core",
463]
464
465[[package]]
466name = "embassy-time-driver"
467version = "0.2.1"
468source = "registry+https://github.com/rust-lang/crates.io-index"
469checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6"
470dependencies = [
471 "document-features",
472]
473
474[[package]]
475name = "embedded-hal"
476version = "0.2.7"
477source = "registry+https://github.com/rust-lang/crates.io-index"
478checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
479dependencies = [
480 "nb 0.1.3",
481 "void",
482]
483
484[[package]]
485name = "embedded-hal"
486version = "1.0.0"
487source = "registry+https://github.com/rust-lang/crates.io-index"
488checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
489
490[[package]]
491name = "embedded-hal-async"
492version = "1.0.0"
493source = "registry+https://github.com/rust-lang/crates.io-index"
494checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
495dependencies = [
496 "embedded-hal 1.0.0",
497]
498
499[[package]]
500name = "embedded-hal-nb"
501version = "1.0.0"
502source = "registry+https://github.com/rust-lang/crates.io-index"
503checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
504dependencies = [
505 "embedded-hal 1.0.0",
506 "nb 1.1.0",
507]
508
509[[package]]
510name = "embedded-io"
511version = "0.6.1"
512source = "registry+https://github.com/rust-lang/crates.io-index"
513checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
514
515[[package]]
516name = "embedded-io-async"
517version = "0.6.1"
518source = "registry+https://github.com/rust-lang/crates.io-index"
519checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f"
520dependencies = [
521 "embedded-io",
522]
523
524[[package]]
525name = "embedded-storage"
526version = "0.3.1"
527source = "registry+https://github.com/rust-lang/crates.io-index"
528checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032"
529
530[[package]]
531name = "embedded-storage-async"
532version = "0.4.1"
533source = "registry+https://github.com/rust-lang/crates.io-index"
534checksum = "1763775e2323b7d5f0aa6090657f5e21cfa02ede71f5dc40eead06d64dcd15cc"
535dependencies = [
536 "embedded-storage",
537]
538
539[[package]]
540name = "equivalent"
541version = "1.0.2"
542source = "registry+https://github.com/rust-lang/crates.io-index"
543checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
544
545[[package]]
546name = "find-msvc-tools"
547version = "0.1.5"
548source = "registry+https://github.com/rust-lang/crates.io-index"
549checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
550
551[[package]]
552name = "fnv"
553version = "1.0.7"
554source = "registry+https://github.com/rust-lang/crates.io-index"
555checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
556
557[[package]]
558name = "funty"
559version = "2.0.0"
560source = "registry+https://github.com/rust-lang/crates.io-index"
561checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
562
563[[package]]
564name = "futures-core"
565version = "0.3.31"
566source = "registry+https://github.com/rust-lang/crates.io-index"
567checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
568
569[[package]]
570name = "futures-sink"
571version = "0.3.31"
572source = "registry+https://github.com/rust-lang/crates.io-index"
573checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
574
575[[package]]
576name = "generator"
577version = "0.8.7"
578source = "registry+https://github.com/rust-lang/crates.io-index"
579checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2"
580dependencies = [
581 "cc",
582 "cfg-if",
583 "libc",
584 "log",
585 "rustversion",
586 "windows",
587]
588
589[[package]]
590name = "hash32"
591version = "0.3.1"
592source = "registry+https://github.com/rust-lang/crates.io-index"
593checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
594dependencies = [
595 "byteorder",
596]
597
598[[package]]
599name = "hashbrown"
600version = "0.16.0"
601source = "registry+https://github.com/rust-lang/crates.io-index"
602checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
603
604[[package]]
605name = "heapless"
606version = "0.8.0"
607source = "registry+https://github.com/rust-lang/crates.io-index"
608checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
609dependencies = [
610 "hash32",
611 "stable_deref_trait",
612]
613
614[[package]]
615name = "heapless"
616version = "0.9.2"
617source = "registry+https://github.com/rust-lang/crates.io-index"
618checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed"
619dependencies = [
620 "hash32",
621 "stable_deref_trait",
622]
623
624[[package]]
625name = "ident_case"
626version = "1.0.1"
627source = "registry+https://github.com/rust-lang/crates.io-index"
628checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
629
630[[package]]
631name = "indexmap"
632version = "2.12.0"
633source = "registry+https://github.com/rust-lang/crates.io-index"
634checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
635dependencies = [
636 "equivalent",
637 "hashbrown",
638]
639
640[[package]]
641name = "itertools"
642version = "0.14.0"
643source = "registry+https://github.com/rust-lang/crates.io-index"
644checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
645dependencies = [
646 "either",
647]
648
649[[package]]
650name = "itoa"
651version = "1.0.15"
652source = "registry+https://github.com/rust-lang/crates.io-index"
653checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
654
655[[package]]
656name = "kdl"
657version = "6.5.0"
658source = "registry+https://github.com/rust-lang/crates.io-index"
659checksum = "81a29e7b50079ff44549f68c0becb1c73d7f6de2a4ea952da77966daf3d4761e"
660dependencies = [
661 "miette",
662 "num",
663 "winnow 0.6.24",
664]
665
666[[package]]
667name = "lazy_static"
668version = "1.5.0"
669source = "registry+https://github.com/rust-lang/crates.io-index"
670checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
671
672[[package]]
673name = "libc"
674version = "0.2.177"
675source = "registry+https://github.com/rust-lang/crates.io-index"
676checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
677
678[[package]]
679name = "litrs"
680version = "1.0.0"
681source = "registry+https://github.com/rust-lang/crates.io-index"
682checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
683
684[[package]]
685name = "log"
686version = "0.4.28"
687source = "registry+https://github.com/rust-lang/crates.io-index"
688checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
689
690[[package]]
691name = "loom"
692version = "0.7.2"
693source = "registry+https://github.com/rust-lang/crates.io-index"
694checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
695dependencies = [
696 "cfg-if",
697 "generator",
698 "scoped-tls",
699 "tracing",
700 "tracing-subscriber",
701]
702
703[[package]]
704name = "maitake-sync"
705version = "0.2.2"
706source = "registry+https://github.com/rust-lang/crates.io-index"
707checksum = "748f86d9befd480b602c3bebc9ef30dbf2f3dfc8acc4a73d07b90f0117e6de3f"
708dependencies = [
709 "cordyceps",
710 "critical-section",
711 "loom",
712 "mutex-traits",
713 "mycelium-bitfield",
714 "pin-project",
715 "portable-atomic",
716 "tracing",
717]
718
719[[package]]
720name = "manyhow"
721version = "0.11.4"
722source = "registry+https://github.com/rust-lang/crates.io-index"
723checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587"
724dependencies = [
725 "manyhow-macros",
726 "proc-macro2",
727 "quote",
728 "syn 1.0.109",
729 "syn 2.0.110",
730]
731
732[[package]]
733name = "manyhow-macros"
734version = "0.11.4"
735source = "registry+https://github.com/rust-lang/crates.io-index"
736checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495"
737dependencies = [
738 "proc-macro-utils",
739 "proc-macro2",
740 "quote",
741]
742
743[[package]]
744name = "matchers"
745version = "0.2.0"
746source = "registry+https://github.com/rust-lang/crates.io-index"
747checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
748dependencies = [
749 "regex-automata",
750]
751
752[[package]]
753name = "maybe-async-cfg"
754version = "0.2.5"
755source = "registry+https://github.com/rust-lang/crates.io-index"
756checksum = "8dbfaa67a76e2623580df07d6bb5e7956c0a4bae4b418314083a9c619bd66627"
757dependencies = [
758 "manyhow",
759 "proc-macro2",
760 "pulldown-cmark",
761 "quote",
762 "syn 1.0.109",
763]
764
765[[package]]
766name = "mcxa-pac"
767version = "0.1.0"
768source = "git+https://github.com/OpenDevicePartnership/mcxa-pac#e18dfb52500ca77b8d8326662b966a80251182ca"
769dependencies = [
770 "cortex-m",
771 "cortex-m-rt",
772 "critical-section",
773 "defmt",
774 "vcell",
775]
776
777[[package]]
778name = "memchr"
779version = "2.7.6"
780source = "registry+https://github.com/rust-lang/crates.io-index"
781checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
782
783[[package]]
784name = "miette"
785version = "7.6.0"
786source = "registry+https://github.com/rust-lang/crates.io-index"
787checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7"
788dependencies = [
789 "cfg-if",
790 "unicode-width",
791]
792
793[[package]]
794name = "mutex-traits"
795version = "1.0.1"
796source = "registry+https://github.com/rust-lang/crates.io-index"
797checksum = "3929f2b5633d29cf7b6624992e5f3c1e9334f1193423e12d17be4faf678cde3f"
798
799[[package]]
800name = "mycelium-bitfield"
801version = "0.1.5"
802source = "registry+https://github.com/rust-lang/crates.io-index"
803checksum = "24e0cc5e2c585acbd15c5ce911dff71e1f4d5313f43345873311c4f5efd741cc"
804
805[[package]]
806name = "nb"
807version = "0.1.3"
808source = "registry+https://github.com/rust-lang/crates.io-index"
809checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
810dependencies = [
811 "nb 1.1.0",
812]
813
814[[package]]
815name = "nb"
816version = "1.1.0"
817source = "registry+https://github.com/rust-lang/crates.io-index"
818checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
819
820[[package]]
821name = "nu-ansi-term"
822version = "0.50.3"
823source = "registry+https://github.com/rust-lang/crates.io-index"
824checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
825dependencies = [
826 "windows-sys",
827]
828
829[[package]]
830name = "num"
831version = "0.4.3"
832source = "registry+https://github.com/rust-lang/crates.io-index"
833checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
834dependencies = [
835 "num-bigint",
836 "num-complex",
837 "num-integer",
838 "num-iter",
839 "num-rational",
840 "num-traits",
841]
842
843[[package]]
844name = "num-bigint"
845version = "0.4.6"
846source = "registry+https://github.com/rust-lang/crates.io-index"
847checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
848dependencies = [
849 "num-integer",
850 "num-traits",
851]
852
853[[package]]
854name = "num-complex"
855version = "0.4.6"
856source = "registry+https://github.com/rust-lang/crates.io-index"
857checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
858dependencies = [
859 "num-traits",
860]
861
862[[package]]
863name = "num-integer"
864version = "0.1.46"
865source = "registry+https://github.com/rust-lang/crates.io-index"
866checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
867dependencies = [
868 "num-traits",
869]
870
871[[package]]
872name = "num-iter"
873version = "0.1.45"
874source = "registry+https://github.com/rust-lang/crates.io-index"
875checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
876dependencies = [
877 "autocfg",
878 "num-integer",
879 "num-traits",
880]
881
882[[package]]
883name = "num-rational"
884version = "0.4.2"
885source = "registry+https://github.com/rust-lang/crates.io-index"
886checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
887dependencies = [
888 "num-bigint",
889 "num-integer",
890 "num-traits",
891]
892
893[[package]]
894name = "num-traits"
895version = "0.2.19"
896source = "registry+https://github.com/rust-lang/crates.io-index"
897checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
898dependencies = [
899 "autocfg",
900]
901
902[[package]]
903name = "once_cell"
904version = "1.21.3"
905source = "registry+https://github.com/rust-lang/crates.io-index"
906checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
907
908[[package]]
909name = "panic-probe"
910version = "1.0.0"
911source = "registry+https://github.com/rust-lang/crates.io-index"
912checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a"
913dependencies = [
914 "cortex-m",
915 "defmt",
916]
917
918[[package]]
919name = "paste"
920version = "1.0.15"
921source = "registry+https://github.com/rust-lang/crates.io-index"
922checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
923
924[[package]]
925name = "percent-encoding"
926version = "2.3.2"
927source = "registry+https://github.com/rust-lang/crates.io-index"
928checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
929
930[[package]]
931name = "pin-project"
932version = "1.1.10"
933source = "registry+https://github.com/rust-lang/crates.io-index"
934checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
935dependencies = [
936 "pin-project-internal",
937]
938
939[[package]]
940name = "pin-project-internal"
941version = "1.1.10"
942source = "registry+https://github.com/rust-lang/crates.io-index"
943checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
944dependencies = [
945 "proc-macro2",
946 "quote",
947 "syn 2.0.110",
948]
949
950[[package]]
951name = "pin-project-lite"
952version = "0.2.16"
953source = "registry+https://github.com/rust-lang/crates.io-index"
954checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
955
956[[package]]
957name = "portable-atomic"
958version = "1.11.1"
959source = "registry+https://github.com/rust-lang/crates.io-index"
960checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
961dependencies = [
962 "critical-section",
963]
964
965[[package]]
966name = "proc-macro-error-attr2"
967version = "2.0.0"
968source = "registry+https://github.com/rust-lang/crates.io-index"
969checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
970dependencies = [
971 "proc-macro2",
972 "quote",
973]
974
975[[package]]
976name = "proc-macro-error2"
977version = "2.0.1"
978source = "registry+https://github.com/rust-lang/crates.io-index"
979checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
980dependencies = [
981 "proc-macro-error-attr2",
982 "proc-macro2",
983 "quote",
984 "syn 2.0.110",
985]
986
987[[package]]
988name = "proc-macro-utils"
989version = "0.10.0"
990source = "registry+https://github.com/rust-lang/crates.io-index"
991checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071"
992dependencies = [
993 "proc-macro2",
994 "quote",
995 "smallvec",
996]
997
998[[package]]
999name = "proc-macro2"
1000version = "1.0.103"
1001source = "registry+https://github.com/rust-lang/crates.io-index"
1002checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
1003dependencies = [
1004 "unicode-ident",
1005]
1006
1007[[package]]
1008name = "pulldown-cmark"
1009version = "0.11.3"
1010source = "registry+https://github.com/rust-lang/crates.io-index"
1011checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625"
1012dependencies = [
1013 "bitflags 2.10.0",
1014 "memchr",
1015 "unicase",
1016]
1017
1018[[package]]
1019name = "quote"
1020version = "1.0.42"
1021source = "registry+https://github.com/rust-lang/crates.io-index"
1022checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
1023dependencies = [
1024 "proc-macro2",
1025]
1026
1027[[package]]
1028name = "radium"
1029version = "0.7.0"
1030source = "registry+https://github.com/rust-lang/crates.io-index"
1031checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
1032
1033[[package]]
1034name = "regex-automata"
1035version = "0.4.13"
1036source = "registry+https://github.com/rust-lang/crates.io-index"
1037checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
1038dependencies = [
1039 "aho-corasick",
1040 "memchr",
1041 "regex-syntax",
1042]
1043
1044[[package]]
1045name = "regex-syntax"
1046version = "0.8.8"
1047source = "registry+https://github.com/rust-lang/crates.io-index"
1048checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
1049
1050[[package]]
1051name = "rustc-hash"
1052version = "2.1.1"
1053source = "registry+https://github.com/rust-lang/crates.io-index"
1054checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
1055
1056[[package]]
1057name = "rustc_version"
1058version = "0.2.3"
1059source = "registry+https://github.com/rust-lang/crates.io-index"
1060checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
1061dependencies = [
1062 "semver",
1063]
1064
1065[[package]]
1066name = "rustversion"
1067version = "1.0.22"
1068source = "registry+https://github.com/rust-lang/crates.io-index"
1069checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
1070
1071[[package]]
1072name = "ryu"
1073version = "1.0.20"
1074source = "registry+https://github.com/rust-lang/crates.io-index"
1075checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
1076
1077[[package]]
1078name = "scoped-tls"
1079version = "1.0.1"
1080source = "registry+https://github.com/rust-lang/crates.io-index"
1081checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
1082
1083[[package]]
1084name = "semver"
1085version = "0.9.0"
1086source = "registry+https://github.com/rust-lang/crates.io-index"
1087checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
1088dependencies = [
1089 "semver-parser",
1090]
1091
1092[[package]]
1093name = "semver-parser"
1094version = "0.7.0"
1095source = "registry+https://github.com/rust-lang/crates.io-index"
1096checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
1097
1098[[package]]
1099name = "serde"
1100version = "1.0.228"
1101source = "registry+https://github.com/rust-lang/crates.io-index"
1102checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
1103dependencies = [
1104 "serde_core",
1105]
1106
1107[[package]]
1108name = "serde_core"
1109version = "1.0.228"
1110source = "registry+https://github.com/rust-lang/crates.io-index"
1111checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
1112dependencies = [
1113 "serde_derive",
1114]
1115
1116[[package]]
1117name = "serde_derive"
1118version = "1.0.228"
1119source = "registry+https://github.com/rust-lang/crates.io-index"
1120checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
1121dependencies = [
1122 "proc-macro2",
1123 "quote",
1124 "syn 2.0.110",
1125]
1126
1127[[package]]
1128name = "serde_json"
1129version = "1.0.145"
1130source = "registry+https://github.com/rust-lang/crates.io-index"
1131checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
1132dependencies = [
1133 "itoa",
1134 "memchr",
1135 "ryu",
1136 "serde",
1137 "serde_core",
1138]
1139
1140[[package]]
1141name = "serde_spanned"
1142version = "0.6.9"
1143source = "registry+https://github.com/rust-lang/crates.io-index"
1144checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
1145dependencies = [
1146 "serde",
1147]
1148
1149[[package]]
1150name = "sharded-slab"
1151version = "0.1.7"
1152source = "registry+https://github.com/rust-lang/crates.io-index"
1153checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
1154dependencies = [
1155 "lazy_static",
1156]
1157
1158[[package]]
1159name = "shlex"
1160version = "1.3.0"
1161source = "registry+https://github.com/rust-lang/crates.io-index"
1162checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
1163
1164[[package]]
1165name = "smallvec"
1166version = "1.15.1"
1167source = "registry+https://github.com/rust-lang/crates.io-index"
1168checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
1169
1170[[package]]
1171name = "stable_deref_trait"
1172version = "1.2.1"
1173source = "registry+https://github.com/rust-lang/crates.io-index"
1174checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
1175
1176[[package]]
1177name = "strsim"
1178version = "0.11.1"
1179source = "registry+https://github.com/rust-lang/crates.io-index"
1180checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
1181
1182[[package]]
1183name = "syn"
1184version = "1.0.109"
1185source = "registry+https://github.com/rust-lang/crates.io-index"
1186checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
1187dependencies = [
1188 "proc-macro2",
1189 "quote",
1190 "unicode-ident",
1191]
1192
1193[[package]]
1194name = "syn"
1195version = "2.0.110"
1196source = "registry+https://github.com/rust-lang/crates.io-index"
1197checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
1198dependencies = [
1199 "proc-macro2",
1200 "quote",
1201 "unicode-ident",
1202]
1203
1204[[package]]
1205name = "tap"
1206version = "1.0.1"
1207source = "registry+https://github.com/rust-lang/crates.io-index"
1208checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
1209
1210[[package]]
1211name = "thiserror"
1212version = "2.0.17"
1213source = "registry+https://github.com/rust-lang/crates.io-index"
1214checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
1215dependencies = [
1216 "thiserror-impl",
1217]
1218
1219[[package]]
1220name = "thiserror-impl"
1221version = "2.0.17"
1222source = "registry+https://github.com/rust-lang/crates.io-index"
1223checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
1224dependencies = [
1225 "proc-macro2",
1226 "quote",
1227 "syn 2.0.110",
1228]
1229
1230[[package]]
1231name = "thread_local"
1232version = "1.1.9"
1233source = "registry+https://github.com/rust-lang/crates.io-index"
1234checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
1235dependencies = [
1236 "cfg-if",
1237]
1238
1239[[package]]
1240name = "tmp108"
1241version = "0.4.0"
1242source = "registry+https://github.com/rust-lang/crates.io-index"
1243checksum = "e0d644cc97d3cee96793f454b834881f78b5d4e89c90ecf26b3690f42004d111"
1244dependencies = [
1245 "device-driver",
1246 "embedded-hal 1.0.0",
1247 "maybe-async-cfg",
1248]
1249
1250[[package]]
1251name = "toml"
1252version = "0.8.23"
1253source = "registry+https://github.com/rust-lang/crates.io-index"
1254checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
1255dependencies = [
1256 "indexmap",
1257 "serde",
1258 "serde_spanned",
1259 "toml_datetime",
1260 "toml_edit",
1261]
1262
1263[[package]]
1264name = "toml_datetime"
1265version = "0.6.11"
1266source = "registry+https://github.com/rust-lang/crates.io-index"
1267checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
1268dependencies = [
1269 "serde",
1270]
1271
1272[[package]]
1273name = "toml_edit"
1274version = "0.22.27"
1275source = "registry+https://github.com/rust-lang/crates.io-index"
1276checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
1277dependencies = [
1278 "indexmap",
1279 "serde",
1280 "serde_spanned",
1281 "toml_datetime",
1282 "toml_write",
1283 "winnow 0.7.13",
1284]
1285
1286[[package]]
1287name = "toml_write"
1288version = "0.1.2"
1289source = "registry+https://github.com/rust-lang/crates.io-index"
1290checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
1291
1292[[package]]
1293name = "tracing"
1294version = "0.1.42"
1295source = "registry+https://github.com/rust-lang/crates.io-index"
1296checksum = "8eb41cbdb933e23b7929f47bb577710643157d7602ef3a2ebd3902b13ac5eda6"
1297dependencies = [
1298 "pin-project-lite",
1299 "tracing-attributes",
1300 "tracing-core",
1301]
1302
1303[[package]]
1304name = "tracing-attributes"
1305version = "0.1.31"
1306source = "registry+https://github.com/rust-lang/crates.io-index"
1307checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
1308dependencies = [
1309 "proc-macro2",
1310 "quote",
1311 "syn 2.0.110",
1312]
1313
1314[[package]]
1315name = "tracing-core"
1316version = "0.1.35"
1317source = "registry+https://github.com/rust-lang/crates.io-index"
1318checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
1319dependencies = [
1320 "once_cell",
1321 "valuable",
1322]
1323
1324[[package]]
1325name = "tracing-log"
1326version = "0.2.0"
1327source = "registry+https://github.com/rust-lang/crates.io-index"
1328checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
1329dependencies = [
1330 "log",
1331 "once_cell",
1332 "tracing-core",
1333]
1334
1335[[package]]
1336name = "tracing-subscriber"
1337version = "0.3.20"
1338source = "registry+https://github.com/rust-lang/crates.io-index"
1339checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
1340dependencies = [
1341 "matchers",
1342 "nu-ansi-term",
1343 "once_cell",
1344 "regex-automata",
1345 "sharded-slab",
1346 "smallvec",
1347 "thread_local",
1348 "tracing",
1349 "tracing-core",
1350 "tracing-log",
1351]
1352
1353[[package]]
1354name = "unicase"
1355version = "2.8.1"
1356source = "registry+https://github.com/rust-lang/crates.io-index"
1357checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
1358
1359[[package]]
1360name = "unicode-ident"
1361version = "1.0.22"
1362source = "registry+https://github.com/rust-lang/crates.io-index"
1363checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
1364
1365[[package]]
1366name = "unicode-segmentation"
1367version = "1.12.0"
1368source = "registry+https://github.com/rust-lang/crates.io-index"
1369checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
1370
1371[[package]]
1372name = "unicode-width"
1373version = "0.1.14"
1374source = "registry+https://github.com/rust-lang/crates.io-index"
1375checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
1376
1377[[package]]
1378name = "valuable"
1379version = "0.1.1"
1380source = "registry+https://github.com/rust-lang/crates.io-index"
1381checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
1382
1383[[package]]
1384name = "vcell"
1385version = "0.1.3"
1386source = "registry+https://github.com/rust-lang/crates.io-index"
1387checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
1388
1389[[package]]
1390name = "void"
1391version = "1.0.2"
1392source = "registry+https://github.com/rust-lang/crates.io-index"
1393checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
1394
1395[[package]]
1396name = "volatile-register"
1397version = "0.2.2"
1398source = "registry+https://github.com/rust-lang/crates.io-index"
1399checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
1400dependencies = [
1401 "vcell",
1402]
1403
1404[[package]]
1405name = "windows"
1406version = "0.61.3"
1407source = "registry+https://github.com/rust-lang/crates.io-index"
1408checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
1409dependencies = [
1410 "windows-collections",
1411 "windows-core",
1412 "windows-future",
1413 "windows-link 0.1.3",
1414 "windows-numerics",
1415]
1416
1417[[package]]
1418name = "windows-collections"
1419version = "0.2.0"
1420source = "registry+https://github.com/rust-lang/crates.io-index"
1421checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
1422dependencies = [
1423 "windows-core",
1424]
1425
1426[[package]]
1427name = "windows-core"
1428version = "0.61.2"
1429source = "registry+https://github.com/rust-lang/crates.io-index"
1430checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
1431dependencies = [
1432 "windows-implement",
1433 "windows-interface",
1434 "windows-link 0.1.3",
1435 "windows-result",
1436 "windows-strings",
1437]
1438
1439[[package]]
1440name = "windows-future"
1441version = "0.2.1"
1442source = "registry+https://github.com/rust-lang/crates.io-index"
1443checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
1444dependencies = [
1445 "windows-core",
1446 "windows-link 0.1.3",
1447 "windows-threading",
1448]
1449
1450[[package]]
1451name = "windows-implement"
1452version = "0.60.2"
1453source = "registry+https://github.com/rust-lang/crates.io-index"
1454checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
1455dependencies = [
1456 "proc-macro2",
1457 "quote",
1458 "syn 2.0.110",
1459]
1460
1461[[package]]
1462name = "windows-interface"
1463version = "0.59.3"
1464source = "registry+https://github.com/rust-lang/crates.io-index"
1465checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
1466dependencies = [
1467 "proc-macro2",
1468 "quote",
1469 "syn 2.0.110",
1470]
1471
1472[[package]]
1473name = "windows-link"
1474version = "0.1.3"
1475source = "registry+https://github.com/rust-lang/crates.io-index"
1476checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
1477
1478[[package]]
1479name = "windows-link"
1480version = "0.2.1"
1481source = "registry+https://github.com/rust-lang/crates.io-index"
1482checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
1483
1484[[package]]
1485name = "windows-numerics"
1486version = "0.2.0"
1487source = "registry+https://github.com/rust-lang/crates.io-index"
1488checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
1489dependencies = [
1490 "windows-core",
1491 "windows-link 0.1.3",
1492]
1493
1494[[package]]
1495name = "windows-result"
1496version = "0.3.4"
1497source = "registry+https://github.com/rust-lang/crates.io-index"
1498checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
1499dependencies = [
1500 "windows-link 0.1.3",
1501]
1502
1503[[package]]
1504name = "windows-strings"
1505version = "0.4.2"
1506source = "registry+https://github.com/rust-lang/crates.io-index"
1507checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
1508dependencies = [
1509 "windows-link 0.1.3",
1510]
1511
1512[[package]]
1513name = "windows-sys"
1514version = "0.61.2"
1515source = "registry+https://github.com/rust-lang/crates.io-index"
1516checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
1517dependencies = [
1518 "windows-link 0.2.1",
1519]
1520
1521[[package]]
1522name = "windows-threading"
1523version = "0.1.0"
1524source = "registry+https://github.com/rust-lang/crates.io-index"
1525checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
1526dependencies = [
1527 "windows-link 0.1.3",
1528]
1529
1530[[package]]
1531name = "winnow"
1532version = "0.6.24"
1533source = "registry+https://github.com/rust-lang/crates.io-index"
1534checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
1535dependencies = [
1536 "memchr",
1537]
1538
1539[[package]]
1540name = "winnow"
1541version = "0.7.13"
1542source = "registry+https://github.com/rust-lang/crates.io-index"
1543checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
1544dependencies = [
1545 "memchr",
1546]
1547
1548[[package]]
1549name = "wyz"
1550version = "0.5.1"
1551source = "registry+https://github.com/rust-lang/crates.io-index"
1552checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
1553dependencies = [
1554 "tap",
1555]
diff --git a/examples/mcxa/Cargo.toml b/examples/mcxa/Cargo.toml
new file mode 100644
index 000000000..4da94811f
--- /dev/null
+++ b/examples/mcxa/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2name = "embassy-mcxa-examples"
3version = "0.1.0"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6
7[dependencies]
8cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
9cortex-m-rt = { version = "0.7", features = ["set-sp", "set-vtor"] }
10critical-section = "1.2.0"
11defmt = "1.0"
12defmt-rtt = "1.0"
13embassy-embedded-hal = "0.5.0"
14embassy-executor = { version = "0.9.0", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"], default-features = false }
15embassy-mcxa = { path = "../../embassy-mcxa", features = ["defmt", "unstable-pac", "time"] }
16embassy-sync = "0.7.2"
17embassy-time = "0.5.0"
18embassy-time-driver = "0.2.1"
19embedded-io-async = "0.6.1"
20heapless = "0.9.2"
21panic-probe = { version = "1.0", features = ["print-defmt"] }
22tmp108 = "0.4.0"
23
24[profile.release]
25lto = true # better optimizations
26debug = 2 # enough information for defmt/rtt locations
diff --git a/examples/mcxa/build.rs b/examples/mcxa/build.rs
new file mode 100644
index 000000000..f076bba9f
--- /dev/null
+++ b/examples/mcxa/build.rs
@@ -0,0 +1,20 @@
1use std::env;
2use std::fs::File;
3use std::io::Write;
4use std::path::PathBuf;
5
6fn main() {
7 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
8
9 // Generate memory.x - put "FLASH" at start of RAM, RAM after "FLASH"
10 // cortex-m-rt expects FLASH for code, RAM for data/bss/stack
11 // Both are in RAM, but separated to satisfy cortex-m-rt's expectations
12 // MCXA256 has 128KB RAM total
13 File::create(out.join("memory.x"))
14 .unwrap()
15 .write_all(include_bytes!("memory.x"))
16 .unwrap();
17
18 println!("cargo:rustc-link-search={}", out.display());
19 println!("cargo:rerun-if-changed=memory.x");
20}
diff --git a/examples/mcxa/memory.x b/examples/mcxa/memory.x
new file mode 100644
index 000000000..315ced58a
--- /dev/null
+++ b/examples/mcxa/memory.x
@@ -0,0 +1,5 @@
1MEMORY
2{
3 FLASH : ORIGIN = 0x00000000, LENGTH = 1M
4 RAM : ORIGIN = 0x20000000, LENGTH = 128K
5}
diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs
new file mode 100644
index 000000000..83d8046b3
--- /dev/null
+++ b/examples/mcxa/src/bin/adc_interrupt.rs
@@ -0,0 +1,84 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa_examples::init_adc_pins;
6use hal::adc::{LpadcConfig, TriggerPriorityPolicy};
7use hal::clocks::periph_helpers::{AdcClockSel, Div4};
8use hal::clocks::PoweredClock;
9use hal::pac::adc1::cfg::{Pwrsel, Refsel};
10use hal::pac::adc1::cmdl1::{Adch, Mode};
11use hal::pac::adc1::ctrl::CalAvgs;
12use hal::pac::adc1::tctrl::Tcmd;
13use hal::{bind_interrupts, InterruptExt};
14use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
15
16bind_interrupts!(struct Irqs {
17 ADC1 => hal::adc::AdcHandler;
18});
19
20#[used]
21#[no_mangle]
22static KEEP_ADC: unsafe extern "C" fn() = ADC1;
23
24#[embassy_executor::main]
25async fn main(_spawner: Spawner) {
26 let p = hal::init(hal::config::Config::default());
27
28 defmt::info!("ADC interrupt Example");
29
30 unsafe {
31 init_adc_pins();
32 }
33
34 let adc_config = LpadcConfig {
35 enable_in_doze_mode: true,
36 conversion_average_mode: CalAvgs::Average128,
37 enable_analog_preliminary: true,
38 power_up_delay: 0x80,
39 reference_voltage_source: Refsel::Option3,
40 power_level_mode: Pwrsel::Lowest,
41 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
42 enable_conv_pause: false,
43 conv_pause_delay: 0,
44 fifo_watermark: 0,
45 power: PoweredClock::NormalEnabledDeepSleepDisabled,
46 source: AdcClockSel::FroLfDiv,
47 div: Div4::no_div(),
48 };
49 let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config);
50
51 adc.do_offset_calibration();
52 adc.do_auto_calibration();
53
54 let mut conv_command_config = adc.get_default_conv_command_config();
55 conv_command_config.channel_number = Adch::SelectCorrespondingChannel8;
56 conv_command_config.conversion_resolution_mode = Mode::Data16Bits;
57 adc.set_conv_command_config(1, &conv_command_config);
58
59 let mut conv_trigger_config = adc.get_default_conv_trigger_config();
60 conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1;
61 conv_trigger_config.enable_hardware_trigger = false;
62 adc.set_conv_trigger_config(0, &conv_trigger_config);
63
64 defmt::info!("ADC configuration done...");
65
66 adc.enable_interrupt(0x1);
67
68 unsafe {
69 hal::interrupt::ADC1.enable();
70 }
71
72 unsafe {
73 cortex_m::interrupt::enable();
74 }
75
76 loop {
77 adc.do_software_trigger(1);
78 while !adc.is_interrupt_triggered() {
79 // Wait until the interrupt is triggered
80 }
81 defmt::info!("*** ADC interrupt TRIGGERED! ***");
82 //TBD need to print the value
83 }
84}
diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs
new file mode 100644
index 000000000..ddf3f586b
--- /dev/null
+++ b/examples/mcxa/src/bin/adc_polling.rs
@@ -0,0 +1,68 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa_examples::init_adc_pins;
6use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy};
7use hal::clocks::periph_helpers::{AdcClockSel, Div4};
8use hal::clocks::PoweredClock;
9use hal::pac::adc1::cfg::{Pwrsel, Refsel};
10use hal::pac::adc1::cmdl1::{Adch, Mode};
11use hal::pac::adc1::ctrl::CalAvgs;
12use hal::pac::adc1::tctrl::Tcmd;
13use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
14
15const G_LPADC_RESULT_SHIFT: u32 = 0;
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 let p = hal::init(hal::config::Config::default());
20
21 unsafe {
22 init_adc_pins();
23 }
24
25 defmt::info!("=== ADC polling Example ===");
26
27 let adc_config = LpadcConfig {
28 enable_in_doze_mode: true,
29 conversion_average_mode: CalAvgs::Average128,
30 enable_analog_preliminary: true,
31 power_up_delay: 0x80,
32 reference_voltage_source: Refsel::Option3,
33 power_level_mode: Pwrsel::Lowest,
34 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
35 enable_conv_pause: false,
36 conv_pause_delay: 0,
37 fifo_watermark: 0,
38 power: PoweredClock::NormalEnabledDeepSleepDisabled,
39 source: AdcClockSel::FroLfDiv,
40 div: Div4::no_div(),
41 };
42 let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config);
43
44 adc.do_offset_calibration();
45 adc.do_auto_calibration();
46
47 let mut conv_command_config = adc.get_default_conv_command_config();
48 conv_command_config.channel_number = Adch::SelectCorrespondingChannel8;
49 conv_command_config.conversion_resolution_mode = Mode::Data16Bits;
50 adc.set_conv_command_config(1, &conv_command_config);
51
52 let mut conv_trigger_config = adc.get_default_conv_trigger_config();
53 conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1;
54 conv_trigger_config.enable_hardware_trigger = false;
55 adc.set_conv_trigger_config(0, &conv_trigger_config);
56
57 defmt::info!("=== ADC configuration done... ===");
58
59 loop {
60 adc.do_software_trigger(1);
61 let mut result: Option<ConvResult> = None;
62 while result.is_none() {
63 result = hal::adc::get_conv_result();
64 }
65 let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT;
66 defmt::info!("value: {=u16}", value);
67 }
68}
diff --git a/examples/mcxa/src/bin/blinky.rs b/examples/mcxa/src/bin/blinky.rs
new file mode 100644
index 000000000..dd08ec0d9
--- /dev/null
+++ b/examples/mcxa/src/bin/blinky.rs
@@ -0,0 +1,36 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::gpio::{DriveStrength, Level, Output, SlewRate};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 let p = hal::init(hal::config::Config::default());
12
13 defmt::info!("Blink example");
14
15 let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast);
16 let mut green = Output::new(p.P3_19, Level::High, DriveStrength::Normal, SlewRate::Fast);
17 let mut blue = Output::new(p.P3_21, Level::High, DriveStrength::Normal, SlewRate::Fast);
18
19 loop {
20 defmt::info!("Toggle LEDs");
21
22 red.toggle();
23 Timer::after_millis(250).await;
24
25 red.toggle();
26 green.toggle();
27 Timer::after_millis(250).await;
28
29 green.toggle();
30 blue.toggle();
31 Timer::after_millis(250).await;
32 blue.toggle();
33
34 Timer::after_millis(250).await;
35 }
36}
diff --git a/examples/mcxa/src/bin/button.rs b/examples/mcxa/src/bin/button.rs
new file mode 100644
index 000000000..943edbb15
--- /dev/null
+++ b/examples/mcxa/src/bin/button.rs
@@ -0,0 +1,23 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::gpio::{Input, Pull};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 let p = hal::init(hal::config::Config::default());
12
13 defmt::info!("Button example");
14
15 // This button is labeled "WAKEUP" on the FRDM-MCXA276
16 // The board already has a 10K pullup
17 let monitor = Input::new(p.P1_7, Pull::Disabled);
18
19 loop {
20 defmt::info!("Pin level is {:?}", monitor.get_level());
21 Timer::after_millis(1000).await;
22 }
23}
diff --git a/examples/mcxa/src/bin/button_async.rs b/examples/mcxa/src/bin/button_async.rs
new file mode 100644
index 000000000..6cc7b62cd
--- /dev/null
+++ b/examples/mcxa/src/bin/button_async.rs
@@ -0,0 +1,29 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::gpio::{Input, Pull};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 let p = hal::init(hal::config::Config::default());
12
13 defmt::info!("GPIO interrupt example");
14
15 // This button is labeled "WAKEUP" on the FRDM-MCXA276
16 // The board already has a 10K pullup
17 let mut pin = Input::new(p.P1_7, Pull::Disabled);
18
19 let mut press_count = 0u32;
20
21 loop {
22 pin.wait_for_falling_edge().await;
23
24 press_count += 1;
25
26 defmt::info!("Button pressed! Count: {}", press_count);
27 Timer::after_millis(50).await;
28 }
29}
diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs
new file mode 100644
index 000000000..bfd963540
--- /dev/null
+++ b/examples/mcxa/src/bin/clkout.rs
@@ -0,0 +1,69 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4};
6use embassy_mcxa::clocks::PoweredClock;
7use embassy_mcxa::gpio::{DriveStrength, SlewRate};
8use embassy_mcxa::{Level, Output};
9use embassy_time::Timer;
10use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
11
12/// Demonstrate CLKOUT, using Pin P4.2
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let p = hal::init(hal::config::Config::default());
16 let mut pin = p.P4_2;
17 let mut clkout = p.CLKOUT;
18
19 loop {
20 defmt::info!("Set Low...");
21 let mut output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow);
22 Timer::after_millis(500).await;
23
24 defmt::info!("Set High...");
25 output.set_high();
26 Timer::after_millis(400).await;
27
28 defmt::info!("Set Low...");
29 output.set_low();
30 Timer::after_millis(500).await;
31
32 defmt::info!("16k...");
33 // Run Clock Out with the 16K clock
34 let _clock_out = ClockOut::new(
35 clkout.reborrow(),
36 pin.reborrow(),
37 Config {
38 sel: ClockOutSel::Clk16K,
39 div: Div4::no_div(),
40 level: PoweredClock::NormalEnabledDeepSleepDisabled,
41 },
42 )
43 .unwrap();
44
45 Timer::after_millis(3000).await;
46
47 defmt::info!("Set Low...");
48 drop(_clock_out);
49
50 let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow);
51 Timer::after_millis(500).await;
52
53 // Run Clock Out with the 12M clock, divided by 3
54 defmt::info!("4M...");
55 let _clock_out = ClockOut::new(
56 clkout.reborrow(),
57 pin.reborrow(),
58 Config {
59 sel: ClockOutSel::Fro12M,
60 div: const { Div4::from_divisor(3).unwrap() },
61 level: PoweredClock::NormalEnabledDeepSleepDisabled,
62 },
63 )
64 .unwrap();
65
66 // Let it run for 3 seconds...
67 Timer::after_millis(3000).await;
68 }
69}
diff --git a/examples/mcxa/src/bin/hello.rs b/examples/mcxa/src/bin/hello.rs
new file mode 100644
index 000000000..e371d9413
--- /dev/null
+++ b/examples/mcxa/src/bin/hello.rs
@@ -0,0 +1,119 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clocks::config::Div8;
6use hal::lpuart::{Blocking, Config, Lpuart};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9/// Simple helper to write a byte as hex to UART
10fn write_hex_byte(uart: &mut Lpuart<'_, Blocking>, byte: u8) {
11 const HEX_DIGITS: &[u8] = b"0123456789ABCDEF";
12 let _ = uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]);
13 let _ = uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]);
14}
15
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) {
18 let mut cfg = hal::config::Config::default();
19 cfg.clock_cfg.sirc.fro_12m_enabled = true;
20 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
21 let p = hal::init(cfg);
22
23 defmt::info!("boot");
24
25 // Create UART configuration
26 let config = Config {
27 baudrate_bps: 115_200,
28 ..Default::default()
29 };
30
31 // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX
32 let mut uart = Lpuart::new_blocking(
33 p.LPUART2, // Peripheral
34 p.P2_2, // TX pin
35 p.P2_3, // RX pin
36 config,
37 )
38 .unwrap();
39
40 // Print welcome message before any async delays to guarantee early console output
41 uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n");
42 uart.write_str_blocking("Available commands:\r\n");
43 uart.write_str_blocking(" help - Show this help\r\n");
44 uart.write_str_blocking(" echo <text> - Echo back the text\r\n");
45 uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n");
46 uart.write_str_blocking("Type a command: ");
47
48 let mut buffer = [0u8; 64];
49 let mut buf_idx = 0;
50
51 loop {
52 // Read a byte from UART
53 let byte = uart.read_byte_blocking();
54
55 // Echo the character back
56 if byte == b'\r' || byte == b'\n' {
57 // Enter pressed - process command
58 uart.write_str_blocking("\r\n");
59
60 if buf_idx > 0 {
61 let command = &buffer[0..buf_idx];
62
63 if command == b"help" {
64 uart.write_str_blocking("Available commands:\r\n");
65 uart.write_str_blocking(" help - Show this help\r\n");
66 uart.write_str_blocking(" echo <text> - Echo back the text\r\n");
67 uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n");
68 } else if command.starts_with(b"echo ") && command.len() > 5 {
69 uart.write_str_blocking("Echo: ");
70 uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or(""));
71 uart.write_str_blocking("\r\n");
72 } else if command.starts_with(b"hex ") && command.len() > 4 {
73 // Parse the byte value
74 let num_str = &command[4..];
75 if let Ok(num) = parse_u8(num_str) {
76 uart.write_str_blocking("Hex: 0x");
77 write_hex_byte(&mut uart, num);
78 uart.write_str_blocking("\r\n");
79 } else {
80 uart.write_str_blocking("Invalid number for hex command\r\n");
81 }
82 } else if !command.is_empty() {
83 uart.write_str_blocking("Unknown command: ");
84 uart.write_str_blocking(core::str::from_utf8(command).unwrap_or(""));
85 uart.write_str_blocking("\r\n");
86 }
87 }
88
89 // Reset buffer and prompt
90 buf_idx = 0;
91 uart.write_str_blocking("Type a command: ");
92 } else if byte == 8 || byte == 127 {
93 // Backspace
94 if buf_idx > 0 {
95 buf_idx -= 1;
96 uart.write_str_blocking("\x08 \x08"); // Erase character
97 }
98 } else if buf_idx < buffer.len() - 1 {
99 // Regular character
100 buffer[buf_idx] = byte;
101 buf_idx += 1;
102 let _ = uart.write_byte(byte);
103 }
104 }
105}
106
107/// Simple parser for u8 from ASCII bytes
108fn parse_u8(bytes: &[u8]) -> Result<u8, ()> {
109 let mut result = 0u8;
110 for &b in bytes {
111 if b.is_ascii_digit() {
112 result = result.checked_mul(10).ok_or(())?;
113 result = result.checked_add(b - b'0').ok_or(())?;
114 } else {
115 return Err(());
116 }
117 }
118 Ok(result)
119}
diff --git a/examples/mcxa/src/bin/i2c-async.rs b/examples/mcxa/src/bin/i2c-async.rs
new file mode 100644
index 000000000..47b5f3cbe
--- /dev/null
+++ b/examples/mcxa/src/bin/i2c-async.rs
@@ -0,0 +1,39 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::bind_interrupts;
7use hal::clocks::config::Div8;
8use hal::config::Config;
9use hal::i2c::controller::{self, I2c, Speed};
10use hal::i2c::InterruptHandler;
11use hal::peripherals::LPI2C3;
12use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
13
14bind_interrupts!(
15 struct Irqs {
16 LPI2C3 => InterruptHandler<LPI2C3>;
17 }
18);
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 let mut config = Config::default();
23 config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);
24
25 let p = hal::init(config);
26
27 defmt::info!("I2C example");
28
29 let mut config = controller::Config::default();
30 config.speed = Speed::Standard;
31 let mut i2c = I2c::new_async(p.LPI2C3, p.P3_27, p.P3_28, Irqs, config).unwrap();
32 let mut buf = [0u8; 2];
33
34 loop {
35 i2c.async_write_read(0x48, &[0x00], &mut buf).await.unwrap();
36 defmt::info!("Buffer: {:02x}", buf);
37 Timer::after_secs(1).await;
38 }
39}
diff --git a/examples/mcxa/src/bin/i2c-blocking.rs b/examples/mcxa/src/bin/i2c-blocking.rs
new file mode 100644
index 000000000..0f6c8cbae
--- /dev/null
+++ b/examples/mcxa/src/bin/i2c-blocking.rs
@@ -0,0 +1,31 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_time::Timer;
6use hal::clocks::config::Div8;
7use hal::config::Config;
8use hal::i2c::controller::{self, I2c, Speed};
9use tmp108::Tmp108;
10use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let mut config = Config::default();
15 config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);
16
17 let p = hal::init(config);
18
19 defmt::info!("I2C example");
20
21 let mut config = controller::Config::default();
22 config.speed = Speed::Standard;
23 let i2c = I2c::new_blocking(p.LPI2C3, p.P3_27, p.P3_28, config).unwrap();
24 let mut tmp = Tmp108::new_with_a0_gnd(i2c);
25
26 loop {
27 let temperature = tmp.temperature().unwrap();
28 defmt::info!("Temperature: {}C", temperature);
29 Timer::after_secs(1).await;
30 }
31}
diff --git a/examples/mcxa/src/bin/i2c-scan-blocking.rs b/examples/mcxa/src/bin/i2c-scan-blocking.rs
new file mode 100644
index 000000000..4e203597b
--- /dev/null
+++ b/examples/mcxa/src/bin/i2c-scan-blocking.rs
@@ -0,0 +1,41 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::gpio::Pull;
6use embassy_mcxa::Input;
7use embassy_time::Timer;
8use hal::clocks::config::Div8;
9use hal::config::Config;
10use hal::i2c::controller::{self, I2c, Speed};
11use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let mut config = Config::default();
16 config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);
17
18 let p = hal::init(config);
19
20 defmt::info!("I2C example");
21
22 let mut config = controller::Config::default();
23 config.speed = Speed::Standard;
24
25 // Note: P0_2 is connected to P1_8 on the FRDM_MCXA276 via a resistor, and
26 // defaults to SWO on the debug peripheral. Explicitly make it a high-z
27 // input.
28 let _pin = Input::new(p.P0_2, Pull::Disabled);
29 let mut i2c = I2c::new_blocking(p.LPI2C2, p.P1_9, p.P1_8, config).unwrap();
30
31 for addr in 0x01..=0x7f {
32 let result = i2c.blocking_write(addr, &[]);
33 if result.is_ok() {
34 defmt::info!("Device found at addr {:02x}", addr);
35 }
36 }
37
38 loop {
39 Timer::after_secs(10).await;
40 }
41}
diff --git a/examples/mcxa/src/bin/lpuart_buffered.rs b/examples/mcxa/src/bin/lpuart_buffered.rs
new file mode 100644
index 000000000..420589d00
--- /dev/null
+++ b/examples/mcxa/src/bin/lpuart_buffered.rs
@@ -0,0 +1,62 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clocks::config::Div8;
6use embassy_mcxa::lpuart::buffered::BufferedLpuart;
7use embassy_mcxa::lpuart::Config;
8use embassy_mcxa::{bind_interrupts, lpuart};
9use embedded_io_async::Write;
10use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
11
12// Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver
13bind_interrupts!(struct Irqs {
14 LPUART2 => lpuart::buffered::BufferedInterruptHandler::<hal::peripherals::LPUART2>;
15});
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 let mut cfg = hal::config::Config::default();
20 cfg.clock_cfg.sirc.fro_12m_enabled = true;
21 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
22 let p = hal::init(cfg);
23
24 // Configure NVIC for LPUART2
25 hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3);
26
27 // UART configuration (enable both TX and RX)
28 let config = Config {
29 baudrate_bps: 115_200,
30 rx_fifo_watermark: 0,
31 tx_fifo_watermark: 0,
32 ..Default::default()
33 };
34
35 let mut tx_buf = [0u8; 256];
36 let mut rx_buf = [0u8; 256];
37
38 // Create a buffered LPUART2 instance with both TX and RX
39 let mut uart = BufferedLpuart::new(
40 p.LPUART2,
41 p.P2_2, // TX pin
42 p.P2_3, // RX pin
43 Irqs,
44 &mut tx_buf,
45 &mut rx_buf,
46 config,
47 )
48 .unwrap();
49
50 // Split into TX and RX parts
51 let (tx, rx) = uart.split_ref();
52
53 tx.write(b"Hello buffered LPUART.\r\n").await.unwrap();
54 tx.write(b"Type characters to echo them back.\r\n").await.unwrap();
55
56 // Echo loop
57 let mut buf = [0u8; 4];
58 loop {
59 let used = rx.read(&mut buf).await.unwrap();
60 tx.write_all(&buf[..used]).await.unwrap();
61 }
62}
diff --git a/examples/mcxa/src/bin/lpuart_polling.rs b/examples/mcxa/src/bin/lpuart_polling.rs
new file mode 100644
index 000000000..b80668834
--- /dev/null
+++ b/examples/mcxa/src/bin/lpuart_polling.rs
@@ -0,0 +1,47 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::clocks::config::Div8;
6use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
7
8use crate::hal::lpuart::{Config, Lpuart};
9
10#[embassy_executor::main]
11async fn main(_spawner: Spawner) {
12 let mut cfg = hal::config::Config::default();
13 cfg.clock_cfg.sirc.fro_12m_enabled = true;
14 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
15 let p = hal::init(cfg);
16
17 defmt::info!("boot");
18
19 // Create UART configuration
20 let config = Config {
21 baudrate_bps: 115_200,
22 ..Default::default()
23 };
24
25 // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX
26 let lpuart = Lpuart::new_blocking(
27 p.LPUART2, // Peripheral
28 p.P2_2, // TX pin
29 p.P2_3, // RX pin
30 config,
31 )
32 .unwrap();
33
34 // Split into separate TX and RX parts
35 let (mut tx, mut rx) = lpuart.split();
36
37 // Write hello messages
38 tx.blocking_write(b"Hello world.\r\n").unwrap();
39 tx.blocking_write(b"Echoing. Type characters...\r\n").unwrap();
40
41 // Echo loop
42 loop {
43 let mut buf = [0u8; 1];
44 rx.blocking_read(&mut buf).unwrap();
45 tx.blocking_write(&buf).unwrap();
46 }
47}
diff --git a/examples/mcxa/src/bin/rtc_alarm.rs b/examples/mcxa/src/bin/rtc_alarm.rs
new file mode 100644
index 000000000..fe355888b
--- /dev/null
+++ b/examples/mcxa/src/bin/rtc_alarm.rs
@@ -0,0 +1,47 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::bind_interrupts;
6use hal::rtc::{InterruptHandler, Rtc, RtcDateTime};
7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
8
9bind_interrupts!(struct Irqs {
10 RTC => InterruptHandler<hal::rtc::Rtc0>;
11});
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let p = hal::init(hal::config::Config::default());
16
17 defmt::info!("=== RTC Alarm Example ===");
18
19 let rtc_config = hal::rtc::get_default_config();
20
21 let mut rtc = Rtc::new(p.RTC0, Irqs, rtc_config);
22
23 let now = RtcDateTime {
24 year: 2025,
25 month: 10,
26 day: 15,
27 hour: 14,
28 minute: 30,
29 second: 0,
30 };
31
32 rtc.stop();
33
34 defmt::info!("Time set to: 2025-10-15 14:30:00");
35 rtc.set_datetime(now);
36
37 let mut alarm = now;
38 alarm.second += 10;
39
40 defmt::info!("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)");
41 defmt::info!("RTC started, waiting for alarm...");
42
43 rtc.wait_for_alarm(alarm).await;
44 defmt::info!("*** ALARM TRIGGERED! ***");
45
46 defmt::info!("Example complete - Test PASSED!");
47}
diff --git a/examples/mcxa/src/lib.rs b/examples/mcxa/src/lib.rs
new file mode 100644
index 000000000..2573a6adc
--- /dev/null
+++ b/examples/mcxa/src/lib.rs
@@ -0,0 +1,16 @@
1#![no_std]
2#![allow(clippy::missing_safety_doc)]
3
4//! Shared board-specific helpers for the FRDM-MCXA276 examples.
5//! These live with the examples so the HAL stays generic.
6
7use hal::{clocks, pins};
8use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
9
10/// Initialize clocks and pin muxing for ADC.
11pub unsafe fn init_adc_pins() {
12 // NOTE: Lpuart has been updated to properly enable + reset its own clocks.
13 // GPIO has not.
14 _ = clocks::enable_and_reset::<hal::peripherals::PORT1>(&clocks::periph_helpers::NoConfig);
15 pins::configure_adc_pins();
16}