diff --git a/.github/workflows/rp2040_hal.yml b/.github/workflows/rp2040_hal.yml
index 52b75c24a..30aae2a75 100644
--- a/.github/workflows/rp2040_hal.yml
+++ b/.github/workflows/rp2040_hal.yml
@@ -2,6 +2,7 @@ on: [push, pull_request]
name: Check rp2040-hal
env:
PACKAGE: rp2040-hal
+ TARGET: thumbv6m-none-eabi
jobs:
build:
runs-on: ubuntu-20.04
@@ -9,12 +10,12 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
- target: thumbv6m-none-eabi
+ target: ${{ env.TARGET }}
- name: Install cargo-hack
run: |
curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
- name: Build rp2040-hal
- run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature --target=thumbv6m-none-eabi
+ run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature --target=${TARGET}
- name: Build rp2040-hal-macros
run: cd ${PACKAGE}-macros && cargo hack build --optional-deps --each-feature
test:
@@ -23,7 +24,7 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
- target: thumbv6m-none-eabi
+ target: ${{ env.TARGET }}
- name: Install cargo-hack
run: |
curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
@@ -42,7 +43,7 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly-2024-01-30
- target: thumbv6m-none-eabi
+ target: ${{ env.TARGET }}
- name: Install cargo-hack
run: |
curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
@@ -50,7 +51,7 @@ jobs:
run: |
curl -sSL https://github.com/est31/cargo-udeps/releases/download/v0.1.45/cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - --strip-components=2 -C ~/.cargo/bin ./cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu/cargo-udeps
- name: Run cargo-udeps on rp2040-hal
- run: cd ${PACKAGE} && cargo hack udeps --optional-deps --each-feature --target=thumbv6m-none-eabi
+ run: cd ${PACKAGE} && cargo hack udeps --optional-deps --each-feature --target=${TARGET}
- name: Run cargo-udeps on rp2040-hal-macros
run: cd ${PACKAGE}-macros && cargo hack udeps --optional-deps --each-feature
msrv:
@@ -61,14 +62,14 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.77
- target: thumbv6m-none-eabi
+ target: ${{ env.TARGET }}
- name: Install cargo-hack
run: |
curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
- name: Use older version of regex
run: cd ${PACKAGE}-examples && cargo update -p regex --precise 1.9.3
- name: Build rp2040-hal (on MSRV)
- run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature --target=thumbv6m-none-eabi
+ run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature --target=${TARGET}
- name: Build rp2040-hal-macros (on MSRV)
run: cd ${PACKAGE}-macros && cargo hack build --optional-deps --each-feature
fmt:
@@ -92,7 +93,7 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
- target: thumbv6m-none-eabi
+ target: ${{ env.TARGET }}
components: clippy
- name: Run cargo clippy
- run: cd ${PACKAGE} && cargo clippy --target=thumbv6m-none-eabi
+ run: cd ${PACKAGE} && cargo clippy --target=${TARGET}
diff --git a/.github/workflows/rp2040_hal_examples.yml b/.github/workflows/rp2040_hal_examples.yml
index bdf8e46f5..19889aec9 100644
--- a/.github/workflows/rp2040_hal_examples.yml
+++ b/.github/workflows/rp2040_hal_examples.yml
@@ -2,6 +2,7 @@ on: [push, pull_request]
name: Check rp2040-hal-examples
env:
PACKAGE: rp2040-hal-examples
+ TARGET: thumbv6m-none-eabi
jobs:
build:
runs-on: ubuntu-20.04
@@ -9,12 +10,9 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
- target: thumbv6m-none-eabi
- - name: Install cargo-hack
- run: |
- curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ target: ${{ env.TARGET }}
- name: Build
- run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature
+ run: cd ${PACKAGE} && cargo build
udeps:
runs-on: ubuntu-20.04
steps:
@@ -22,15 +20,12 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly-2024-01-30
- target: thumbv6m-none-eabi
- - name: Install cargo-hack
- run: |
- curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ target: ${{ env.TARGET }}
- name: Install cargo-udeps
run: |
curl -sSL https://github.com/est31/cargo-udeps/releases/download/v0.1.45/cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - --strip-components=2 -C ~/.cargo/bin ./cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu/cargo-udeps
- name: Run cargo-udeps
- run: cd ${PACKAGE} && cargo hack udeps --optional-deps --each-feature
+ run: cd ${PACKAGE} && cargo udeps
msrv:
name: Verify build on MSRV
runs-on: ubuntu-20.04
@@ -39,14 +34,11 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.77
- target: thumbv6m-none-eabi
- - name: Install cargo-hack
- run: |
- curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ target: ${{ env.TARGET }}
- name: Use older version of regex
run: cd ${PACKAGE} && cargo update -p regex --precise 1.9.3
- name: Build on MSRV
- run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature
+ run: cd ${PACKAGE} && cargo build
fmt:
runs-on: ubuntu-20.04
env:
@@ -66,7 +58,7 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
- target: thumbv6m-none-eabi
+ target: ${{ env.TARGET }}
components: clippy
- name: Run cargo clippy
run: cd ${PACKAGE} && cargo clippy
diff --git a/.github/workflows/rp235x_hal_arm.yml b/.github/workflows/rp235x_hal_arm.yml
new file mode 100644
index 000000000..b1a167c50
--- /dev/null
+++ b/.github/workflows/rp235x_hal_arm.yml
@@ -0,0 +1,99 @@
+on: [push, pull_request]
+name: Check rp235x-hal on Arm
+env:
+ PACKAGE: rp235x-hal
+ TARGET: thumbv8m.main-none-eabihf
+jobs:
+ build:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ target: ${{ env.TARGET }}
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Build rp235x-hal
+ run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature --target=${TARGET}
+ - name: Build rp235x-hal-macros
+ run: cd ${PACKAGE}-macros && cargo hack build --optional-deps --each-feature
+ test:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ target: ${{ env.TARGET }}
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Test rp235x-hal
+ run: cd ${PACKAGE} && cargo hack test --optional-deps --each-feature --tests --features critical-section-impl
+ - name: Test rp235x-hal docs
+ run: cd ${PACKAGE} && cargo hack test --optional-deps --each-feature --doc --features critical-section-impl
+ - name: Test rp235x-hal-macros
+ run: cd ${PACKAGE}-macros && cargo hack test --optional-deps --tests --each-feature
+ - name: Test rp235x-hal-macros docs
+ run: cd ${PACKAGE}-macros && cargo hack test --optional-deps --doc --each-feature
+ udeps:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: nightly-2024-01-30
+ target: ${{ env.TARGET }}
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Install cargo-udeps
+ run: |
+ curl -sSL https://github.com/est31/cargo-udeps/releases/download/v0.1.45/cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - --strip-components=2 -C ~/.cargo/bin ./cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu/cargo-udeps
+ - name: Run cargo-udeps on rp235x-hal
+ run: cd ${PACKAGE} && cargo hack udeps --optional-deps --each-feature --target=${TARGET}
+ - name: Run cargo-udeps on rp235x-hal-macros
+ run: cd ${PACKAGE}-macros && cargo hack udeps --optional-deps --each-feature
+ msrv:
+ name: Verify build on MSRV
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: 1.77
+ target: ${{ env.TARGET }}
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Use older version of regex
+ run: cd ${PACKAGE}-examples && cargo update -p regex --precise 1.9.3
+ - name: Build rp235x-hal (on MSRV)
+ run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature --target=${TARGET}
+ - name: Build rp235x-hal-macros (on MSRV)
+ run: cd ${PACKAGE}-macros && cargo hack build --optional-deps --each-feature
+ fmt:
+ runs-on: ubuntu-20.04
+ env:
+ RUSTFLAGS: "-D warnings"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt
+ - name: Check format of rp235x-hal
+ run: cd ${PACKAGE} && cargo fmt -- --check
+ - name: Check format of rp235x-hal-macros
+ run: cd ${PACKAGE}-macros && cargo fmt -- --check
+ clippy:
+ runs-on: ubuntu-20.04
+ env:
+ RUSTFLAGS: "-D warnings"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ target: ${{ env.TARGET }}
+ components: clippy
+ - name: Run cargo clippy
+ run: cd ${PACKAGE} && cargo clippy --target=${TARGET}
diff --git a/.github/workflows/rp235x_hal_examples_arm.yml b/.github/workflows/rp235x_hal_examples_arm.yml
new file mode 100644
index 000000000..63b13f931
--- /dev/null
+++ b/.github/workflows/rp235x_hal_examples_arm.yml
@@ -0,0 +1,64 @@
+on: [push, pull_request]
+name: Check rp235x-hal-examples on Arm
+env:
+ PACKAGE: rp235x-hal-examples
+ TARGET: thumbv8m.main-none-eabihf
+jobs:
+ build:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ target: ${{ env.TARGET }}
+ - name: Build
+ run: cd ${PACKAGE} && cargo build --target=${TARGET}
+ udeps:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: nightly-2024-01-30
+ target: ${{ env.TARGET }}
+ - name: Install cargo-udeps
+ run: |
+ curl -sSL https://github.com/est31/cargo-udeps/releases/download/v0.1.45/cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - --strip-components=2 -C ~/.cargo/bin ./cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu/cargo-udeps
+ - name: Run cargo-udeps
+ run: cd ${PACKAGE} && cargo udeps --target=${TARGET}
+ msrv:
+ name: Verify build on MSRV
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: 1.77
+ target: ${{ env.TARGET }}
+ - name: Use older version of regex
+ run: cd ${PACKAGE} && cargo update -p regex --precise 1.9.3
+ - name: Build on MSRV
+ run: cd ${PACKAGE} && cargo build --target=${TARGET}
+ fmt:
+ runs-on: ubuntu-20.04
+ env:
+ RUSTFLAGS: "-D warnings"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt
+ - name: Check format
+ run: cd ${PACKAGE} && cargo fmt -- --check
+ clippy:
+ runs-on: ubuntu-20.04
+ env:
+ RUSTFLAGS: "-D warnings"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ target: ${{ env.TARGET }}
+ components: clippy
+ - name: Run cargo clippy
+ run: cd ${PACKAGE} && cargo clippy --target=${TARGET}
diff --git a/.github/workflows/rp235x_hal_examples_riscv.yml b/.github/workflows/rp235x_hal_examples_riscv.yml
new file mode 100644
index 000000000..7f86fe2c1
--- /dev/null
+++ b/.github/workflows/rp235x_hal_examples_riscv.yml
@@ -0,0 +1,66 @@
+on: [push, pull_request]
+name: Check rp235x-hal-examples on RISC-V
+env:
+ PACKAGE: rp235x-hal-examples
+ TARGET: riscv32imac-unknown-none-elf
+jobs:
+ build:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ target: ${{ env.TARGET }}
+ - name: Build
+ run: |
+ cd ${PACKAGE}
+ cat riscv_examples.txt | while read example; do
+ echo "Building $example"
+ cargo build --target=${TARGET} --bin $example
+ done
+ msrv:
+ name: Verify build on MSRV
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: 1.77
+ target: ${{ env.TARGET }}
+ - name: Use older version of regex
+ run: cd ${PACKAGE} && cargo update -p regex --precise 1.9.3
+ - name: Build on MSRV
+ run: |
+ cd ${PACKAGE}
+ cat riscv_examples.txt | while read example; do
+ echo "Building $example"
+ cargo build --target=${TARGET} --bin $example
+ done
+ fmt:
+ runs-on: ubuntu-20.04
+ env:
+ RUSTFLAGS: "-D warnings"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt
+ - name: Check format
+ run: cd ${PACKAGE} && cargo fmt -- --check
+ clippy:
+ runs-on: ubuntu-20.04
+ env:
+ RUSTFLAGS: "-D warnings"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ target: ${{ env.TARGET }}
+ components: clippy
+ - name: Run cargo clippy
+ run: |
+ cd ${PACKAGE}
+ cat riscv_examples.txt | while read example; do
+ echo "Checking $example"
+ cargo clippy --target=${TARGET} --bin $example
+ done
diff --git a/.github/workflows/rp235x_hal_riscv.yml b/.github/workflows/rp235x_hal_riscv.yml
new file mode 100644
index 000000000..4d5e85146
--- /dev/null
+++ b/.github/workflows/rp235x_hal_riscv.yml
@@ -0,0 +1,99 @@
+on: [push, pull_request]
+name: Check rp235x-hal on RISC-V
+env:
+ PACKAGE: rp235x-hal
+ TARGET: riscv32imac-unknown-none-elf
+jobs:
+ build:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ target: ${{ env.TARGET }}
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Build rp235x-hal
+ run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature --target=${TARGET}
+ - name: Build rp235x-hal-macros
+ run: cd ${PACKAGE}-macros && cargo hack build --optional-deps --each-feature
+ test:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ target: ${{ env.TARGET }}
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Test rp235x-hal
+ run: cd ${PACKAGE} && cargo hack test --optional-deps --each-feature --tests --features critical-section-impl
+ - name: Test rp235x-hal docs
+ run: cd ${PACKAGE} && cargo hack test --optional-deps --each-feature --doc --features critical-section-impl
+ - name: Test rp235x-hal-macros
+ run: cd ${PACKAGE}-macros && cargo hack test --optional-deps --tests --each-feature
+ - name: Test rp235x-hal-macros docs
+ run: cd ${PACKAGE}-macros && cargo hack test --optional-deps --doc --each-feature
+ udeps:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: nightly-2024-01-30
+ target: ${{ env.TARGET }}
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Install cargo-udeps
+ run: |
+ curl -sSL https://github.com/est31/cargo-udeps/releases/download/v0.1.45/cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - --strip-components=2 -C ~/.cargo/bin ./cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu/cargo-udeps
+ - name: Run cargo-udeps on rp235x-hal
+ run: cd ${PACKAGE} && cargo hack udeps --optional-deps --each-feature --target=${TARGET}
+ - name: Run cargo-udeps on rp235x-hal-macros
+ run: cd ${PACKAGE}-macros && cargo hack udeps --optional-deps --each-feature
+ msrv:
+ name: Verify build on MSRV
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: 1.77
+ target: ${{ env.TARGET }}
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Use older version of regex
+ run: cd ${PACKAGE}-examples && cargo update -p regex --precise 1.9.3
+ - name: Build rp235x-hal (on MSRV)
+ run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature --target=${TARGET}
+ - name: Build rp235x-hal-macros (on MSRV)
+ run: cd ${PACKAGE}-macros && cargo hack build --optional-deps --each-feature
+ fmt:
+ runs-on: ubuntu-20.04
+ env:
+ RUSTFLAGS: "-D warnings"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt
+ - name: Check format of rp235x-hal
+ run: cd ${PACKAGE} && cargo fmt -- --check
+ - name: Check format of rp235x-hal-macros
+ run: cd ${PACKAGE}-macros && cargo fmt -- --check
+ clippy:
+ runs-on: ubuntu-20.04
+ env:
+ RUSTFLAGS: "-D warnings"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ target: ${{ env.TARGET }}
+ components: clippy
+ - name: Run cargo clippy
+ run: cd ${PACKAGE} && cargo clippy --target=${TARGET}
diff --git a/.github/workflows/rp_hal_common.yml b/.github/workflows/rp_hal_common.yml
new file mode 100644
index 000000000..aa26194d2
--- /dev/null
+++ b/.github/workflows/rp_hal_common.yml
@@ -0,0 +1,77 @@
+on: [push, pull_request]
+name: Check rp-hal-common
+env:
+ PACKAGE: rp-hal-common
+jobs:
+ build:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Build
+ run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature
+ test:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Test
+ run: cd ${PACKAGE} && cargo hack test --optional-deps --each-feature --tests
+ - name: Test docs
+ run: cd ${PACKAGE} && cargo hack test --optional-deps --each-feature --doc
+ udeps:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: nightly-2024-01-30
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Install cargo-udeps
+ run: |
+ curl -sSL https://github.com/est31/cargo-udeps/releases/download/v0.1.45/cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - --strip-components=2 -C ~/.cargo/bin ./cargo-udeps-v0.1.45-x86_64-unknown-linux-gnu/cargo-udeps
+ - name: Run cargo-udeps
+ run: cd ${PACKAGE} && cargo hack udeps --optional-deps --each-feature
+ msrv:
+ name: Verify build on MSRV
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: 1.77
+ - name: Install cargo-hack
+ run: |
+ curl -sSL https://github.com/taiki-e/cargo-hack/releases/download/v0.6.17/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xvzf - -C ~/.cargo/bin
+ - name: Build on MSRV
+ run: cd ${PACKAGE} && cargo hack build --optional-deps --each-feature
+ fmt:
+ runs-on: ubuntu-20.04
+ env:
+ RUSTFLAGS: "-D warnings"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt
+ - name: Check format
+ run: cd ${PACKAGE} && cargo fmt -- --check
+ clippy:
+ runs-on: ubuntu-20.04
+ env:
+ RUSTFLAGS: "-D warnings"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ components: clippy
+ - name: Run cargo clippy
+ run: cd ${PACKAGE} && cargo clippy
diff --git a/format.bat b/format.bat
index c22532265..44d08a1e5 100644
--- a/format.bat
+++ b/format.bat
@@ -1,6 +1,10 @@
rem Formats all the files in the repo
-cargo fmt --manifest-path rp2040-hal\Cargo.toml -- --check
-cargo fmt --manifest-path rp2040-hal-macros\Cargo.toml -- --check
-cargo fmt --manifest-path rp2040-hal-examples\Cargo.toml -- --check
-cargo fmt --manifest-path on-target-tests\Cargo.toml -- --check
+cargo fmt --manifest-path rp2040-hal\Cargo.toml
+cargo fmt --manifest-path rp2040-hal-macros\Cargo.toml
+cargo fmt --manifest-path rp2040-hal-examples\Cargo.toml
+cargo fmt --manifest-path on-target-tests\Cargo.toml
+cargo fmt --manifest-path rp235x-hal\Cargo.toml
+cargo fmt --manifest-path rp235x-hal-macros\Cargo.toml
+cargo fmt --manifest-path rp235x-hal-examples\Cargo.toml
+cargo fmt --manifest-path rp-hal-common\Cargo.toml
diff --git a/format.sh b/format.sh
index 3f141459a..ecf8adbda 100755
--- a/format.sh
+++ b/format.sh
@@ -2,7 +2,11 @@
# Formats all the files in the repo
-cargo fmt --manifest-path rp2040-hal/Cargo.toml -- --check
-cargo fmt --manifest-path rp2040-hal-macros/Cargo.toml -- --check
-cargo fmt --manifest-path rp2040-hal-examples/Cargo.toml -- --check
-cargo fmt --manifest-path on-target-tests/Cargo.toml -- --check
+cargo fmt --manifest-path rp2040-hal/Cargo.toml
+cargo fmt --manifest-path rp2040-hal-macros/Cargo.toml
+cargo fmt --manifest-path rp2040-hal-examples/Cargo.toml
+cargo fmt --manifest-path on-target-tests/Cargo.toml
+cargo fmt --manifest-path rp235x-hal/Cargo.toml
+cargo fmt --manifest-path rp235x-hal-macros/Cargo.toml
+cargo fmt --manifest-path rp235x-hal-examples/Cargo.toml
+cargo fmt --manifest-path rp-hal-common/Cargo.toml
diff --git a/rp-binary-info/README.md b/rp-binary-info/README.md
index 18541f865..68bcfcb06 100644
--- a/rp-binary-info/README.md
+++ b/rp-binary-info/README.md
@@ -11,5 +11,5 @@ license when you re-use this code. See [`LICENSE-MIT`](./LICENSE-MIT) or
license. Our Apache 2.0 notices can be found in [`NOTICE`](./NOTICE).
Unless you explicitly state otherwise, any contribution intentionally submitted
-for inclus`ion in the work by you, as defined in the Apache-2.0 license, shall be
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
diff --git a/rp-binary-info/src/macros.rs b/rp-binary-info/src/macros.rs
index 0689ddb54..7431cf4eb 100644
--- a/rp-binary-info/src/macros.rs
+++ b/rp-binary-info/src/macros.rs
@@ -56,6 +56,19 @@ macro_rules! rp_program_name {
};
}
+/// Generate a static item containing the `CARGO_BIN_NAME` as the program name,
+/// and return its [`EntryAddr`](super::EntryAddr).
+#[macro_export]
+macro_rules! rp_cargo_bin_name {
+ () => {
+ $crate::env!(
+ $crate::consts::TAG_RASPBERRY_PI,
+ $crate::consts::ID_RP_PROGRAM_NAME,
+ "CARGO_BIN_NAME"
+ )
+ };
+}
+
/// Generate a static item containing the program version, and return its
/// [`EntryAddr`](super::EntryAddr).
#[macro_export]
@@ -82,7 +95,7 @@ macro_rules! rp_cargo_version {
};
}
-/// Generate a static item containing the program url, and return its
+/// Generate a static item containing the program URL, and return its
/// [`EntryAddr`](super::EntryAddr).
#[macro_export]
macro_rules! rp_program_url {
@@ -95,6 +108,19 @@ macro_rules! rp_program_url {
};
}
+/// Generate a static item containing the `CARGO_PKG_HOMEPAGE` as the program URL,
+/// and return its [`EntryAddr`](super::EntryAddr).
+#[macro_export]
+macro_rules! rp_cargo_homepage_url {
+ () => {
+ $crate::env!(
+ $crate::consts::TAG_RASPBERRY_PI,
+ $crate::consts::ID_RP_PROGRAM_URL,
+ "CARGO_PKG_HOMEPAGE"
+ )
+ };
+}
+
/// Generate a static item containing the program description, and return its
/// [`EntryAddr`](super::EntryAddr).
#[macro_export]
diff --git a/rp-binary-info/src/types.rs b/rp-binary-info/src/types.rs
index 319d6e2c3..d2b192e32 100644
--- a/rp-binary-info/src/types.rs
+++ b/rp-binary-info/src/types.rs
@@ -4,7 +4,7 @@
/// file/ELF file/Pico in Bootloader Mode to give you useful metadata about your
/// program.
///
-/// It should be placed in the first 512 bytes of flash, so use your `memory.x`
+/// It should be placed in the first 4096 bytes of flash, so use your `memory.x`
/// to insert a section between `.text` and `.vector_table` and put a static
/// value of this type in that section.
#[repr(C)]
diff --git a/rp-hal-common/Cargo.toml b/rp-hal-common/Cargo.toml
new file mode 100644
index 000000000..08b49e347
--- /dev/null
+++ b/rp-hal-common/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+authors = ["The rp-rs Developers"]
+description = "Shared HAL code for the Raspberry Pi microcontrollers"
+edition = "2021"
+homepage = "https://github.com/rp-rs/rp-hal"
+license = "MIT OR Apache-2.0"
+name = "rp-hal-common"
+repository = "https://github.com/rp-rs/rp-hal"
+rust-version = "1.77"
+version = "0.1.0"
+
+# DO NOT LIST ANY PAC CRATES OR ARCHITECTURE CRATES HERE
+[dependencies]
+fugit = "0.3.7"
diff --git a/rp-hal-common/LICENSE-APACHE b/rp-hal-common/LICENSE-APACHE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/rp-hal-common/LICENSE-APACHE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/rp-hal-common/LICENSE-MIT b/rp-hal-common/LICENSE-MIT
new file mode 100644
index 000000000..6e052e35b
--- /dev/null
+++ b/rp-hal-common/LICENSE-MIT
@@ -0,0 +1,19 @@
+Copyright (c) 2021-2024 The rp-rs Developers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/rp-hal-common/NOTICE b/rp-hal-common/NOTICE
new file mode 100644
index 000000000..790ecb167
--- /dev/null
+++ b/rp-hal-common/NOTICE
@@ -0,0 +1,3 @@
+Copyright (c) 2021-2024 The rp-rs Developers
+
+Originally published at https://github.com/rp-rs/rp-hal
diff --git a/rp-hal-common/README.md b/rp-hal-common/README.md
new file mode 100644
index 000000000..f9f4e0921
--- /dev/null
+++ b/rp-hal-common/README.md
@@ -0,0 +1,15 @@
+# `rp-hal-common`
+
+Code and types useful to both rp2040-hal and rp235x-hal.
+
+# License
+
+The contents of this repository are dual-licensed under the _MIT OR Apache 2.0_
+License. That means you can choose either the MIT license or the Apache 2.0
+license when you re-use this code. See [`LICENSE-MIT`](./LICENSE-MIT) or
+[`LICENSE-APACHE`](./LICENSE-APACHE) for more information on each specific
+license. Our Apache 2.0 notices can be found in [`NOTICE`](./NOTICE).
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
diff --git a/rp-hal-common/src/lib.rs b/rp-hal-common/src/lib.rs
new file mode 100644
index 000000000..7c41ebe03
--- /dev/null
+++ b/rp-hal-common/src/lib.rs
@@ -0,0 +1,11 @@
+//! Common HAL code
+//!
+//! This library contains types and functions which are shared between the
+//! RP2040 HAL and the RP235x HAL.
+//!
+//! You shouldn't include anything here which requires either the `cortex-m`
+//! crate, or a PAC.
+
+#![no_std]
+
+pub mod uart;
diff --git a/rp-hal-common/src/uart/common_configs.rs b/rp-hal-common/src/uart/common_configs.rs
new file mode 100644
index 000000000..0e5bdba75
--- /dev/null
+++ b/rp-hal-common/src/uart/common_configs.rs
@@ -0,0 +1,45 @@
+//! Common UART configurations
+
+use fugit::HertzU32;
+
+use super::{DataBits, StopBits, UartConfig};
+
+/// 9600 baud, 8 data bits, no parity, 1 stop bit
+pub const _9600_8_N_1: UartConfig = UartConfig {
+ baudrate: HertzU32::from_raw(9600),
+ data_bits: DataBits::Eight,
+ stop_bits: StopBits::One,
+ parity: None,
+};
+
+/// 19200 baud, 8 data bits, no parity, 1 stop bit
+pub const _19200_8_N_1: UartConfig = UartConfig {
+ baudrate: HertzU32::from_raw(19200),
+ data_bits: DataBits::Eight,
+ stop_bits: StopBits::One,
+ parity: None,
+};
+
+/// 38400 baud, 8 data bits, no parity, 1 stop bit
+pub const _38400_8_N_1: UartConfig = UartConfig {
+ baudrate: HertzU32::from_raw(38400),
+ data_bits: DataBits::Eight,
+ stop_bits: StopBits::One,
+ parity: None,
+};
+
+/// 57600 baud, 8 data bits, no parity, 1 stop bit
+pub const _57600_8_N_1: UartConfig = UartConfig {
+ baudrate: HertzU32::from_raw(57600),
+ data_bits: DataBits::Eight,
+ stop_bits: StopBits::One,
+ parity: None,
+};
+
+/// 115200 baud, 8 data bits, no parity, 1 stop bit
+pub const _115200_8_N_1: UartConfig = UartConfig {
+ baudrate: HertzU32::from_raw(115200),
+ data_bits: DataBits::Eight,
+ stop_bits: StopBits::One,
+ parity: None,
+};
diff --git a/rp-hal-common/src/uart/mod.rs b/rp-hal-common/src/uart/mod.rs
new file mode 100644
index 000000000..3a4918acf
--- /dev/null
+++ b/rp-hal-common/src/uart/mod.rs
@@ -0,0 +1,6 @@
+//! Shared code and types for Raspberry Pi Silicon UARTS
+
+pub mod common_configs;
+mod utils;
+
+pub use utils::*;
diff --git a/rp-hal-common/src/uart/utils.rs b/rp-hal-common/src/uart/utils.rs
new file mode 100644
index 000000000..a9ad7cb26
--- /dev/null
+++ b/rp-hal-common/src/uart/utils.rs
@@ -0,0 +1,88 @@
+//! Useful UART types
+
+use fugit::HertzU32;
+
+/// Data bits
+pub enum DataBits {
+ /// 5 bits
+ Five,
+ /// 6 bits
+ Six,
+ /// 7 bits
+ Seven,
+ /// 8 bits
+ Eight,
+}
+
+/// Stop bits
+pub enum StopBits {
+ /// 1 bit
+ One,
+ /// 2 bits
+ Two,
+}
+
+/// Parity
+///
+/// The "none" state of parity is represented with the Option type (None).
+pub enum Parity {
+ /// Odd parity
+ Odd,
+ /// Even parity
+ Even,
+}
+
+/// A struct holding the configuration for an UART device.
+///
+/// The `Default` implementation implements the following values:
+/// ```ignore
+/// # // can't actually create this with the non_exhaustive attribute
+/// UartConfig {
+/// baudrate: Baud(115_200),
+/// data_bits: DataBits::Eight,
+/// stop_bits: StopBits::One,
+/// parity: None,
+///}
+/// ```
+#[non_exhaustive]
+pub struct UartConfig {
+ /// The baudrate the uart will run at.
+ pub baudrate: HertzU32,
+
+ /// The amount of data bits the uart should be configured to.
+ pub data_bits: DataBits,
+
+ /// The amount of stop bits the uart should be configured to.
+ pub stop_bits: StopBits,
+
+ /// The parity that this uart should have
+ pub parity: Option,
+}
+
+impl UartConfig {
+ /// Create a new instance of UartConfig
+ pub const fn new(
+ baudrate: HertzU32,
+ data_bits: DataBits,
+ parity: Option,
+ stop_bits: StopBits,
+ ) -> UartConfig {
+ UartConfig {
+ baudrate,
+ data_bits,
+ stop_bits,
+ parity,
+ }
+ }
+}
+
+impl Default for UartConfig {
+ fn default() -> Self {
+ Self {
+ baudrate: HertzU32::from_raw(115_200),
+ data_bits: DataBits::Eight,
+ stop_bits: StopBits::One,
+ parity: None,
+ }
+ }
+}
diff --git a/rp2040-hal-examples/Cargo.toml b/rp2040-hal-examples/Cargo.toml
index 184f373e2..7529eb2df 100644
--- a/rp2040-hal-examples/Cargo.toml
+++ b/rp2040-hal-examples/Cargo.toml
@@ -23,6 +23,7 @@ embedded-alloc = "0.5.1"
embedded-hal = "1.0.0"
embedded-hal-async = "1.0.0"
embedded-hal-bus = {version = "0.2.0", features = ["defmt-03"]}
+embedded-io = "0.6.1"
embedded_hal_0_2 = {package = "embedded-hal", version = "0.2.5", features = ["unproven"]}
fugit = "0.3.6"
futures = {version = "0.3.30", default-features = false, features = ["async-await"]}
diff --git a/rp2040-hal-examples/src/bin/adc.rs b/rp2040-hal-examples/src/bin/adc.rs
index 7dffb0c54..7e9c34bc6 100644
--- a/rp2040-hal-examples/src/bin/adc.rs
+++ b/rp2040-hal-examples/src/bin/adc.rs
@@ -1,7 +1,7 @@
//! # ADC Example
//!
//! This application demonstrates how to read ADC samples from the temperature
-//! sensor and pin and output them to the UART on pins 1 and 2 at 9600 baud.
+//! sensor and pin and output them to the UART on pins 1 and 2 at 115200 baud.
//!
//! It may need to be adapted to your particular board layout and/or pin assignment.
//!
@@ -95,7 +95,7 @@ fn main() -> ! {
// Create a UART driver
let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
.enable(
- UartConfig::new(9600.Hz(), DataBits::Eight, None, StopBits::One),
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
clocks.peripheral_clock.freq(),
)
.unwrap();
diff --git a/rp2040-hal-examples/src/bin/alloc.rs b/rp2040-hal-examples/src/bin/alloc.rs
index d4c465cde..76c4d7ef3 100644
--- a/rp2040-hal-examples/src/bin/alloc.rs
+++ b/rp2040-hal-examples/src/bin/alloc.rs
@@ -6,7 +6,7 @@
//! the on-board LED. It may need to be adapted to your particular board layout
//! and/or pin assignment.
//!
-//! While blinkin the LED, it will continuously push to a `Vec`, which will
+//! While blinking the LED, it will continuously push to a `Vec`, which will
//! eventually lead to a panic due to an out of memory condition.
//!
//! See the top-level `README.md` file for Copyright and licence details.
@@ -58,7 +58,7 @@ const XTAL_FREQ_HZ: u32 = 12_000_000u32;
/// as soon as all global variables are initialised.
///
/// The function configures the RP2040 peripherals, then blinks the LED in an
-/// infinite loop.
+/// infinite loop where the duration indicates how many items were allocated.
#[entry]
fn main() -> ! {
{
diff --git a/rp2040-hal-examples/src/bin/gpio_in_out.rs b/rp2040-hal-examples/src/bin/gpio_in_out.rs
index f95b6c640..2b25b64a6 100644
--- a/rp2040-hal-examples/src/bin/gpio_in_out.rs
+++ b/rp2040-hal-examples/src/bin/gpio_in_out.rs
@@ -1,6 +1,6 @@
-//! # GPIO 'Blinky' Example
+//! # GPIO In/Out Example
//!
-//! This application demonstrates how to control a GPIO pin on the RP2040.
+//! This application demonstrates how to control GPIO pins on the RP2040.
//!
//! It may need to be adapted to your particular board layout and/or pin assignment.
//!
diff --git a/rp2040-hal-examples/src/bin/pio_side_set.rs b/rp2040-hal-examples/src/bin/pio_side_set.rs
index 5db05f260..4d077f107 100644
--- a/rp2040-hal-examples/src/bin/pio_side_set.rs
+++ b/rp2040-hal-examples/src/bin/pio_side_set.rs
@@ -6,12 +6,15 @@
#![no_std]
#![no_main]
+use rp2040_hal as hal;
+
use hal::gpio::{FunctionPio0, Pin};
-use hal::pac;
use hal::pio::PIOExt;
use hal::Sio;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
use panic_halt as _;
-use rp2040_hal as hal;
/// The linker will place this boot block at the start of our program image. We
/// need this to help the ROM bootloader get our code up and running.
@@ -23,11 +26,11 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
/// Entry point to our bare-metal application.
///
-/// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
/// as soon as all global variables and the spinlock are initialised.
-#[rp2040_hal::entry]
+#[hal::entry]
fn main() -> ! {
- let mut pac = pac::Peripherals::take().unwrap();
+ let mut pac = hal::pac::Peripherals::take().unwrap();
let sio = Sio::new(pac.SIO);
let pins = hal::gpio::Pins::new(
@@ -44,10 +47,10 @@ fn main() -> ! {
// Define some simple PIO program.
let program = pio_proc::pio_asm!(
- ".side_set 1", // each instruction may set 1 bit
+ ".side_set 1", // each instruction must set 1 bit
".wrap_target",
- " nop side 1",
- " nop side 0",
+ " nop side 1 [15]",
+ " nop side 0 [15]",
".wrap",
);
@@ -55,7 +58,7 @@ fn main() -> ! {
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
let installed = pio.install(&program.program).unwrap();
let (int, frac) = (0, 0); // as slow as possible (0 is interpreted as 65536)
- let (mut sm, _, _) = rp2040_hal::pio::PIOBuilder::from_installed_program(installed)
+ let (mut sm, _, _) = hal::pio::PIOBuilder::from_installed_program(installed)
.side_set_pin_base(led_pin_id)
.clock_divisor_fixed_point(int, frac)
.build(sm0);
@@ -65,6 +68,19 @@ fn main() -> ! {
// PIO runs in background, independently from CPU
loop {
- cortex_m::asm::wfi();
+ hal::arch::wfi();
}
}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"PIO Side-set Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp2040-hal-examples/src/bin/rom_funcs.rs b/rp2040-hal-examples/src/bin/rom_funcs.rs
index e7e70c394..dc2b984c8 100644
--- a/rp2040-hal-examples/src/bin/rom_funcs.rs
+++ b/rp2040-hal-examples/src/bin/rom_funcs.rs
@@ -91,7 +91,7 @@ fn main() -> ! {
);
let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
.enable(
- UartConfig::new(9600.Hz(), DataBits::Eight, None, StopBits::One),
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
clocks.peripheral_clock.freq(),
)
.unwrap();
diff --git a/rp2040-hal-examples/src/bin/uart.rs b/rp2040-hal-examples/src/bin/uart.rs
index 1eaf17211..bb291223c 100644
--- a/rp2040-hal-examples/src/bin/uart.rs
+++ b/rp2040-hal-examples/src/bin/uart.rs
@@ -91,7 +91,7 @@ fn main() -> ! {
);
let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
.enable(
- UartConfig::new(9600.Hz(), DataBits::Eight, None, StopBits::One),
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
clocks.peripheral_clock.freq(),
)
.unwrap();
diff --git a/rp2040-hal-examples/src/bin/uart_dma.rs b/rp2040-hal-examples/src/bin/uart_dma.rs
index 5f9008083..77f65d02b 100644
--- a/rp2040-hal-examples/src/bin/uart_dma.rs
+++ b/rp2040-hal-examples/src/bin/uart_dma.rs
@@ -91,7 +91,7 @@ fn main() -> ! {
);
let uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
.enable(
- UartConfig::new(9600.Hz(), DataBits::Eight, None, StopBits::One),
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
clocks.peripheral_clock.freq(),
)
.unwrap();
diff --git a/rp2040-hal-examples/src/bin/uart_loopback.rs b/rp2040-hal-examples/src/bin/uart_loopback.rs
new file mode 100644
index 000000000..fd41b5e1a
--- /dev/null
+++ b/rp2040-hal-examples/src/bin/uart_loopback.rs
@@ -0,0 +1,429 @@
+//! # UART Loopback Example
+//!
+//! This application tests handling UART errors.
+//!
+//! It may need to be adapted to your particular board layout and/or pin
+//! assignment. We assume you have connected GP0 to a TTL UART on your host
+//! computer at 115200 baud. We assume that GP1 is connected to GP4, which is
+//! our UART loopback connection.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Alias for our HAL crate
+use rp2040_hal as hal;
+
+// Some traits we need
+use core::fmt::Write;
+use embedded_hal::delay::DelayNs;
+use hal::fugit::RateExtU32;
+use rp2040_hal::clocks::Clock;
+
+// UART related types
+use hal::uart::{DataBits, Parity, StopBits, UartConfig};
+
+/// The linker will place this boot block at the start of our program image. We
+/// need this to help the ROM bootloader get our code up and running.
+/// Note: This boot block is not necessary when using a rp-hal based BSP
+/// as the BSPs already perform this step.
+#[link_section = ".boot2"]
+#[used]
+pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
+
+/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
+/// if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Some sample text we can send.
+///
+/// It's the same length as the UART FIFO, deliberately.
+static SAMPLE32: [u8; 32] = *b"abcdefghijklmnopqrstuvwxyz012345";
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the RP2040 peripherals, then writes to the UART in
+/// an infinite loop.
+#[rp2040_hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ let mut delay = hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ let uart0_pins = (
+ // UART TX (characters sent from RP2350)
+ pins.gpio0.into_function(),
+ // UART RX (characters received by RP2350)
+ pins.gpio1.into_pull_up_input().into_function(),
+ );
+ let mut uart0 = hal::uart::UartPeripheral::new(pac.UART0, uart0_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Even),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart0.set_fifos(true);
+
+ let uart1_pins = (
+ // UART TX (characters sent from RP2350)
+ pins.gpio4.into_function(),
+ // UART RX (characters received by RP2350)
+ pins.gpio5.into_pull_up_input().into_function(),
+ );
+ let mut uart1 = hal::uart::UartPeripheral::new(pac.UART1, uart1_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Even),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart1.set_fifos(true);
+
+ let mut buffer = [0u8; 128];
+
+ // ======================================================================
+ // Single byte read/write
+ // ======================================================================
+
+ let sample = &SAMPLE32[0..1];
+ _ = writeln!(uart0, "** Testing single byte write/read");
+ uart1.write_full_blocking(sample);
+ delay.delay_ms(100);
+ uart0
+ .read_full_blocking(&mut buffer[0..sample.len()])
+ .unwrap();
+ if &buffer[0..sample.len()] != sample {
+ _ = writeln!(
+ uart0,
+ "Failed:\n{:02x?} !=\n{:02x?}",
+ &buffer[0..sample.len()],
+ sample
+ );
+ panic!("Test failed");
+ }
+ _ = writeln!(uart0, "Tested OK");
+
+ // ======================================================================
+ // FIFO backed read/write
+ // ======================================================================
+
+ let sample = &SAMPLE32[..];
+ _ = writeln!(uart0, "** Testing FIFO write/read");
+ uart1.write_full_blocking(sample);
+ delay.delay_ms(100);
+ uart0
+ .read_full_blocking(&mut buffer[0..sample.len()])
+ .unwrap();
+ if &buffer[0..sample.len()] != sample {
+ _ = writeln!(
+ uart0,
+ "Failed:\n{:02x?} !=\n{:02x?}",
+ &buffer[0..sample.len()],
+ sample
+ );
+ panic!("Test failed");
+ }
+ _ = writeln!(uart0, "Tested OK");
+
+ // ======================================================================
+ // FIFO overflow read/write
+ //
+ // Note: The Arm Primecell PL022 UART that Raspberry Pi uses has a 32-byte
+ // FIFO. We're about to overflow that FIFO.
+ // ======================================================================
+
+ let sample = &SAMPLE32[..];
+ _ = writeln!(uart0, "** Testing FIFO overflow write/read");
+ _ = writeln!(uart0, "Sending {} bytes...", sample.len() + 1);
+ // send 32 bytes to the receiving FIFO
+ uart1.write_full_blocking(sample);
+ // Now send one more byte to overflow the receiving FIFO. This byte is lost
+ // to the wind.
+ //
+ // NB: It fits into the TX FIFO because this is a 'blocking' call that
+ // waited for FIFO space.
+ uart1.write_full_blocking(&[0x00]);
+ // Let the TX FIFO drain.
+ delay.delay_ms(100);
+ // the first 32 bytes should read fine
+ uart0
+ .read_full_blocking(&mut buffer[0..sample.len()])
+ .unwrap();
+ if &buffer[0..sample.len()] != sample {
+ _ = writeln!(
+ uart0,
+ "Failed:\n{:02x?} !=\n{:02x?}",
+ &buffer[0..sample.len()],
+ sample
+ );
+ panic!("Test failed");
+ }
+ _ = writeln!(uart0, "Got first 32 bytes OK...");
+
+ _ = writeln!(
+ uart0,
+ "I now want to see Overrun([]), WouldBlock, WouldBlock"
+ );
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+ // Now send two more bytes - the first will also be flagged with an overrun error.
+ _ = writeln!(uart0, "Sending two more bytes...");
+ uart1.write_full_blocking(&[0x01, 0x02]);
+ // let them transfer over
+ delay.delay_ms(100);
+ // annoyingly we see the overrun error again, then our data
+ _ = writeln!(uart0, "I want to see Overrun([1]), Ok(1), WouldBlock");
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+
+ // ======================================================================
+ // FIFO read/write with parity error
+ // ======================================================================
+
+ _ = writeln!(uart0, "** Testing FIFO read with parity errors");
+ // Send three bytes with correct parity
+ uart1.write_full_blocking(&[0x00, 0x01, 0x02]);
+ delay.delay_ms(100);
+ // send one with bad settings
+ uart1 = uart1
+ .disable()
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Odd),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart1.write_full_blocking(&[0x03]);
+ delay.delay_ms(100);
+ // send three more with good parity
+ uart1 = uart1
+ .disable()
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Even),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart1.write_full_blocking(&[0x04, 0x05, 0x06]);
+ delay.delay_ms(100);
+
+ _ = writeln!(uart0, "I want to see Parity error [0, 1, 2]");
+ match uart0.read_raw(&mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+ _ = writeln!(uart0, "I want to see RX: [4, 5, 6]");
+ match uart0.read_raw(&mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+ _ = writeln!(uart0, "I want to see WouldBlock");
+ match uart0.read_raw(&mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+
+ // ======================================================================
+ // FIFO backed read/write with embedded_io traits.
+ // ======================================================================
+
+ let sample = &SAMPLE32[..];
+ _ = writeln!(uart0, "** Testing FIFO write/read with embedded-io");
+
+ embedded_io::Write::write_all(&mut uart1, sample).unwrap();
+ delay.delay_ms(100);
+
+ embedded_io::Read::read_exact(&mut uart0, &mut buffer[0..sample.len()]).unwrap();
+ if &buffer[0..sample.len()] != sample {
+ _ = writeln!(
+ uart0,
+ "Failed:\n{:02x?} !=\n{:02x?}",
+ &buffer[0..sample.len()],
+ sample
+ );
+ panic!("Test failed");
+ }
+ _ = writeln!(uart0, "Tested OK");
+
+ // ======================================================================
+ // FIFO read/write with parity error using embedded-io
+ // ======================================================================
+
+ _ = writeln!(uart0, "** Testing FIFO read with parity errors");
+ // Send three bytes with correct parity
+ uart1.write_full_blocking(&[0x00, 0x01, 0x02]);
+ delay.delay_ms(100);
+ // send one with bad settings
+ uart1 = uart1
+ .disable()
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Odd),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart1.write_full_blocking(&[0x03]);
+ delay.delay_ms(100);
+ // send three more with good parity
+ uart1 = uart1
+ .disable()
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Even),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart1.write_full_blocking(&[0x04, 0x05, 0x06]);
+ delay.delay_ms(100);
+
+ _ = writeln!(uart0, "I want to see RX: [0, 1, 2]");
+ match embedded_io::Read::read(&mut uart0, &mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+ _ = writeln!(uart0, "I want to see ParityError");
+ match embedded_io::Read::read(&mut uart0, &mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+
+ _ = writeln!(uart0, "I want to see RX: [4, 5, 6]");
+ match embedded_io::Read::read(&mut uart0, &mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+
+ _ = writeln!(uart0, "I want to see RX ready: false");
+ match embedded_io::ReadReady::read_ready(&mut uart0) {
+ Ok(ready) => {
+ _ = writeln!(uart0, "RX ready: {}", ready);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+
+ // ======================================================================
+ // Tests complete
+ // ======================================================================
+
+ _ = writeln!(uart0, "Tests complete. Review output for correctness.");
+
+ // Reboot back into USB mode (no activity, both interfaces enabled)
+ rp2040_hal::rom_data::reset_to_usb_boot(0, 0);
+
+ // In case the reboot fails
+ loop {
+ cortex_m::asm::wfi();
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"UART Loopback Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+#[panic_handler]
+fn panic(_: &core::panic::PanicInfo) -> ! {
+ // wait about 1s for UART FIFOs to flush
+ for _ in 0..75_000_000 {
+ cortex_m::asm::nop();
+ }
+ // Reboot back into USB mode (no activity, both interfaces enabled)
+ rp2040_hal::rom_data::reset_to_usb_boot(0, 0);
+
+ // In case the reboot fails
+ loop {
+ cortex_m::asm::wfi();
+ }
+}
+
+// End of file
diff --git a/rp2040-hal-examples/src/bin/watchdog.rs b/rp2040-hal-examples/src/bin/watchdog.rs
index c8112b26e..139100f69 100644
--- a/rp2040-hal-examples/src/bin/watchdog.rs
+++ b/rp2040-hal-examples/src/bin/watchdog.rs
@@ -98,7 +98,7 @@ fn main() -> ! {
watchdog.feed();
}
- // Blink 10 times per second, not feeding the watchdog.
+ // Blink 5 times per second, not feeding the watchdog.
// The processor should reset in 1.05 seconds, or 5 blinks time
loop {
led_pin.set_low().unwrap();
diff --git a/rp2040-hal-macros/.gitignore b/rp2040-hal-macros/.gitignore
new file mode 100644
index 000000000..ff47c2d77
--- /dev/null
+++ b/rp2040-hal-macros/.gitignore
@@ -0,0 +1,11 @@
+# Generated by Cargo
+# will have compiled files and executables
+debug/
+target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
diff --git a/rp2040-hal-macros/README.md b/rp2040-hal-macros/README.md
index de3532087..dc587979b 100644
--- a/rp2040-hal-macros/README.md
+++ b/rp2040-hal-macros/README.md
@@ -17,5 +17,5 @@ license when you re-use this code. See [`LICENSE-MIT`](./LICENSE-MIT) or
license. Our Apache 2.0 notices can be found in [`NOTICE`](./NOTICE).
Unless you explicitly state otherwise, any contribution intentionally submitted
-for inclus`ion in the work by you, as defined in the Apache-2.0 license, shall be
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
diff --git a/rp2040-hal/CHANGELOG.md b/rp2040-hal/CHANGELOG.md
index 967f2dc5a..d957a9447 100644
--- a/rp2040-hal/CHANGELOG.md
+++ b/rp2040-hal/CHANGELOG.md
@@ -16,6 +16,10 @@ The Minimum-Supported Rust Version (MSRV) for the next release is 1.77
- Support for *binary info*, which is metadata that `picotool` can read from your binary.
- Bump MSRV to 1.77, because *binary info* examples need C-Strings.
+### Fixed
+
+- Let UART embedded\_io::Write::write return some bytes were written.
+
## [0.10.0] - 2024-03-10
### Added
diff --git a/rp2040-hal/Cargo.toml b/rp2040-hal/Cargo.toml
index c1a07286a..858ca79f9 100644
--- a/rp2040-hal/Cargo.toml
+++ b/rp2040-hal/Cargo.toml
@@ -36,6 +36,7 @@ paste = "1.0"
pio = "0.2.0"
rand_core = "0.6.3"
rp-binary-info = { version = "0.1.0", path = "../rp-binary-info" }
+rp-hal-common = {version="0.1.0", path="../rp-hal-common"}
rp2040-hal-macros = {version = "0.1.0", path = "../rp2040-hal-macros"}
rp2040-pac = {version = "0.6.0", features = ["critical-section"]}
usb-device = "0.3"
diff --git a/rp2040-hal/src/adc.rs b/rp2040-hal/src/adc.rs
index 1a5ba5457..2da0aaf80 100644
--- a/rp2040-hal/src/adc.rs
+++ b/rp2040-hal/src/adc.rs
@@ -1,6 +1,6 @@
//! Analog-Digital Converter (ADC)
//!
-//! See [Chapter 4 Section 9](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) of the datasheet for more details
+//! See [Chapter 4 Section 9](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) of the datasheet for more details
//!
//! ## Usage
//!
diff --git a/rp2040-hal/src/arch.rs b/rp2040-hal/src/arch.rs
new file mode 100644
index 000000000..4360d4b1b
--- /dev/null
+++ b/rp2040-hal/src/arch.rs
@@ -0,0 +1,67 @@
+//! Portable in-line assembly
+//!
+//! On the RP235x, this is useful to write code portable between ARM and RISC-V cores.
+//! While there's no such choice on the RP2040, providing the same functions helps writing
+//! code that works on both RP2040 and RP235x.
+
+#[cfg(all(target_arch = "arm", target_os = "none"))]
+mod inner {
+ pub use cortex_m::asm::{delay, dsb, nop, sev, wfe, wfi};
+ pub use cortex_m::interrupt::{disable as interrupt_disable, enable as interrupt_enable};
+
+ /// Are interrupts current enabled?
+ pub fn interrupts_enabled() -> bool {
+ cortex_m::register::primask::read().is_active()
+ }
+
+ /// Run the closure without interrupts
+ ///
+ /// No critical-section token because we haven't blocked the second core
+ pub fn interrupt_free(f: F) -> T
+ where
+ F: FnOnce() -> T,
+ {
+ let active = interrupts_enabled();
+ if active {
+ interrupt_disable();
+ }
+ let t = f();
+ if active {
+ unsafe {
+ interrupt_enable();
+ }
+ }
+ t
+ }
+}
+
+#[cfg(not(all(any(target_arch = "arm", target_arch = "riscv32"), target_os = "none")))]
+mod inner {
+ /// Placeholder function to disable interrupts
+ pub fn interrupt_disable() {}
+ /// Placeholder function to enable interrupts
+ pub fn interrupt_enable() {}
+ /// Placeholder function to check if interrupts are enabled
+ pub fn interrupts_enabled() -> bool {
+ false
+ }
+ /// Placeholder function to wait for an event
+ pub fn wfe() {}
+ /// Placeholder function to do nothing
+ pub fn nop() {}
+ /// Placeholder function to emit a data synchronisation barrier
+ pub fn dsb() {}
+ /// Placeholder function to run a closure with interrupts disabled
+ pub fn interrupt_free(f: F) -> T
+ where
+ F: FnOnce() -> T,
+ {
+ f()
+ }
+ /// Placeholder function to wait for some clock cycles
+ pub fn delay(_: u32) {}
+ /// Placeholder function to emit an event
+ pub fn sev() {}
+}
+
+pub use inner::*;
diff --git a/rp2040-hal/src/clocks/mod.rs b/rp2040-hal/src/clocks/mod.rs
index 014f0a1b8..f2c4c88ef 100644
--- a/rp2040-hal/src/clocks/mod.rs
+++ b/rp2040-hal/src/clocks/mod.rs
@@ -60,7 +60,7 @@
//! # }
//! ```
//!
-//! See [Chapter 2 Section 15](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
+//! See [Chapter 2 Section 15](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details
use core::{convert::Infallible, marker::PhantomData};
use fugit::{HertzU32, RateExtU32};
diff --git a/rp2040-hal/src/i2c.rs b/rp2040-hal/src/i2c.rs
index 18d91287f..dfa06c349 100644
--- a/rp2040-hal/src/i2c.rs
+++ b/rp2040-hal/src/i2c.rs
@@ -1,6 +1,6 @@
//! Inter-Integrated Circuit (I2C) bus
//!
-//! See [Chapter 4 Section 3](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
+//! See [Chapter 4 Section 3](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details
//!
//! ## Usage
//! ```no_run
diff --git a/rp2040-hal/src/lib.rs b/rp2040-hal/src/lib.rs
index 73efe6d17..2c16312e8 100644
--- a/rp2040-hal/src/lib.rs
+++ b/rp2040-hal/src/lib.rs
@@ -35,6 +35,8 @@
//! `rtic_monotonic::Monotonic` based on the RP2040 timer peripheral
//! * **i2c-write-iter** -
//! Implement `i2c_write_iter` traits for `I2C<_, _, Controller>`.
+//! * **binary-info** -
+//! Include a `static` variable containing picotool compatible binary info.
#![warn(missing_docs)]
#![no_std]
@@ -49,6 +51,7 @@ pub use rp2040_pac as pac;
mod intrinsics;
pub mod adc;
+pub mod arch;
#[macro_use]
pub mod async_utils;
pub(crate) mod atomic_register_access;
diff --git a/rp2040-hal/src/pio.rs b/rp2040-hal/src/pio.rs
index 0656bc4f6..214db8dec 100644
--- a/rp2040-hal/src/pio.rs
+++ b/rp2040-hal/src/pio.rs
@@ -1,5 +1,7 @@
//! Programmable IO (PIO)
-//! See [Chapter 3 of the datasheet](https://rptl.io/rp2040-datasheet#section_pio) for more details.
+//!
+//! See [Chapter 3 of the datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf#section_pio) for more details.
+
use core::ops::Deref;
use pio::{Instruction, InstructionOperands, Program, SideSet, Wrap};
diff --git a/rp2040-hal/src/pll.rs b/rp2040-hal/src/pll.rs
index 2255cd39b..786e2eb74 100644
--- a/rp2040-hal/src/pll.rs
+++ b/rp2040-hal/src/pll.rs
@@ -1,5 +1,6 @@
//! Phase-Locked Loops (PLL)
-// See [Chapter 2 Section 18](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
+//!
+//! See [Chapter 2 Section 18](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details.
use core::{
convert::Infallible,
diff --git a/rp2040-hal/src/resets.rs b/rp2040-hal/src/resets.rs
index 0c34a0e7b..cb091d265 100644
--- a/rp2040-hal/src/resets.rs
+++ b/rp2040-hal/src/resets.rs
@@ -1,5 +1,7 @@
//! Subsystem Resets
-// See [Chapter 2 Section 14](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
+//!
+//! See [Chapter 2 Section 14](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details.
+
mod private {
pub trait SubsystemReset {
fn reset_bring_up(&self, resets: &mut crate::pac::RESETS);
diff --git a/rp2040-hal/src/rosc.rs b/rp2040-hal/src/rosc.rs
index d05025295..abad4f3e9 100644
--- a/rp2040-hal/src/rosc.rs
+++ b/rp2040-hal/src/rosc.rs
@@ -1,6 +1,6 @@
//! Ring Oscillator (ROSC)
//!
-//! See [Chapter 2 Section 17](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
+//! See [Chapter 2 Section 17](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details
//!
//! In addition to its obvious role as a clock source, [`RingOscillator`] can also be used as a random number source
//! for the [`rand`] crate:
diff --git a/rp2040-hal/src/spi.rs b/rp2040-hal/src/spi.rs
index f8c8590bf..0969b1e6b 100644
--- a/rp2040-hal/src/spi.rs
+++ b/rp2040-hal/src/spi.rs
@@ -3,7 +3,7 @@
//! [`Spi`] is the main struct exported by this module, representing a configured Spi bus. See its
//! docs for more information on its type parameters.
//!
-//! See [Chapter 4 Section 4](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
+//! See [Chapter 4 Section 4](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details
//!
//! ## Usage
//!
diff --git a/rp2040-hal/src/ssi.rs b/rp2040-hal/src/ssi.rs
index 382a68b44..f536cdbaa 100644
--- a/rp2040-hal/src/ssi.rs
+++ b/rp2040-hal/src/ssi.rs
@@ -1,3 +1,5 @@
//! Synchronous Serial Interface (SSI)
-// See [Chapter 4 Section 10](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
+//!
+//! See [Chapter 4 Section 10](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details.
+
// TODO
diff --git a/rp2040-hal/src/timer.rs b/rp2040-hal/src/timer.rs
index b9b49f03c..b6edbde37 100644
--- a/rp2040-hal/src/timer.rs
+++ b/rp2040-hal/src/timer.rs
@@ -6,7 +6,7 @@
//!
//! Each of the 4 alarms can match on the lower 32 bits of Counter and trigger an interrupt.
//!
-//! See [Chapter 4 Section 6](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) of the datasheet for more details.
+//! See [Chapter 4 Section 6](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) of the datasheet for more details.
use core::sync::atomic::{AtomicU8, Ordering};
use fugit::{MicrosDurationU32, MicrosDurationU64, TimerInstantU64};
diff --git a/rp2040-hal/src/uart/common_configs.rs b/rp2040-hal/src/uart/common_configs.rs
index 3515e72d6..b36445413 100644
--- a/rp2040-hal/src/uart/common_configs.rs
+++ b/rp2040-hal/src/uart/common_configs.rs
@@ -1,43 +1,2 @@
-use fugit::HertzU32;
-
-use super::{DataBits, StopBits, UartConfig};
-
-/// 9600 baud, 8 data bits, no parity, 1 stop bit
-pub const _9600_8_N_1: UartConfig = UartConfig {
- baudrate: HertzU32::from_raw(9600),
- data_bits: DataBits::Eight,
- stop_bits: StopBits::One,
- parity: None,
-};
-
-/// 19200 baud, 8 data bits, no parity, 1 stop bit
-pub const _19200_8_N_1: UartConfig = UartConfig {
- baudrate: HertzU32::from_raw(19200),
- data_bits: DataBits::Eight,
- stop_bits: StopBits::One,
- parity: None,
-};
-
-/// 38400 baud, 8 data bits, no parity, 1 stop bit
-pub const _38400_8_N_1: UartConfig = UartConfig {
- baudrate: HertzU32::from_raw(38400),
- data_bits: DataBits::Eight,
- stop_bits: StopBits::One,
- parity: None,
-};
-
-/// 57600 baud, 8 data bits, no parity, 1 stop bit
-pub const _57600_8_N_1: UartConfig = UartConfig {
- baudrate: HertzU32::from_raw(57600),
- data_bits: DataBits::Eight,
- stop_bits: StopBits::One,
- parity: None,
-};
-
-/// 115200 baud, 8 data bits, no parity, 1 stop bit
-pub const _115200_8_N_1: UartConfig = UartConfig {
- baudrate: HertzU32::from_raw(115200),
- data_bits: DataBits::Eight,
- stop_bits: StopBits::One,
- parity: None,
-};
+#[doc(inline)]
+pub use rp_hal_common::uart::common_configs::*;
diff --git a/rp2040-hal/src/uart/mod.rs b/rp2040-hal/src/uart/mod.rs
index 5b1f65223..c7b4d734d 100644
--- a/rp2040-hal/src/uart/mod.rs
+++ b/rp2040-hal/src/uart/mod.rs
@@ -1,6 +1,6 @@
//! Universal Asynchronous Receiver Transmitter (UART)
//!
-//! See [Chapter 4 Section 2](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) of the datasheet for more details
+//! See [Chapter 4 Section 2](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) of the datasheet for more details
//!
//! ## Usage
//!
diff --git a/rp2040-hal/src/uart/peripheral.rs b/rp2040-hal/src/uart/peripheral.rs
index 20b5f555a..6435229db 100644
--- a/rp2040-hal/src/uart/peripheral.rs
+++ b/rp2040-hal/src/uart/peripheral.rs
@@ -195,12 +195,14 @@ impl> UartPeripheral {
}
/// Writes bytes to the UART.
+ ///
/// This function blocks until the full buffer has been sent.
pub fn write_full_blocking(&self, data: &[u8]) {
super::writer::write_full_blocking(&self.device, data);
}
/// Reads bytes from the UART.
+ ///
/// This function blocks until the full buffer has been received.
pub fn read_full_blocking(&self, buffer: &mut [u8]) -> Result<(), ReadErrorType> {
super::reader::read_full_blocking(&self.device, buffer)
@@ -219,15 +221,16 @@ impl> UartPeripheral {
/// ```no_run
/// # use rp2040_hal::uart::{Pins, ValidUartPinout, Enabled, UartPeripheral};
/// # use rp2040_hal::pac::UART0;
+ /// # use rp2040_hal::timer::Timer;
/// # use rp2040_hal::typelevel::OptionTNone;
/// # use embedded_hal_0_2::blocking::delay::DelayUs;
/// # type PINS = Pins;
- /// # let mut serial: UartPeripheral = unsafe { core::mem::zeroed() };
- /// # let mut timer: rp2040_hal::Timer = unsafe { core::mem::zeroed() };
+ /// # fn example(mut serial: UartPeripheral, mut timer: Timer) {
/// serial.lowlevel_break_start();
/// // at 115_200Bps on 8N1 configuration, 20bits takes (20*10⁶)/115200 = 173.611…μs.
/// timer.delay_us(175);
/// serial.lowlevel_break_stop();
+ /// }
/// ```
pub fn lowlevel_break_start(&mut self) {
self.device.uartlcr_h().modify(|_, w| w.brk().set_bit());
@@ -490,6 +493,15 @@ impl> embedded_io::Read for UartPeripheral<
}
}
}
+
+impl> embedded_io::ReadReady
+ for UartPeripheral
+{
+ fn read_ready(&mut self) -> Result {
+ Ok(self.uart_is_readable() || self.read_error.is_some())
+ }
+}
+
impl> embedded_io::Write for UartPeripheral {
fn write(&mut self, buf: &[u8]) -> Result {
self.write_full_blocking(buf);
@@ -500,3 +512,11 @@ impl> embedded_io::Write for UartPeripheral
Ok(())
}
}
+
+impl> embedded_io::WriteReady
+ for UartPeripheral
+{
+ fn write_ready(&mut self) -> Result {
+ Ok(self.uart_is_writable())
+ }
+}
diff --git a/rp2040-hal/src/uart/reader.rs b/rp2040-hal/src/uart/reader.rs
index 11829f90d..73acd4ab4 100644
--- a/rp2040-hal/src/uart/reader.rs
+++ b/rp2040-hal/src/uart/reader.rs
@@ -121,7 +121,29 @@ pub(crate) fn read_raw<'b, D: UartDevice>(
Ok(loop {
if !is_readable(device) {
if bytes_read == 0 {
- return Err(WouldBlock);
+ // The overrun error (OE) bit is checked separately as it
+ // doesn't really correspond to a specific byte we've read. If
+ // we don't do this here, the overrun error is hidden until the
+ // next byte turns up - which may never happen.
+ if device.uartrsr().read().oe().bit_is_set() {
+ // We observed a FIFO overrun on an empty FIFO. Clear the
+ // error otherwise it sticks.
+ unsafe {
+ device.uartrsr().write_with_zero(|w| w);
+ }
+ // Now report the error.
+ //
+ // Note that you will also get an overrun error on the first
+ // byte that turns up after this error - we can't stop that
+ // as we have no mutable state to indicate that it's already
+ // been handled. But two overrun errors is better that none.
+ return Err(Other(ReadError {
+ err_type: ReadErrorType::Overrun,
+ discarded: &buffer[..bytes_read],
+ }));
+ } else {
+ return Err(WouldBlock);
+ }
} else {
break bytes_read;
}
@@ -135,15 +157,20 @@ pub(crate) fn read_raw<'b, D: UartDevice>(
// If multiple status bits are set, report
// the most serious or most specific condition,
// in the following order of precedence:
- // overrun > break > parity > framing
- if read.oe().bit_is_set() {
- error = Some(ReadErrorType::Overrun);
- } else if read.be().bit_is_set() {
+ // break > parity > framing
+ //
+ // overrun is last because the byte associated with it is still good.
+ if read.be().bit_is_set() {
error = Some(ReadErrorType::Break);
} else if read.pe().bit_is_set() {
error = Some(ReadErrorType::Parity);
} else if read.fe().bit_is_set() {
error = Some(ReadErrorType::Framing);
+ } else if read.oe().bit_is_set() {
+ error = Some(ReadErrorType::Overrun);
+ // if we get an overrun - there's still data there
+ buffer[bytes_read] = read.data().bits();
+ bytes_read += 1;
}
if let Some(err_type) = error {
@@ -244,6 +271,12 @@ impl> embedded_io::Read for Reader {
}
}
+impl> embedded_io::ReadReady for Reader {
+ fn read_ready(&mut self) -> Result {
+ Ok(is_readable(&self.device))
+ }
+}
+
impl> Read02 for Reader {
type Error = ReadErrorType;
diff --git a/rp2040-hal/src/uart/utils.rs b/rp2040-hal/src/uart/utils.rs
index 81841ef3b..74abc90a6 100644
--- a/rp2040-hal/src/uart/utils.rs
+++ b/rp2040-hal/src/uart/utils.rs
@@ -3,7 +3,9 @@ use crate::pac::{uart0::RegisterBlock, UART0, UART1};
use crate::resets::SubsystemReset;
use crate::typelevel::Sealed;
use core::ops::Deref;
-use fugit::HertzU32;
+
+#[doc(inline)]
+pub use rp_hal_common::uart::{DataBits, Parity, StopBits, UartConfig};
/// Error type for UART operations.
#[derive(Debug)]
@@ -68,79 +70,6 @@ impl Sealed for Enabled {}
impl State for Disabled {}
impl Sealed for Disabled {}
-/// Data bits
-pub enum DataBits {
- /// 5 bits
- Five,
- /// 6 bits
- Six,
- /// 7 bits
- Seven,
- /// 8 bits
- Eight,
-}
-
-/// Stop bits
-pub enum StopBits {
- /// 1 bit
- One,
- /// 2 bits
- Two,
-}
-
-/// Parity
-/// The "none" state of parity is represented with the Option type (None).
-pub enum Parity {
- /// Odd parity
- Odd,
- /// Even parity
- Even,
-}
-
-/// A struct holding the configuration for an UART device.
-///
-/// The `Default` implementation implements the following values:
-/// ```ignore
-/// # // can't actually create this with the non_exhaustive attribute
-/// UartConfig {
-/// baudrate: Baud(115_200),
-/// data_bits: DataBits::Eight,
-/// stop_bits: StopBits::One,
-/// parity: None,
-///}
-/// ```
-#[non_exhaustive]
-pub struct UartConfig {
- /// The baudrate the uart will run at.
- pub baudrate: HertzU32,
-
- /// The amount of data bits the uart should be configured to.
- pub data_bits: DataBits,
-
- /// The amount of stop bits the uart should be configured to.
- pub stop_bits: StopBits,
-
- /// The parity that this uart should have
- pub parity: Option,
-}
-
-impl UartConfig {
- /// Create a new instance of UartConfig
- pub const fn new(
- baudrate: HertzU32,
- data_bits: DataBits,
- parity: Option,
- stop_bits: StopBits,
- ) -> UartConfig {
- UartConfig {
- baudrate,
- data_bits,
- stop_bits,
- parity,
- }
- }
-}
-
/// Rx/Tx FIFO Watermark
///
/// Determine the FIFO level that trigger DMA/Interrupt
@@ -162,14 +91,3 @@ pub enum FifoWatermark {
/// Trigger when 28 bytes are (Rx: filled / Tx: available)
Bytes28,
}
-
-impl Default for UartConfig {
- fn default() -> Self {
- Self {
- baudrate: HertzU32::from_raw(115_200),
- data_bits: DataBits::Eight,
- stop_bits: StopBits::One,
- parity: None,
- }
- }
-}
diff --git a/rp2040-hal/src/uart/writer.rs b/rp2040-hal/src/uart/writer.rs
index 747e1d620..47bc5a0aa 100644
--- a/rp2040-hal/src/uart/writer.rs
+++ b/rp2040-hal/src/uart/writer.rs
@@ -214,8 +214,8 @@ impl> embedded_io::ErrorType for Writer> embedded_io::Write for Writer {
fn write(&mut self, buf: &[u8]) -> Result {
- self.write_full_blocking(buf);
- Ok(buf.len())
+ let remaining = nb::block!(write_raw(&self.device, buf)).unwrap(); // Infallible
+ Ok(buf.len() - remaining.len())
}
fn flush(&mut self) -> Result<(), Self::Error> {
nb::block!(transmit_flushed(&self.device)).unwrap(); // Infallible
@@ -223,6 +223,12 @@ impl> embedded_io::Write for Writer {
}
}
+impl> embedded_io::WriteReady for Writer {
+ fn write_ready(&mut self) -> Result {
+ Ok(uart_is_writable(&self.device))
+ }
+}
+
impl> Write02 for Writer {
type Error = Infallible;
diff --git a/rp2040-hal/src/usb.rs b/rp2040-hal/src/usb.rs
index 3f2f9f375..1e5e9dc1a 100644
--- a/rp2040-hal/src/usb.rs
+++ b/rp2040-hal/src/usb.rs
@@ -1,5 +1,7 @@
//! Universal Serial Bus (USB)
-// See [Chapter 4 Section 1](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
+//!
+//! See [Chapter 4 Section 1](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details.
+//!
//! ## Usage
//!
//! Initialize the Usb Bus forcing the VBUS detection.
diff --git a/rp2040-hal/src/watchdog.rs b/rp2040-hal/src/watchdog.rs
index 97e30fa1e..cfa11a74b 100644
--- a/rp2040-hal/src/watchdog.rs
+++ b/rp2040-hal/src/watchdog.rs
@@ -4,7 +4,7 @@
//! processor if software gets stuck in an infinite loop. The programmer must periodically write a value to the watchdog to
//! stop it from reaching zero.
//!
-//! See [Chapter 4 Section 7](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) of the datasheet for more details
+//! See [Chapter 4 Section 7](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) of the datasheet for more details
//!
//! ## Usage
//! ```no_run
diff --git a/rp2040-hal/src/xosc.rs b/rp2040-hal/src/xosc.rs
index 1c2ec68ac..8fb244796 100644
--- a/rp2040-hal/src/xosc.rs
+++ b/rp2040-hal/src/xosc.rs
@@ -1,5 +1,6 @@
//! Crystal Oscillator (XOSC)
-// See [Chapter 2 Section 16](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
+//!
+//! See [Chapter 2 Section 16](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details.
use core::{convert::Infallible, ops::RangeInclusive};
diff --git a/rp235x-hal-examples/.cargo/config.toml b/rp235x-hal-examples/.cargo/config.toml
new file mode 100644
index 000000000..1f5f243d7
--- /dev/null
+++ b/rp235x-hal-examples/.cargo/config.toml
@@ -0,0 +1,81 @@
+#
+# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository.
+#
+# You might want to make a similar file in your own repository if you are
+# writing programs for Raspberry Silicon microcontrollers.
+#
+
+[build]
+# Set the default target to match the Cortex-M33 in the RP2350
+target = "thumbv8m.main-none-eabihf"
+
+# This is the hard-float ABI for Arm mode.
+#
+# The FPU is enabled by default, and float function arguments use FPU
+# registers.
+[target.thumbv8m.main-none-eabihf]
+# Pass some extra options to rustc, some of which get passed on to the linker.
+#
+# * linker argument --nmagic turns off page alignment of sections (which saves
+# flash space)
+# * linker argument -Tlink.x tells the linker to use link.x as a linker script.
+# This is usually provided by the cortex-m-rt crate, and by default the
+# version in that crate will include a file called `memory.x` which describes
+# the particular memory layout for your specific chip.
+# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a
+# secondary linker script. This is required to make defmt_rtt work.
+rustflags = [
+ "-C", "link-arg=--nmagic",
+ "-C", "link-arg=-Tlink.x",
+ "-C", "link-arg=-Tdefmt.x",
+ "-C", "target-cpu=cortex-m33",
+]
+
+# Use picotool for loading.
+#
+# Load an elf, skipping unchanged flash sectors, verify it, and execute it
+runner = "picotool load -u -v -x -t elf"
+
+# This is the soft-float ABI for Arm mode.
+#
+# The FPU is disabled by default, and float function arguments use integer
+# registers. Only useful for making the `float_test` example give really bad
+# results on the `f32` benchmark.
+[target.thumbv8m.main-none-eabi]
+# Pass some extra options to rustc. See above for descriptions.
+rustflags = [
+ "-C", "link-arg=--nmagic",
+ "-C", "link-arg=-Tlink.x",
+ "-C", "link-arg=-Tdefmt.x",
+]
+
+# Use picotool for loading.
+#
+# Load an elf, skipping unchanged flash sectors, verify it, and execute it
+runner = "picotool load -u -v -x -t elf"
+
+# This is the soft-float ABI for RISC-V mode.
+#
+# Hazard 3 does not have an FPU and so float function arguments use integer
+# registers.
+[target.riscv32imac-unknown-none-elf]
+# Pass some extra options to rustc, some of which get passed on to the linker.
+#
+# * linker argument --nmagic turns off page alignment of sections (which saves
+# flash space)
+# * linker argument -Trp235x_riscv.x also tells the linker to use
+# `rp235x_riscv.x` as a linker script. This adds in RP2350 RISC-V specific
+# things that the riscv-rt crate's `link.x` requires and then includes
+# `link.x` automatically. This is the reverse of how we do it on Cortex-M.
+# * linker argument -Tdefmt.x also tells the linker to use `defmt.x` as a
+# secondary linker script. This is required to make defmt_rtt work.
+rustflags = [
+ "-C", "link-arg=--nmagic",
+ "-C", "link-arg=-Trp235x_riscv.x",
+ "-C", "link-arg=-Tdefmt.x",
+]
+
+# Use picotool for loading.
+#
+# Load an elf, skipping unchanged flash sectors, verify it, and execute it
+runner = "picotool load -u -v -x -t elf"
diff --git a/rp235x-hal-examples/.gitignore b/rp235x-hal-examples/.gitignore
new file mode 100644
index 000000000..ff47c2d77
--- /dev/null
+++ b/rp235x-hal-examples/.gitignore
@@ -0,0 +1,11 @@
+# Generated by Cargo
+# will have compiled files and executables
+debug/
+target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
diff --git a/rp235x-hal-examples/Cargo.toml b/rp235x-hal-examples/Cargo.toml
new file mode 100644
index 000000000..bb9f25f7e
--- /dev/null
+++ b/rp235x-hal-examples/Cargo.toml
@@ -0,0 +1,38 @@
+[package]
+authors = ["The rp-rs Developers"]
+categories = ["embedded", "hardware-support", "no-std", "no-std::no-alloc"]
+description = "Examples for the rp235x-hal crate"
+edition = "2021"
+homepage = "https://github.com/rp-rs/rp-hal"
+keywords = ["embedded", "hal", "raspberry-pi", "rp235x", "rp2350", "embedded-hal"]
+license = "MIT OR Apache-2.0"
+name = "rp235x-hal-examples"
+repository = "https://github.com/rp-rs/rp-hal"
+rust-version = "1.77"
+version = "0.1.0"
+
+[dependencies]
+cortex-m = "0.7.2"
+cortex-m-rt = "0.7"
+cortex-m-rtic = "1.1.4"
+critical-section = {version = "1.0.0"}
+defmt = "0.3"
+defmt-rtt = "0.4.0"
+dht-sensor = "0.2.1"
+embedded-alloc = "0.5.1"
+embedded-hal = "1.0.0"
+embedded-hal-async = "1.0.0"
+embedded-io = "0.6.1"
+embedded_hal_0_2 = {package = "embedded-hal", version = "0.2.5", features = ["unproven"]}
+fugit = "0.3.6"
+futures = {version = "0.3.30", default-features = false, features = ["async-await"]}
+hd44780-driver = "0.4.0"
+heapless = "0.8.0"
+nb = "1.0"
+nostd_async = {version = "0.6.1", features = ["cortex_m"]}
+panic-halt = "0.2.0"
+pio = "0.2.0"
+pio-proc = "0.2.0"
+rp235x-hal = {path = "../rp235x-hal", version = "0.2.0", features = ["binary-info", "critical-section-impl", "rt", "defmt"]}
+usb-device = "0.3.2"
+usbd-serial = "0.2.2"
diff --git a/rp235x-hal-examples/LICENSE-APACHE b/rp235x-hal-examples/LICENSE-APACHE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/rp235x-hal-examples/LICENSE-APACHE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/rp235x-hal-examples/LICENSE-MIT b/rp235x-hal-examples/LICENSE-MIT
new file mode 100644
index 000000000..6e052e35b
--- /dev/null
+++ b/rp235x-hal-examples/LICENSE-MIT
@@ -0,0 +1,19 @@
+Copyright (c) 2021-2024 The rp-rs Developers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/rp235x-hal-examples/NOTICE b/rp235x-hal-examples/NOTICE
new file mode 100644
index 000000000..790ecb167
--- /dev/null
+++ b/rp235x-hal-examples/NOTICE
@@ -0,0 +1,3 @@
+Copyright (c) 2021-2024 The rp-rs Developers
+
+Originally published at https://github.com/rp-rs/rp-hal
diff --git a/rp235x-hal-examples/README.md b/rp235x-hal-examples/README.md
new file mode 100644
index 000000000..6395e2c4c
--- /dev/null
+++ b/rp235x-hal-examples/README.md
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
+
rp-hal
+
+
+ Rust Examples for the Raspberry Silicon RP235x family Microcontrollers
+
+ View the examples »
+
+
+ Explore the API docs
+ ·
+ Report a Bug
+ ·
+ Chat on Matrix
+
+
+
+
+
+
+
+ Table of Contents
+
+ - Introduction
+ - Getting Started
+ - Roadmap
+ - Contributing
+ - License
+ - Contact
+ - Acknowledgements
+
+
+
+
+## Introduction
+
+The `rp235x-hal` package is a library crate of high-level Rust drivers for the
+Raspberry Silicon RP235x family of microcontrollers. This folder contains a
+collection of non-board specific example programs for you to study.
+
+We also provide a series of [*Board Support Package* (BSP) crates][BSPs], which
+take the HAL crate and pre-configure the pins according to a specific PCB
+design. If you are using one of the supported boards, you should use one of
+those crates in preference, and return here to see documentation about specific
+peripherals on the RP235x and how to use them. See the `boards` folder in
+https://github.com/rp-rs/rp-hal-boards/ for more details.
+
+[BSPs]: https://github.com/rp-rs/rp-hal-boards/
+
+
+## Getting Started
+
+To build all the examples, first grab a copy of the source code:
+
+```console
+$ git clone https://github.com/rp-rs/rp-hal.git
+```
+
+Then use `rustup` to grab the Rust Standard Library for the appropriate targets. There are two targets because the RP2350 has both an Arm mode and a RISC-V mode.
+
+```console
+$ rustup target add thumbv8m.main-none-eabihf
+$ rustup target add riscv32imac-unknown-none-elf
+```
+
+To build all the examples for Arm mode, run:
+
+```console
+$ cargo build --target=thumbv8m.main-none-eabihf
+ Compiling rp235x-hal-examples v0.1.0 (/home/user/rp-hal/rp235x-hal-examples)
+ Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.53s
+$ cargo build --bin blinky
+ Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s
+$ file ./target/thumbv8m.main-none-eabihf/debug/blinky
+./target/thumbv8m.main-none-eabihf/debug/blinky: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped
+```
+
+You can also 'run' an example, which thanks to our supplied
+[`.cargo/config.toml`](./.cargo/config.toml) will invoke Raspberry Pi's
+`picotool` to copy it to an RP235x in USB Bootloader mode. You should install
+that if you don't have it already, from
+.
+
+```console
+$ cargo run --bin blinky --target=thumbv8m.main-none-eabihf
+ Compiling rp235x-hal v0.10.0 (/home/user/rp-hal/rp235x-hal)
+ Compiling rp235x-hal-examples v0.1.0 (/home/user/rp-hal/rp235x-hal-examples)
+ Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.62s
+ Running `picotool load -u -v -x -t elf target/thumbv8m.main-none-eabihf/debug/blinky`
+Family id 'rp2350-arm-s' can be downloaded in absolute space:
+ 00000000->02000000
+Loading into Flash: [==============================] 100%
+Verifying Flash: [==============================] 100%
+ OK
+
+The device was rebooted to start the application.
+```
+
+It is currently possible to build *some* of the examples in RISC-V mode. See
+[`riscv_examples.txt`](./riscv_examples.txt) for a list of the examples known to
+work. The missing ones probably rely on interrupts, or some other thing we
+haven't ported to work in RISC-V mode yet.
+
+```console
+$ cargo build --bin blinky --target=riscv32imac-unknown-none-elf
+ Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s
+$ file ./target/riscv32imac-unknown-none-elf/debug/blinky
+./target/riscv32imac-unknown-none-elf/debug/blinky: ELF 32-bit LSB executable, UCB RISC-V, RVC, soft-float ABI, version 1 (SYSV), statically linked, with debug_info, not stripped
+$ cargo run --bin blinky --target=riscv32imac-unknown-none-elf
+ Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s
+ Running `picotool load -u -v -x -t elf target/riscv32imac-unknown-none-elf/debug/blinky`
+Family id 'rp2350-riscv' can be downloaded in absolute space:
+ 00000000->02000000
+Loading into Flash: [==============================] 100%
+Verifying Flash: [==============================] 100%
+ OK
+
+The device was rebooted to start the application.
+```
+
+
+## Roadmap
+
+NOTE The HAL is under active development, and so are these examples. As such, it
+is likely to remain volatile until a 1.0.0 release.
+
+See the [open issues](https://github.com/rp-rs/rp-hal/issues) for a list of
+proposed features (and known issues).
+
+
+## Contributing
+
+Contributions are what make the open source community such an amazing place to
+be learn, inspire, and create. Any contributions you make are **greatly
+appreciated**.
+
+1. Fork the Project
+2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
+3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
+4. Push to the Branch (`git push origin feature/AmazingFeature`)
+5. Open a Pull Request
+
+
+
+## License
+
+The contents of this repository are dual-licensed under the _MIT OR Apache 2.0_
+License. That means you can choose either the MIT license or the Apache 2.0
+license when you re-use this code. See [`LICENSE-MIT`](./LICENSE-MIT) or
+[`LICENSE-APACHE`](./LICENSE-APACHE) for more information on each specific
+license. Our Apache 2.0 notices can be found in [`NOTICE`](./NOTICE).
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
+
+
+## Contact
+
+* Project Link: [https://github.com/rp-rs/rp-hal/issues](https://github.com/rp-rs/rp-hal/issues)
+* Matrix: [#rp-rs:matrix.org](https://matrix.to/#/#rp-rs:matrix.org)
+
+
+## Acknowledgements
+
+* [Othneil Drew's README template](https://github.com/othneildrew)
diff --git a/rp235x-hal-examples/build.rs b/rp235x-hal-examples/build.rs
new file mode 100644
index 000000000..5cc06c04d
--- /dev/null
+++ b/rp235x-hal-examples/build.rs
@@ -0,0 +1,27 @@
+//! Set up linker scripts for the rp235x-hal examples
+
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+ // Put the linker script somewhere the linker can find it
+ let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
+ println!("cargo:rustc-link-search={}", out.display());
+
+ // The file `memory.x` is loaded by cortex-m-rt's `link.x` script, which
+ // is what we specify in `.cargo/config.toml` for Arm builds
+ let memory_x = include_bytes!("memory.x");
+ let mut f = File::create(out.join("memory.x")).unwrap();
+ f.write_all(memory_x).unwrap();
+ println!("cargo:rerun-if-changed=memory.x");
+
+ // The file `rp235x_riscv.x` is what we specify in `.cargo/config.toml` for
+ // RISC-V builds
+ let rp235x_riscv_x = include_bytes!("rp235x_riscv.x");
+ let mut f = File::create(out.join("rp235x_riscv.x")).unwrap();
+ f.write_all(rp235x_riscv_x).unwrap();
+ println!("cargo:rerun-if-changed=rp235x_riscv.x");
+
+ println!("cargo:rerun-if-changed=build.rs");
+}
diff --git a/rp235x-hal-examples/memory.x b/rp235x-hal-examples/memory.x
new file mode 100644
index 000000000..1e155038e
--- /dev/null
+++ b/rp235x-hal-examples/memory.x
@@ -0,0 +1,76 @@
+MEMORY {
+ /*
+ * The RP2350 has either external or internal flash.
+ *
+ * 2 MiB is a safe default here, although a Pico 2 has 4 MiB.
+ */
+ FLASH : ORIGIN = 0x10000000, LENGTH = 2048K
+ /*
+ * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping.
+ * This is usually good for performance, as it distributes load on
+ * those banks evenly.
+ */
+ RAM : ORIGIN = 0x20000000, LENGTH = 512K
+ /*
+ * RAM banks 8 and 9 use a direct mapping. They can be used to have
+ * memory areas dedicated for some specific job, improving predictability
+ * of access times.
+ * Example: Separate stacks for core0 and core1.
+ */
+ SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K
+ SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K
+}
+
+SECTIONS {
+ /* ### Boot ROM info
+ *
+ * Goes after .vector_table, to keep it in the first 4K of flash
+ * where the Boot ROM (and picotool) can find it
+ */
+ .start_block : ALIGN(4)
+ {
+ __start_block_addr = .;
+ KEEP(*(.start_block));
+ } > FLASH
+
+} INSERT AFTER .vector_table;
+
+/* move .text to start /after/ the boot info */
+_stext = ADDR(.start_block) + SIZEOF(.start_block);
+
+SECTIONS {
+ /* ### Picotool 'Binary Info' Entries
+ *
+ * Picotool looks through this block (as we have pointers to it in our
+ * header) to find interesting information.
+ */
+ .bi_entries : ALIGN(4)
+ {
+ /* We put this in the header */
+ __bi_entries_start = .;
+ /* Here are the entries */
+ KEEP(*(.bi_entries));
+ /* Keep this block a nice round size */
+ . = ALIGN(4);
+ /* We put this in the header */
+ __bi_entries_end = .;
+ } > FLASH
+} INSERT AFTER .text;
+
+SECTIONS {
+ /* ### Boot ROM extra info
+ *
+ * Goes after everything in our program, so it can contain a signature.
+ */
+ .end_block : ALIGN(4)
+ {
+ __end_block_addr = .;
+ KEEP(*(.end_block));
+ } > FLASH
+
+} INSERT AFTER .uninit;
+
+PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
+PROVIDE(end_to_start = __start_block_addr - __end_block_addr);
+
+
diff --git a/rp235x-hal-examples/riscv_examples.txt b/rp235x-hal-examples/riscv_examples.txt
new file mode 100644
index 000000000..0800802a5
--- /dev/null
+++ b/rp235x-hal-examples/riscv_examples.txt
@@ -0,0 +1,30 @@
+adc
+adc_fifo_dma
+adc_fifo_poll
+alloc
+arch_flip
+binary_info_demo
+blinky
+block_loop
+dht11
+gpio_dyn_pin_array
+gpio_in_out
+i2c
+lcd_display
+mem_to_mem_dma
+pio_blink
+pio_dma
+pio_proc_blink
+pio_side_set
+pio_synchronized
+pwm_blink
+pwm_blink_embedded_hal_1
+rom_funcs
+rosc_as_system_clock
+spi
+spi_dma
+uart
+uart_dma
+uart_loopback
+usb
+watchdog
diff --git a/rp235x-hal-examples/rp235x_riscv.x b/rp235x-hal-examples/rp235x_riscv.x
new file mode 100644
index 000000000..ac355eab4
--- /dev/null
+++ b/rp235x-hal-examples/rp235x_riscv.x
@@ -0,0 +1,252 @@
+MEMORY {
+ /*
+ * The RP2350 has either external or internal flash.
+ *
+ * 2 MiB is a safe default here, although a Pico 2 has 4 MiB.
+ */
+ FLASH : ORIGIN = 0x10000000, LENGTH = 2048K
+ /*
+ * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping.
+ * This is usually good for performance, as it distributes load on
+ * those banks evenly.
+ */
+ RAM : ORIGIN = 0x20000000, LENGTH = 512K
+ /*
+ * RAM banks 8 and 9 use a direct mapping. They can be used to have
+ * memory areas dedicated for some specific job, improving predictability
+ * of access times.
+ * Example: Separate stacks for core0 and core1.
+ */
+ SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K
+ SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K
+}
+
+/* # Developer notes
+
+- Symbols that start with a double underscore (__) are considered "private"
+
+- Symbols that start with a single underscore (_) are considered "semi-public"; they can be
+ overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" {
+ static mut _heap_size }`).
+
+- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a
+ symbol is not dropped if it appears in or near the front of the linker arguments and "it's not
+ needed" by any of the preceding objects (linker arguments)
+
+- `PROVIDE` is used to provide default values that can be overridden by a user linker script
+
+- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
+ the LMA of .data are all `32`-byte aligned. These alignments are assumed by the RAM
+ initialization routine. There's also a second benefit: `32`-byte aligned boundaries
+ means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`.
+*/
+
+PROVIDE(_stext = ORIGIN(FLASH));
+PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM));
+PROVIDE(_max_hart_id = 0);
+PROVIDE(_hart_stack_size = 2K);
+PROVIDE(_heap_size = 0);
+
+PROVIDE(InstructionMisaligned = ExceptionHandler);
+PROVIDE(InstructionFault = ExceptionHandler);
+PROVIDE(IllegalInstruction = ExceptionHandler);
+PROVIDE(Breakpoint = ExceptionHandler);
+PROVIDE(LoadMisaligned = ExceptionHandler);
+PROVIDE(LoadFault = ExceptionHandler);
+PROVIDE(StoreMisaligned = ExceptionHandler);
+PROVIDE(StoreFault = ExceptionHandler);
+PROVIDE(UserEnvCall = ExceptionHandler);
+PROVIDE(SupervisorEnvCall = ExceptionHandler);
+PROVIDE(MachineEnvCall = ExceptionHandler);
+PROVIDE(InstructionPageFault = ExceptionHandler);
+PROVIDE(LoadPageFault = ExceptionHandler);
+PROVIDE(StorePageFault = ExceptionHandler);
+
+PROVIDE(SupervisorSoft = DefaultHandler);
+PROVIDE(MachineSoft = DefaultHandler);
+PROVIDE(SupervisorTimer = DefaultHandler);
+PROVIDE(MachineTimer = DefaultHandler);
+PROVIDE(SupervisorExternal = DefaultHandler);
+PROVIDE(MachineExternal = DefaultHandler);
+
+PROVIDE(DefaultHandler = DefaultInterruptHandler);
+PROVIDE(ExceptionHandler = DefaultExceptionHandler);
+
+/* # Pre-initialization function */
+/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function,
+ then the function this points to will be called before the RAM is initialized. */
+PROVIDE(__pre_init = default_pre_init);
+
+/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */
+PROVIDE(_setup_interrupts = default_setup_interrupts);
+
+/* # Multi-processing hook function
+ fn _mp_hook() -> bool;
+
+ This function is called from all the harts and must return true only for one hart,
+ which will perform memory initialization. For other harts it must return false
+ and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt).
+*/
+PROVIDE(_mp_hook = default_mp_hook);
+
+/* # Start trap function override
+ By default uses the riscv crates default trap handler
+ but by providing the `_start_trap` symbol external crates can override.
+*/
+PROVIDE(_start_trap = default_start_trap);
+
+SECTIONS
+{
+ .text.dummy (NOLOAD) :
+ {
+ /* This section is intended to make _stext address work */
+ . = ABSOLUTE(_stext);
+ } > FLASH
+
+ .text _stext :
+ {
+ /* Put reset handler first in .text section so it ends up as the entry */
+ /* point of the program. */
+ KEEP(*(.init));
+ KEEP(*(.init.rust));
+ . = ALIGN(4);
+ __start_block_addr = .;
+ KEEP(*(.start_block));
+ . = ALIGN(4);
+ *(.trap);
+ *(.trap.rust);
+ *(.text.abort);
+ *(.text .text.*);
+ . = ALIGN(4);
+ } > FLASH
+
+ /* ### Picotool 'Binary Info' Entries
+ *
+ * Picotool looks through this block (as we have pointers to it in our
+ * header) to find interesting information.
+ */
+ .bi_entries : ALIGN(4)
+ {
+ /* We put this in the header */
+ __bi_entries_start = .;
+ /* Here are the entries */
+ KEEP(*(.bi_entries));
+ /* Keep this block a nice round size */
+ . = ALIGN(4);
+ /* We put this in the header */
+ __bi_entries_end = .;
+ } > FLASH
+
+ .rodata : ALIGN(4)
+ {
+ *(.srodata .srodata.*);
+ *(.rodata .rodata.*);
+
+ /* 4-byte align the end (VMA) of this section.
+ This is required by LLD to ensure the LMA of the following .data
+ section will have the correct alignment. */
+ . = ALIGN(4);
+ } > FLASH
+
+ .data : ALIGN(32)
+ {
+ _sidata = LOADADDR(.data);
+ __sidata = LOADADDR(.data);
+ _sdata = .;
+ __sdata = .;
+ /* Must be called __global_pointer$ for linker relaxations to work. */
+ PROVIDE(__global_pointer$ = . + 0x800);
+ *(.sdata .sdata.* .sdata2 .sdata2.*);
+ *(.data .data.*);
+ . = ALIGN(32);
+ _edata = .;
+ __edata = .;
+ } > RAM AT > FLASH
+
+ .bss (NOLOAD) : ALIGN(32)
+ {
+ _sbss = .;
+ *(.sbss .sbss.* .bss .bss.*);
+ . = ALIGN(32);
+ _ebss = .;
+ } > RAM
+
+ .end_block : ALIGN(4)
+ {
+ __end_block_addr = .;
+ KEEP(*(.end_block));
+ } > FLASH
+
+ /* fictitious region that represents the memory available for the heap */
+ .heap (NOLOAD) :
+ {
+ _sheap = .;
+ . += _heap_size;
+ . = ALIGN(4);
+ _eheap = .;
+ } > RAM
+
+ /* fictitious region that represents the memory available for the stack */
+ .stack (NOLOAD) :
+ {
+ _estack = .;
+ . = ABSOLUTE(_stack_start);
+ _sstack = .;
+ } > RAM
+
+ /* fake output .got section */
+ /* Dynamic relocations are unsupported. This section is only used to detect
+ relocatable code in the input files and raise an error if relocatable code
+ is found */
+ .got (INFO) :
+ {
+ KEEP(*(.got .got.*));
+ }
+
+ .eh_frame (INFO) : { KEEP(*(.eh_frame)) }
+ .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) }
+}
+
+PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
+PROVIDE(end_to_start = __start_block_addr - __end_block_addr);
+
+
+/* Do not exceed this mark in the error messages above | */
+ASSERT(ORIGIN(FLASH) % 4 == 0, "
+ERROR(riscv-rt): the start of the FLASH must be 4-byte aligned");
+
+ASSERT(ORIGIN(RAM) % 32 == 0, "
+ERROR(riscv-rt): the start of the RAM must be 32-byte aligned");
+
+ASSERT(_stext % 4 == 0, "
+ERROR(riscv-rt): `_stext` must be 4-byte aligned");
+
+ASSERT(_sdata % 32 == 0 && _edata % 32 == 0, "
+BUG(riscv-rt): .data is not 32-byte aligned");
+
+ASSERT(_sidata % 32 == 0, "
+BUG(riscv-rt): the LMA of .data is not 32-byte aligned");
+
+ASSERT(_sbss % 32 == 0 && _ebss % 32 == 0, "
+BUG(riscv-rt): .bss is not 32-byte aligned");
+
+ASSERT(_sheap % 4 == 0, "
+BUG(riscv-rt): start of .heap is not 4-byte aligned");
+
+ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), "
+ERROR(riscv-rt): The .text section must be placed inside the FLASH region.
+Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'");
+
+ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, "
+ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts.
+Consider changing `_max_hart_id` or `_hart_stack_size`.");
+
+ASSERT(SIZEOF(.got) == 0, "
+.got section detected in the input files. Dynamic relocations are not
+supported. If you are linking to C code compiled using the `gcc` crate
+then modify your build script to compile the C code _without_ the
+-fPIC flag. See the documentation of the `gcc::Config.fpic` method for
+details.");
+
+/* Do not exceed this mark in the error messages above | */
+
diff --git a/rp235x-hal-examples/src/bin/adc.rs b/rp235x-hal-examples/src/bin/adc.rs
new file mode 100644
index 000000000..76aad66d7
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/adc.rs
@@ -0,0 +1,131 @@
+//! # ADC Example
+//!
+//! This application demonstrates how to read ADC samples from the temperature
+//! sensor and pin and output them to the UART on pins 1 and 2 at 115200 baud.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use core::fmt::Write;
+// Embedded HAL 1.0.0 doesn't have an ADC trait, so use the one from 0.2
+use embedded_hal::delay::DelayNs;
+use embedded_hal_0_2::adc::OneShot;
+use hal::fugit::RateExtU32;
+use hal::Clock;
+
+// UART related types
+use hal::uart::{DataBits, StopBits, UartConfig};
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then prints the temperature
+/// in an infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The delay object lets us wait for specified amounts of time (in
+ // milliseconds)
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // UART TX (characters sent from pico) on pin 1 (GPIO0) and RX (on pin 2 (GPIO1)
+ let uart_pins = (
+ pins.gpio0.into_function::(),
+ pins.gpio1.into_function::(),
+ );
+
+ // Create a UART driver
+ let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+
+ // Write to the UART
+ uart.write_full_blocking(b"ADC example\r\n");
+
+ // Enable ADC
+ let mut adc = hal::Adc::new(pac.ADC, &mut pac.RESETS);
+
+ // Enable the temperature sense channel
+ let mut temperature_sensor = adc.take_temp_sensor().unwrap();
+
+ // Configure GPIO26 as an ADC input
+ let mut adc_pin_0 = hal::adc::AdcPin::new(pins.gpio26).unwrap();
+ loop {
+ // Read the raw ADC counts from the temperature sensor channel.
+ let temp_sens_adc_counts: u16 = adc.read(&mut temperature_sensor).unwrap();
+ let pin_adc_counts: u16 = adc.read(&mut adc_pin_0).unwrap();
+ writeln!(
+ uart,
+ "ADC readings: Temperature: {temp_sens_adc_counts:02} Pin: {pin_adc_counts:02}\r\n"
+ )
+ .unwrap();
+ delay.delay_ms(1000);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"ADC Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/adc_fifo_dma.rs b/rp235x-hal-examples/src/bin/adc_fifo_dma.rs
new file mode 100644
index 000000000..74f1e0e10
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/adc_fifo_dma.rs
@@ -0,0 +1,193 @@
+//! # ADC FIFO DMA Example
+//!
+//! This application demonstrates how to read ADC samples in free-running mode,
+//! and reading them from the FIFO by using a DMA transfer.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+use hal::singleton;
+
+// Some things we need
+use core::fmt::Write;
+use embedded_hal::delay::DelayNs;
+use fugit::RateExtU32;
+use hal::dma::{single_buffer, DMAExt};
+use hal::Clock;
+
+// UART related types
+use hal::uart::{DataBits, StopBits, UartConfig};
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then prints the temperature
+/// in an infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .ok()
+ .unwrap();
+
+ // The delay object lets us wait for specified amounts of time (in
+ // milliseconds)
+ let mut delay = hal::Timer::new_timer1(pac.TIMER1, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // UART TX (characters sent from pico) on pin 1 (GPIO0) and RX (on pin 2 (GPIO1)
+ let uart_pins = (
+ pins.gpio0.into_function::(),
+ pins.gpio1.into_function::(),
+ );
+
+ // Create a UART driver
+ let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+
+ // Write to the UART
+ uart.write_full_blocking(b"ADC FIFO DMA example\r\n");
+
+ // Initialize DMA
+ let dma = pac.DMA.split(&mut pac.RESETS);
+
+ // Enable ADC
+ let mut adc = hal::Adc::new(pac.ADC, &mut pac.RESETS);
+
+ // Enable the temperature sense channel
+ let mut temperature_sensor = adc.take_temp_sensor().unwrap();
+
+ // Configure GPIO26 as an ADC input
+ let adc_pin_0 = hal::adc::AdcPin::new(pins.gpio26.into_floating_input()).unwrap();
+
+ // we'll capture 1000 samples in total (500 per channel)
+ // NOTE: when calling `shift_8bit` below, the type here must be changed from `u16` to `u8`
+ let buf_for_samples = singleton!(: [u16; 1000] = [0; 1000]).unwrap();
+
+ // Configure free-running mode:
+ let mut adc_fifo = adc
+ .build_fifo()
+ // Set clock divider to target a sample rate of 1000 samples per second (1ksps).
+ // The value was calculated by `(48MHz / 1ksps) - 1 = 47999.0`.
+ // Please check the `clock_divider` method documentation for details.
+ .clock_divider(47999, 0)
+ // sample the temperature sensor first
+ .set_channel(&mut temperature_sensor)
+ // then alternate between GPIO26 and the temperature sensor
+ .round_robin((&adc_pin_0, &temperature_sensor))
+ // Uncomment this line to produce 8-bit samples, instead of 12 bit (lower bits are discarded)
+ //.shift_8bit()
+ // Enable DMA transfers for the FIFO
+ .enable_dma()
+ // Create the FIFO, but don't start it just yet
+ .start_paused();
+
+ // Start a DMA transfer (must happen before resuming the ADC FIFO)
+ let dma_transfer =
+ single_buffer::Config::new(dma.ch0, adc_fifo.dma_read_target(), buf_for_samples).start();
+
+ // Resume the FIFO to start capturing
+ adc_fifo.resume();
+
+ // initialize a timer, to measure the total sampling time (printed below)
+ let timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // NOTE: in a real-world program, instead of calling `wait` now, you would probably:
+ // 1. Enable one of the DMA interrupts for the channel (e.g. `dma.ch0.enable_irq0()`)
+ // 2. Set up a handler for the respective `DMA_IRQ_*` interrupt
+ // 3. Call `wait` only within that interrupt, which will be fired once the transfer is complete.
+
+ // the DMA unit takes care of shuffling data from the FIFO into the buffer.
+ // We just sit here and wait... 😴
+ let (_ch, _adc_read_target, buf_for_samples) = dma_transfer.wait();
+
+ // ^^^ the three results here (channel, adc::DmaReadTarget, write target) can be reused
+ // right away to start another transfer.
+
+ let time_taken = timer.get_counter();
+
+ uart.write_full_blocking(b"Done sampling, printing results:\r\n");
+
+ // Stop free-running mode (the returned `adc` can be reused for future captures)
+ let _adc = adc_fifo.stop();
+
+ // Print the measured values
+ for i in 0..500 {
+ writeln!(
+ uart,
+ "Temp:\t{}\tPin\t{}\r",
+ buf_for_samples[i * 2],
+ buf_for_samples[i * 2 + 1]
+ )
+ .unwrap();
+ }
+
+ writeln!(uart, "Sampling took: {}\r", time_taken).unwrap();
+
+ loop {
+ delay.delay_ms(1000);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"ADC FIFO DMA Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/adc_fifo_irq.rs b/rp235x-hal-examples/src/bin/adc_fifo_irq.rs
new file mode 100644
index 000000000..5553a9c38
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/adc_fifo_irq.rs
@@ -0,0 +1,199 @@
+//! # ADC FIFO Interrupt Example
+//!
+//! This application demonstrates how to read ADC samples in free-running mode,
+//! using the FIFO interrupt.
+//!
+//! It utilizes `rtic` (cortex-m-rtic crate) to safely share peripheral access between
+//! initialization code and interrupt handlers.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+use rp235x_hal as hal;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+#[rtic::app(device = hal::pac)]
+mod app {
+ use core::fmt::Write;
+ use fugit::RateExtU32;
+ use hal::Clock;
+ use rp235x_hal as hal;
+
+ /// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+ /// Adjust if your board has a different frequency
+ const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+ // This example will capture 1000 samples to `shared.buf`.
+ // When it is done, it will stop the ADC, set `shared.done` to true,
+ // print the result and loop forever.
+
+ const SAMPLE_COUNT: usize = 1000;
+
+ type Uart = hal::uart::UartPeripheral<
+ hal::uart::Enabled,
+ hal::pac::UART0,
+ (
+ hal::gpio::Pin,
+ hal::gpio::Pin,
+ ),
+ >;
+
+ #[shared]
+ struct Shared {
+ done: bool,
+ buf: [u16; SAMPLE_COUNT],
+ uart: Uart,
+ }
+
+ #[local]
+ struct Local {
+ adc_fifo: Option>,
+ }
+
+ #[init(local = [adc: Option = None])]
+ fn init(c: init::Context) -> (Shared, Local, init::Monotonics) {
+ // Soft-reset does not release the hardware spinlocks
+ // Release them now to avoid a deadlock after debug or watchdog reset
+ unsafe {
+ hal::sio::spinlock_reset();
+ }
+
+ let mut resets = c.device.RESETS;
+ let mut watchdog = hal::Watchdog::new(c.device.WATCHDOG);
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ c.device.XOSC,
+ c.device.CLOCKS,
+ c.device.PLL_SYS,
+ c.device.PLL_USB,
+ &mut resets,
+ &mut watchdog,
+ )
+ .unwrap();
+ let sio = hal::Sio::new(c.device.SIO);
+ let pins = hal::gpio::Pins::new(
+ c.device.IO_BANK0,
+ c.device.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut resets,
+ );
+
+ // UART TX (characters sent from pico) on pin 1 (GPIO0) and RX (on pin 2 (GPIO1)
+ let uart_pins = (
+ pins.gpio0.into_function::(),
+ pins.gpio1.into_function::(),
+ );
+
+ // Create a UART driver
+ let uart = hal::uart::UartPeripheral::new(c.device.UART0, uart_pins, &mut resets)
+ .enable(
+ hal::uart::UartConfig::new(
+ 115200.Hz(),
+ hal::uart::DataBits::Eight,
+ None,
+ hal::uart::StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+
+ // the ADC is put into a local, to gain static lifetime
+ *c.local.adc = Some(hal::Adc::new(c.device.ADC, &mut resets));
+ let adc = c.local.adc.as_mut().unwrap();
+
+ let mut adc_pin_0 = hal::adc::AdcPin::new(pins.gpio26.into_floating_input()).unwrap();
+
+ uart.write_full_blocking(b"ADC FIFO interrupt example\r\n");
+
+ let adc_fifo = adc
+ .build_fifo()
+ // Set clock divider to target a sample rate of 1000 samples per second (1ksps).
+ // The value was calculated by `(48MHz / 1ksps) - 1 = 47999.0`.
+ // Please check the `clock_divider` method documentation for details.
+ .clock_divider(47999, 0)
+ .set_channel(&mut adc_pin_0)
+ .enable_interrupt(1)
+ .start();
+
+ (
+ Shared {
+ done: false,
+ buf: [0; SAMPLE_COUNT],
+ uart,
+ },
+ Local {
+ adc_fifo: Some(adc_fifo),
+ },
+ init::Monotonics(),
+ )
+ }
+
+ #[idle(shared = [done, buf, uart])]
+ fn idle(mut c: idle::Context) -> ! {
+ loop {
+ let finished = (&mut c.shared.done, &mut c.shared.buf, &mut c.shared.uart).lock(
+ |done, buf, uart| {
+ if *done {
+ for sample in buf {
+ writeln!(uart, "Sample: {}\r", sample).unwrap();
+ }
+ writeln!(uart, "All done, going to sleep 😴\r").unwrap();
+ true
+ } else {
+ false
+ }
+ },
+ );
+
+ if finished {
+ break;
+ }
+ }
+
+ #[allow(clippy::empty_loop)]
+ loop {}
+ }
+
+ #[task(
+ binds = ADC_IRQ_FIFO,
+ priority = 1,
+ shared = [done, buf],
+ local = [adc_fifo, counter: usize = 0]
+ )]
+ fn adc_irq_fifo(mut c: adc_irq_fifo::Context) {
+ let sample = c.local.adc_fifo.as_mut().unwrap().read();
+ let i = *c.local.counter;
+ c.shared.buf.lock(|buf| buf[i] = sample);
+ *c.local.counter += 1;
+
+ if *c.local.counter == SAMPLE_COUNT {
+ c.local.adc_fifo.take().unwrap().stop();
+ c.shared.done.lock(|done| *done = true);
+ }
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"ADC FIFO IRQ Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/adc_fifo_poll.rs b/rp235x-hal-examples/src/bin/adc_fifo_poll.rs
new file mode 100644
index 000000000..ad4ec5cb1
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/adc_fifo_poll.rs
@@ -0,0 +1,198 @@
+//! # ADC FIFO Example
+//!
+//! This application demonstrates how to read ADC samples in free-running mode,
+//! and reading them from the FIFO by polling the fifo's `len()`.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use core::fmt::Write;
+use embedded_hal::delay::DelayNs;
+use fugit::RateExtU32;
+use hal::Clock;
+
+// UART related types
+use hal::uart::{DataBits, StopBits, UartConfig};
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then prints the temperature
+/// in an infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The delay object lets us wait for specified amounts of time (in
+ // milliseconds)
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // UART TX (characters sent from pico) on pin 1 (GPIO0) and RX (on pin 2 (GPIO1)
+ let uart_pins = (
+ pins.gpio0.into_function::(),
+ pins.gpio1.into_function::(),
+ );
+
+ // Create a UART driver
+ let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+
+ // Write to the UART
+ uart.write_full_blocking(b"ADC FIFO poll example\r\n");
+
+ // Enable ADC
+ let mut adc = hal::Adc::new(pac.ADC, &mut pac.RESETS);
+
+ // Enable the temperature sense channel
+ let mut temperature_sensor = adc.take_temp_sensor().unwrap();
+
+ // Configure GPIO26 as an ADC input
+ let adc_pin_0 = hal::adc::AdcPin::new(pins.gpio26.into_floating_input()).unwrap();
+
+ // Configure free-running mode:
+ let mut adc_fifo = adc
+ .build_fifo()
+ // Set clock divider to target a sample rate of 1000 samples per second (1ksps).
+ // The value was calculated by `(48MHz / 1ksps) - 1 = 47999.0`.
+ // Please check the `clock_divider` method documentation for details.
+ .clock_divider(47999, 0)
+ // sample the temperature sensor first
+ .set_channel(&mut temperature_sensor)
+ // then alternate between GPIO26 and the temperature sensor
+ .round_robin((&adc_pin_0, &temperature_sensor))
+ // Uncomment this line to produce 8-bit samples, instead of 12 bit (lower bits are discarded)
+ //.shift_8bit()
+ // start sampling
+ .start();
+
+ // we'll capture 1000 samples in total (500 per channel)
+ let mut temp_samples = [0; 500];
+ let mut pin_samples = [0; 500];
+ let mut i = 0;
+
+ // initialize a timer, to measure the total sampling time (printed below)
+ let timer = hal::Timer::new_timer1(pac.TIMER1, &mut pac.RESETS, &clocks);
+
+ loop {
+ // busy-wait until the FIFO contains at least two samples:
+ while adc_fifo.len() < 2 {}
+
+ // fetch two values from the fifo
+ let temp_result = adc_fifo.read();
+ let pin_result = adc_fifo.read();
+
+ // uncomment this line, to trigger an "underrun" condition
+ //let _extra_sample = adc_fifo.read();
+
+ if adc_fifo.is_over() {
+ // samples were pushed into the fifo faster they were read
+ uart.write_full_blocking(b"FIFO overrun!\r\n");
+ }
+ if adc_fifo.is_under() {
+ // we tried to read samples more quickly than they were pushed into the fifo
+ uart.write_full_blocking(b"FIFO underrun!\r\n");
+ }
+
+ temp_samples[i] = temp_result;
+ pin_samples[i] = pin_result;
+
+ i += 1;
+
+ // uncomment this line to trigger an "overrun" condition
+ //delay.delay_ms(1000);
+
+ if i == 500 {
+ break;
+ }
+ }
+
+ let time_taken = timer.get_counter();
+
+ uart.write_full_blocking(b"Done sampling, printing results:\r\n");
+
+ // Stop free-running mode (the returned `adc` can be reused for future captures)
+ let _adc = adc_fifo.stop();
+
+ // Print the measured values
+ for i in 0..500 {
+ writeln!(
+ uart,
+ "Temp:\t{}\tPin\t{}\r",
+ temp_samples[i], pin_samples[i]
+ )
+ .unwrap();
+ }
+
+ writeln!(uart, "Sampling took: {}\r", time_taken).unwrap();
+
+ loop {
+ delay.delay_ms(1000);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"ADC FIFO Poll Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/alloc.rs b/rp235x-hal-examples/src/bin/alloc.rs
new file mode 100644
index 000000000..9c5b3d233
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/alloc.rs
@@ -0,0 +1,124 @@
+//! # Alloc Example
+//!
+//! Uses alloc to create a Vec.
+//!
+//! This will blink an LED attached to GP25, which is the pin the Pico uses for
+//! the on-board LED. It may need to be adapted to your particular board layout
+//! and/or pin assignment.
+//!
+//! While blinking the LED, it will continuously push to a `Vec`, which will
+//! eventually lead to a panic due to an out of memory condition.
+//!
+//! See the `Cargo.toml` file for Copyright and licence details.
+
+#![no_std]
+#![no_main]
+
+extern crate alloc;
+
+use alloc::vec::Vec;
+use embedded_alloc::Heap;
+
+#[global_allocator]
+static ALLOCATOR: Heap = Heap::empty();
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::delay::DelayNs;
+use embedded_hal::digital::OutputPin;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables are initialised.
+///
+/// The function configures the RP2350 peripherals, then blinks the LED in an
+/// infinite loop where the duration indicates how many items were allocated.
+#[hal::entry]
+fn main() -> ! {
+ {
+ use core::mem::MaybeUninit;
+ const HEAP_SIZE: usize = 1024;
+ static mut HEAP: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
+ unsafe { ALLOCATOR.init(HEAP.as_ptr() as usize, HEAP_SIZE) }
+ }
+
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ //
+ // The default is to generate a 125 MHz system clock
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .ok()
+ .unwrap();
+
+ let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Configure GPIO25 as an output
+ let mut led_pin = pins.gpio25.into_push_pull_output();
+
+ let mut xs = Vec::new();
+ xs.push(1);
+
+ // Blink the LED at 1 Hz
+ loop {
+ led_pin.set_high().unwrap();
+ let len = xs.len() as u32;
+ timer.delay_ms(100 * len);
+ xs.push(1);
+ led_pin.set_low().unwrap();
+ timer.delay_ms(100 * len);
+ xs.push(1);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"Memory Allocation Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/arch_flip.rs b/rp235x-hal-examples/src/bin/arch_flip.rs
new file mode 100644
index 000000000..f79c6beea
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/arch_flip.rs
@@ -0,0 +1,110 @@
+//! # Architecture Flip example
+//!
+//! This application demonstrates running both Arm and RISC-V code on the same
+//! chip, using partitions.
+//!
+//! You need a partition table with one partition for Arm (marked NO-BOOT for
+//! RISC-V) and one partition for RISC-V (marked NO-BOOT for Arm).
+//!
+//! It will run for a few seconds, and then reboot into the other. You can tell
+//! the difference because they have different blink rates.
+//!
+//! It may need to be adapted to your particular board layout and/or pin
+//! assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::delay::DelayNs;
+use embedded_hal::digital::OutputPin;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then toggles a GPIO pin in
+/// an infinite loop. If there is an LED connected to that pin, it will blink.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ #[cfg(target_arch = "arm")]
+ let (delay_ms, arch) = (500, hal::reboot::RebootArch::Riscv);
+
+ #[cfg(not(target_arch = "arm"))]
+ let (delay_ms, arch) = (250, hal::reboot::RebootArch::Arm);
+
+ // Configure GPIO25 as an output
+ let mut led_pin = pins.gpio25.into_push_pull_output();
+ for _ in 0..10 {
+ led_pin.set_high().unwrap();
+ timer.delay_ms(delay_ms);
+ led_pin.set_low().unwrap();
+ timer.delay_ms(delay_ms);
+ }
+
+ // Do an asynchronous reset into the bootloader, but flipping the architecture
+ hal::reboot::reboot(hal::reboot::RebootKind::Normal, arch);
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"Architecture Flip Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/binary_info_demo.rs b/rp235x-hal-examples/src/bin/binary_info_demo.rs
new file mode 100644
index 000000000..a0cb55835
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/binary_info_demo.rs
@@ -0,0 +1,102 @@
+//! # GPIO 'Blinky' Example, with Binary Info
+//!
+//! This application demonstrates how to control a GPIO pin on the RP2350, and
+//! includes some picotool-compatible metadata.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+use hal::binary_info;
+
+// Some things we need
+use embedded_hal::delay::DelayNs;
+use embedded_hal::digital::OutputPin;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then toggles a GPIO pin in
+/// an infinite loop. If there is an LED connected to that pin, it will blink.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Configure GPIO25 as an output
+ let mut led_pin = pins.gpio25.into_push_pull_output();
+ loop {
+ led_pin.set_high().unwrap();
+ timer.delay_ms(500);
+ led_pin.set_low().unwrap();
+ timer.delay_ms(500);
+ }
+}
+
+/// This is a list of references to our table entries
+///
+/// They must be in the `.bi_entries` section as we tell picotool the start and
+/// end addresses of that section.
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 7] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"A GPIO blinky with extra metadata."),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+ hal::binary_info::rp_pico_board!(c"pico2"),
+ // An example with a non-Raspberry-Pi tag
+ hal::binary_info::int!(binary_info::make_tag(b"JP"), 0x0000_0001, 0x12345678),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/blinky.rs b/rp235x-hal-examples/src/bin/blinky.rs
new file mode 100644
index 000000000..69472b90e
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/blinky.rs
@@ -0,0 +1,93 @@
+//! # GPIO 'Blinky' Example
+//!
+//! This application demonstrates how to control a GPIO pin on the rp235x.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::delay::DelayNs;
+use embedded_hal::digital::OutputPin;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then toggles a GPIO pin in
+/// an infinite loop. If there is an LED connected to that pin, it will blink.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Configure GPIO25 as an output
+ let mut led_pin = pins.gpio25.into_push_pull_output();
+ loop {
+ led_pin.set_high().unwrap();
+ timer.delay_ms(500);
+ led_pin.set_low().unwrap();
+ timer.delay_ms(500);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"Blinky Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/block_loop.rs b/rp235x-hal-examples/src/bin/block_loop.rs
new file mode 100644
index 000000000..c2bc96274
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/block_loop.rs
@@ -0,0 +1,118 @@
+//! # An example application with two Blocks.
+//!
+//! Our first block is an *Image Definition* Block and it points at our second
+//! Block which contains a placeholder item for the purposes of this example.
+//! The placeholder isn't useful per se, but it allows us to show how to
+//! construct two Blocks in a loop without having to use `picotool` to modify
+//! the application after compilation and linking.
+//!
+//! See [Section
+//! 5.1.5.2](https://rptl.io/rp2350-datasheet#bootrom-concept-block-loop) in the
+//! RP2350 datasheet for more information about Blocks and Block Loops.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::delay::DelayNs;
+use embedded_hal::digital::OutputPin;
+
+// The linker script exports these symbols.
+extern "C" {
+ /// This value is at an address equal to the difference between the start block and end block
+ static start_to_end: u32;
+ /// This value is at an address equal to the difference between the end block and start block
+ static end_to_start: u32;
+}
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static START_IMAGE_DEF: hal::block::ImageDef =
+ hal::block::ImageDef::secure_exe().with_offset(unsafe { core::ptr::addr_of!(start_to_end) });
+
+/// A second Block, and the end of the program in flash
+#[link_section = ".end_block"]
+#[used]
+pub static END_IMAGE_DEF: hal::block::Block<1> =
+ // Put a placeholder item in the block.
+ hal::block::Block::new([hal::block::item_ignored()])
+ .with_offset(unsafe { core::ptr::addr_of!(end_to_start) });
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then toggles a GPIO pin in
+/// an infinite loop. If there is an LED connected to that pin, it will blink.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Configure GPIO25 as an output
+ let mut led_pin = pins.gpio25.into_push_pull_output();
+ loop {
+ led_pin.set_high().unwrap();
+ timer.delay_ms(250);
+ led_pin.set_low().unwrap();
+ timer.delay_ms(250);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(
+ c"Blinks an LED, contains a Block Loop with two Blocks"
+ ),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/dht11.rs b/rp235x-hal-examples/src/bin/dht11.rs
new file mode 100644
index 000000000..0a033871b
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/dht11.rs
@@ -0,0 +1,102 @@
+//! # DHT11 Example
+//!
+//! This application demonstrates how to read a DHT11 sensor on the rp235x.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//! In this example, the DHT11 data pin should be connected to GPIO28.
+//!
+//! NOTE: The DHT11 driver only works reliably when compiled in release mode.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::digital::OutputPin;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+use dht_sensor::{dht11, DhtReading};
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, assigns GPIO 28 to the
+/// DHT11 driver, and takes a single measurement.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // Use GPIO 28 as an InOutPin
+ let mut pin = hal::gpio::InOutPin::new(pins.gpio28);
+ let _ = pin.set_high();
+
+ // Perform a sensor reading
+ let _measurement = dht11::Reading::read(&mut delay, &mut pin);
+
+ // In this case, we just ignore the result. A real application
+ // would do something with the measurement.
+
+ loop {
+ hal::arch::wfi();
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"DHT11 Sensor Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/float_test.rs b/rp235x-hal-examples/src/bin/float_test.rs
new file mode 100644
index 000000000..3cf65478d
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/float_test.rs
@@ -0,0 +1,324 @@
+//! # DCP Example
+//!
+//! This example only works on Arm.
+//!
+//! This application demonstrates performing floating point operations
+//! using the *Double Co-Processor*. Note that by default, DCP acceleration
+//! is enabled. To see how slow software floating point operations are,
+//! build this example with `--no-default-features`.
+//!
+//! It may need to be adapted to your particular board layout and/or pin
+//! assignment.
+//!
+//! Typical output (with default features):
+//!
+//! ```text
+//! Floating Point Test
+//! Testing software f64 addition
+//! Running 1000 loops for f64
+//! Start: acc=0, arg=0.048638031340549406
+//! End : acc=48.63803134055, took 23.8 cycles per op
+//! Testing DCP f64 addition
+//! Running 1000 loops for f64
+//! Start: acc=0, arg=0.048638031340549406
+//! End : acc=48.63803134055, took 37.9 cycles per op
+//! Testing FPU f32 addition
+//! Running 1000 loops for f32
+//! Start: acc=0, arg=0.04863803
+//! End : acc=48.63764, took 3.5 cycles per op
+//! Testing software f64 multiplication
+//! Running 1000 loops for f64
+//! Start: acc=0.048638031340549406, arg=1.0001
+//! End : acc=0.053753069001926, took 40.8 cycles per op
+//! Testing DCP f64 multiplication
+//! Running 1000 loops for f64
+//! Start: acc=0.048638031340549406, arg=1.0001
+//! End : acc=0.053753069001926, took 54.7 cycles per op
+//! Testing FPU f32 multiplication
+//! Running 1000 loops for f32
+//! Start: acc=0.04863803, arg=1.0001
+//! End : acc=0.05375396, took 3.4 cycles per op
+//! Rebooting now
+//! PANIC:
+//! PanicInfo { payload: Any { .. }, message: Some(Finished!), location: Location { file: "rp235x-hal/examples/float_test.rs", line: 166, col: 5 }, can_unwind: true, force_no_backtrace: false }
+//! ```
+//!
+//! Typical output (with no default features):
+//!
+//! ```text
+//! Floating Point Test
+//! Testing software f64 addition
+//! Running 1000 loops for f64
+//! Start: acc=0, arg=0.8188217810460334
+//! End : acc=818.8217810460395, took 151.3 cycles per op
+//! Testing DCP f64 addition
+//! Running 1000 loops for f64
+//! Start: acc=0, arg=0.8188217810460334
+//! End : acc=818.8217810460395, took 38.0 cycles per op
+//! Testing FPU f32 addition
+//! Running 1000 loops for f32
+//! Start: acc=0, arg=0.8188218
+//! End : acc=818.82947, took 3.4 cycles per op
+//! Testing software f64 multiplication
+//! Running 1000 loops for f64
+//! Start: acc=0.8188217810460334, arg=1.0001
+//! End : acc=0.9049334951218081, took 123.0 cycles per op
+//! Testing DCP f64 multiplication
+//! Running 1000 loops for f64
+//! Start: acc=0.8188217810460334, arg=1.0001
+//! End : acc=0.9049334951218081, took 55.0 cycles per op
+//! Testing FPU f32 multiplication
+//! Running 1000 loops for f32
+//! Start: acc=0.8188218, arg=1.0001
+//! End : acc=0.9049483, took 3.4 cycles per op
+//! Rebooting now
+//! PANIC:
+//! PanicInfo { payload: Any { .. }, message: Some(Finished!), location: Location { file: "rp235x-hal/examples/float_test.rs", line: 166, col: 5 }, can_unwind: true, force_no_backtrace: false }
+//! ```
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+use rp235x_hal as hal;
+
+use hal::{
+ gpio,
+ uart::{DataBits, StopBits, UartConfig, UartPeripheral},
+};
+
+use cortex_m_rt::exception;
+
+// Some things we need
+use core::fmt::Write;
+use hal::fugit::RateExtU32;
+use hal::Clock;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+struct GlobalUart {
+ inner: critical_section::Mutex>>,
+}
+
+type MyUart = UartPeripheral<
+ hal::uart::Enabled,
+ hal::pac::UART0,
+ (
+ gpio::Pin,
+ gpio::Pin,
+ ),
+>;
+
+static GLOBAL_UART: GlobalUart = GlobalUart {
+ inner: critical_section::Mutex::new(core::cell::RefCell::new(None)),
+};
+
+impl core::fmt::Write for &GlobalUart {
+ fn write_str(&mut self, s: &str) -> core::fmt::Result {
+ critical_section::with(|cs| {
+ let mut cell = self.inner.borrow_ref_mut(cs);
+ let uart = cell.as_mut().unwrap();
+ uart.write_str(s)
+ })
+ }
+}
+
+impl GlobalUart {
+ fn init(&self, uart: MyUart) {
+ critical_section::with(|cs| {
+ self.inner.borrow(cs).replace(Some(uart));
+ });
+ }
+}
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then writes to the UART in
+/// an infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+ let mut cp = cortex_m::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ let uart_pins = (
+ // UART TX (characters sent from rp235x) on pin 1 (GPIO0)
+ pins.gpio0.into_function::(),
+ // UART RX (characters received by rp235x) on pin 2 (GPIO1)
+ pins.gpio1.into_function::(),
+ );
+ let uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+
+ GLOBAL_UART.init(uart);
+
+ writeln!(&GLOBAL_UART, "\n\nFloating Point Test").unwrap();
+
+ // Optionally start a DCP operation, but not finish it, to see if the save/restore fires.
+ //
+ // If you uncomment this, you'll observe the DCP operations take ~46 cycles longer
+ // to do all the stacking/restoring of DCP state.
+
+ // unsafe {
+ // let val0 = 0;
+ // let val1 = 0;
+ // core::arch::asm!(
+ // "mcrr p4,#1,{0},{1},c0 // WXUP {0},{1} - write {1}|{0} to xm and xe",
+ // in(reg) val0,
+ // in(reg) val1
+ // );
+ // }
+
+ cp.DCB.enable_trace();
+ cp.DWT.enable_cycle_counter();
+
+ let int_seed = get_random_seed();
+ // make up a random number between 0 and 1 so the optimiser can't cheat and
+ // we are forced to do the maths
+ let seed = (int_seed & u128::from(u64::MAX)) as f64 / u64::MAX as f64;
+
+ writeln!(&GLOBAL_UART, "Testing aeabi f64 addition").unwrap();
+ benchmark(0.0, seed, |x, y| x + y, &cp.DWT);
+ writeln!(&GLOBAL_UART, "Testing DCP f64 addition").unwrap();
+ benchmark(0.0, seed, hal::dcp::dadd, &cp.DWT);
+ writeln!(&GLOBAL_UART, "Testing aeabi f32 addition").unwrap();
+ benchmark(0.0, seed as f32, |x, y| x + y, &cp.DWT);
+
+ writeln!(&GLOBAL_UART, "Testing aeabi f64 multiplication").unwrap();
+ benchmark(seed, 1.0001f64, |x, y| x * y, &cp.DWT);
+ writeln!(&GLOBAL_UART, "Testing DCP f64 multiplication").unwrap();
+ benchmark(seed, 1.0001f64, hal::dcp::dmul, &cp.DWT);
+ writeln!(&GLOBAL_UART, "Testing aeabi f32 multiplication").unwrap();
+ benchmark(seed as f32, 1.0001f32, |x, y| x * y, &cp.DWT);
+
+ writeln!(&GLOBAL_UART, "Rebooting now").unwrap();
+
+ panic!("Finished!");
+}
+
+/// Use the SysInfo API to get a boot-time generated random number.
+fn get_random_seed() -> u128 {
+ let mut buffer = [0u32; 5];
+ let sys_info_mask = 0x0010;
+ unsafe { hal::rom_data::get_sys_info(buffer.as_mut_ptr(), buffer.len(), sys_info_mask) };
+ let mut result: u128 = 0;
+ for word in &buffer[1..] {
+ result <<= 32;
+ result |= u128::from(*word);
+ }
+ result
+}
+
+/// Run the given operation in a loop.
+///
+/// Uses the DWT to calculate elapsed CPU cycles.
+fn benchmark(acc: T, arg: T, mut f: F, dwt: &cortex_m::peripheral::DWT)
+where
+ T: core::fmt::Display + core::marker::Copy,
+ F: FnMut(T, T) -> T,
+{
+ const LOOPS: u16 = 1000;
+ writeln!(
+ &GLOBAL_UART,
+ "Running {} loops for {}",
+ LOOPS,
+ core::any::type_name::()
+ )
+ .unwrap();
+ let mut acc: T = acc;
+ writeln!(&GLOBAL_UART, "Start: acc={}, arg={}", acc, arg).unwrap();
+
+ let start_count = dwt.cyccnt.read();
+ for _ in 0..LOOPS {
+ acc = f(acc, arg);
+ }
+ let end_count = dwt.cyccnt.read();
+
+ let delta = end_count.wrapping_sub(start_count);
+ let cycles_per_op = delta as f32 / LOOPS as f32;
+ writeln!(
+ &GLOBAL_UART,
+ "End : acc={acc}, took {cycles_per_op:.1} cycles per op"
+ )
+ .unwrap();
+}
+
+#[exception]
+unsafe fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! {
+ let _ = writeln!(&GLOBAL_UART, "HARD FAULT:\n{:#?}", ef);
+
+ hal::reboot::reboot(
+ hal::reboot::RebootKind::BootSel {
+ msd_disabled: false,
+ picoboot_disabled: false,
+ },
+ hal::reboot::RebootArch::Normal,
+ );
+}
+
+#[panic_handler]
+fn panic(info: &core::panic::PanicInfo) -> ! {
+ let _ = writeln!(&GLOBAL_UART, "PANIC:\n{:?}", info);
+
+ hal::reboot::reboot(
+ hal::reboot::RebootKind::BootSel {
+ msd_disabled: false,
+ picoboot_disabled: false,
+ },
+ hal::reboot::RebootArch::Normal,
+ );
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"Floating Point Test"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/gpio_dyn_pin_array.rs b/rp235x-hal-examples/src/bin/gpio_dyn_pin_array.rs
new file mode 100644
index 000000000..0ca06f868
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/gpio_dyn_pin_array.rs
@@ -0,0 +1,138 @@
+//! # GPIO Dynamic Pin Type Mode Example
+//!
+//! This application demonstrates how to put GPIO pins into their Dynamic Pin type on the rp235x.
+//!
+//! Usually, the type of each pin is different (which allows the type system to catch misuse).
+//! But this stops you storing the pins in an array, or allowing a struct to take any pin.
+//! This mode is also referred to as "Erased", "Downgraded", "Degraded", or "Dynamic".
+//!
+//! In order to see the result of this program, you will need to put LEDs and a current limiting
+//! resistor on each of GPIO 2, 3, 4, 5.
+//! The other side of the LED + resistor pair should be connected to GND.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+use hal::gpio::{DynPinId, FunctionSioOutput, Pin, PullNone, PullUp};
+
+// Some things we need
+use embedded_hal::delay::DelayNs;
+use embedded_hal::digital::OutputPin;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then toggles a GPIO pin in
+/// an infinite loop. If there is an LED connected to that pin, it will blink.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // We will use the rp235x timer peripheral as our delay source
+ let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // To put pins into an array we have to convert them to Dynamically Typed pins.
+ // This means they'll carry their pin and bank numbers around with them at run time,
+ // rather than relying on the Type of the pin to track that.
+ let mut pinarray: [Pin; 4] = [
+ pins.gpio2
+ .into_push_pull_output()
+ .into_pull_type()
+ .into_dyn_pin(),
+ pins.gpio3
+ .into_push_pull_output()
+ .into_pull_type()
+ .into_dyn_pin(),
+ pins.gpio4
+ .into_push_pull_output()
+ .into_pull_type()
+ .into_dyn_pin(),
+ pins.gpio5
+ .into_push_pull_output()
+ .into_pull_type()
+ .into_dyn_pin(),
+ ];
+
+ // Also set a pin as a dynamic input. We won't use this, it is just to demonstrate that
+ // pins can have other functions and still be Dynamically typed.
+ let _in_pin = pins.gpio23.into_floating_input().into_dyn_pin();
+
+ // You can also let the target type set the pin mode, using the type system to guide it.
+ // Once again, we're not going to use this array. The only reason it is here is to demonstrate a less verbose way to set pin modes
+ let mut _type_coerce: [Pin; 1] =
+ [pins.gpio22.reconfigure().into_dyn_pin()];
+
+ // Light one LED at a time. Start at GPIO2 and go through to GPIO5, then reverse.
+ loop {
+ for led in pinarray.iter_mut() {
+ led.set_high().unwrap();
+ timer.delay_ms(50);
+ led.set_low().unwrap();
+ }
+ for led in pinarray.iter_mut().rev() {
+ led.set_high().unwrap();
+ timer.delay_ms(50);
+ led.set_low().unwrap();
+ }
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"GPIO Dynamic Pin Array Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/gpio_in_out.rs b/rp235x-hal-examples/src/bin/gpio_in_out.rs
new file mode 100644
index 000000000..06ab24e80
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/gpio_in_out.rs
@@ -0,0 +1,97 @@
+//! # GPIO In/Out Example
+//!
+//! This application demonstrates how to control GPIO pins on the RP2040.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::digital::InputPin;
+use embedded_hal::digital::OutputPin;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then toggles a GPIO pin in
+/// an infinite loop. If there is an LED connected to that pin, it will blink.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let _clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Configure GPIO 25 as an output
+ let mut out_pin = pins.gpio25.into_push_pull_output();
+
+ // Configure GPIO 23 as an input
+ let mut in_pin = pins.gpio23.into_pull_down_input();
+
+ // Output is the opposite of the input
+ loop {
+ if in_pin.is_low().unwrap() {
+ out_pin.set_high().unwrap();
+ } else {
+ out_pin.set_low().unwrap();
+ }
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"GPIO Input/Output Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/gpio_irq_example.rs b/rp235x-hal-examples/src/bin/gpio_irq_example.rs
new file mode 100644
index 000000000..03baf9886
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/gpio_irq_example.rs
@@ -0,0 +1,189 @@
+//! # GPIO IRQ Example
+//!
+//! This application demonstrates use of GPIO Interrupts.
+//! It is also intended as a general introduction to interrupts with rp235x.
+//!
+//! Each GPIO can be triggered on the input being high (LevelHigh), being low (LevelLow)
+//! starting high and then going low (EdgeLow) or starting low and becoming high (EdgeHigh)
+//!
+//! In this example, we trigger on EdgeLow. Our input pin configured to be pulled to the high logic-level
+//! via an internal pullup resistor. This resistor is quite weak, so you can bring the logic level back to low
+//! via an external jumper wire or switch.
+//! Whenever we see the edge transition, we will toggle the output on GPIO25 - this is the LED pin on a Pico.
+//!
+//! Note that this demo does not perform any [software debouncing](https://en.wikipedia.org/wiki/Switch#Contact_bounce).
+//! You can fix that through hardware, or you could disable the button interrupt in the interrupt and re-enable it
+//! some time later using one of the Alarms of the Timer peripheral - this is left as an exercise for the reader.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::digital::StatefulOutputPin;
+
+// Our interrupt macro
+use hal::pac::interrupt;
+
+// Some short-cuts to useful types
+use core::cell::RefCell;
+use critical_section::Mutex;
+use hal::gpio;
+
+// The GPIO interrupt type we're going to generate
+use gpio::Interrupt::EdgeLow;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+// Pin types quickly become very long!
+// We'll create some type aliases using `type` to help with that
+
+/// This pin will be our output - it will drive an LED if you run this on a Pico
+type LedPin = gpio::Pin;
+
+/// This pin will be our interrupt source.
+/// It will trigger an interrupt if pulled to ground (via a switch or jumper wire)
+type ButtonPin = gpio::Pin;
+
+/// Since we're always accessing these pins together we'll store them in a tuple.
+/// Giving this tuple a type alias means we won't need to use () when putting them
+/// inside an Option. That will be easier to read.
+type LedAndButton = (LedPin, ButtonPin);
+
+/// This how we transfer our Led and Button pins into the Interrupt Handler.
+/// We'll have the option hold both using the LedAndButton type.
+/// This will make it a bit easier to unpack them later.
+static GLOBAL_PINS: Mutex>> = Mutex::new(RefCell::new(None));
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then toggles a GPIO pin in
+/// an infinite loop. If there is an LED connected to that pin, it will blink.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let _clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Configure GPIO 25 as an output to drive our LED.
+ // we can use reconfigure() instead of into_pull_up_input()
+ // since the variable we're pushing it into has that type
+ let led = pins.gpio25.reconfigure();
+
+ // Set up the GPIO pin that will be our input
+ let in_pin = pins.gpio26.reconfigure();
+
+ // Trigger on the 'falling edge' of the input pin.
+ // This will happen as the button is being pressed
+ in_pin.set_interrupt_enabled(EdgeLow, true);
+
+ // Give away our pins by moving them into the `GLOBAL_PINS` variable.
+ // We won't need to access them in the main thread again
+ critical_section::with(|cs| {
+ GLOBAL_PINS.borrow(cs).replace(Some((led, in_pin)));
+ });
+
+ // Unmask the IO_BANK0 IRQ so that the NVIC interrupt controller
+ // will jump to the interrupt function when the interrupt occurs.
+ // We do this last so that the interrupt can't go off while
+ // it is in the middle of being configured
+ unsafe {
+ cortex_m::peripheral::NVIC::unmask(hal::pac::Interrupt::IO_IRQ_BANK0);
+ }
+
+ loop {
+ // interrupts handle everything else in this example.
+ hal::arch::wfi();
+ }
+}
+
+#[interrupt]
+fn IO_IRQ_BANK0() {
+ // The `#[interrupt]` attribute covertly converts this to `&'static mut Option`
+ static mut LED_AND_BUTTON: Option = None;
+
+ // This is one-time lazy initialisation. We steal the variables given to us
+ // via `GLOBAL_PINS`.
+ if LED_AND_BUTTON.is_none() {
+ critical_section::with(|cs| {
+ *LED_AND_BUTTON = GLOBAL_PINS.borrow(cs).take();
+ });
+ }
+
+ // Need to check if our Option contains our pins
+ if let Some(gpios) = LED_AND_BUTTON {
+ // borrow led and button by *destructuring* the tuple
+ // these will be of type `&mut LedPin` and `&mut ButtonPin`, so we don't have
+ // to move them back into the static after we use them
+ let (led, button) = gpios;
+ // Check if the interrupt source is from the pushbutton going from high-to-low.
+ // Note: this will always be true in this example, as that is the only enabled GPIO interrupt source
+ if button.interrupt_status(EdgeLow) {
+ // toggle can't fail, but the embedded-hal traits always allow for it
+ // we can discard the return value by assigning it to an unnamed variable
+ let _ = led.toggle();
+
+ // Our interrupt doesn't clear itself.
+ // Do that now so we don't immediately jump back to this interrupt handler.
+ button.clear_interrupt(EdgeLow);
+ }
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"GPIO IRQ Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/i2c.rs b/rp235x-hal-examples/src/bin/i2c.rs
new file mode 100644
index 000000000..3b9627867
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/i2c.rs
@@ -0,0 +1,108 @@
+//! # I²C Example
+//!
+//! This application demonstrates how to talk to I²C devices with an rp235x.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal_0_2::blocking::i2c::Write;
+use hal::fugit::RateExtU32;
+use hal::gpio::{FunctionI2C, Pin};
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then performs a single I²C
+/// write to a fixed address.
+#[hal::entry]
+fn main() -> ! {
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Configure two pins as being I²C, not GPIO
+ let sda_pin: Pin<_, FunctionI2C, _> = pins.gpio18.reconfigure();
+ let scl_pin: Pin<_, FunctionI2C, _> = pins.gpio19.reconfigure();
+ // let not_an_scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio20.reconfigure();
+
+ // Create the I²C drive, using the two pre-configured pins. This will fail
+ // at compile time if the pins are in the wrong mode, or if this I²C
+ // peripheral isn't available on these pins!
+ let mut i2c = hal::I2C::i2c1(
+ pac.I2C1,
+ sda_pin,
+ scl_pin, // Try `not_an_scl_pin` here
+ 400.kHz(),
+ &mut pac.RESETS,
+ &clocks.system_clock,
+ );
+
+ // Write three bytes to the I²C device with 7-bit address 0x2C
+ i2c.write(0x2Cu8, &[1, 2, 3]).unwrap();
+
+ // Demo finish - just loop until reset
+
+ loop {
+ hal::arch::wfi();
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"I²C Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/i2c_async.rs b/rp235x-hal-examples/src/bin/i2c_async.rs
new file mode 100644
index 000000000..8b104bc99
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/i2c_async.rs
@@ -0,0 +1,133 @@
+//! # I²C Example
+//!
+//! This application demonstrates how to talk to I²C devices with an RP235x.
+//! in an Async environment.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use hal::{
+ fugit::RateExtU32,
+ gpio::bank0::{Gpio20, Gpio21},
+ gpio::{FunctionI2C, Pin, PullUp},
+ i2c::Controller,
+ pac::interrupt,
+ Clock, I2C,
+};
+
+// Import required types & traits.
+use embedded_hal_async::i2c::I2c;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Bind the interrupt handler with the peripheral
+#[interrupt]
+unsafe fn I2C0_IRQ() {
+ use hal::async_utils::AsyncPeripheral;
+ I2C::::on_interrupt();
+}
+
+/// The function configures the RP235x peripherals, then performs a single I²C
+/// write to a fixed address.
+async fn demo() {
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .ok()
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Configure two pins as being I²C, not GPIO
+ let sda_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio20.reconfigure();
+ let scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio21.reconfigure();
+
+ // Create the I²C drive, using the two pre-configured pins. This will fail
+ // at compile time if the pins are in the wrong mode, or if this I²C
+ // peripheral isn't available on these pins!
+ let mut i2c = hal::I2C::new_controller(
+ pac.I2C0,
+ sda_pin,
+ scl_pin,
+ 400.kHz(),
+ &mut pac.RESETS,
+ clocks.system_clock.freq(),
+ );
+
+ // Unmask the interrupt in the NVIC to let the core wake up & enter the interrupt handler.
+ // Each core has its own NVIC so these needs to executed from the core where the IRQ are
+ // expected.
+ unsafe {
+ cortex_m::peripheral::NVIC::unpend(hal::pac::Interrupt::I2C0_IRQ);
+ cortex_m::peripheral::NVIC::unmask(hal::pac::Interrupt::I2C0_IRQ);
+ }
+
+ // Asynchronously write three bytes to the I²C device with 7-bit address 0x2C
+ i2c.write(0x76u8, &[1, 2, 3]).await.unwrap();
+
+ // Demo finish - just loop until reset
+ core::future::pending().await
+}
+
+/// Entry point to our bare-metal application.
+#[hal::entry]
+fn main() -> ! {
+ let runtime = nostd_async::Runtime::new();
+ let mut task = nostd_async::Task::new(demo());
+ let handle = task.spawn(&runtime);
+ handle.join();
+ unreachable!()
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"I²C Async Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/i2c_async_cancelled.rs b/rp235x-hal-examples/src/bin/i2c_async_cancelled.rs
new file mode 100644
index 000000000..f9b069389
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/i2c_async_cancelled.rs
@@ -0,0 +1,153 @@
+//! # I²C Example
+//!
+//! This application demonstrates how to talk to I²C devices with an RP235x.
+//! in an Async environment.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+use core::task::Poll;
+use embedded_hal_async::i2c::I2c;
+use futures::FutureExt;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use hal::{
+ fugit::RateExtU32,
+ gpio::bank0::{Gpio20, Gpio21},
+ gpio::{FunctionI2C, Pin, PullUp},
+ i2c::Controller,
+ pac::interrupt,
+ Clock, I2C,
+};
+
+use defmt_rtt as _;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+#[interrupt]
+unsafe fn I2C0_IRQ() {
+ use hal::async_utils::AsyncPeripheral;
+ I2C::::on_interrupt();
+}
+
+/// The function configures the RP235x peripherals, then performs a single I²C
+/// write to a fixed address.
+async fn demo() {
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .ok()
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Configure two pins as being I²C, not GPIO
+ let sda_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio20.reconfigure();
+ let scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio21.reconfigure();
+
+ // Create the I²C drive, using the two pre-configured pins. This will fail
+ // at compile time if the pins are in the wrong mode, or if this I²C
+ // peripheral isn't available on these pins!
+ let mut i2c = hal::I2C::new_controller(
+ pac.I2C0,
+ sda_pin,
+ scl_pin,
+ 400.kHz(),
+ &mut pac.RESETS,
+ clocks.system_clock.freq(),
+ );
+
+ // Unmask the interrupt in the NVIC to let the core wake up & enter the interrupt handler.
+ unsafe {
+ cortex_m::peripheral::NVIC::unpend(hal::pac::Interrupt::I2C0_IRQ);
+ cortex_m::peripheral::NVIC::unmask(hal::pac::Interrupt::I2C0_IRQ);
+ }
+
+ let mut cnt = 0;
+ let timeout = core::future::poll_fn(|cx| {
+ cx.waker().wake_by_ref();
+ if cnt == 1 {
+ Poll::Ready(())
+ } else {
+ cnt += 1;
+ Poll::Pending
+ }
+ });
+
+ let mut v = [0; 32];
+ v.iter_mut().enumerate().for_each(|(i, v)| *v = i as u8);
+
+ // Asynchronously write three bytes to the I²C device with 7-bit address 0x2C
+ futures::select_biased! {
+ r = i2c.write(0x76u8, &v).fuse() => r.unwrap(),
+ _ = timeout.fuse() => {
+ defmt::info!("Timed out.");
+ }
+ }
+ i2c.write(0x76u8, &v).await.unwrap();
+
+ // Demo finish - just loop until reset
+ core::future::pending().await
+}
+
+/// Entry point to our bare-metal application.
+#[hal::entry]
+fn main() -> ! {
+ let runtime = nostd_async::Runtime::new();
+ let mut task = nostd_async::Task::new(demo());
+ let handle = task.spawn(&runtime);
+ handle.join();
+ unreachable!()
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"I²C Async Cancellation Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/lcd_display.rs b/rp235x-hal-examples/src/bin/lcd_display.rs
new file mode 100644
index 000000000..b94301e7a
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/lcd_display.rs
@@ -0,0 +1,118 @@
+//! # LCD Display Example
+//!
+//! In this example, the rp235x is configured to drive a small two-line
+//! alphanumeric LCD using the
+//! [HD44780](https://crates.io/crates/hd44780-driver) driver.
+//!
+//! It drives the LCD by pushing data out of six GPIO pins. It may need to be
+//! adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Our LCD driver
+use hd44780_driver as hd44780;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, writes to the LCD, then goes
+/// to sleep.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The delay object lets us wait for specified amounts of time (in
+ // milliseconds)
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Create the LCD driver from some GPIO pins
+ let mut lcd = hd44780::HD44780::new_4bit(
+ pins.gpio16.into_push_pull_output(), // Register Select
+ pins.gpio17.into_push_pull_output(), // Enable
+ pins.gpio18.into_push_pull_output(), // d4
+ pins.gpio19.into_push_pull_output(), // d5
+ pins.gpio20.into_push_pull_output(), // d6
+ pins.gpio21.into_push_pull_output(), // d7
+ &mut delay,
+ )
+ .unwrap();
+
+ // Clear the screen
+ lcd.reset(&mut delay).unwrap();
+ lcd.clear(&mut delay).unwrap();
+
+ // Write to the top line
+ lcd.write_str("rp-hal on", &mut delay).unwrap();
+
+ // Move the cursor
+ lcd.set_cursor_pos(40, &mut delay).unwrap();
+
+ // Write more more text
+ lcd.write_str("HD44780!", &mut delay).unwrap();
+
+ // Do nothing - we're finished
+ loop {
+ hal::arch::wfi();
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"LCD Display Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/mem_to_mem_dma.rs b/rp235x-hal-examples/src/bin/mem_to_mem_dma.rs
new file mode 100644
index 000000000..8748289cc
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/mem_to_mem_dma.rs
@@ -0,0 +1,95 @@
+//! # Memory to memory DMA transfer Example
+//!
+//! This application demonstrates how to use DMA to transfer data from memory to memory buffers.
+//!
+//! See the `Cargo.toml` file for Copyright and licence details.
+#![no_std]
+#![no_main]
+
+use rp235x_hal as hal;
+
+use hal::singleton;
+
+use embedded_hal::delay::DelayNs;
+use embedded_hal::digital::OutputPin;
+
+use hal::dma::{single_buffer, DMAExt};
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+#[hal::entry]
+fn main() -> ! {
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Setup clocks and the watchdog.
+ let mut watchdog = hal::watchdog::Watchdog::new(pac.WATCHDOG);
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // Setup the pins.
+ let sio = hal::sio::Sio::new(pac.SIO);
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Initialize DMA.
+ let dma = pac.DMA.split(&mut pac.RESETS);
+ // Configure GPIO25 as an output
+ let mut led_pin = pins.gpio25.into_push_pull_output();
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // Use DMA to transfer some bytes (single buffering).
+ let tx_buf = singleton!(: [u8; 16] = [0x42; 16]).unwrap();
+ let rx_buf = singleton!(: [u8; 16] = [0; 16]).unwrap();
+
+ // Use a single_buffer to read from tx_buf and write into rx_buf
+ let transfer = single_buffer::Config::new(dma.ch0, tx_buf, rx_buf).start();
+ // Wait for both DMA channels to finish
+ let (_ch, tx_buf, rx_buf) = transfer.wait();
+
+ // Compare buffers to see if the data was transferred correctly
+ // Slow blink on success, fast on failure
+ let delay_ms = if tx_buf == rx_buf { 1000 } else { 100 };
+
+ loop {
+ led_pin.set_high().unwrap();
+ delay.delay_ms(delay_ms);
+ led_pin.set_low().unwrap();
+ delay.delay_ms(delay_ms);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"Memory-Memory DMA Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/multicore_fifo_blink.rs b/rp235x-hal-examples/src/bin/multicore_fifo_blink.rs
new file mode 100644
index 000000000..50a901dc3
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/multicore_fifo_blink.rs
@@ -0,0 +1,180 @@
+//! # Multicore FIFO + GPIO 'Blinky' Example
+//!
+//! This application demonstrates FIFO communication between the CPU cores on the rp235x.
+//! Core 0 will calculate and send a delay value to Core 1, which will then wait that long
+//! before toggling the LED.
+//! Core 0 will wait for Core 1 to complete this task and send an acknowledgement value.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::digital::StatefulOutputPin;
+use hal::clocks::Clock;
+use hal::multicore::{Multicore, Stack};
+use hal::sio::Sio;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Value to indicate that Core 1 has completed its task
+const CORE1_TASK_COMPLETE: u32 = 0xEE;
+
+/// Stack for core 1
+///
+/// Core 0 gets its stack via the normal route - any memory not used by static values is
+/// reserved for stack and initialised by cortex-m-rt.
+/// To get the same for Core 1, we would need to compile everything seperately and
+/// modify the linker file for both programs, and that's quite annoying.
+/// So instead, core1.spawn takes a [usize] which gets used for the stack.
+/// NOTE: We use the `Stack` struct here to ensure that it has 32-byte alignment, which allows
+/// the stack guard to take up the least amount of usable RAM.
+static mut CORE1_STACK: Stack<4096> = Stack::new();
+
+fn core1_task(sys_freq: u32) -> ! {
+ let mut pac = unsafe { hal::pac::Peripherals::steal() };
+ let core = unsafe { cortex_m::Peripherals::steal() };
+
+ let mut sio = Sio::new(pac.SIO);
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ let mut led_pin = pins.gpio25.into_push_pull_output();
+ let mut delay = cortex_m::delay::Delay::new(core.SYST, sys_freq);
+ loop {
+ let input = sio.fifo.read();
+ if let Some(word) = input {
+ delay.delay_ms(word);
+ led_pin.toggle().unwrap();
+ sio.fifo.write_blocking(CORE1_TASK_COMPLETE);
+ };
+ }
+}
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then toggles a GPIO pin in
+/// an infinite loop. If there is an LED connected to that pin, it will blink.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::watchdog::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ let sys_freq = clocks.system_clock.freq().to_Hz();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let mut sio = hal::sio::Sio::new(pac.SIO);
+
+ let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo);
+ let cores = mc.cores();
+ let core1 = &mut cores[1];
+ let _test = core1.spawn(unsafe { &mut CORE1_STACK.mem }, move || {
+ core1_task(sys_freq)
+ });
+
+ /// How much we adjust the LED period every cycle
+ const LED_PERIOD_INCREMENT: i32 = 2;
+
+ /// The minimum LED toggle interval we allow for.
+ const LED_PERIOD_MIN: i32 = 0;
+
+ /// The maximum LED toggle interval period we allow for. Keep it reasonably short so it's easy to see.
+ const LED_PERIOD_MAX: i32 = 100;
+
+ // Our current LED period. It starts at the shortest period, which is the highest blink frequency
+ let mut led_period: i32 = LED_PERIOD_MIN;
+
+ // The direction we're incrementing our LED period.
+ // Since we start at the minimum value, start by counting up
+ let mut count_up = true;
+
+ loop {
+ if count_up {
+ // Increment our period
+ led_period += LED_PERIOD_INCREMENT;
+
+ // Change direction of increment if we hit the limit
+ if led_period > LED_PERIOD_MAX {
+ led_period = LED_PERIOD_MAX;
+ count_up = false;
+ }
+ } else {
+ // Decrement our period
+ led_period -= LED_PERIOD_INCREMENT;
+
+ // Change direction of increment if we hit the limit
+ if led_period < LED_PERIOD_MIN {
+ led_period = LED_PERIOD_MIN;
+ count_up = true;
+ }
+ }
+
+ // It should not be possible for led_period to go negative, but let's ensure that.
+ if led_period < 0 {
+ led_period = 0;
+ }
+
+ // Send the new delay time to Core 1. We convert it
+ sio.fifo.write(led_period as u32);
+
+ // Sleep until Core 1 sends a message to tell us it is done
+ let ack = sio.fifo.read_blocking();
+ if ack != CORE1_TASK_COMPLETE {
+ // In a real application you might want to handle the case
+ // where the CPU sent the wrong message - we're going to
+ // ignore it here.
+ }
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"Multicore FIFO Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/multicore_polyblink.rs b/rp235x-hal-examples/src/bin/multicore_polyblink.rs
new file mode 100644
index 000000000..a8ccfe9a1
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/multicore_polyblink.rs
@@ -0,0 +1,136 @@
+//! # Multicore Blinking Example
+//!
+//! This application blinks two LEDs on GPIOs 2 and 3 at different rates (3Hz
+//! and 4Hz respectively.)
+//!
+//! See the `Cargo.toml` file for Copyright and licence details.
+
+#![no_std]
+#![no_main]
+
+use cortex_m::delay::Delay;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+use hal::clocks::Clock;
+use hal::gpio::Pins;
+use hal::multicore::{Multicore, Stack};
+use hal::sio::Sio;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Some things we need
+use embedded_hal::digital::StatefulOutputPin;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// The frequency at which core 0 will blink its LED (Hz).
+const CORE0_FREQ: u32 = 3;
+/// The frequency at which core 1 will blink its LED (Hz).
+const CORE1_FREQ: u32 = 4;
+/// The delay between each toggle of core 0's LED (us).
+const CORE0_DELAY: u32 = 1_000_000 / CORE0_FREQ;
+/// The delay between each toggle of core 1's LED (us).
+const CORE1_DELAY: u32 = 1_000_000 / CORE1_FREQ;
+
+/// Stack for core 1
+///
+/// Core 0 gets its stack via the normal route - any memory not used by static
+/// values is reserved for stack and initialised by cortex-m-rt.
+/// To get the same for Core 1, we would need to compile everything seperately
+/// and modify the linker file for both programs, and that's quite annoying.
+/// So instead, core1.spawn takes a [usize] which gets used for the stack.
+/// NOTE: We use the `Stack` struct here to ensure that it has 32-byte
+/// alignment, which allows the stack guard to take up the least amount of
+/// usable RAM.
+static mut CORE1_STACK: Stack<4096> = Stack::new();
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+ let core = cortex_m::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::watchdog::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // Set up the GPIO pins
+ let mut sio = Sio::new(pac.SIO);
+ let pins = Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+ let mut led1 = pins.gpio2.into_push_pull_output();
+ let mut led2 = pins.gpio3.into_push_pull_output();
+
+ // Set up the delay for the first core.
+ let sys_freq = clocks.system_clock.freq().to_Hz();
+ let mut delay = Delay::new(core.SYST, sys_freq);
+
+ // Start up the second core to blink the second LED
+ let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo);
+ let cores = mc.cores();
+ let core1 = &mut cores[1];
+ core1
+ .spawn(unsafe { &mut CORE1_STACK.mem }, move || {
+ // Get the second core's copy of the `CorePeripherals`, which are per-core.
+ // Unfortunately, `cortex-m` doesn't support this properly right now,
+ // so we have to use `steal`.
+ let core = unsafe { cortex_m::Peripherals::steal() };
+ // Set up the delay for the second core.
+ let mut delay = Delay::new(core.SYST, sys_freq);
+ // Blink the second LED.
+ loop {
+ led2.toggle().unwrap();
+ delay.delay_us(CORE1_DELAY)
+ }
+ })
+ .unwrap();
+
+ // Blink the first LED.
+ loop {
+ led1.toggle().unwrap();
+ delay.delay_us(CORE0_DELAY)
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"Multicore Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/pio_blink.rs b/rp235x-hal-examples/src/bin/pio_blink.rs
new file mode 100644
index 000000000..e814dd8d6
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/pio_blink.rs
@@ -0,0 +1,92 @@
+//! This example toggles the GPIO25 pin, using a PIO program.
+//!
+//! If a LED is connected to that pin, like on a Pico board, the LED should blink.
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+use rp235x_hal as hal;
+
+use hal::gpio::{FunctionPio0, Pin};
+use hal::pio::PIOExt;
+use hal::Sio;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then blinks an LED using the PIO peripheral.
+#[hal::entry]
+fn main() -> ! {
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ let sio = Sio::new(pac.SIO);
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // configure LED pin for Pio0.
+ let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function();
+ // PIN id for use inside of PIO
+ let led_pin_id = led.id().num;
+
+ // Define some simple PIO program.
+ const MAX_DELAY: u8 = 31;
+ let mut a = pio::Assembler::<32>::new();
+ let mut wrap_target = a.label();
+ let mut wrap_source = a.label();
+ // Set pin as Out
+ a.set(pio::SetDestination::PINDIRS, 1);
+ // Define begin of program loop
+ a.bind(&mut wrap_target);
+ // Set pin low
+ a.set_with_delay(pio::SetDestination::PINS, 0, MAX_DELAY);
+ // Set pin high
+ a.set_with_delay(pio::SetDestination::PINS, 1, MAX_DELAY);
+ // Define end of program loop
+ a.bind(&mut wrap_source);
+ // The labels wrap_target and wrap_source, as set above,
+ // define a loop which is executed repeatedly by the PIO
+ // state machine.
+ let program = a.assemble_with_wrap(wrap_source, wrap_target);
+
+ // Initialize and start PIO
+ let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
+ let installed = pio.install(&program).unwrap();
+ let (int, frac) = (0, 0); // as slow as possible (0 is interpreted as 65536)
+ let (sm, _, _) = hal::pio::PIOBuilder::from_installed_program(installed)
+ .set_pins(led_pin_id, 1)
+ .clock_divisor_fixed_point(int, frac)
+ .build(sm0);
+ sm.start();
+
+ // PIO runs in background, independently from CPU
+ loop {
+ hal::arch::wfi();
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"PIO Blinky Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/pio_dma.rs b/rp235x-hal-examples/src/bin/pio_dma.rs
new file mode 100644
index 000000000..63add2e26
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/pio_dma.rs
@@ -0,0 +1,134 @@
+//! This example shows how to read from and write to PIO using DMA.
+//!
+//! If a LED is connected to that pin, like on a Pico board, it will continously output "HELLO
+//! WORLD" in morse code. The example also tries to read the data back. If reading the data fails,
+//! the message will only be shown once, and then the LED remains dark.
+//!
+//! See the `Cargo.toml` file for Copyright and licence details.
+#![no_std]
+#![no_main]
+
+use hal::singleton;
+
+use rp235x_hal as hal;
+
+use hal::dma::{double_buffer, single_buffer, DMAExt};
+use hal::gpio::{FunctionPio0, Pin};
+use hal::pio::PIOExt;
+use hal::sio::Sio;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+#[hal::entry]
+fn main() -> ! {
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ let sio = Sio::new(pac.SIO);
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // configure LED pin for Pio0.
+ let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function();
+ // PIN id for use inside of PIO
+ let led_pin_id = led.id().num;
+
+ // HELLO WORLD in morse code:
+ // .... . .-.. .-.. --- / .-- --- .-. .-.. -..
+ #[allow(clippy::unusual_byte_groupings)]
+ let message = [
+ 0b10101010_00100010_11101010_00101110,
+ 0b10100011_10111011_10000000_10111011,
+ 0b10001110_11101110_00101110_10001011,
+ 0b10101000_11101010_00000000_00000000,
+ ];
+
+ // Define a PIO program which reads data from the TX FIFO bit by bit, configures the LED
+ // according to the data, and then writes the data back to the RX FIFO.
+ let program = pio_proc::pio_asm!(
+ ".wrap_target",
+ " out x, 1",
+ " mov pins, x",
+ " in x, 1 [13]",
+ ".wrap"
+ );
+
+ // Initialize and start PIO
+ let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
+ let installed = pio.install(&program.program).unwrap();
+ let (mut sm, rx, tx) = hal::pio::PIOBuilder::from_installed_program(installed)
+ .out_pins(led_pin_id, 1)
+ .clock_divisor_fixed_point(0, 0) // as slow as possible (0 is interpreted as 65536)
+ .autopull(true)
+ .autopush(true)
+ .build(sm0);
+ // The GPIO pin needs to be configured as an output.
+ sm.set_pindirs([(led_pin_id, hal::pio::PinDir::Output)]);
+ sm.start();
+
+ let dma = pac.DMA.split(&mut pac.RESETS);
+
+ // Transfer a single message via DMA.
+ let tx_buf = singleton!(: [u32; 4] = message).unwrap();
+ let rx_buf = singleton!(: [u32; 4] = [0; 4]).unwrap();
+ let tx_transfer = single_buffer::Config::new(dma.ch0, tx_buf, tx).start();
+ let rx_transfer = single_buffer::Config::new(dma.ch1, rx, rx_buf).start();
+ let (ch0, tx_buf, tx) = tx_transfer.wait();
+ let (ch1, rx, rx_buf) = rx_transfer.wait();
+ for i in 0..rx_buf.len() {
+ if rx_buf[i] != tx_buf[i] {
+ // The data did not match, abort.
+ #[allow(clippy::empty_loop)]
+ loop {}
+ }
+ }
+
+ // Chain some buffers together for continuous transfers
+ let tx_buf2 = singleton!(: [u32; 4] = message).unwrap();
+ let rx_buf2 = singleton!(: [u32; 4] = [0; 4]).unwrap();
+ let tx_transfer = double_buffer::Config::new((ch0, ch1), tx_buf, tx).start();
+ let mut tx_transfer = tx_transfer.read_next(tx_buf2);
+ let rx_transfer = double_buffer::Config::new((dma.ch2, dma.ch3), rx, rx_buf).start();
+ let mut rx_transfer = rx_transfer.write_next(rx_buf2);
+ loop {
+ // When a transfer is done we immediately enqueue the buffers again.
+ if tx_transfer.is_done() {
+ let (tx_buf, next_tx_transfer) = tx_transfer.wait();
+ tx_transfer = next_tx_transfer.read_next(tx_buf);
+ }
+ if rx_transfer.is_done() {
+ let (rx_buf, next_rx_transfer) = rx_transfer.wait();
+ for i in 0..rx_buf.len() {
+ if rx_buf[i] != message[i] {
+ // The data did not match, abort.
+ #[allow(clippy::empty_loop)]
+ loop {}
+ }
+ }
+ rx_transfer = next_rx_transfer.write_next(rx_buf);
+ }
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"PIO Blinky with DMA Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/pio_proc_blink.rs b/rp235x-hal-examples/src/bin/pio_proc_blink.rs
new file mode 100644
index 000000000..711701cdd
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/pio_proc_blink.rs
@@ -0,0 +1,80 @@
+//! This example toggles the GPIO25 pin, using a PIO program compiled via pio_proc::pio!().
+//!
+//! If a LED is connected to that pin, like on a Pico board, the LED should blink.
+#![no_std]
+#![no_main]
+
+use rp235x_hal as hal;
+
+use hal::gpio::{FunctionPio0, Pin};
+use hal::pio::PIOExt;
+use hal::Sio;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+#[hal::entry]
+fn main() -> ! {
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ let sio = Sio::new(pac.SIO);
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // configure LED pin for Pio0.
+ let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function();
+ // PIN id for use inside of PIO
+ let led_pin_id = led.id().num;
+
+ // Define some simple PIO program.
+ let program = pio_proc::pio_asm!(
+ ".wrap_target",
+ "set pins, 1 [31]",
+ "set pins, 0 [31]",
+ ".wrap"
+ );
+
+ // Initialize and start PIO
+ let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
+ let installed = pio.install(&program.program).unwrap();
+ let (int, frac) = (0, 0); // as slow as possible (0 is interpreted as 65536)
+ let (mut sm, _, _) = hal::pio::PIOBuilder::from_installed_program(installed)
+ .set_pins(led_pin_id, 1)
+ .clock_divisor_fixed_point(int, frac)
+ .build(sm0);
+ // The GPIO pin needs to be configured as an output.
+ sm.set_pindirs([(led_pin_id, hal::pio::PinDir::Output)]);
+ sm.start();
+
+ // PIO runs in background, independently from CPU
+ loop {
+ hal::arch::wfi();
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"PIO Proc-macro Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/pio_side_set.rs b/rp235x-hal-examples/src/bin/pio_side_set.rs
new file mode 100644
index 000000000..ab1079904
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/pio_side_set.rs
@@ -0,0 +1,83 @@
+//! This example toggles the GPIO25 pin, using a PIO program compiled via pio_proc::pio!().
+//!
+//! If a LED is connected to that pin, like on a Pico board, the LED should blink.
+//!
+//! This example makes use of side setting.
+#![no_std]
+#![no_main]
+
+use rp235x_hal as hal;
+
+use hal::gpio::{FunctionPio0, Pin};
+use hal::pio::PIOExt;
+use hal::Sio;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+#[hal::entry]
+fn main() -> ! {
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ let sio = Sio::new(pac.SIO);
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // configure LED pin for Pio0.
+ let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function();
+ // PIN id for use inside of PIO
+ let led_pin_id = led.id().num;
+
+ // Define some simple PIO program.
+ let program = pio_proc::pio_asm!(
+ ".side_set 1", // each instruction must set 1 bit
+ ".wrap_target",
+ " nop side 1 [15]",
+ " nop side 0 [15]",
+ ".wrap",
+ );
+
+ // Initialize and start PIO
+ let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
+ let installed = pio.install(&program.program).unwrap();
+ let (int, frac) = (0, 0); // as slow as possible (0 is interpreted as 65536)
+ let (mut sm, _, _) = hal::pio::PIOBuilder::from_installed_program(installed)
+ .side_set_pin_base(led_pin_id)
+ .clock_divisor_fixed_point(int, frac)
+ .build(sm0);
+ // The GPIO pin needs to be configured as an output.
+ sm.set_pindirs([(led_pin_id, hal::pio::PinDir::Output)]);
+ sm.start();
+
+ // PIO runs in background, independently from CPU
+ loop {
+ hal::arch::wfi();
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"PIO Side-set Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/pio_synchronized.rs b/rp235x-hal-examples/src/bin/pio_synchronized.rs
new file mode 100644
index 000000000..b64839fba
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/pio_synchronized.rs
@@ -0,0 +1,117 @@
+//! This example toggles the GPIO0 and GPIO1 pins, with each controlled from a
+//! separate PIO state machine.
+//!
+//! Despite running in separate state machines, the clocks are synchronized at
+//! the rise and fall times will be simultaneous.
+#![no_std]
+#![no_main]
+
+use rp235x_hal as hal;
+
+use hal::gpio::{FunctionPio0, Pin};
+use hal::pio::PIOExt;
+use hal::Sio;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+#[hal::entry]
+fn main() -> ! {
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ let sio = Sio::new(pac.SIO);
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // configure pins for Pio0.
+ let gp0: Pin<_, FunctionPio0, _> = pins.gpio0.into_function();
+ let gp1: Pin<_, FunctionPio0, _> = pins.gpio1.into_function();
+
+ // PIN id for use inside of PIO
+ let pin0 = gp0.id().num;
+ let pin1 = gp1.id().num;
+
+ // Define some simple PIO program.
+ let program = pio_proc::pio_asm!(
+ "
+.wrap_target
+ set pins, 1 [31]
+ set pins, 0 [31]
+.wrap
+ "
+ );
+
+ // Initialize and start PIO
+ let (mut pio, sm0, sm1, _, _) = pac.PIO0.split(&mut pac.RESETS);
+ // I'm "measuring" the phase offset between the two pins by connecting
+ // then through a LED. If there is a clock offset, there will be a
+ // short time with a voltage between the pins, so the LED will flash up.
+ // With a slow clock this is not visible, so use a reasonably fast clock.
+ let (int, frac) = (256, 0);
+
+ let installed = pio.install(&program.program).unwrap();
+ let (mut sm0, _, _) = hal::pio::PIOBuilder::from_installed_program(
+ // Safety: We won't uninstall the program, ever
+ unsafe { installed.share() },
+ )
+ .set_pins(pin0, 1)
+ .clock_divisor_fixed_point(int, frac)
+ .build(sm0);
+ // The GPIO pin needs to be configured as an output.
+ sm0.set_pindirs([(pin0, hal::pio::PinDir::Output)]);
+
+ let (mut sm1, _, _) = hal::pio::PIOBuilder::from_installed_program(installed)
+ .set_pins(pin1, 1)
+ .clock_divisor_fixed_point(int, frac)
+ .build(sm1);
+ // The GPIO pin needs to be configured as an output.
+ sm1.set_pindirs([(pin1, hal::pio::PinDir::Output)]);
+
+ // Start both SMs at the same time
+ let group = sm0.with(sm1).sync().start();
+ hal::arch::delay(10_000_000);
+
+ // Stop both SMs at the same time
+ let group = group.stop();
+ hal::arch::delay(10_000_000);
+
+ // Start them again and extract the individual state machines
+ let (sm1, sm2) = group.start().free();
+ hal::arch::delay(10_000_000);
+
+ // Stop the two state machines separately
+ let _sm1 = sm1.stop();
+ hal::arch::delay(10_000_000);
+ let _sm2 = sm2.stop();
+
+ loop {
+ hal::arch::wfi();
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"PIO Synchronisation Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/powman_test.rs b/rp235x-hal-examples/src/bin/powman_test.rs
new file mode 100644
index 000000000..80a517ee0
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/powman_test.rs
@@ -0,0 +1,326 @@
+//! # POWMAN Example
+//!
+//! This application demonstrates talking to the POWMAN peripheral.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+use rp235x_hal::{
+ self as hal, gpio, pac,
+ powman::{AotClockSource, FractionalFrequency, Powman},
+ uart::{DataBits, StopBits, UartConfig, UartPeripheral},
+};
+
+use cortex_m_rt::exception;
+use pac::interrupt;
+
+// Some traits we need
+use core::fmt::Write;
+use hal::fugit::RateExtU32;
+use hal::Clock;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+struct GlobalUart {
+ inner: critical_section::Mutex>>,
+}
+
+type MyUart = UartPeripheral<
+ hal::uart::Enabled,
+ pac::UART0,
+ (
+ gpio::Pin,
+ gpio::Pin,
+ ),
+>;
+
+static GLOBAL_UART: GlobalUart = GlobalUart {
+ inner: critical_section::Mutex::new(core::cell::RefCell::new(None)),
+};
+
+impl core::fmt::Write for &GlobalUart {
+ fn write_str(&mut self, s: &str) -> core::fmt::Result {
+ critical_section::with(|cs| {
+ let mut cell = self.inner.borrow_ref_mut(cs);
+ let uart = cell.as_mut().unwrap();
+ uart.write_str(s)
+ })
+ }
+}
+
+impl GlobalUart {
+ fn init(&self, uart: MyUart) {
+ critical_section::with(|cs| {
+ self.inner.borrow(cs).replace(Some(uart));
+ });
+ }
+}
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then writes to the UART in
+/// an infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = pac::Peripherals::take().unwrap();
+ let mut cp = cortex_m::Peripherals::take().unwrap();
+
+ // Enable the cycle counter
+ cp.DCB.enable_trace();
+ cp.DWT.enable_cycle_counter();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ let uart_pins = (
+ // UART TX (characters sent from rp235x) on pin 1 (GPIO0)
+ pins.gpio0.into_function::(),
+ // UART RX (characters received by rp235x) on pin 2 (GPIO1)
+ pins.gpio1.into_function::(),
+ );
+ let uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+
+ GLOBAL_UART.init(uart);
+
+ let mut powman = Powman::new(pac.POWMAN, None);
+
+ _ = writeln!(&GLOBAL_UART, "\n\nPOWMAN Test");
+
+ print_aot_status(&mut powman);
+ _ = writeln!(&GLOBAL_UART, "AOT time: 0x{:016x}", powman.aot_get_time());
+
+ unsafe {
+ cortex_m::peripheral::NVIC::unmask(pac::Interrupt::POWMAN_IRQ_TIMER);
+ }
+
+ _ = writeln!(&GLOBAL_UART, "Starting AOT...");
+ powman.aot_start();
+ // we don't know what oscillator we're on, so give it time to start whatever it is
+ cortex_m::asm::delay(150_000);
+ print_aot_status(&mut powman);
+ rollover_test(&mut powman);
+ loop_test(&mut powman);
+ alarm_test(&mut powman);
+
+ let source = AotClockSource::Xosc(FractionalFrequency::from_hz(12_000_000));
+ _ = writeln!(&GLOBAL_UART, "Switching AOT to {}", source);
+ powman.aot_set_clock(source).expect("selecting XOSC");
+ print_aot_status(&mut powman);
+ loop_test(&mut powman);
+
+ let source = AotClockSource::Lposc(FractionalFrequency::from_hz(32768));
+ _ = writeln!(&GLOBAL_UART, "Switching AOT to {}", source);
+ powman.aot_set_clock(source).expect("selecting LPOSC");
+ print_aot_status(&mut powman);
+ loop_test(&mut powman);
+
+ _ = writeln!(&GLOBAL_UART, "Rebooting now");
+
+ panic!("Finished!");
+}
+
+fn print_aot_status(powman: &mut Powman) {
+ if powman.aot_is_running() {
+ _ = writeln!(
+ &GLOBAL_UART,
+ "AOT is running on {}",
+ powman.aot_get_clock().unwrap()
+ );
+ } else {
+ _ = writeln!(&GLOBAL_UART, "AOT is not running.");
+ }
+}
+
+/// Check we can roll-over a 32-bit boundary
+fn rollover_test(powman: &mut Powman) {
+ let start_loop = 0x0000_0000_FFFF_FF00u64;
+ let end_loop = 0x0000_0001_0000_00FFu64;
+ _ = writeln!(
+ &GLOBAL_UART,
+ "Setting AOT to 0x{:016x} and waiting for rollover...",
+ start_loop
+ );
+ powman.aot_stop();
+ powman.aot_set_time(start_loop);
+ powman.aot_start();
+
+ let mut last = 0;
+ loop {
+ let now = powman.aot_get_time();
+ if now == end_loop {
+ break;
+ } else if now < last || now > end_loop {
+ panic!("bad AOT read {}!", now);
+ }
+ last = now;
+ }
+ _ = writeln!(&GLOBAL_UART, "Rollover test complete - no glitches found",);
+ _ = writeln!(&GLOBAL_UART, "Clearing AOT...");
+ powman.aot_clear();
+ _ = writeln!(&GLOBAL_UART, "AOT time: 0x{:016x}", powman.aot_get_time());
+}
+
+/// In this function, we see how long it takes to pass a certain number of ticks.
+fn loop_test(powman: &mut Powman) {
+ let start_loop = 0;
+ let end_loop = 2_000; // 2 seconds
+ _ = writeln!(
+ &GLOBAL_UART,
+ "Setting AOT to 0x{:016x} and benchmarking...",
+ start_loop
+ );
+ powman.aot_stop();
+ powman.aot_set_time(start_loop);
+ powman.aot_start();
+
+ let start_clocks = cortex_m::peripheral::DWT::cycle_count();
+ loop {
+ let now = powman.aot_get_time();
+ if now == end_loop {
+ break;
+ }
+ }
+ let end_clocks = cortex_m::peripheral::DWT::cycle_count();
+ // Compare our AOT against our CPU clock speed
+ let delta_clocks = end_clocks.wrapping_sub(start_clocks) as u64;
+ let delta_ticks = end_loop - start_loop;
+ let cycles_per_tick = delta_clocks / delta_ticks;
+ // Assume we're running at 150 MHz
+ let ms_per_tick = (cycles_per_tick as f32 * 1000.0) / 150_000_000.0;
+ let percent = ((ms_per_tick - 1.0) / 1.0) * 100.0;
+ _ = writeln!(
+ &GLOBAL_UART,
+ "Loop complete ... {delta_ticks} ticks in {delta_clocks} CPU clock cycles = {cycles_per_tick} cycles/tick ~= {ms_per_tick} ms/tick ({percent:.3}%)",
+ )
+ ;
+}
+
+/// Test the alarm function
+fn alarm_test(powman: &mut Powman) {
+ let start_time = 0x1_0000;
+ let alarm_time = start_time + 3000; // alarm is 3 seconds in the future
+ _ = writeln!(
+ &GLOBAL_UART,
+ "Setting AOT for {} ms in the future...",
+ alarm_time - start_time
+ );
+
+ powman.aot_stop();
+ powman.aot_set_time(start_time);
+ powman.aot_alarm_clear();
+ powman.aot_set_alarm(alarm_time);
+ powman.aot_alarm_interrupt_enable();
+ powman.aot_alarm_enable();
+ powman.aot_start();
+
+ _ = writeln!(
+ &GLOBAL_UART,
+ "Sleeping until alarm (* = wakeup, ! = POWMAN interrupt)...",
+ );
+ while !powman.aot_alarm_ringing() {
+ cortex_m::asm::wfe();
+ _ = write!(&GLOBAL_UART, "*",);
+ }
+
+ _ = writeln!(
+ &GLOBAL_UART,
+ "\nAlarm fired at 0x{:016x}. Clearing alarm.",
+ powman.aot_get_time()
+ );
+
+ powman.aot_alarm_clear();
+
+ if powman.aot_alarm_ringing() {
+ panic!("Alarm did not clear!");
+ }
+
+ _ = writeln!(&GLOBAL_UART, "Alarm cleared OK");
+}
+
+#[interrupt]
+#[allow(non_snake_case)]
+fn POWMAN_IRQ_TIMER() {
+ Powman::static_aot_alarm_interrupt_disable();
+ _ = write!(&GLOBAL_UART, "!");
+ cortex_m::asm::sev();
+}
+
+#[exception]
+unsafe fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! {
+ let _ = writeln!(&GLOBAL_UART, "HARD FAULT:\n{:#?}", ef);
+
+ hal::reboot::reboot(
+ hal::reboot::RebootKind::BootSel {
+ msd_disabled: false,
+ picoboot_disabled: false,
+ },
+ hal::reboot::RebootArch::Normal,
+ );
+}
+
+#[panic_handler]
+fn panic(info: &core::panic::PanicInfo) -> ! {
+ let _ = writeln!(&GLOBAL_UART, "PANIC:\n{:?}", info);
+
+ hal::reboot::reboot(
+ hal::reboot::RebootKind::BootSel {
+ msd_disabled: false,
+ picoboot_disabled: false,
+ },
+ hal::reboot::RebootArch::Normal,
+ );
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"POWMAN Test Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/pwm_blink.rs b/rp235x-hal-examples/src/bin/pwm_blink.rs
new file mode 100644
index 000000000..34eae8d29
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/pwm_blink.rs
@@ -0,0 +1,124 @@
+//! # PWM Blink Example
+//!
+//! If you have an LED connected to pin 25, it will fade the LED using the PWM
+//! peripheral.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::delay::DelayNs;
+use embedded_hal::pwm::SetDutyCycle;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+/// The minimum PWM value (i.e. LED brightness) we want
+const LOW: u16 = 0;
+
+/// The maximum PWM value (i.e. LED brightness) we want
+const HIGH: u16 = 25000;
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then fades the LED in an
+/// infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ //
+ // The default is to generate a 125 MHz system clock
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .ok()
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins up according to their function on this particular board
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // The delay object lets us wait for specified amounts of time (in
+ // milliseconds)
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // Init PWMs
+ let mut pwm_slices = hal::pwm::Slices::new(pac.PWM, &mut pac.RESETS);
+
+ // Configure PWM4
+ let pwm = &mut pwm_slices.pwm4;
+ pwm.set_ph_correct();
+ pwm.enable();
+
+ // Output channel B on PWM4 to GPIO 25
+ let channel = &mut pwm.channel_b;
+ channel.output_to(pins.gpio25);
+
+ // Infinite loop, fading LED up and down
+ loop {
+ // Ramp brightness up
+ for i in LOW..=HIGH {
+ delay.delay_us(8);
+ let _ = channel.set_duty_cycle(i);
+ }
+
+ // Ramp brightness down
+ for i in (LOW..=HIGH).rev() {
+ delay.delay_us(8);
+ let _ = channel.set_duty_cycle(i);
+ }
+
+ delay.delay_ms(500);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"PWM Blinky Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/pwm_blink_embedded_hal_1.rs b/rp235x-hal-examples/src/bin/pwm_blink_embedded_hal_1.rs
new file mode 100644
index 000000000..6695d872e
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/pwm_blink_embedded_hal_1.rs
@@ -0,0 +1,124 @@
+//! # PWM Blink Example
+//!
+//! If you have an LED connected to pin 25, it will fade the LED using the PWM
+//! peripheral.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::delay::DelayNs;
+use embedded_hal::pwm::SetDutyCycle;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// The minimum PWM value (i.e. LED brightness) we want
+const LOW: u16 = 0;
+
+/// The maximum PWM value (i.e. LED brightness) we want
+const HIGH: u16 = 25000;
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the RP2350 peripherals, then fades the LED in an
+/// infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ //
+ // The default is to generate a 125 MHz system clock
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins up according to their function on this particular board
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // The delay object lets us wait for specified amounts of time (in
+ // milliseconds)
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // Init PWMs
+ let mut pwm_slices = hal::pwm::Slices::new(pac.PWM, &mut pac.RESETS);
+
+ // Configure PWM4
+ let pwm = &mut pwm_slices.pwm4;
+ pwm.set_ph_correct();
+ pwm.enable();
+
+ // Output channel B on PWM4 to GPIO 25
+ let channel = &mut pwm.channel_b;
+ channel.output_to(pins.gpio25);
+
+ // Infinite loop, fading LED up and down
+ loop {
+ // Ramp brightness up
+ for i in LOW..=HIGH {
+ delay.delay_us(8);
+ channel.set_duty_cycle(i).unwrap();
+ }
+
+ // Ramp brightness down
+ for i in (LOW..=HIGH).rev() {
+ delay.delay_us(8);
+ channel.set_duty_cycle(i).unwrap();
+ }
+
+ delay.delay_ms(500);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"PWM Blinky Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/pwm_irq_input.rs b/rp235x-hal-examples/src/bin/pwm_irq_input.rs
new file mode 100644
index 000000000..75c428df0
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/pwm_irq_input.rs
@@ -0,0 +1,222 @@
+//! # PWM IRQ Input Example
+//!
+//! Read a 5V 50Hz PWM servo input signal from gpio pin 1 and turn the LED on when
+//! the input signal is high ( > 1600 us duty pulse width ) and off when low ( < 1400 us ).
+//!
+//! This signal is commonly used with radio control model systems and small servos.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::digital::OutputPin;
+
+// Our interrupt macro
+use hal::pac::interrupt;
+
+// Shorter alias for gpio and pwm modules
+use hal::gpio;
+use hal::pwm;
+
+// Some short-cuts to useful types for sharing data with the interrupt handlers
+use core::cell::RefCell;
+use critical_section::Mutex;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// 50 Hz PWM servo signals have a pulse width between 1000 us and 2000 us with
+/// 1500 us as the centre point. us is the abbreviation for micro seconds.
+
+/// The PWM threshold value for turning off the LED in us
+const LOW_US: u16 = 1475;
+
+/// The PWM threshold value for turning on the LED in us
+const HIGH_US: u16 = 1525;
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Pin types quickly become very long!
+/// We'll create some type aliases using `type` to help with that
+
+/// This pin will be our output - it will drive an LED if you run this on a Pico
+type LedPin = gpio::Pin, gpio::PullNone>;
+
+/// This pin will be our input for a 50 Hz servo PWM signal
+type InputPwmPin = gpio::Pin;
+
+/// This will be our PWM Slice - it will interpret the PWM signal from the pin
+type PwmSlice = pwm::Slice;
+
+/// Since we're always accessing these pins together we'll store them in a tuple.
+/// Giving this tuple a type alias means we won't need to use () when putting them
+/// inside an Option. That will be easier to read.
+type LedInputAndPwm = (LedPin, InputPwmPin, PwmSlice);
+
+/// This how we transfer our LED pin, input pin and PWM slice into the Interrupt Handler.
+/// We'll have the option hold both using the LedAndInput type.
+/// This will make it a bit easier to unpack them later.
+static GLOBAL_PINS: Mutex>> = Mutex::new(RefCell::new(None));
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then fades the LED in an
+/// infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ //
+ // The default is to generate a 125 MHz system clock
+ hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins up according to their function on this particular board
+ let pins = gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Init PWMs
+ let pwm_slices = pwm::Slices::new(pac.PWM, &mut pac.RESETS);
+
+ // Configure PWM0 slice
+ // The PWM slice clock should only run when the input is high (InputHighRunning)
+ let mut pwm: pwm::Slice<_, pwm::InputHighRunning> = pwm_slices.pwm0.into_mode();
+
+ // Divide the 125 MHz system clock by 125 to give a 1 MHz PWM slice clock (1 us per tick)
+ pwm.set_div_int(125);
+ pwm.enable();
+
+ // Connect to GPI O1 as the input to channel B on PWM0
+ let input_pin = pins.gpio1.reconfigure();
+ let channel = &mut pwm.channel_b;
+ channel.set_enabled(true);
+
+ // Enable an interrupt whenever GPI O1 goes from high to low (the end of a pulse)
+ input_pin.set_interrupt_enabled(gpio::Interrupt::EdgeLow, true);
+
+ // Configure GPIO 25 as an output to drive our LED.
+ // we can use reconfigure() instead of into_pull_up_input()
+ // since the variable we're pushing it into has that type
+ let led = pins.gpio25.reconfigure();
+
+ // Give away our pins by moving them into the `GLOBAL_PINS` variable.
+ // We won't need to access them in the main thread again
+ critical_section::with(|cs| {
+ GLOBAL_PINS.borrow(cs).replace(Some((led, input_pin, pwm)));
+ });
+
+ // Unmask the IO_BANK0 IRQ so that the NVIC interrupt controller
+ // will jump to the interrupt function when the interrupt occurs.
+ // We do this last so that the interrupt can't go off while
+ // it is in the middle of being configured
+ unsafe {
+ cortex_m::peripheral::NVIC::unmask(hal::pac::Interrupt::IO_IRQ_BANK0);
+ }
+
+ loop {
+ // interrupts handle everything else in this example.
+ hal::arch::wfi();
+ }
+}
+
+#[interrupt]
+fn IO_IRQ_BANK0() {
+ // The `#[interrupt]` attribute covertly converts this to `&'static mut Option`
+ static mut LED_INPUT_AND_PWM: Option = None;
+
+ // This is one-time lazy initialisation. We steal the variables given to us
+ // via `GLOBAL_PINS`.
+ if LED_INPUT_AND_PWM.is_none() {
+ critical_section::with(|cs| {
+ *LED_INPUT_AND_PWM = GLOBAL_PINS.borrow(cs).take();
+ });
+ }
+
+ // Need to check if our Option contains our pins and pwm slice
+ // borrow led, input and pwm by *destructuring* the tuple
+ // these will be of type `&mut LedPin`, `&mut InputPwmPin` and `&mut PwmSlice`, so we
+ // don't have to move them back into the static after we use them
+ if let Some((led, input, pwm)) = LED_INPUT_AND_PWM {
+ // Check if the interrupt source is from the input pin going from high-to-low.
+ // Note: this will always be true in this example, as that is the only enabled GPIO interrupt source
+ if input.interrupt_status(gpio::Interrupt::EdgeLow) {
+ // Read the width of the last pulse from the PWM Slice counter
+ let pulse_width_us = pwm.get_counter();
+
+ // if the PWM signal indicates low, turn off the LED
+ if pulse_width_us < LOW_US {
+ // set_low can't fail, but the embedded-hal traits always allow for it
+ // we can discard the Result
+ let _ = led.set_low();
+ }
+ // if the PWM signal indicates high, turn on the LED
+ else if pulse_width_us > HIGH_US {
+ // set_high can't fail, but the embedded-hal traits always allow for it
+ // we can discard the Result
+ let _ = led.set_high();
+ }
+
+ // If the PWM signal was in the dead-zone between LOW and HIGH, don't change the LED's
+ // state. The dead-zone avoids the LED flickering rapidly when receiving a signal close
+ // to the mid-point, 1500 us in this case.
+
+ // Reset the pwm counter back to 0, ready for the next pulse
+ pwm.set_counter(0);
+
+ // Our interrupt doesn't clear itself.
+ // Do that now so we don't immediately jump back to this interrupt handler.
+ input.clear_interrupt(gpio::Interrupt::EdgeLow);
+ }
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"PWM IRQ Input Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/rom_funcs.rs b/rp235x-hal-examples/src/bin/rom_funcs.rs
new file mode 100644
index 000000000..c572cb59d
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/rom_funcs.rs
@@ -0,0 +1,377 @@
+//! # 'ROM Functions' Example
+//!
+//! This application demonstrates how to call functions in the rp235x's boot ROM.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use core::fmt::Write;
+use hal::fugit::RateExtU32;
+use hal::Clock;
+
+// UART related types
+use hal::uart::{DataBits, StopBits, UartConfig};
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then writes to the UART in
+/// an infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ let uart_pins = (
+ // UART TX (characters sent from rp235x) on pin 1 (GPIO0)
+ pins.gpio0.into_function::(),
+ // UART RX (characters received by rp235x) on pin 2 (GPIO1)
+ pins.gpio1.into_function::(),
+ );
+ let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+
+ _ = writeln!(uart);
+ _ = writeln!(uart, "rom_func example for RP2350");
+ _ = writeln!(uart, "===========================");
+ _ = writeln!(uart);
+
+ _ = writeln!(uart, "CPU Secure Mode: {}", hal::rom_data::is_secure_mode());
+
+ _ = writeln!(
+ uart,
+ "ROM Git Revision: 0x{:x}",
+ hal::rom_data::git_revision()
+ );
+
+ _ = writeln!(
+ uart,
+ "Partition Table Info @ {:?}",
+ hal::rom_data::partition_table_pointer()
+ );
+
+ _ = writeln!(
+ uart,
+ "ROM Version: A{}",
+ hal::rom_data::rom_version_number()
+ );
+
+ get_otp_data(&mut uart, &mut pac.OTP_DATA);
+ get_otp_data_raw(&mut uart, &mut pac.OTP_DATA_RAW);
+ get_sys_info_chip_info(&mut uart);
+ get_sys_info_cpu_info(&mut uart);
+ get_sys_info_flash_dev_info(&mut uart);
+ get_sys_info_boot_random(&mut uart);
+ get_sys_info_start_block(&mut uart);
+ get_partition_table_info(&mut uart);
+
+ const WORDS_PER_SCREEN_LINE: usize = 16;
+ _ = writeln!(uart, "Reading OTP:");
+ _ = write!(uart, "Page Adr: ");
+ for col in 0..WORDS_PER_SCREEN_LINE {
+ _ = write!(uart, " {:04x}", col);
+ }
+ // These are the factory programmed pages
+ for page in [0, 1, 62, 63] {
+ for row in 0..hal::otp::NUM_ROWS_PER_PAGE {
+ let index = page * hal::otp::NUM_ROWS_PER_PAGE + row;
+ if row == 0 {
+ _ = write!(uart, "\nP{:02} {:04x}: ", page, index);
+ } else if (index % WORDS_PER_SCREEN_LINE) == 0 {
+ _ = write!(uart, "\n {:04x}: ", index);
+ }
+ match hal::otp::read_ecc_word(index) {
+ Ok(0) => {
+ _ = write!(uart, " ----");
+ }
+ Ok(word) => {
+ _ = write!(uart, " {:04x}", word);
+ }
+ Err(hal::otp::Error::InvalidPermissions) => {
+ _ = write!(uart, " xxxx");
+ }
+ Err(hal::otp::Error::InvalidIndex) => {
+ _ = write!(uart, " ????");
+ }
+ }
+ }
+ }
+ _ = writeln!(uart);
+
+ _ = writeln!(uart, "Reading raw OTP:");
+ _ = write!(uart, "Page Adr: ");
+ for col in 0..WORDS_PER_SCREEN_LINE {
+ _ = write!(uart, " {:06x}", col);
+ }
+ // These are the factory programmed pages
+ for page in [0, 1, 62, 63] {
+ for row in 0..hal::otp::NUM_ROWS_PER_PAGE {
+ let index = page * hal::otp::NUM_ROWS_PER_PAGE + row;
+ if row == 0 {
+ _ = write!(uart, "\nP{:02} {:04x}: ", page, index);
+ } else if (index % WORDS_PER_SCREEN_LINE) == 0 {
+ _ = write!(uart, "\n {:04x}: ", index);
+ }
+ match hal::otp::read_raw_word(index) {
+ Ok(0) => {
+ _ = write!(uart, " ------");
+ }
+ Ok(word) => {
+ _ = write!(uart, " {:06x}", word);
+ }
+ Err(hal::otp::Error::InvalidPermissions) => {
+ _ = write!(uart, " xxxxxx");
+ }
+ Err(hal::otp::Error::InvalidIndex) => {
+ _ = write!(uart, " ??????");
+ }
+ }
+ }
+ }
+ _ = writeln!(uart);
+
+ // Do a reset into the bootloader.
+ hal::reboot::reboot(
+ hal::reboot::RebootKind::BootSel {
+ msd_disabled: false,
+ picoboot_disabled: false,
+ },
+ hal::reboot::RebootArch::Normal,
+ );
+}
+
+/// Read OTP using the PAC
+fn get_otp_data(uart: &mut T, otp_data: &mut hal::pac::OTP_DATA)
+where
+ T: core::fmt::Write,
+{
+ _ = writeln!(uart, "Reading OTP_DATA");
+ let package_id = (otp_data.chipid1().read().chipid1().bits() as u32) << 16
+ | otp_data.chipid0().read().chipid0().bits() as u32;
+ let device_id = (otp_data.chipid3().read().chipid3().bits() as u32) << 16
+ | otp_data.chipid2().read().chipid2().bits() as u32;
+ _ = writeln!(uart, "\tRP2350 Package ID: {:#010x}", package_id);
+ _ = writeln!(uart, "\tRP2350 Device ID : {:#010x}", device_id);
+}
+
+/// Read OTP in raw mode using the PAC
+///
+/// Currently this doesn't work due to SVD issues.
+fn get_otp_data_raw(uart: &mut T, otp_data_raw: &mut hal::pac::OTP_DATA_RAW)
+where
+ T: core::fmt::Write,
+{
+ _ = writeln!(uart, "Reading OTP_DATA_RAW");
+ _ = writeln!(
+ uart,
+ "\tRP2350 Package ID: {:#010x} {:#010x}",
+ otp_data_raw.chipid0().read().bits(),
+ otp_data_raw.chipid1().read().bits()
+ );
+ _ = writeln!(
+ uart,
+ "\tRP2350 Device ID : {:#010x} {:#010x}",
+ otp_data_raw.chipid2().read().bits(),
+ otp_data_raw.chipid3().read().bits()
+ );
+}
+
+/// Run get_sys_info with 0x0001
+fn get_sys_info_chip_info(uart: &mut T)
+where
+ T: core::fmt::Write,
+{
+ let mut buffer = [0u32; 16];
+ let result = unsafe { hal::rom_data::get_sys_info(buffer.as_mut_ptr(), buffer.len(), 0x0001) };
+ _ = writeln!(uart, "get_sys_info(CHIP_INFO/0x0001) -> {}", result);
+ _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]);
+ _ = writeln!(uart, "\tRP2350 Package ID: {:#010x}", buffer[1]);
+ _ = writeln!(uart, "\tRP2350 Device ID : {:#010x}", buffer[2]);
+ _ = writeln!(uart, "\tRP2350 Wafer ID : {:#010x}", buffer[3]);
+}
+
+/// Run get_sys_info with 0x0004
+fn get_sys_info_cpu_info(uart: &mut T)
+where
+ T: core::fmt::Write,
+{
+ let mut buffer = [0u32; 16];
+ let result = unsafe { hal::rom_data::get_sys_info(buffer.as_mut_ptr(), buffer.len(), 0x0004) };
+ _ = writeln!(uart, "get_sys_info(CPU_INFO/0x0004) -> {}", result);
+ _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]);
+ _ = writeln!(
+ uart,
+ "\tCPU Architecture: {}",
+ match buffer[1] {
+ 0 => "Arm",
+ 1 => "RISC-V",
+ _ => "Unknown",
+ }
+ );
+}
+
+/// Run get_sys_info with 0x0008
+fn get_sys_info_flash_dev_info(uart: &mut T)
+where
+ T: core::fmt::Write,
+{
+ let mut buffer = [0u32; 16];
+ let result = unsafe { hal::rom_data::get_sys_info(buffer.as_mut_ptr(), buffer.len(), 0x0008) };
+ _ = writeln!(uart, "get_sys_info(FLASH_DEV_INFO/0x0008) -> {}", result);
+ let size_lookup = |value| match value {
+ 0 => "None",
+ 1 => "8K",
+ 2 => "16K",
+ 3 => "32K",
+ 4 => "64K",
+ 5 => "128K",
+ 6 => "256K",
+ 7 => "512K",
+ 8 => "1M",
+ 9 => "2M",
+ 10 => "4M",
+ 11 => "8M",
+ 12 => "16M",
+ _ => "Unknown",
+ };
+ _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]);
+ _ = writeln!(uart, "\tCS0 Size: {}", size_lookup((buffer[1] >> 8) & 15));
+ _ = writeln!(uart, "\tCS1 Size: {}", size_lookup((buffer[1] >> 12) & 15));
+}
+
+/// Run get_sys_info with 0x0010
+fn get_sys_info_boot_random(uart: &mut T)
+where
+ T: core::fmt::Write,
+{
+ let mut buffer = [0u32; 16];
+ let result = unsafe { hal::rom_data::get_sys_info(buffer.as_mut_ptr(), buffer.len(), 0x0010) };
+ _ = writeln!(uart, "get_sys_info(BOOT_RANDOM/0x0010) -> {}", result);
+ _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]);
+ let a = buffer[1];
+ let b = buffer[2];
+ let c = buffer[3];
+ let d = buffer[4];
+ _ = writeln!(uart, "\tA random number: 0x{a:08x}{b:08x}{c:08x}{d:08x}");
+}
+
+/// Run get_sys_info with 0x0040;
+fn get_sys_info_start_block(uart: &mut T)
+where
+ T: core::fmt::Write,
+{
+ let mut buffer = [0u32; 16];
+ let result = unsafe { hal::rom_data::get_sys_info(buffer.as_mut_ptr(), buffer.len(), 0x0040) };
+ _ = writeln!(uart, "get_sys_info(start_block/0x0040) -> {}", result);
+ _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]);
+ _ = writeln!(uart, "\tBoot Info: {:08x?}", &buffer[1..result as usize]);
+}
+
+/// Run get_partition_table_info
+fn get_partition_table_info(uart: &mut T)
+where
+ T: core::fmt::Write,
+{
+ let mut buffer = [0u32; 16];
+ let result = unsafe {
+ hal::rom_data::get_partition_table_info(buffer.as_mut_ptr(), buffer.len(), 0x0001)
+ };
+ _ = writeln!(
+ uart,
+ "get_partition_table_info(PT_INFO/0x0001) -> {}",
+ result
+ );
+ _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]);
+ let partition_count = buffer[1] & 0xFF;
+ let has_partition_table = (buffer[1] & (1 << 8)) != 0;
+ let unpart = hal::block::UnpartitionedSpace::from_raw(buffer[2], buffer[3]);
+ _ = writeln!(
+ uart,
+ "\tNum Partitions: {} (Has Partition Table? {})",
+ partition_count, has_partition_table
+ );
+ _ = writeln!(uart, "\tUnpartitioned Space: {}", unpart);
+ for part_idx in 0..partition_count {
+ let result = unsafe {
+ hal::rom_data::get_partition_table_info(
+ buffer.as_mut_ptr(),
+ buffer.len(),
+ (part_idx << 24) | 0x8010,
+ )
+ };
+ _ = writeln!(
+ uart,
+ "get_partition_table_info(PARTITION_LOCATION_AND_FLAGS|SINGLE_PARTITION/0x8010) -> {}",
+ result
+ );
+ _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]);
+ let part = hal::block::Partition::from_raw(buffer[1], buffer[2]);
+ _ = writeln!(uart, "\tPartition {}: {}", part_idx, part);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"Tests ROM functions and reading OTP"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/rosc_as_system_clock.rs b/rp235x-hal-examples/src/bin/rosc_as_system_clock.rs
new file mode 100644
index 000000000..96d54d971
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/rosc_as_system_clock.rs
@@ -0,0 +1,337 @@
+//! # ROSC as system clock Example
+//!
+//! This application demonstrates how to use the ROSC as the system clock on the rp235x.
+//!
+//! It shows setting the frequency of the ROSC to a measured known frequency, and contains
+//! helper functions to configure the ROSC drive strength to reach a desired target frequency.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+use embedded_hal::delay::DelayNs;
+use embedded_hal::digital::OutputPin;
+use fugit::{HertzU32, RateExtU32};
+use hal::clocks::{Clock, ClockSource, ClocksManager, StoppableClock};
+use hal::pac::rosc::ctrl::FREQ_RANGE_A;
+use hal::pac::{CLOCKS, ROSC};
+use hal::rosc::RingOscillator;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function attempts to find appropriate settings for the RingOscillator to reach a target
+/// frequency, and then logs the actual attained frequency.
+///
+/// The main reasons you'd want to use this is for power-saving in applications where precise
+/// timings are not critical (you don't need to use USB peripherals for example).
+/// Using the ROSC as the system clock allows under-clocking or over-clocking rp235x, and
+/// it also can allow fast waking from a dormant state on the order of µs, which the XOSC cannot do.
+///
+/// A motivating application for this was a flir lepton thermal camera module which
+/// makes thermal images available via SPI at a rate of around 9Hz. Using the rp235xs ROSC, we
+/// are able to clock out the thermal image via SPI and then enter dormant mode until the next vsync
+/// interrupt wakes us again, saving some power.
+#[hal::entry]
+fn main() -> ! {
+ // Set target rosc frequency to 150Mhz
+ // Setting frequencies can be a matter of a bit of trial and error to see what
+ // actual frequencies you can easily hit. In practice, the lowest achieved with this method
+ // is around 32Mhz, and it seems to be able to ramp up to around 230Mhz
+ let desired_rosc_freq: HertzU32 = 150_000_000.Hz();
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+ // Configure GPIO25 as an output
+ let mut led_pin = pins.gpio25.into_push_pull_output();
+ led_pin.set_low().unwrap();
+
+ // Setup the crystal oscillator to do accurate measurements against
+ let xosc = hal::xosc::setup_xosc_blocking(pac.XOSC, XTAL_FREQ_HZ.Hz()).unwrap();
+
+ // Find appropriate settings for the desired ring oscillator frequency.
+ let measured_rosc_frequency =
+ find_target_rosc_frequency(&pac.ROSC, &pac.CLOCKS, desired_rosc_freq);
+ let rosc = RingOscillator::new(pac.ROSC);
+
+ // Now initialise the ROSC with the reached frequency and set it as the system clock.
+ let rosc = rosc.initialize_with_freq(measured_rosc_frequency);
+
+ let mut clocks = ClocksManager::new(pac.CLOCKS);
+ clocks
+ .system_clock
+ .configure_clock(&rosc, rosc.get_freq())
+ .unwrap();
+
+ clocks
+ .peripheral_clock
+ .configure_clock(&clocks.system_clock, clocks.system_clock.freq())
+ .unwrap();
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // Now we can disable the crystal oscillator and run off the ring oscillator, for power savings.
+ let _xosc_disabled = xosc.disable();
+ // You may also wish to disable other clocks/peripherals that you don't need.
+ clocks.usb_clock.disable();
+ clocks.gpio_output0_clock.disable();
+ clocks.gpio_output1_clock.disable();
+ clocks.gpio_output2_clock.disable();
+ clocks.gpio_output3_clock.disable();
+ clocks.adc_clock.disable();
+
+ // Check that desired frequency is close to the frequency speed.
+ // If it is, turn the LED on. If not, blink the LED.
+ let got_to_within_1_mhz_of_target = desired_rosc_freq
+ .to_kHz()
+ .abs_diff(measured_rosc_frequency.to_kHz())
+ < 1000;
+
+ if got_to_within_1_mhz_of_target {
+ // Now it's possible to easily take the ROSC dormant, to be woken by an external interrupt.
+ led_pin.set_high().unwrap();
+ loop {
+ hal::arch::wfi();
+ }
+ } else {
+ loop {
+ led_pin.set_high().unwrap();
+ delay.delay_ms(500);
+ led_pin.set_low().unwrap();
+ delay.delay_ms(500);
+ }
+ }
+}
+
+/// Measure the actual speed of the ROSC at the current freq_range and drive strength config
+fn rosc_frequency_count_hz(clocks: &CLOCKS) -> HertzU32 {
+ // Wait for the frequency counter to be ready
+ while clocks.fc0_status().read().running().bit_is_set() {
+ hal::arch::nop();
+ }
+
+ // Set the speed of the reference clock in kHz.
+ clocks
+ .fc0_ref_khz()
+ .write(|w| unsafe { w.fc0_ref_khz().bits(XTAL_FREQ_HZ / 1000) });
+
+ // Corresponds to a 1ms test time, which seems to give good enough accuracy
+ clocks
+ .fc0_interval()
+ .write(|w| unsafe { w.fc0_interval().bits(10) });
+
+ // We don't really care about the min/max, so these are just set to min/max values.
+ clocks
+ .fc0_min_khz()
+ .write(|w| unsafe { w.fc0_min_khz().bits(0) });
+ clocks
+ .fc0_max_khz()
+ .write(|w| unsafe { w.fc0_max_khz().bits(0xffffffff) });
+
+ // To measure rosc directly we use the value 0x03.
+ clocks
+ .fc0_src()
+ .write(|w| unsafe { w.fc0_src().bits(0x03) });
+
+ // Wait until the measurement is ready
+ while clocks.fc0_status().read().done().bit_is_clear() {
+ hal::arch::nop();
+ }
+
+ let speed_hz = clocks.fc0_result().read().khz().bits() * 1000;
+ speed_hz.Hz()
+}
+
+/// Resets ROSC frequency range and stages drive strength, then increases the frequency range,
+/// drive strength bits, and finally divider in order to try to come close to the desired target
+/// frequency, returning the final measured ROSC frequency attained.
+fn find_target_rosc_frequency(
+ rosc: &ROSC,
+ clocks: &CLOCKS,
+ target_frequency: HertzU32,
+) -> HertzU32 {
+ reset_rosc_operating_frequency(rosc);
+ let mut div = 1;
+ let mut measured_rosc_frequency;
+ loop {
+ measured_rosc_frequency = rosc_frequency_count_hz(clocks);
+ // If it has overshot the target frequency, increase the divider and continue.
+ if measured_rosc_frequency > target_frequency {
+ div += 1;
+ set_rosc_div(rosc, div);
+ } else {
+ break;
+ }
+ }
+ loop {
+ measured_rosc_frequency = rosc_frequency_count_hz(clocks);
+ if measured_rosc_frequency > target_frequency {
+ // And probably want to step it down a notch?
+ break;
+ }
+ let can_increase = increase_drive_strength(rosc);
+ if !can_increase {
+ let can_increase_range = increase_freq_range(rosc);
+ if !can_increase_range {
+ break;
+ }
+ }
+ }
+ measured_rosc_frequency
+}
+
+fn set_rosc_div(rosc: &ROSC, div: u32) {
+ assert!(div <= 32);
+ let div = if div == 32 { 0 } else { div };
+ rosc.div().write(|w| unsafe { w.bits(0xaa0 + div) });
+}
+
+fn reset_rosc_operating_frequency(rosc: &ROSC) {
+ // Set divider to 1
+ set_rosc_div(rosc, 1);
+ rosc.ctrl().write(|w| w.freq_range().low());
+ write_freq_stages(rosc, &[0, 0, 0, 0, 0, 0, 0, 0]);
+}
+
+fn read_freq_stage(rosc: &ROSC, stage: u8) -> u8 {
+ match stage {
+ 0 => rosc.freqa().read().ds0().bits(),
+ 1 => rosc.freqa().read().ds1().bits(),
+ 2 => rosc.freqa().read().ds2().bits(),
+ 3 => rosc.freqa().read().ds3().bits(),
+ 4 => rosc.freqb().read().ds4().bits(),
+ 5 => rosc.freqb().read().ds5().bits(),
+ 6 => rosc.freqb().read().ds6().bits(),
+ 7 => rosc.freqb().read().ds7().bits(),
+ _ => panic!("invalid frequency drive strength stage"),
+ }
+}
+
+/// Increase the ROSC drive strength bits for the current freq_range
+fn increase_drive_strength(rosc: &ROSC) -> bool {
+ const MAX_STAGE_DRIVE: u8 = 3;
+ // Assume div is 1, and freq_range is high
+ let mut stages: [u8; 8] = [0; 8];
+ for (stage_index, stage) in stages.iter_mut().enumerate() {
+ *stage = read_freq_stage(rosc, stage_index as u8)
+ }
+ let num_stages_at_drive_level = match rosc.ctrl().read().freq_range().variant() {
+ Some(FREQ_RANGE_A::LOW) => 8,
+ Some(FREQ_RANGE_A::MEDIUM) => 6,
+ Some(FREQ_RANGE_A::HIGH) => 4,
+ Some(FREQ_RANGE_A::TOOHIGH) => panic!("Don't use TOOHIGH freq_range"),
+ None => {
+ // Start out at initial unset drive stage
+ return false;
+ }
+ };
+ let mut next_i = 0;
+ for (index, x) in stages[0..num_stages_at_drive_level].windows(2).enumerate() {
+ if x[1] < x[0] {
+ next_i = index + 1;
+ break;
+ }
+ }
+ if stages[next_i] < MAX_STAGE_DRIVE {
+ stages[next_i] += 1;
+ let min = *stages[0..num_stages_at_drive_level]
+ .iter()
+ .min()
+ .unwrap_or(&0);
+ for stage in &mut stages[num_stages_at_drive_level..] {
+ *stage = min;
+ }
+ write_freq_stages(rosc, &stages);
+ true
+ } else {
+ false
+ }
+}
+
+/// Sets the `freqa` and `freqb` ROSC drive strength stage registers.
+fn write_freq_stages(rosc: &ROSC, stages: &[u8; 8]) {
+ let passwd: u32 = 0x9696 << 16;
+ let mut freq_a = passwd;
+ let mut freq_b = passwd;
+ for (stage_index, stage) in stages.iter().enumerate().take(4) {
+ freq_a |= ((*stage & 0x07) as u32) << (stage_index * 4);
+ }
+ for (stage_index, stage) in stages.iter().enumerate().skip(4) {
+ freq_b |= ((*stage & 0x07) as u32) << ((stage_index - 4) * 4);
+ }
+ rosc.freqa().write(|w| unsafe { w.bits(freq_a) });
+ rosc.freqb().write(|w| unsafe { w.bits(freq_b) });
+}
+
+/// Increase the rosc frequency range up to the next step.
+/// Returns a boolean to indicate whether the frequency was increased.
+fn increase_freq_range(rosc: &ROSC) -> bool {
+ match rosc.ctrl().read().freq_range().variant() {
+ None => {
+ // Initial unset frequency range, move to LOW frequency range
+ rosc.ctrl().write(|w| w.freq_range().low());
+ // Reset all the drive strength bits.
+ write_freq_stages(rosc, &[0, 0, 0, 0, 0, 0, 0, 0]);
+ true
+ }
+ Some(FREQ_RANGE_A::LOW) => {
+ // Transition from LOW to MEDIUM frequency range
+ rosc.ctrl().write(|w| w.freq_range().medium());
+ // Reset all the drive strength bits.
+ write_freq_stages(rosc, &[0, 0, 0, 0, 0, 0, 0, 0]);
+ true
+ }
+ Some(FREQ_RANGE_A::MEDIUM) => {
+ // Transition from MEDIUM to HIGH frequency range
+ rosc.ctrl().write(|w| w.freq_range().high());
+ // Reset all the drive strength bits.
+ write_freq_stages(rosc, &[0, 0, 0, 0, 0, 0, 0, 0]);
+ true
+ }
+ Some(FREQ_RANGE_A::HIGH) | Some(FREQ_RANGE_A::TOOHIGH) => {
+ // Already in the HIGH frequency range, and can't increase
+ false
+ }
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"Ring Oscillator Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/spi.rs b/rp235x-hal-examples/src/bin/spi.rs
new file mode 100644
index 000000000..04d5aae62
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/spi.rs
@@ -0,0 +1,131 @@
+//! # SPI Example
+//!
+//! This application demonstrates how to use the SPI Driver to talk to a remote
+//! SPI device.
+//!
+//!
+//! It may need to be adapted to your particular board layout and/or pin
+//! assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal_0_2::prelude::*;
+use hal::clocks::Clock;
+use hal::fugit::RateExtU32;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then performs some example
+/// SPI transactions, then goes to sleep.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // These are implicitly used by the spi driver if they are in the correct mode
+ let spi_mosi = pins.gpio7.into_function::();
+ let spi_miso = pins.gpio4.into_function::();
+ let spi_sclk = pins.gpio6.into_function::();
+ let spi_bus = hal::spi::Spi::<_, _, _, 8>::new(pac.SPI0, (spi_mosi, spi_miso, spi_sclk));
+
+ // Exchange the uninitialised SPI driver for an initialised one
+ let mut spi_bus = spi_bus.init(
+ &mut pac.RESETS,
+ clocks.peripheral_clock.freq(),
+ 16.MHz(),
+ embedded_hal::spi::MODE_0,
+ );
+
+ // Write out 0, ignore return value
+ if spi_bus.write(&[0]).is_ok() {
+ // SPI write was successful
+ };
+
+ // write 50, then check the return
+ let send_success = spi_bus.send(50);
+ match send_success {
+ Ok(_) => {
+ // We succeeded, check the read value
+ if let Ok(_x) = spi_bus.read() {
+ // We got back `x` in exchange for the 0x50 we sent.
+ };
+ }
+ Err(_) => todo!(),
+ }
+
+ // Do a read+write at the same time. Data in `buffer` will be replaced with
+ // the data read from the SPI device.
+ let mut buffer: [u8; 4] = [1, 2, 3, 4];
+ let transfer_success = spi_bus.transfer(&mut buffer);
+ #[allow(clippy::single_match)]
+ match transfer_success {
+ Ok(_) => {} // Handle success
+ Err(_) => {} // handle errors
+ };
+
+ loop {
+ hal::arch::wfi();
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"SPI Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/spi_dma.rs b/rp235x-hal-examples/src/bin/spi_dma.rs
new file mode 100644
index 000000000..d78704ae0
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/spi_dma.rs
@@ -0,0 +1,123 @@
+//! # SPI DMA Example
+//!
+//! This application demonstrates how to use DMA for SPI transfers.
+//!
+//! The application expects the MISO and MOSI pins to be wired together so that it is able to check
+//! whether the data was sent and received correctly.
+//!
+//! See the `Cargo.toml` file for Copyright and licence details.
+#![no_std]
+#![no_main]
+
+use rp235x_hal as hal;
+
+use hal::singleton;
+
+use embedded_hal::delay::DelayNs;
+use embedded_hal::digital::OutputPin;
+use hal::clocks::Clock;
+use hal::dma::{bidirectional, DMAExt};
+use hal::fugit::RateExtU32;
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+#[hal::entry]
+fn main() -> ! {
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Setup clocks and the watchdog.
+ let mut watchdog = hal::watchdog::Watchdog::new(pac.WATCHDOG);
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ // Setup the pins.
+ let sio = hal::sio::Sio::new(pac.SIO);
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Set up our SPI pins into the correct mode
+ let spi_mosi = pins.gpio7.into_function::();
+ let spi_miso = pins.gpio4.into_function::();
+ let spi_sclk = pins.gpio6.into_function::();
+ let spi = hal::spi::Spi::<_, _, _, 8>::new(pac.SPI0, (spi_mosi, spi_miso, spi_sclk));
+
+ // Exchange the uninitialised SPI driver for an initialised one
+ let spi = spi.init(
+ &mut pac.RESETS,
+ clocks.peripheral_clock.freq(),
+ 16_000_000u32.Hz(),
+ embedded_hal::spi::MODE_0,
+ );
+
+ // Initialize DMA.
+ let dma = pac.DMA.split(&mut pac.RESETS);
+ // Configure GPIO25 as an output
+ let mut led_pin = pins.gpio25.into_push_pull_output();
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // Use DMA to transfer some bytes (single buffering).
+ let tx_buf = singleton!(: [u8; 16] = [0x42; 16]).unwrap();
+ let rx_buf = singleton!(: [u8; 16] = [0; 16]).unwrap();
+
+ // Use BidirectionalConfig to simultaneously write to spi from tx_buf and read into rx_buf
+ let transfer = bidirectional::Config::new((dma.ch0, dma.ch1), tx_buf, spi, rx_buf).start();
+ // Wait for both DMA channels to finish
+ let ((_ch0, _ch1), tx_buf, _spi, rx_buf) = transfer.wait();
+
+ // Compare buffers to see if the data was transferred correctly
+ for i in 0..rx_buf.len() {
+ if rx_buf[i] != tx_buf[i] {
+ // Fast blink on error
+ loop {
+ led_pin.set_high().unwrap();
+ delay.delay_ms(100);
+ led_pin.set_low().unwrap();
+ delay.delay_ms(100);
+ }
+ }
+ }
+
+ // Slow blink on success
+ loop {
+ led_pin.set_high().unwrap();
+ delay.delay_ms(500);
+ led_pin.set_low().unwrap();
+ delay.delay_ms(500);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"SPI DMA Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/uart.rs b/rp235x-hal-examples/src/bin/uart.rs
new file mode 100644
index 000000000..a5bfb4125
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/uart.rs
@@ -0,0 +1,163 @@
+//! # UART Example
+//!
+//! This application demonstrates how to use the UART Driver to talk to a serial
+//! connection.
+//!
+//! It may need to be adapted to your particular board layout and/or pin
+//! assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+use hal::gpio;
+
+// Some things we need
+use core::fmt::Write;
+use embedded_hal::delay::DelayNs;
+use hal::clocks::Clock;
+use hal::fugit::RateExtU32;
+
+// UART related types
+use hal::uart::{DataBits, StopBits, UartConfig, ValidatedPinRx, ValidatedPinTx};
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then writes to the UART in
+/// an infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ let uart0_pins = (
+ // UART TX (characters sent from rp235x) on pin 4 (GPIO2) in Aux mode
+ pins.gpio2.into_function(),
+ // UART RX (characters received by rp235x) on pin 5 (GPIO3) in Aux mode
+ pins.gpio3.into_function(),
+ );
+ let mut uart0 = hal::uart::UartPeripheral::new(pac.UART0, uart0_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+
+ // Set UART1 up with some dynamic pins
+
+ // UART TX (characters sent from rp235x) on pin 6 (GPIO4)
+ //
+ // We erase the type-state and make a dynamically typed pin. this is useful
+ // if you want to store it in a struct, or pass it as an argument to a
+ // library.
+ let mut uart1_tx = pins
+ .gpio4
+ .reconfigure::()
+ .into_dyn_pin();
+ // try and put it into UART mode (the type of the variable doesn't change)
+ if uart1_tx.try_set_function(gpio::DynFunction::Uart).is_err() {
+ panic!("Can't set pin as UART")
+ }
+ // wrap it, to prove to the UartPeripheral that it *is* in Uart mode
+ let Ok(uart1_tx) = ValidatedPinTx::validate(uart1_tx, &pac.UART1) else {
+ panic!("Can't use pin for UART 1 TX")
+ };
+
+ // UART RX (characters received by rp235x) on pin 7 (GPIO5)
+ //
+ // We erase the type-state and make a dynamically typed pin. this is useful
+ // if you want to store it in a struct, or pass it as an argument to a
+ // library.
+ let mut uart1_rx = pins
+ .gpio5
+ .reconfigure::()
+ .into_dyn_pin();
+ // try and put it into UART mode
+ if uart1_rx.try_set_function(gpio::DynFunction::Uart).is_err() {
+ panic!("Can't set pin as UART")
+ }
+ // wrap it, to prove to the UartPeripheral that it *is* in Uart mode
+ let Ok(uart1_rx) = ValidatedPinRx::validate(uart1_rx, &pac.UART1) else {
+ panic!("Can't use pin for UART 1 RX")
+ };
+ // make a UART with our dynamic pin types
+ let uart1_pins = (uart1_tx, uart1_rx);
+ let mut uart1 = hal::uart::UartPeripheral::new(pac.UART1, uart1_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+
+ uart0.write_full_blocking(b"UART example on UART0\r\n");
+ uart1.write_full_blocking(b"UART example on UART1\r\n");
+
+ let mut value = 0u32;
+ loop {
+ writeln!(uart0, "UART0 says value: {value:02}\r").unwrap();
+ writeln!(uart1, "UART1 says value: {value:02}\r").unwrap();
+ delay.delay_ms(1000);
+ value += 1
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"UART Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/uart_dma.rs b/rp235x-hal-examples/src/bin/uart_dma.rs
new file mode 100644
index 000000000..e6b603c1c
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/uart_dma.rs
@@ -0,0 +1,142 @@
+//! # UART DMA Example
+//!
+//! This application demonstrates how to use the UART peripheral with the
+//! DMA controller.
+//!
+//! It may need to be adapted to your particular board layout and/or pin
+//! assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+use hal::singleton;
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::delay::DelayNs;
+use hal::clocks::Clock;
+use hal::dma::DMAExt;
+use hal::fugit::RateExtU32;
+
+// UART related types
+use hal::uart::{DataBits, StopBits, UartConfig};
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then writes to the UART in
+/// an infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ let uart_pins = (
+ // UART TX (characters sent from rp235x) on pin 1 (GPIO0)
+ pins.gpio0.into_function(),
+ // UART RX (characters received by rp235x) on pin 2 (GPIO1)
+ pins.gpio1.into_function(),
+ );
+ let uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(115200.Hz(), DataBits::Eight, None, StopBits::One),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ // Initialize DMA.
+ let dma = pac.DMA.split(&mut pac.RESETS);
+
+ uart.write_full_blocking(b"\r\n\r\nUART DMA echo example\r\n\r\n");
+
+ // In order to use DMA we need to split the UART into a RX (receive) and TX (transmit) pair
+ let (rx, tx) = uart.split();
+
+ // We can still write to the tx side of the UART after splitting
+ tx.write_full_blocking(b"Regular UART write\r\n");
+
+ // And we can DMA from a buffer into the UART
+ let teststring = b"DMA UART write\r\n";
+ let tx_transfer = hal::dma::single_buffer::Config::new(dma.ch0, teststring, tx).start();
+
+ // Wait for the DMA transfer to finish so we can reuse the tx and the dma channel
+ let (ch0, _teststring, tx) = tx_transfer.wait();
+
+ // Let's test DMA RX into a buffer.
+ tx.write_full_blocking(b"Waiting for you to type 5 letters...\r\n");
+ let rx_buf = singleton!(: [u8; 5] = [0; 5]).unwrap();
+ let rx_transfer = hal::dma::single_buffer::Config::new(ch0, rx, rx_buf).start();
+ let (ch0, rx, rx_buf) = rx_transfer.wait();
+
+ // Echo back the 5 characters the user typed
+ tx.write_full_blocking(b"You wrote \"");
+ tx.write_full_blocking(rx_buf);
+ tx.write_full_blocking(b"\"\r\n");
+
+ // Now just keep echoing anything that is received back out of TX
+ tx.write_full_blocking(b"Now echoing any character you write...\r\n");
+ let _tx_transfer = hal::dma::single_buffer::Config::new(ch0, rx, tx).start();
+
+ loop {
+ // everything should be handled by DMA, nothing else to do
+ delay.delay_ms(1000);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"UART DMA Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/uart_loopback.rs b/rp235x-hal-examples/src/bin/uart_loopback.rs
new file mode 100644
index 000000000..8e4a93050
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/uart_loopback.rs
@@ -0,0 +1,428 @@
+//! # UART Loopback Example
+//!
+//! This application tests handling UART errors.
+//!
+//! It may need to be adapted to your particular board layout and/or pin
+//! assignment. We assume you have connected GP0 to a TTL UART on your host
+//! computer at 115200 baud. We assume that GP1 is connected to GP4, which is
+//! our UART loopback connection.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use core::fmt::Write;
+use embedded_hal::delay::DelayNs;
+use hal::clocks::Clock;
+use hal::fugit::RateExtU32;
+
+// UART related types
+use hal::uart::{DataBits, Parity, StopBits, UartConfig};
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Some sample text we can send.
+///
+/// It's the same length as the UART FIFO, deliberately.
+static SAMPLE32: [u8; 32] = *b"abcdefghijklmnopqrstuvwxyz012345";
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then writes to the UART in
+/// an infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ let uart0_pins = (
+ // UART TX (characters sent from RP2350)
+ pins.gpio0.into_function(),
+ // UART RX (characters received by RP2350)
+ pins.gpio1.into_pull_up_input().into_function(),
+ );
+ let mut uart0 = hal::uart::UartPeripheral::new(pac.UART0, uart0_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Even),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart0.set_fifos(true);
+
+ let uart1_pins = (
+ // UART TX (characters sent from RP2350)
+ pins.gpio4.into_function(),
+ // UART RX (characters received by RP2350)
+ pins.gpio5.into_pull_up_input().into_function(),
+ );
+ let mut uart1 = hal::uart::UartPeripheral::new(pac.UART1, uart1_pins, &mut pac.RESETS)
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Even),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart1.set_fifos(true);
+
+ let mut buffer = [0u8; 128];
+
+ // ======================================================================
+ // Single byte read/write
+ // ======================================================================
+
+ let sample = &SAMPLE32[0..1];
+ _ = writeln!(uart0, "** Testing single byte write/read");
+ uart1.write_full_blocking(sample);
+ delay.delay_ms(100);
+ uart0
+ .read_full_blocking(&mut buffer[0..sample.len()])
+ .unwrap();
+ if &buffer[0..sample.len()] != sample {
+ _ = writeln!(
+ uart0,
+ "Failed:\n{:02x?} !=\n{:02x?}",
+ &buffer[0..sample.len()],
+ sample
+ );
+ panic!("Test failed");
+ }
+ _ = writeln!(uart0, "Tested OK");
+
+ // ======================================================================
+ // FIFO backed read/write
+ // ======================================================================
+
+ let sample = &SAMPLE32[..];
+ _ = writeln!(uart0, "** Testing FIFO write/read");
+ uart1.write_full_blocking(sample);
+ delay.delay_ms(100);
+ uart0
+ .read_full_blocking(&mut buffer[0..sample.len()])
+ .unwrap();
+ if &buffer[0..sample.len()] != sample {
+ _ = writeln!(
+ uart0,
+ "Failed:\n{:02x?} !=\n{:02x?}",
+ &buffer[0..sample.len()],
+ sample
+ );
+ panic!("Test failed");
+ }
+ _ = writeln!(uart0, "Tested OK");
+
+ // ======================================================================
+ // FIFO overflow read/write
+ //
+ // Note: The Arm Primecell PL022 UART that Raspberry Pi uses has a 32-byte
+ // FIFO. We're about to overflow that FIFO.
+ // ======================================================================
+
+ let sample = &SAMPLE32[..];
+ _ = writeln!(uart0, "** Testing FIFO overflow write/read");
+ _ = writeln!(uart0, "Sending {} bytes...", sample.len() + 1);
+ // send 32 bytes to the receiving FIFO
+ uart1.write_full_blocking(sample);
+ // Now send one more byte to overflow the receiving FIFO. This byte is lost
+ // to the wind.
+ //
+ // NB: It fits into the TX FIFO because this is a 'blocking' call that
+ // waited for FIFO space.
+ uart1.write_full_blocking(&[0x00]);
+ // Let the TX FIFO drain.
+ delay.delay_ms(100);
+ // the first 32 bytes should read fine
+ uart0
+ .read_full_blocking(&mut buffer[0..sample.len()])
+ .unwrap();
+ if &buffer[0..sample.len()] != sample {
+ _ = writeln!(
+ uart0,
+ "Failed:\n{:02x?} !=\n{:02x?}",
+ &buffer[0..sample.len()],
+ sample
+ );
+ panic!("Test failed");
+ }
+ _ = writeln!(uart0, "Got first 32 bytes OK...");
+
+ _ = writeln!(
+ uart0,
+ "I now want to see Overrun([]), WouldBlock, WouldBlock"
+ );
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+ // Now send two more bytes - the first will also be flagged with an overrun error.
+ _ = writeln!(uart0, "Sending two more bytes...");
+ uart1.write_full_blocking(&[0x01, 0x02]);
+ // let them transfer over
+ delay.delay_ms(100);
+ // annoyingly we see the overrun error again, then our data
+ _ = writeln!(uart0, "I want to see Overrun([1]), Ok(1), WouldBlock");
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+ _ = writeln!(uart0, "RX: {:?}", uart0.read_raw(&mut buffer[..]));
+
+ // ======================================================================
+ // FIFO read/write with parity error
+ // ======================================================================
+
+ _ = writeln!(uart0, "** Testing FIFO read with parity errors");
+ // Send three bytes with correct parity
+ uart1.write_full_blocking(&[0x00, 0x01, 0x02]);
+ delay.delay_ms(100);
+ // send one with bad settings
+ uart1 = uart1
+ .disable()
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Odd),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart1.write_full_blocking(&[0x03]);
+ delay.delay_ms(100);
+ // send three more with good parity
+ uart1 = uart1
+ .disable()
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Even),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart1.write_full_blocking(&[0x04, 0x05, 0x06]);
+ delay.delay_ms(100);
+
+ _ = writeln!(uart0, "I want to see Parity error [0, 1, 2]");
+ match uart0.read_raw(&mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+ _ = writeln!(uart0, "I want to see RX: [4, 5, 6]");
+ match uart0.read_raw(&mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+ _ = writeln!(uart0, "I want to see WouldBlock");
+ match uart0.read_raw(&mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+
+ // ======================================================================
+ // FIFO backed read/write with embedded_io traits.
+ // ======================================================================
+
+ let sample = &SAMPLE32[..];
+ _ = writeln!(uart0, "** Testing FIFO write/read with embedded-io");
+
+ embedded_io::Write::write_all(&mut uart1, sample).unwrap();
+ delay.delay_ms(100);
+
+ embedded_io::Read::read_exact(&mut uart0, &mut buffer[0..sample.len()]).unwrap();
+ if &buffer[0..sample.len()] != sample {
+ _ = writeln!(
+ uart0,
+ "Failed:\n{:02x?} !=\n{:02x?}",
+ &buffer[0..sample.len()],
+ sample
+ );
+ panic!("Test failed");
+ }
+ _ = writeln!(uart0, "Tested OK");
+
+ // ======================================================================
+ // FIFO read/write with parity error using embedded-io
+ // ======================================================================
+
+ _ = writeln!(uart0, "** Testing FIFO read with parity errors");
+ // Send three bytes with correct parity
+ uart1.write_full_blocking(&[0x00, 0x01, 0x02]);
+ delay.delay_ms(100);
+ // send one with bad settings
+ uart1 = uart1
+ .disable()
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Odd),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart1.write_full_blocking(&[0x03]);
+ delay.delay_ms(100);
+ // send three more with good parity
+ uart1 = uart1
+ .disable()
+ .enable(
+ UartConfig::new(
+ 115200.Hz(),
+ DataBits::Eight,
+ Some(Parity::Even),
+ StopBits::One,
+ ),
+ clocks.peripheral_clock.freq(),
+ )
+ .unwrap();
+ uart1.write_full_blocking(&[0x04, 0x05, 0x06]);
+ delay.delay_ms(100);
+
+ _ = writeln!(uart0, "I want to see RX: [0, 1, 2]");
+ match embedded_io::Read::read(&mut uart0, &mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+ _ = writeln!(uart0, "I want to see ParityError");
+ match embedded_io::Read::read(&mut uart0, &mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+
+ _ = writeln!(uart0, "I want to see RX: [4, 5, 6]");
+ match embedded_io::Read::read(&mut uart0, &mut buffer[..]) {
+ Ok(n) => {
+ _ = writeln!(uart0, "RX: {:?}", &buffer[0..n]);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+
+ _ = writeln!(uart0, "I want to see RX ready: false");
+ match embedded_io::ReadReady::read_ready(&mut uart0) {
+ Ok(ready) => {
+ _ = writeln!(uart0, "RX ready: {}", ready);
+ }
+ Err(e) => {
+ _ = writeln!(uart0, "RXE: {:?}", e);
+ }
+ }
+
+ // ======================================================================
+ // Tests complete
+ // ======================================================================
+
+ _ = writeln!(uart0, "Tests complete. Review output for correctness.");
+
+ // Do a reset into the bootloader.
+ hal::reboot::reboot(
+ hal::reboot::RebootKind::BootSel {
+ msd_disabled: false,
+ picoboot_disabled: false,
+ },
+ hal::reboot::RebootArch::Normal,
+ );
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"UART Loopback Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+#[panic_handler]
+fn panic(_: &core::panic::PanicInfo) -> ! {
+ // wait about 1s for UART FIFOs to flush
+ for _ in 0..75_000_000 {
+ hal::arch::nop();
+ }
+ // Do a reset into the bootloader.
+ hal::reboot::reboot(
+ hal::reboot::RebootKind::BootSel {
+ msd_disabled: false,
+ picoboot_disabled: false,
+ },
+ hal::reboot::RebootArch::Normal,
+ );
+}
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/usb.rs b/rp235x-hal-examples/src/bin/usb.rs
new file mode 100644
index 000000000..51e93a417
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/usb.rs
@@ -0,0 +1,153 @@
+//! # Pico USB Serial Example
+//!
+//! Creates a USB Serial device on a Pico board, with the USB driver running in
+//! the main thread.
+//!
+//! This will create a USB Serial device echoing anything it receives. Incoming
+//! ASCII characters are converted to upercase, so you can tell it is working
+//! and not just local-echo!
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use core::fmt::Write;
+use heapless::String;
+
+// USB Device support
+use usb_device::{class_prelude::*, prelude::*};
+
+// USB Communications Class Device support
+use usbd_serial::SerialPort;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then writes to the UART in
+/// an infinite loop.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ let timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // Set up the USB driver
+ let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
+ pac.USB,
+ pac.USB_DPRAM,
+ clocks.usb_clock,
+ true,
+ &mut pac.RESETS,
+ ));
+
+ // Set up the USB Communications Class Device driver
+ let mut serial = SerialPort::new(&usb_bus);
+
+ // Create a USB device with a fake VID and PID
+ let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
+ .strings(&[StringDescriptors::default()
+ .manufacturer("Fake company")
+ .product("Serial port")
+ .serial_number("TEST")])
+ .unwrap()
+ .device_class(2) // from: https://www.usb.org/defined-class-codes
+ .build();
+
+ let mut said_hello = false;
+ loop {
+ // A welcome message at the beginning
+ if !said_hello && timer.get_counter().ticks() >= 2_000_000 {
+ said_hello = true;
+ let _ = serial.write(b"Hello, World!\r\n");
+
+ let time = timer.get_counter().ticks();
+ let mut text: String<64> = String::new();
+ writeln!(&mut text, "Current timer ticks: {}", time).unwrap();
+
+ // This only works reliably because the number of bytes written to
+ // the serial port is smaller than the buffers available to the USB
+ // peripheral. In general, the return value should be handled, so that
+ // bytes not transferred yet don't get lost.
+ let _ = serial.write(text.as_bytes());
+ }
+
+ // Check for new data
+ if usb_dev.poll(&mut [&mut serial]) {
+ let mut buf = [0u8; 64];
+ match serial.read(&mut buf) {
+ Err(_e) => {
+ // Do nothing
+ }
+ Ok(0) => {
+ // Do nothing
+ }
+ Ok(count) => {
+ // Convert to upper case
+ buf.iter_mut().take(count).for_each(|b| {
+ b.make_ascii_uppercase();
+ });
+ // Send back to the host
+ let mut wr_ptr = &buf[..count];
+ while !wr_ptr.is_empty() {
+ match serial.write(wr_ptr) {
+ Ok(len) => wr_ptr = &wr_ptr[len..],
+ // On error, just drop unwritten data.
+ // One possible error is Err(WouldBlock), meaning the USB
+ // write buffer is full.
+ Err(_) => break,
+ };
+ }
+ }
+ }
+ }
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"USB Serial Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/vector_table.rs b/rp235x-hal-examples/src/bin/vector_table.rs
new file mode 100644
index 000000000..f975c1966
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/vector_table.rs
@@ -0,0 +1,196 @@
+//! # RAM Vector Table example
+//!
+//! This application demonstrates how to create a new Interrupt Vector Table in RAM.
+//! To demonstrate the extra utility of this, we also replace an entry in the Vector Table
+//! with a new one.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use core::cell::RefCell;
+use critical_section::Mutex;
+use embedded_hal::delay::DelayNs;
+use embedded_hal::digital::StatefulOutputPin;
+use hal::fugit::MicrosDurationU32;
+use hal::timer::Alarm;
+
+use hal::pac::interrupt;
+use hal::vector_table::VectorTable;
+
+// Memory that will hold our vector table in RAM
+static mut RAM_VTABLE: VectorTable = VectorTable::new();
+
+// Give our LED and Alarm a type alias to make it easier to refer to them
+type LedAndAlarm = (
+ hal::gpio::Pin,
+ hal::timer::Alarm0,
+);
+
+// Place our LED and Alarm type in a static variable, so we can access it from interrupts
+static mut LED_AND_ALARM: Mutex>> = Mutex::new(RefCell::new(None));
+
+// Period that each of the alarms will be set for - 1 second and 300ms respectively
+const SLOW_BLINK_INTERVAL_US: MicrosDurationU32 = MicrosDurationU32::secs(1);
+const FAST_BLINK_INTERVAL_US: MicrosDurationU32 = MicrosDurationU32::millis(300);
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then toggles a GPIO pin in
+/// an infinite loop. If there is an LED connected to that pin, it will blink.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Need to make a reference to the Peripheral Base at this scope to avoid confusing the borrow checker
+ let ppb = &mut pac.PPB;
+ unsafe {
+ // Copy the vector table that cortex_m_rt produced into the RAM vector table
+ RAM_VTABLE.init(ppb);
+ // Replace the function that is called on Alarm0 interrupts with a new one
+ RAM_VTABLE.register_handler(
+ hal::pac::Interrupt::TIMER0_IRQ_0 as usize,
+ timer_irq0_replacement,
+ );
+ }
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .ok()
+ .unwrap();
+
+ // Create simple delay
+ let mut delay = hal::Timer::new_timer1(pac.TIMER1, &mut pac.RESETS, &clocks);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Configure GPIO25 as an output
+ let led_pin = pins.gpio25.into_push_pull_output();
+
+ let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+ critical_section::with(|cs| {
+ let mut alarm = timer.alarm_0().unwrap();
+ // Schedule an alarm in 1 second
+ let _ = alarm.schedule(SLOW_BLINK_INTERVAL_US);
+ // Enable generating an interrupt on alarm
+ alarm.enable_interrupt();
+ // Move alarm into ALARM, so that it can be accessed from interrupts
+ unsafe {
+ LED_AND_ALARM.borrow(cs).replace(Some((led_pin, alarm)));
+ }
+ });
+ // Unmask the timer0 IRQ so that it will generate an interrupt
+ unsafe {
+ cortex_m::peripheral::NVIC::unmask(hal::pac::Interrupt::TIMER0_IRQ_0);
+ }
+
+ // After 5 seconds, switch to our modified vector rable
+ delay.delay_ms(5000);
+ unsafe {
+ critical_section::with(|_| {
+ RAM_VTABLE.activate(ppb);
+ });
+ }
+
+ loop {
+ // Wait for an interrupt to fire before doing any more work
+ hal::arch::wfi();
+ }
+}
+
+// Regular interrupt handler for Alarm0. The `interrupt` macro will perform some transformations to ensure
+// that this interrupt entry ends up in the vector table.
+#[interrupt]
+fn TIMER0_IRQ_0() {
+ critical_section::with(|cs| {
+ // Temporarily take our LED_AND_ALARM
+ let ledalarm = unsafe { LED_AND_ALARM.borrow(cs).take() };
+ if let Some((mut led, mut alarm)) = ledalarm {
+ // Clear the alarm interrupt or this interrupt service routine will keep firing
+ alarm.clear_interrupt();
+ // Schedule a new alarm after SLOW_BLINK_INTERVAL_US have passed (1 second)
+ let _ = alarm.schedule(SLOW_BLINK_INTERVAL_US);
+ // Blink the LED so we know we hit this interrupt
+ led.toggle().unwrap();
+ // Return LED_AND_ALARM into our static variable
+ unsafe {
+ LED_AND_ALARM
+ .borrow(cs)
+ .replace_with(|_| Some((led, alarm)));
+ }
+ }
+ });
+}
+
+// This is the function we will use to replace TIMER_IRQ_0 in our RAM Vector Table
+extern "C" fn timer_irq0_replacement() {
+ critical_section::with(|cs| {
+ let ledalarm = unsafe { LED_AND_ALARM.borrow(cs).take() };
+ if let Some((mut led, mut alarm)) = ledalarm {
+ // Clear the alarm interrupt or this interrupt service routine will keep firing
+ alarm.clear_interrupt();
+ // Schedule a new alarm after FAST_BLINK_INTERVAL_US have passed (300 milliseconds)
+ let _ = alarm.schedule(FAST_BLINK_INTERVAL_US);
+ led.toggle().unwrap();
+ // Return LED_AND_ALARM into our static variable
+ unsafe {
+ LED_AND_ALARM
+ .borrow(cs)
+ .replace_with(|_| Some((led, alarm)));
+ }
+ }
+ });
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"Interrupt Vector Table Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-examples/src/bin/watchdog.rs b/rp235x-hal-examples/src/bin/watchdog.rs
new file mode 100644
index 000000000..51e186bed
--- /dev/null
+++ b/rp235x-hal-examples/src/bin/watchdog.rs
@@ -0,0 +1,114 @@
+//! # Watchdog Example
+//!
+//! This application demonstrates how to use the rp235x Watchdog.
+//!
+//! It may need to be adapted to your particular board layout and/or pin assignment.
+//!
+//! See the `Cargo.toml` file for Copyright and license details.
+
+#![no_std]
+#![no_main]
+
+// Ensure we halt the program on panic (if we don't mention this crate it won't
+// be linked)
+use panic_halt as _;
+
+// Alias for our HAL crate
+use rp235x_hal as hal;
+
+// Some things we need
+use embedded_hal::delay::DelayNs;
+use embedded_hal::digital::OutputPin;
+use hal::fugit::ExtU32;
+
+/// Tell the Boot ROM about our application
+#[link_section = ".start_block"]
+#[used]
+pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
+
+/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
+/// Adjust if your board has a different frequency
+const XTAL_FREQ_HZ: u32 = 12_000_000u32;
+
+/// Entry point to our bare-metal application.
+///
+/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
+/// as soon as all global variables and the spinlock are initialised.
+///
+/// The function configures the rp235x peripherals, then toggles a GPIO pin in
+/// an infinite loop. After a period of time, the watchdog will kick in to reset
+/// the CPU.
+#[hal::entry]
+fn main() -> ! {
+ // Grab our singleton objects
+ let mut pac = hal::pac::Peripherals::take().unwrap();
+
+ // Set up the watchdog driver - needed by the clock setup code
+ let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
+
+ // Configure the clocks
+ let clocks = hal::clocks::init_clocks_and_plls(
+ XTAL_FREQ_HZ,
+ pac.XOSC,
+ pac.CLOCKS,
+ pac.PLL_SYS,
+ pac.PLL_USB,
+ &mut pac.RESETS,
+ &mut watchdog,
+ )
+ .unwrap();
+
+ let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
+
+ // The single-cycle I/O block controls our GPIO pins
+ let sio = hal::Sio::new(pac.SIO);
+
+ // Set the pins to their default state
+ let pins = hal::gpio::Pins::new(
+ pac.IO_BANK0,
+ pac.PADS_BANK0,
+ sio.gpio_bank0,
+ &mut pac.RESETS,
+ );
+
+ // Configure an LED so we can show the current state of the watchdog
+ let mut led_pin = pins.gpio25.into_push_pull_output();
+
+ // Set the LED high for 2 seconds so we know when we're about to start the watchdog
+ led_pin.set_high().unwrap();
+ delay.delay_ms(2000);
+
+ // Set to watchdog to reset if it's not reloaded within 1.05 seconds, and start it
+ watchdog.start(1_050.millis());
+
+ // Blink once a second for 5 seconds, refreshing the watchdog timer once a second to avoid a reset
+ for _ in 1..=5 {
+ led_pin.set_low().unwrap();
+ delay.delay_ms(500);
+ led_pin.set_high().unwrap();
+ delay.delay_ms(500);
+ watchdog.feed();
+ }
+
+ // Blink 5 times per second, not feeding the watchdog.
+ // The processor should reset in 1.05 seconds, or 5 blinks time
+ loop {
+ led_pin.set_low().unwrap();
+ delay.delay_ms(100);
+ led_pin.set_high().unwrap();
+ delay.delay_ms(100);
+ }
+}
+
+/// Program metadata for `picotool info`
+#[link_section = ".bi_entries"]
+#[used]
+pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
+ hal::binary_info::rp_cargo_bin_name!(),
+ hal::binary_info::rp_cargo_version!(),
+ hal::binary_info::rp_program_description!(c"Watchdog Example"),
+ hal::binary_info::rp_cargo_homepage_url!(),
+ hal::binary_info::rp_program_build_attribute!(),
+];
+
+// End of file
diff --git a/rp235x-hal-macros/.gitignore b/rp235x-hal-macros/.gitignore
new file mode 100644
index 000000000..ff47c2d77
--- /dev/null
+++ b/rp235x-hal-macros/.gitignore
@@ -0,0 +1,11 @@
+# Generated by Cargo
+# will have compiled files and executables
+debug/
+target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
diff --git a/rp235x-hal-macros/Cargo.toml b/rp235x-hal-macros/Cargo.toml
new file mode 100644
index 000000000..6bef22073
--- /dev/null
+++ b/rp235x-hal-macros/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+description = "Macros used by rp235x-hal"
+license = "MIT OR Apache-2.0"
+name = "rp235x-hal-macros"
+readme = "README.md"
+version = "0.1.0"
+edition = "2021"
+repository = "https://github.com/rp-rs/rp-hal"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+quote = "1.0"
+proc-macro2 = "1.0"
+
+[dependencies.syn]
+features = ["full"]
+version = "2.0"
+
diff --git a/rp235x-hal-macros/LICENSE-APACHE b/rp235x-hal-macros/LICENSE-APACHE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/rp235x-hal-macros/LICENSE-APACHE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/rp235x-hal-macros/LICENSE-MIT b/rp235x-hal-macros/LICENSE-MIT
new file mode 100644
index 000000000..6e052e35b
--- /dev/null
+++ b/rp235x-hal-macros/LICENSE-MIT
@@ -0,0 +1,19 @@
+Copyright (c) 2021-2024 The rp-rs Developers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/rp235x-hal-macros/NOTICE b/rp235x-hal-macros/NOTICE
new file mode 100644
index 000000000..790ecb167
--- /dev/null
+++ b/rp235x-hal-macros/NOTICE
@@ -0,0 +1,3 @@
+Copyright (c) 2021-2024 The rp-rs Developers
+
+Originally published at https://github.com/rp-rs/rp-hal
diff --git a/rp235x-hal-macros/README.md b/rp235x-hal-macros/README.md
new file mode 100644
index 000000000..e07be925d
--- /dev/null
+++ b/rp235x-hal-macros/README.md
@@ -0,0 +1,22 @@
+# `rp235x-hal-macros`
+
+Macros used by rp235x-hal.
+
+## Entry macro
+
+Extension of the `cortex-m-rt` `#[entry]` with rp235x specific initialization code.
+
+Currently, it just unlocks all spinlocks before calling the entry function, and
+sets up the Double Precision and GPIO Co-Procesors.
+
+# License
+
+The contents of this repository are dual-licensed under the _MIT OR Apache 2.0_
+License. That means you can choose either the MIT license or the Apache 2.0
+license when you re-use this code. See [`LICENSE-MIT`](./LICENSE-MIT) or
+[`LICENSE-APACHE`](./LICENSE-APACHE) for more information on each specific
+license. Our Apache 2.0 notices can be found in [`NOTICE`](./NOTICE).
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
diff --git a/rp235x-hal-macros/src/lib.rs b/rp235x-hal-macros/src/lib.rs
new file mode 100644
index 000000000..ad2a255f0
--- /dev/null
+++ b/rp235x-hal-macros/src/lib.rs
@@ -0,0 +1,76 @@
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::quote;
+use syn::{parse, parse_macro_input, Item, ItemFn, Stmt};
+
+#[proc_macro_attribute]
+pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
+ let mut f = parse_macro_input!(input as ItemFn);
+
+ if !args.is_empty() {
+ return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
+ .to_compile_error()
+ .into();
+ }
+
+ let clear_locks: TokenStream = quote!(unsafe {
+ const SIO_BASE: u32 = 0xd0000000;
+ const SPINLOCK0_PTR: *mut u32 = (SIO_BASE + 0x100) as *mut u32;
+ const SPINLOCK_COUNT: usize = 32;
+ for i in 0..SPINLOCK_COUNT {
+ SPINLOCK0_PTR.wrapping_add(i).write_volatile(1);
+ }
+ #[cfg(target_arch = "arm")]
+ {
+ // Enable the Double-Co-Pro and the GPIO Co-Pro in the CPACR register.
+ // We have to do this early, before there's a chance we might call
+ // any accelerated functions.
+ const SCB_CPACR_PTR: *mut u32 = 0xE000_ED88 as *mut u32;
+ const SCB_CPACR_FULL_ACCESS: u32 = 0b11;
+ // Do a R-M-W, because the FPU enable is here and that's already been enabled
+ let mut temp = SCB_CPACR_PTR.read_volatile();
+ // DCP Co-Pro is 4, two-bits per entry
+ temp |= SCB_CPACR_FULL_ACCESS << (4 * 2);
+ // GPIO Co-Pro is 0, two-bits per entry
+ temp |= SCB_CPACR_FULL_ACCESS << (0 * 2);
+ SCB_CPACR_PTR.write_volatile(temp);
+ // Don't allow any DCP code to be moved before this fence.
+ core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
+ }
+ })
+ .into();
+ let clear_locks = parse_macro_input!(clear_locks as Stmt);
+
+ // statics must stay first so cortex_m_rt::entry still finds them
+ let stmts = insert_after_static(f.block.stmts, clear_locks);
+ f.block.stmts = stmts;
+
+ quote!(
+ #[rp235x_hal::arch_entry]
+ #f
+ )
+ .into()
+}
+
+/// Insert new statements after initial block of statics
+fn insert_after_static(stmts: impl IntoIterator- , insert: Stmt) -> Vec {
+ let mut istmts = stmts.into_iter();
+ let mut stmts = vec![];
+ for stmt in istmts.by_ref() {
+ match stmt {
+ Stmt::Item(Item::Static(var)) => {
+ stmts.push(Stmt::Item(Item::Static(var)));
+ }
+ _ => {
+ stmts.push(insert);
+ stmts.push(stmt);
+ break;
+ }
+ }
+ }
+ stmts.extend(istmts);
+
+ stmts
+}
diff --git a/rp235x-hal/.gitignore b/rp235x-hal/.gitignore
new file mode 100644
index 000000000..ff47c2d77
--- /dev/null
+++ b/rp235x-hal/.gitignore
@@ -0,0 +1,11 @@
+# Generated by Cargo
+# will have compiled files and executables
+debug/
+target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
diff --git a/rp235x-hal/CHANGELOG.md b/rp235x-hal/CHANGELOG.md
new file mode 100644
index 000000000..65fab2dbd
--- /dev/null
+++ b/rp235x-hal/CHANGELOG.md
@@ -0,0 +1,13 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+### Changed
+
+- First version
+
diff --git a/rp235x-hal/Cargo.toml b/rp235x-hal/Cargo.toml
new file mode 100644
index 000000000..a4ba69722
--- /dev/null
+++ b/rp235x-hal/Cargo.toml
@@ -0,0 +1,101 @@
+[package]
+authors = ["The rp-rs Developers"]
+categories = ["embedded", "hardware-support", "no-std", "no-std::no-alloc"]
+description = "A Rust Embeded-HAL impl for the RP2350 microcontroller"
+edition = "2021"
+homepage = "https://github.com/rp-rs/rp-hal"
+keywords = ["embedded", "hal", "raspberry-pi", "rp2350", "embedded-hal"]
+license = "MIT OR Apache-2.0"
+name = "rp235x-hal"
+repository = "https://github.com/rp-rs/rp-hal"
+rust-version = "1.77"
+version = "0.2.0"
+
+[package.metadata.docs.rs]
+features = ["rt", "defmt", "rtic-monotonic"]
+targets = ["thumbv8m.main-none-eabihf"]
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+# Non-optional dependencies. Keep these sorted by name.
+bitfield = "0.14.0"
+critical-section = "1.0.0"
+embedded-dma = "0.2.0"
+embedded-hal = "1.0.0"
+embedded-hal-async = "1.0.0"
+embedded-hal-nb = "1.0.0"
+embedded-io = "0.6.1"
+embedded_hal_0_2 = {package = "embedded-hal", version = "0.2.5", features = ["unproven"]}
+frunk = {version = "0.4.1", default-features = false}
+fugit = "0.3.6"
+gcd = ">=2.1,<3.0"
+itertools = {version = "0.13.0", default-features = false}
+nb = "1.0"
+paste = "1.0"
+pio = "0.2.0"
+rand_core = "0.6.3"
+rp-binary-info = {version = "0.1.0", path = "../rp-binary-info"}
+rp-hal-common = {version = "0.1.0", path = "../rp-hal-common"}
+rp235x-hal-macros = {version = "0.1.0", path = "../rp235x-hal-macros"}
+rp235x-pac = {version = "0.1.0", features = ["critical-section", "rt"]}
+sha2-const-stable = "0.1"
+usb-device = "0.3.2"
+vcell = "0.1"
+void = {version = "1.0.2", default-features = false}
+
+# Optional dependencies. Keep these sorted by name.
+defmt = {version = ">=0.2.0, <0.4", optional = true}
+i2c-write-iter = {version = "1.0.0", features = ["async"], optional = true}
+rtic-monotonic = {version = "1.0.0", optional = true}
+
+[target.'thumbv8m.main-none-eabihf'.dependencies]
+cortex-m = "0.7.2"
+cortex-m-rt = "0.7"
+
+[target.riscv32imac-unknown-none-elf.dependencies]
+riscv = "0.11"
+riscv-rt = "0.12"
+
+[dev-dependencies]
+# Non-optional dependencies. Keep these sorted by name.
+pio-proc = "0.2.0"
+rand = {version = "0.8.5", default-features = false}
+
+# Optional dependencies. Keep these sorted by name.
+# None
+
+[features]
+# Enable the boot-up code from the arch runtime
+rt = ["rp235x-pac/rt"]
+
+# Memoize(cache) ROM function pointers on first use to improve performance
+rom-func-cache = []
+
+# critical section that is safe for multicore use
+critical-section-impl = ["critical-section/restore-state-u8"]
+
+# Implement `defmt::Format` for several types.
+defmt = ["dep:defmt"]
+
+# Implement `rtic_monotonic::Monotonic` based on the RP235x timer peripheral
+rtic-monotonic = ["dep:rtic-monotonic"]
+
+# Implement `i2c-write-iter` traits
+i2c-write-iter = ["dep:i2c-write-iter"]
+
+# Use DCP to accelerate some (but not all) f64 operations.
+#
+# If you really want to save every last micro-amp, and know you aren't doing any
+# f64 operations, you can disable this feature (which is on by default) and then
+# manually disable the DCP by either clearing the bits we set for you in the
+# CPACR register, or changing the #[entry] macro to not set those bits.
+#
+# Almost everyone will want this on, but we let the BSPs make that choice.
+dcp-fast-f64 = []
+
+# Add a binary-info header block containing picotool-compatible metadata.
+#
+# Requires 'rt' so that the vector table is correctly sized and therefore the
+# header is within reach of picotool.
+binary-info = ["rt", "rp-binary-info/binary-info"]
diff --git a/rp235x-hal/README.md b/rp235x-hal/README.md
new file mode 100644
index 000000000..1e3318f0d
--- /dev/null
+++ b/rp235x-hal/README.md
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
rp-hal
+
+
+ High-level Rust drivers for the Raspberry Silicon RP2350 Microcontroller
+
+ Explore the API docs »
+
+
+ View Demos
+ ·
+ Report a Bug
+ ·
+ Chat on Matrix
+
+
+
+
+
+
+
+ Table of Contents
+
+ - Introduction
+ - Getting Started
+ - Roadmap
+ - Contributing
+ - License
+ - Contact
+ - Acknowledgements
+
+
+
+
+## Introduction
+
+This is the `rp235x-hal` package - a library crate of high-level Rust drivers
+for the Raspberry Silicon RP2350 microcontroller, along with a collection of
+non-board specific example programs for you to study. You should use this crate
+in your application if you want to write code for the RP2350 microcontroller.
+The *HAL* in the name standards for *Hardware Abstraction Layer*, and comes from
+the fact that many of the drivers included implement the generic
+hardware-abstraction interfaces defined in the Rust Embedded Working Group's
+[embedded-hal](https://github.com/rust-embedded/embedded-hal) crate.
+
+We also provide a series of [*Board Support Package* (BSP) crates][BSPs], which take
+this HAL crate and pre-configure the pins according to a specific PCB design. If
+you are using one of the supported boards, you should use one of those crates in
+preference, and return here to see documentation about specific peripherals on
+the RP2350 and how to use them. See the `boards` folder in
+https://github.com/rp-rs/rp-hal-boards/ for more details.
+
+[BSPs]: https://github.com/rp-rs/rp-hal-boards/
+
+Some of the source code herein refers to the "RP2350 Datasheet". This can be
+found at .
+
+
+## Getting Started
+
+To include this crate in your project, amend your `Cargo.toml` file to include
+
+```toml
+rp235x-hal = "*"
+```
+
+To obtain a copy of the source code (e.g. if you want to propose a bug-fix or
+new feature, or simply to study the code), run:
+
+```console
+$ git clone https://github.com/rp-rs/rp-hal.git
+```
+
+For details on how to program an RP2350 microcontroller, see the [top-level
+rp-hal README](https://github.com/rp-rs/rp-hal/).
+
+
+## Roadmap
+
+NOTE This HAL is under active development. As such, it is likely to remain
+volatile until a 1.0.0 release.
+
+See the [open issues](https://github.com/rp-rs/rp-hal/issues) for a list of
+proposed features (and known issues).
+
+### Implemented traits
+
+This crate aims to implement all traits from embedded-hal, both version
+0.2 and 1.0. They can be used at the same time, so you can upgrade drivers
+incrementally.
+
+
+## Contributing
+
+Contributions are what make the open source community such an amazing place to
+be learn, inspire, and create. Any contributions you make are **greatly
+appreciated**.
+
+1. Fork the Project
+2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
+3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
+4. Push to the Branch (`git push origin feature/AmazingFeature`)
+5. Open a Pull Request
+
+
+## License
+
+The contents of this repository are dual-licensed under the _MIT OR Apache 2.0_
+License. That means you can choose either the MIT license or the Apache 2.0
+license when you re-use this code. See [`LICENSE-MIT`](./LICENSE-MIT) or
+[`LICENSE-APACHE`](./LICENSE-APACHE) for more information on each specific
+license. Our Apache 2.0 notices can be found in [`NOTICE`](./NOTICE).
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
+
+
+## Contact
+
+* Project Link: [https://github.com/rp-rs/rp-hal/issues](https://github.com/rp-rs/rp-hal/issues)
+* Matrix: [#rp-rs:matrix.org](https://matrix.to/#/#rp-rs:matrix.org)
+
+
+## Acknowledgements
+
+* [Othneil Drew's README template](https://github.com/othneildrew)
diff --git a/rp235x-hal/src/adc.rs b/rp235x-hal/src/adc.rs
new file mode 100644
index 000000000..7401dd5f1
--- /dev/null
+++ b/rp235x-hal/src/adc.rs
@@ -0,0 +1,950 @@
+//! Analog-Digital Converter (ADC)
+//!
+//! See [Section 12.4](https://rptl.io/rp2350-datasheet#section_adc) of the datasheet for more details
+//!
+//! ## Usage
+//!
+//! Capture ADC reading from a pin:
+
+//! ```no_run
+//! // Embedded HAL 1.0.0 doesn't have an ADC trait, so use the one from 0.2
+//! use embedded_hal_0_2::adc::OneShot;
+//! use rp235x_hal::{self as hal, adc::Adc, adc::AdcPin, gpio::Pins, Sio};
+//! let mut peripherals = hal::pac::Peripherals::take().unwrap();
+//! let sio = Sio::new(peripherals.SIO);
+//! let pins = Pins::new(
+//! peripherals.IO_BANK0,
+//! peripherals.PADS_BANK0,
+//! sio.gpio_bank0,
+//! &mut peripherals.RESETS,
+//! );
+//! // Enable adc
+//! let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
+//! // Configure one of the pins as an ADC input
+//! let mut adc_pin_0 = AdcPin::new(pins.gpio26.into_floating_input()).unwrap();
+//! // Read the ADC counts from the ADC channel
+//! let pin_adc_counts: u16 = adc.read(&mut adc_pin_0).unwrap();
+//! ```
+//!
+//! Capture ADC reading from temperature sensor. Note that this needs conversion to be a real-world temperature.
+//!
+//! ```no_run
+//! // Embedded HAL 1.0.0 doesn't have an ADC trait, so use the one from 0.2
+//! use embedded_hal_0_2::adc::OneShot;
+//! use rp235x_hal::{self as hal, adc::Adc, gpio::Pins, Sio};
+//! let mut peripherals = hal::pac::Peripherals::take().unwrap();
+//! let sio = Sio::new(peripherals.SIO);
+//! let pins = Pins::new(
+//! peripherals.IO_BANK0,
+//! peripherals.PADS_BANK0,
+//! sio.gpio_bank0,
+//! &mut peripherals.RESETS,
+//! );
+//! // Enable adc
+//! let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
+//! // Enable the temperature sensor
+//! let mut temperature_sensor = adc.take_temp_sensor().unwrap();
+//! // Read the ADC counts from the ADC channel
+//! let temperature_adc_counts: u16 = adc.read(&mut temperature_sensor).unwrap();
+//! ```
+//!
+//! See [examples/adc.rs](https://github.com/rp-rs/rp-hal/tree/main/rp235x-hal-examples/src/bin/adc.rs) and
+//! [pimoroni_pico_explorer_showcase.rs](https://github.com/rp-rs/rp-hal-boards/tree/main/boards/pimoroni-pico-explorer/examples/pimoroni_pico_explorer_showcase.rs) for more complete examples
+//!
+//! ### Free running mode with FIFO
+//!
+//! In free-running mode the ADC automatically captures samples in regular intervals.
+//! The samples are written to a FIFO, from which they can be retrieved.
+//!
+//! ```no_run
+//! # use rp235x_hal::{self as hal, adc::Adc, gpio::Pins, pac, Sio};
+//! let mut peripherals = hal::pac::Peripherals::take().unwrap();
+//! let sio = Sio::new(peripherals.SIO);
+//! let pins = Pins::new(
+//! peripherals.IO_BANK0,
+//! peripherals.PADS_BANK0,
+//! sio.gpio_bank0,
+//! &mut peripherals.RESETS,
+//! );
+//! // Enable adc
+//! let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
+//! // Enable the temperature sensor
+//! let mut temperature_sensor = adc.take_temp_sensor().unwrap();
+//!
+//! // Configure & start capturing to the fifo:
+//! let mut fifo = adc
+//! .build_fifo()
+//! .clock_divider(0, 0) // sample as fast as possible (500ksps. This is the default)
+//! .set_channel(&mut temperature_sensor)
+//! .start();
+//!
+//! loop {
+//! if fifo.len() > 0 {
+//! // Read one captured ADC sample from the FIFO:
+//! let temperature_adc_counts: u16 = fifo.read();
+//! }
+//! }
+//! ```
+//! See [examples/adc_fifo_poll.rs](https://github.com/rp-rs/rp-hal/tree/main/rp235x-hal-examples/src/bin/adc_fifo_poll.rs) for a more complete example.
+//!
+//! ### Using DMA
+//!
+//! When the ADC is in free-running mode, it's possible to use DMA to transfer data from the FIFO elsewhere, without having to read the FIFO manually.
+//!
+//! This requires a number of steps:
+//! 1. Build an `AdcFifo`, with DMA enabled ([`AdcFifoBuilder::enable_dma`])
+//! 2. Use [`AdcFifoBuilder::prepare`] instead of [`AdcFifoBuilder::start`], so that the FIFO is created in `paused` state
+//! 3. Start a DMA transfer ([`dma::single_buffer::Transfer`], [`dma::double_buffer::Transfer`], ...), using the [`AdcFifo::dma_read_target`] as the source (`from` parameter)
+//! 4. Finally unpause the FIFO by calling [`AdcFifo::resume`], to start capturing
+//!
+//! Example:
+//! ```no_run
+//! use rp235x_hal::{self as hal, singleton, adc::Adc, gpio::Pins, pac, Sio, dma::{single_buffer, DMAExt}};
+//! let mut peripherals = hal::pac::Peripherals::take().unwrap();
+//! let sio = Sio::new(peripherals.SIO);
+//! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
+//! let dma = peripherals.DMA.split(&mut peripherals.RESETS);
+//! // Enable adc
+//! let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
+//! // Enable the temperature sensor
+//! let mut temperature_sensor = adc.take_temp_sensor().unwrap();
+//!
+//! // Configure & start capturing to the fifo:
+//! let mut fifo = adc.build_fifo()
+//! .clock_divider(0, 0) // sample as fast as possible (500ksps. This is the default)
+//! .set_channel(&mut temperature_sensor)
+//! .enable_dma()
+//! .prepare();
+//!
+//! // Set up a buffer, where the samples should be written:
+//! let buf = singleton!(: [u16; 500] = [0; 500]).unwrap();
+//!
+//! // Start DMA transfer
+//! let transfer = single_buffer::Config::new(dma.ch0, fifo.dma_read_target(), buf).start();
+//!
+//! // Resume the FIFO to start capturing
+//! fifo.resume();
+//!
+//! // Wait for the transfer to complete:
+//! let (ch, adc_read_target, buf) = transfer.wait();
+//!
+//! // do something with `buf` (it now contains 500 samples read from the ADC)
+//! //...
+//! ```
+//! //! See [examples/adc_fifo_dma.rs](https://github.com/rp-rs/rp-hal/tree/main/rp235x-hal-examples/src/bin/adc_fifo_dma.rs) for a more complete example.
+//!
+//! ### Free running mode without FIFO
+//!
+//! While free-running mode is usually used in combination with a FIFO, there are
+//! use cases where it can be used without. For example, if you want to be able to
+//! get the latest available sample at any point in time, and without waiting 96 ADC clock
+//! cycles (2µs).
+//!
+//! In this case, you can just enable free-running mode on it's own. The ADC will
+//! continuously do ADC conversions. The ones not read will just be discarded, but it's
+//! always possible to read the latest value, without additional delay:
+//!
+//! ```no_run
+//! use rp235x_hal::{self as hal, adc::Adc, adc::AdcPin, gpio::Pins, Sio};
+//! // Embedded HAL 1.0.0 doesn't have an ADC trait, so use the one from 0.2
+//! use embedded_hal_0_2::adc::OneShot;
+//! let mut peripherals = hal::pac::Peripherals::take().unwrap();
+//! let sio = Sio::new(peripherals.SIO);
+//! let pins = Pins::new(
+//! peripherals.IO_BANK0,
+//! peripherals.PADS_BANK0,
+//! sio.gpio_bank0,
+//! &mut peripherals.RESETS,
+//! );
+//! // Enable adc
+//! let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
+//! // Configure one of the pins as an ADC input
+//! let mut adc_pin_0 = AdcPin::new(pins.gpio26.into_floating_input()).unwrap();
+//! // Enable free-running mode
+//! adc.free_running(&adc_pin_0);
+//! // Read the ADC counts from the ADC channel whenever necessary
+//! loop {
+//! let pin_adc_counts: u16 = adc.read_single();
+//! // Do time critical stuff
+//! }
+//! ```
+
+use core::convert::Infallible;
+use core::marker::PhantomData;
+// Embedded HAL 1.0.0 doesn't have an ADC trait, so use the one from 0.2
+use embedded_hal_0_2::adc::{Channel, OneShot};
+
+use crate::{
+ dma,
+ gpio::{
+ bank0::{Gpio26, Gpio27, Gpio28, Gpio29},
+ AnyPin, DynBankId, DynPinId, Function, OutputEnableOverride, Pin, PullType, ValidFunction,
+ },
+ pac::{dma::ch::ch_ctrl_trig::TREQ_SEL_A, ADC, RESETS},
+ resets::SubsystemReset,
+ typelevel::Sealed,
+};
+
+const TEMPERATURE_SENSOR_CHANNEL: u8 = 4;
+
+/// The pin was invalid for the requested operation
+#[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct InvalidPinError;
+
+/// A pin locked in use with the ADC.
+pub struct AdcPin
+where
+ P: AnyPin,
+{
+ pin: P,
+ saved_output_disable: bool,
+ saved_input_enable: bool,
+}
+
+impl
AdcPin
+where
+ P: AnyPin,
+{
+ /// Captures the pin to be used with an ADC and disables its digital circuitry.
+ pub fn new(pin: P) -> Result {
+ let pin_id = pin.borrow().id();
+ if (26..=29).contains(&pin_id.num) && pin_id.bank == DynBankId::Bank0 {
+ let mut p = pin.into();
+ let (od, ie) = (p.get_output_disable(), p.get_input_enable());
+ p.set_output_enable_override(OutputEnableOverride::Disable);
+ p.set_input_enable(false);
+ Ok(Self {
+ pin: P::from(p),
+ saved_output_disable: od,
+ saved_input_enable: ie,
+ })
+ } else {
+ Err(InvalidPinError)
+ }
+ }
+
+ /// Release the pin and restore its digital circuitry's state.
+ pub fn release(self) -> P {
+ let mut p = self.pin.into();
+ p.set_output_disable(self.saved_output_disable);
+ p.set_input_enable(self.saved_input_enable);
+ P::from(p)
+ }
+
+ /// Returns the ADC channel of this AdcPin.
+ pub fn channel(&self) -> u8 {
+ let pin_id = self.pin.borrow().id();
+ // Self::new() makes sure that this is a valid channel number
+ pin_id.num - 26
+ }
+}
+
+/// Trait for entities that can be used as ADC channels.
+///
+/// This is implemented by [`AdcPin`] and by [`TempSense`].
+/// The trait is sealed and can't be implemented in other crates.
+pub trait AdcChannel: Sealed {
+ /// Get the channel id used to configure the ADC peripheral.
+ fn channel(&self) -> u8;
+}
+
+impl Sealed for AdcPin {}
+impl AdcChannel for AdcPin {
+ fn channel(&self) -> u8 {
+ self.channel()
+ }
+}
+
+impl Sealed for TempSense {}
+impl AdcChannel for TempSense {
+ fn channel(&self) -> u8 {
+ TEMPERATURE_SENSOR_CHANNEL
+ }
+}
+
+macro_rules! channel {
+ ($pin:ident, $channel:expr) => {
+ impl Channel for AdcPin>
+ where
+ $pin: crate::gpio::ValidFunction,
+ {
+ type ID = u8; // ADC channels are identified numerically
+
+ fn channel() -> u8 {
+ $channel
+ }
+ }
+ };
+}
+
+channel!(Gpio26, 0);
+channel!(Gpio27, 1);
+channel!(Gpio28, 2);
+channel!(Gpio29, 3);
+
+impl Channel for AdcPin>
+where
+ DynPinId: crate::gpio::ValidFunction,
+{
+ type ID = (); // ADC channels are identified at run time
+ fn channel() {}
+}
+
+/// Internal temperature sensor type
+pub struct TempSense {
+ __private: (),
+}
+
+impl Channel for TempSense {
+ type ID = u8; // ADC channels are identified numerically
+
+ fn channel() -> u8 {
+ TEMPERATURE_SENSOR_CHANNEL
+ }
+}
+
+/// Analog to Digital Convertor (ADC).
+///
+/// Represents an ADC within the RP2350. Each ADC has multiple channels, and each
+/// channel is either associated with a specific GPIO pin or attached to the internal
+/// temperature sensor. You should put the relevant pin into ADC mode by creating an
+/// [`AdcPin`] object with it, or you can put the ADC into `Temperature Sensing Mode`
+/// by calling [`Adc::take_temp_sensor()`]. Either way, the resulting objects can be
+/// passed to the [`OneShot::read()`][a] trait method to actually do the read.
+///
+/// [a]: embedded_hal_0_2::adc::OneShot::read
+pub struct Adc {
+ device: ADC,
+}
+
+impl Adc {
+ /// Create new adc struct and bring up adc
+ pub fn new(device: ADC, resets: &mut RESETS) -> Self {
+ device.reset_bring_down(resets);
+ device.reset_bring_up(resets);
+
+ // Enable adc
+ device.cs().write(|w| w.en().set_bit());
+
+ // Wait for adc ready
+ while !device.cs().read().ready().bit_is_set() {}
+
+ Self { device }
+ }
+
+ /// Free underlying register block
+ pub fn free(self) -> ADC {
+ self.device
+ }
+
+ /// Read the most recently sampled ADC value
+ ///
+ /// This function does not wait for the current conversion to finish.
+ /// If a conversion is still in progress, it returns the result of the
+ /// previous one.
+ ///
+ /// It also doesn't trigger a new conversion.
+ pub fn read_single(&self) -> u16 {
+ self.device.result().read().result().bits()
+ }
+
+ /// Enable temperature sensor, returns a channel to use.
+ ///
+ /// This can only be done once before calling [`Adc::disable_temp_sensor()`]. If the sensor has already
+ /// been enabled, this method will panic.
+ #[deprecated(
+ note = "This method may panic, use `take_temp_sensor()` instead.",
+ since = "0.9.0"
+ )]
+ pub fn enable_temp_sensor(&mut self) -> TempSense {
+ self.take_temp_sensor()
+ .expect("Temp sensor is already enabled.")
+ }
+
+ /// Enable temperature sensor, returns a channel to use
+ ///
+ /// If the sensor has already been enabled, this method returns `None`.
+ pub fn take_temp_sensor(&mut self) -> Option {
+ let mut disabled = false;
+ self.device.cs().modify(|r, w| {
+ disabled = r.ts_en().bit_is_clear();
+ // if bit was already set, this is a nop
+ w.ts_en().set_bit()
+ });
+ disabled.then_some(TempSense { __private: () })
+ }
+
+ /// Disable temperature sensor, consumes channel
+ pub fn disable_temp_sensor(&mut self, _: TempSense) {
+ self.device.cs().modify(|_, w| w.ts_en().clear_bit());
+ }
+
+ /// Start configuring free-running mode, and set up the FIFO
+ ///
+ /// The [`AdcFifoBuilder`] returned by this method can be used
+ /// to configure capture options, like sample rate, channels to
+ /// capture from etc.
+ ///
+ /// Capturing is started by calling [`AdcFifoBuilder::start`], which
+ /// returns an [`AdcFifo`] to read from.
+ pub fn build_fifo(&mut self) -> AdcFifoBuilder<'_, u16> {
+ AdcFifoBuilder {
+ adc: self,
+ marker: PhantomData,
+ }
+ }
+
+ /// Enable free-running mode by setting the start_many flag.
+ pub fn free_running(&mut self, pin: &dyn AdcChannel) {
+ self.device.cs().modify(|_, w| {
+ unsafe {
+ w.ainsel().bits(pin.channel());
+ }
+ w.start_many().set_bit();
+ w
+ });
+ }
+
+ /// Disable free-running mode by unsetting the start_many flag.
+ pub fn stop(&mut self) {
+ self.device.cs().modify(|_, w| w.start_many().clear_bit());
+ }
+
+ fn inner_read(&mut self, chan: u8) -> u16 {
+ self.wait_ready();
+
+ self.device
+ .cs()
+ .modify(|_, w| unsafe { w.ainsel().bits(chan).start_once().set_bit() });
+
+ self.wait_ready();
+
+ self.read_single()
+ }
+
+ /// Wait for the ADC to become ready.
+ ///
+ /// Also returns immediately if start_many is set, to avoid indefinite blocking.
+ pub fn wait_ready(&self) {
+ while !self.is_ready_or_free_running() {
+ core::hint::spin_loop();
+ }
+ }
+
+ fn is_ready_or_free_running(&self) -> bool {
+ let cs = self.device.cs().read();
+ cs.ready().bit_is_set() || cs.start_many().bit_is_set()
+ }
+
+ /// Returns true if the ADC is ready for the next conversion.
+ ///
+ /// This implies that any previous conversion has finished.
+ pub fn is_ready(&self) -> bool {
+ self.device.cs().read().ready().bit_is_set()
+ }
+}
+
+// Implementation for TempSense and type-checked pins
+impl OneShot for Adc
+where
+ WORD: From,
+ SRC: Channel,
+{
+ type Error = Infallible;
+
+ fn read(&mut self, _pin: &mut SRC) -> nb::Result {
+ let chan = SRC::channel();
+
+ Ok(self.inner_read(chan).into())
+ }
+}
+
+// Implementation for dyn-pins
+impl OneShot>> for Adc
+where
+ WORD: From,
+ F: Function,
+ M: PullType,
+ DynPinId: ValidFunction,
+ AdcPin>: Channel,
+{
+ type Error = Infallible;
+
+ fn read(&mut self, pin: &mut AdcPin>) -> nb::Result {
+ Ok(self.inner_read(pin.channel()).into())
+ }
+}
+
+/// Used to configure & build an [`AdcFifo`]
+///
+/// See [`Adc::build_fifo`] for details, as well as the `adc_fifo_*` [examples](https://github.com/rp-rs/rp-hal/tree/main/rp235x-hal/examples).
+pub struct AdcFifoBuilder<'a, Word> {
+ adc: &'a mut Adc,
+ marker: PhantomData,
+}
+
+impl<'a, Word> AdcFifoBuilder<'a, Word> {
+ /// Manually set clock divider to control sample rate
+ ///
+ /// The ADC is tied to the USB clock, normally running at 48MHz. ADC
+ /// conversion happens at 96 cycles per sample, so with the dividers both
+ /// set to 0 (the default) the sample rate will be `48MHz / 96 = 500ksps`.
+ ///
+ /// Setting the `int` and / or `frac` dividers will hold off between
+ /// samples, leading to an effective rate of:
+ ///
+ /// ```text
+ /// rate = 48MHz / (1 + int + (frac / 256))
+ /// ```
+ ///
+ /// To determine the required `int` and `frac` values for a given target
+ /// rate, use these equations:
+ ///
+ /// ```text
+ /// int = floor((48MHz / rate) - 1)
+ /// frac = round(256 * ((48MHz / rate) - 1 - int))
+ /// ```
+ ///
+ /// Some examples:
+ ///
+ /// | Target rate | `int` | `frac` |
+ /// |-------------|---------|--------|
+ /// | 1000sps | `47999` | `0` |
+ /// | 1024sps | `46874` | `0` |
+ /// | 1337sps | `35900` | `70` |
+ /// | 4096sps | `11717` | `192` |
+ /// | 96ksps | `499` | `0` |
+ ///
+ /// Since each conversion takes 96 cycles, setting `int` to anything below
+ /// 96 does not make a difference, and leads to the same result as setting
+ /// it to 0.
+ ///
+ /// The lowest possible rate is 732.41Hz, attainable by setting `int =
+ /// 0xFFFF, frac = 0xFF`.
+ ///
+ /// For more details, please refer to [Section
+ /// 12.4.3.2](https://rptl.io/rp2350-datasheet#section_adc) in the RP2350
+ /// datasheet.
+ pub fn clock_divider(self, int: u16, frac: u8) -> Self {
+ self.adc
+ .device
+ .div()
+ .modify(|_, w| unsafe { w.int().bits(int).frac().bits(frac) });
+ self
+ }
+
+ /// Select ADC input channel to sample from
+ ///
+ /// If round-robin mode is used, this will only affect the first sample.
+ ///
+ /// The given `pin` can either be one of the ADC inputs (GPIO26-28) or the
+ /// internal temperature sensor (retrieved via [`Adc::take_temp_sensor`]).
+ pub fn set_channel(self, pin: &mut P) -> Self {
+ self.adc
+ .device
+ .cs()
+ .modify(|_, w| unsafe { w.ainsel().bits(pin.channel()) });
+ self
+ }
+
+ /// Set channels to use for round-robin mode
+ ///
+ /// Takes a tuple of channels, like `(&mut adc_pin, &mut temp_sense)`.
+ ///
+ /// **NOTE:** *The order in which the channels are specified has no effect!
+ /// Channels are always sampled in increasing order, by their channel number (Channel 0, Channel 1, ...).*
+ pub fn round_robin>(self, selected_channels: T) -> Self {
+ let RoundRobin(bits) = selected_channels.into();
+ self.adc
+ .device
+ .cs()
+ .modify(|_, w| unsafe { w.rrobin().bits(bits as u16) });
+ self
+ }
+
+ /// Enable the FIFO interrupt ([`ADC_IRQ_FIFO`](crate::pac::Interrupt::ADC_IRQ_FIFO))
+ ///
+ /// It will be triggered whenever there are at least `threshold` samples waiting in the FIFO.
+ pub fn enable_interrupt(self, threshold: u8) -> Self {
+ self.adc.device.inte().modify(|_, w| w.fifo().set_bit());
+ self.adc
+ .device
+ .fcs()
+ .modify(|_, w| unsafe { w.thresh().bits(threshold) });
+ self
+ }
+
+ /// Shift values to produce 8 bit samples (discarding the lower 4 bits).
+ ///
+ /// Normally the ADC uses 12 bits of precision, packed into a u16.
+ /// Shifting the values loses some precision, but produces smaller samples.
+ ///
+ /// When this method has been called, the resulting fifo's `read` method returns u8.
+ pub fn shift_8bit(self) -> AdcFifoBuilder<'a, u8> {
+ self.adc.device.fcs().modify(|_, w| w.shift().set_bit());
+ AdcFifoBuilder {
+ adc: self.adc,
+ marker: PhantomData,
+ }
+ }
+
+ /// Enable DMA for the FIFO.
+ ///
+ /// This must be called to be able to transfer data from the ADC using a DMA transfer.
+ ///
+ /// **NOTE:** *this method sets the FIFO interrupt threshold to `1`, which is required for DMA transfers to work.
+ /// The threshold is the same one as set by [`AdcFifoBuilder::enable_interrupt`]. If you want to enable FIFO
+ /// interrupts, but also use DMA, the `threshold` parameter passed to `enable_interrupt` *must* be set to `1` as well.*
+ pub fn enable_dma(self) -> Self {
+ self.adc
+ .device
+ .fcs()
+ .modify(|_, w| unsafe { w.dreq_en().set_bit().thresh().bits(1) });
+ self
+ }
+
+ /// Enable ADC FIFO and start free-running conversion
+ ///
+ /// Use the returned [`AdcFifo`] instance to access the captured data.
+ ///
+ /// To stop capturing, call [`AdcFifo::stop`].
+ ///
+ /// Note: if you plan to use the FIFO for DMA transfers, [`AdcFifoBuilder::prepare`] instead.
+ pub fn start(self) -> AdcFifo<'a, Word> {
+ self.adc.device.fcs().modify(|_, w| w.en().set_bit());
+ self.adc.device.cs().modify(|_, w| w.start_many().set_bit());
+ AdcFifo {
+ adc: self.adc,
+ marker: PhantomData,
+ }
+ }
+
+ /// Enable ADC FIFO, but do not start conversion yet
+ ///
+ /// Same as [`AdcFifoBuilder::start`], except the FIFO is initially paused.
+ ///
+ /// Use [`AdcFifo::resume`] to start conversion.
+ pub fn start_paused(self) -> AdcFifo<'a, Word> {
+ self.adc.device.fcs().modify(|_, w| w.en().set_bit());
+ self.adc
+ .device
+ .cs()
+ .modify(|_, w| w.start_many().clear_bit());
+ AdcFifo {
+ adc: self.adc,
+ marker: PhantomData,
+ }
+ }
+
+ /// Alias for [`AdcFifoBuilder::start_paused`].
+ #[deprecated(note = "Use `start_paused()` instead.", since = "0.10.0")]
+ pub fn prepare(self) -> AdcFifo<'a, Word> {
+ self.start_paused()
+ }
+}
+
+/// Represents the ADC fifo
+///
+/// Constructed by [`AdcFifoBuilder::start`], which is accessible through [`Adc::build_fifo`].
+pub struct AdcFifo<'a, Word> {
+ adc: &'a mut Adc,
+ marker: PhantomData,
+}
+
+impl<'a, Word> AdcFifo<'a, Word> {
+ #[allow(clippy::len_without_is_empty)]
+ /// Returns the number of elements currently in the fifo
+ pub fn len(&mut self) -> u8 {
+ self.adc.device.fcs().read().level().bits()
+ }
+
+ /// Check if there was a fifo overrun
+ ///
+ /// An overrun happens when the fifo is filled up faster than `read` is called to consume it.
+ ///
+ /// This function also clears the `over` bit if it was set.
+ pub fn is_over(&mut self) -> bool {
+ let over = self.adc.device.fcs().read().over().bit();
+ if over {
+ self.adc
+ .device
+ .fcs()
+ .modify(|_, w| w.over().clear_bit_by_one());
+ }
+ over
+ }
+
+ /// Check if there was a fifo underrun
+ ///
+ /// An underrun happens when `read` is called on an empty fifo (`len() == 0`).
+ ///
+ /// This function also clears the `under` bit if it was set.
+ pub fn is_under(&mut self) -> bool {
+ let under = self.adc.device.fcs().read().under().bit();
+ if under {
+ self.adc
+ .device
+ .fcs()
+ .modify(|_, w| w.under().clear_bit_by_one());
+ }
+ under
+ }
+
+ /// Read the most recently sampled ADC value
+ ///
+ /// Returns the most recently sampled value, bypassing the FIFO.
+ ///
+ /// This can be used if you want to read samples occasionally, but don't
+ /// want to incur the 96 cycle delay of a one-off read.
+ ///
+ /// Example:
+ /// ```ignore
+ /// // start continuously sampling values:
+ /// let mut fifo = adc.build_fifo().set_channel(&mut adc_pin).start();
+ ///
+ /// loop {
+ /// do_something_timing_critical();
+ ///
+ /// // read the most recent value:
+ /// if fifo.read_single() > THRESHOLD {
+ /// led.set_high().unwrap();
+ /// } else {
+ /// led.set_low().unwrap();
+ /// }
+ /// }
+ ///
+ /// // stop sampling, when it's no longer needed
+ /// fifo.stop();
+ /// ```
+ ///
+ /// Note that when round-robin sampling is used, there is no way
+ /// to tell from which channel this sample came.
+ pub fn read_single(&mut self) -> u16 {
+ self.adc.read_single()
+ }
+
+ /// Returns `true` if conversion is currently paused.
+ ///
+ /// While paused, no samples will be added to the FIFO.
+ ///
+ /// There may be existing samples in the FIFO though, or a conversion may still be in progress.
+ pub fn is_paused(&mut self) -> bool {
+ self.adc.device.cs().read().start_many().bit_is_clear()
+ }
+
+ /// Temporarily pause conversion
+ ///
+ /// This method stops ADC conversion, but leaves everything else configured.
+ ///
+ /// No new samples are captured until [`AdcFifo::resume`] is called.
+ ///
+ /// Note that existing samples can still be read from the FIFO, and can possibly
+ /// cause interrupts and DMA transfer progress until the FIFO is emptied.
+ pub fn pause(&mut self) {
+ self.adc
+ .device
+ .cs()
+ .modify(|_, w| w.start_many().clear_bit());
+ }
+
+ /// Resume conversion after it was paused
+ ///
+ /// There are two situations when it makes sense to use this method:
+ /// - After having called [`AdcFifo::pause`] on an AdcFifo
+ /// - If the FIFO was initialized using [`AdcFifoBuilder::prepare`].
+ ///
+ /// Calling this method when conversion is already running has no effect.
+ pub fn resume(&mut self) {
+ self.adc.device.cs().modify(|_, w| w.start_many().set_bit());
+ }
+
+ /// Clears the FIFO, removing all values
+ ///
+ /// Reads and discards values from the FIFO until it is empty.
+ ///
+ /// This only makes sense to use while the FIFO is paused (see [`AdcFifo::pause`]).
+ pub fn clear(&mut self) {
+ while self.len() > 0 {
+ self.read_from_fifo();
+ }
+ }
+
+ /// Stop capturing in free running mode.
+ ///
+ /// Resets all capture options that can be set via [`AdcFifoBuilder`] to
+ /// their defaults.
+ ///
+ /// Returns the underlying [`Adc`], to be reused.
+ pub fn stop(mut self) -> &'a mut Adc {
+ // stop capture and clear channel selection
+ self.adc
+ .device
+ .cs()
+ .modify(|_, w| unsafe { w.start_many().clear_bit().rrobin().bits(0).ainsel().bits(0) });
+ // disable fifo interrupt
+ self.adc.device.inte().modify(|_, w| w.fifo().clear_bit());
+ // Wait for one more conversion, then drain remaining values from fifo.
+ // This MUST happen *after* the interrupt is disabled, but
+ // *before* `thresh` is modified. Otherwise if `INTS.FIFO = 1`,
+ // the interrupt will be fired one more time.
+ // The only way to clear `INTS.FIFO` is for `FCS.LEVEL` to go
+ // below `FCS.THRESH`, which requires `FCS.THRESH` not to be 0.
+ while self.adc.device.cs().read().ready().bit_is_clear() {}
+ self.clear();
+ // disable fifo, reset threshold to 0 and disable DMA
+ self.adc
+ .device
+ .fcs()
+ .modify(|_, w| unsafe { w.en().clear_bit().thresh().bits(0).dreq_en().clear_bit() });
+ // reset clock divider
+ self.adc
+ .device
+ .div()
+ .modify(|_, w| unsafe { w.int().bits(0).frac().bits(0) });
+ self.adc
+ }
+
+ /// Block until a ADC_IRQ_FIFO interrupt occurs
+ ///
+ /// Interrupts must be enabled ([`AdcFifoBuilder::enable_interrupt`]), or else this methods blocks forever.
+ pub fn wait_for_interrupt(&mut self) {
+ while self.adc.device.intr().read().fifo().bit_is_clear() {}
+ }
+
+ fn read_from_fifo(&mut self) -> u16 {
+ self.adc.device.fifo().read().val().bits()
+ }
+
+ /// Returns a read-target for initiating DMA transfers
+ ///
+ /// The [`DmaReadTarget`] returned by this function can be used to initiate DMA transfers
+ /// reading from the ADC.
+ pub fn dma_read_target(&self) -> DmaReadTarget {
+ DmaReadTarget(self.adc.device.fifo().as_ptr() as u32, PhantomData)
+ }
+
+ /// Trigger a single conversion
+ ///
+ /// Ignored when in [`Adc::free_running`] mode.
+ pub fn trigger(&mut self) {
+ self.adc.device.cs().modify(|_, w| w.start_once().set_bit());
+ }
+
+ /// Check if ADC is ready for the next conversion trigger
+ ///
+ /// Not useful when in [`Adc::free_running`] mode.
+ pub fn is_ready(&self) -> bool {
+ self.adc.device.cs().read().ready().bit_is_set()
+ }
+}
+
+impl<'a> AdcFifo<'a, u16> {
+ /// Read a single value from the fifo (u16 version, not shifted)
+ pub fn read(&mut self) -> u16 {
+ self.read_from_fifo()
+ }
+}
+
+impl<'a> AdcFifo<'a, u8> {
+ /// Read a single value from the fifo (u8 version, shifted)
+ ///
+ /// Also see [`AdcFifoBuilder::shift_8bit`].
+ pub fn read(&mut self) -> u8 {
+ self.read_from_fifo() as u8
+ }
+}
+
+/// Represents a [`dma::ReadTarget`] for the [`AdcFifo`]
+///
+/// If [`AdcFifoBuilder::shift_8bit`] was called when constructing the FIFO,
+/// `Word` will be `u8`, otherwise it will be `u16`.
+pub struct DmaReadTarget(u32, PhantomData);
+
+/// Safety: rx_address_count points to a register which is always a valid
+/// read target.
+unsafe impl dma::ReadTarget for DmaReadTarget {
+ type ReceivedWord = Word;
+
+ fn rx_treq() -> Option {
+ Some(TREQ_SEL_A::ADC.into())
+ }
+
+ fn rx_address_count(&self) -> (u32, u32) {
+ (self.0, u32::MAX)
+ }
+
+ fn rx_increment(&self) -> bool {
+ false
+ }
+}
+
+impl dma::EndlessReadTarget for DmaReadTarget {}
+
+/// Internal struct representing values for the `CS.RROBIN` register.
+///
+/// See [`AdcFifoBuilder::round_robin`], for usage example.
+pub struct RoundRobin(u8);
+
+impl From<&PIN> for RoundRobin {
+ fn from(pin: &PIN) -> Self {
+ Self(1 << pin.channel())
+ }
+}
+
+impl From<(&A, &B)> for RoundRobin
+where
+ A: AdcChannel,
+ B: AdcChannel,
+{
+ fn from(pins: (&A, &B)) -> Self {
+ Self(1 << pins.0.channel() | 1 << pins.1.channel())
+ }
+}
+
+impl From<(&A, &B, &C)> for RoundRobin
+where
+ A: AdcChannel,
+ B: AdcChannel,
+ C: AdcChannel,
+{
+ fn from(pins: (&A, &B, &C)) -> Self {
+ Self(1 << pins.0.channel() | 1 << pins.1.channel() | 1 << pins.2.channel())
+ }
+}
+
+impl From<(&A, &B, &C, &D)> for RoundRobin
+where
+ A: AdcChannel,
+ B: AdcChannel,
+ C: AdcChannel,
+ D: AdcChannel,
+{
+ fn from(pins: (&A, &B, &C, &D)) -> Self {
+ Self(
+ 1 << pins.0.channel()
+ | 1 << pins.1.channel()
+ | 1 << pins.2.channel()
+ | 1 << pins.3.channel(),
+ )
+ }
+}
+
+impl From<(&A, &B, &C, &D, &E)> for RoundRobin
+where
+ A: AdcChannel,
+ B: AdcChannel,
+ C: AdcChannel,
+ D: AdcChannel,
+ E: AdcChannel,
+{
+ fn from(pins: (&A, &B, &C, &D, &E)) -> Self {
+ Self(
+ 1 << pins.0.channel()
+ | 1 << pins.1.channel()
+ | 1 << pins.2.channel()
+ | 1 << pins.3.channel()
+ | 1 << pins.4.channel(),
+ )
+ }
+}
diff --git a/rp235x-hal/src/arch.rs b/rp235x-hal/src/arch.rs
new file mode 100644
index 000000000..a87fe9bc6
--- /dev/null
+++ b/rp235x-hal/src/arch.rs
@@ -0,0 +1,138 @@
+//! Portable in-line assembly
+//!
+//! Replaces `cortex_m::asm` with things that work on RISC-V and Arm.
+
+#[cfg(all(target_arch = "arm", target_os = "none"))]
+mod inner {
+ pub use cortex_m::asm::{delay, dsb, nop, sev, wfe, wfi};
+ pub use cortex_m::interrupt::{disable as interrupt_disable, enable as interrupt_enable};
+
+ /// Are interrupts current enabled?
+ pub fn interrupts_enabled() -> bool {
+ cortex_m::register::primask::read().is_active()
+ }
+
+ /// Run the closure without interrupts
+ ///
+ /// No critical-section token because we haven't blocked the second core
+ pub fn interrupt_free(f: F) -> T
+ where
+ F: FnOnce() -> T,
+ {
+ let active = interrupts_enabled();
+ if active {
+ interrupt_disable();
+ }
+ let t = f();
+ if active {
+ unsafe {
+ interrupt_enable();
+ }
+ }
+ t
+ }
+}
+
+#[cfg(all(target_arch = "riscv32", target_os = "none"))]
+mod inner {
+ pub use riscv::asm::{delay, nop, wfi};
+ pub use riscv::interrupt::machine::{
+ disable as interrupt_disable, enable as interrupt_enable, free as interrupt_free,
+ };
+
+ /// Send Event
+ #[inline(always)]
+ pub fn sev() {
+ unsafe {
+ // This is how h3.unblock is encoded.
+ core::arch::asm!("slt x0, x0, x1");
+ }
+ }
+
+ /// Wait for Event
+ ///
+ /// This is the interrupt-safe version of WFI.
+ pub fn wfe() {
+ let active = interrupts_enabled();
+ if active {
+ interrupt_disable();
+ }
+ wfi();
+ if active {
+ unsafe {
+ interrupt_enable();
+ }
+ }
+ }
+
+ /// Data Synchronization Barrier
+ #[inline(always)]
+ pub fn dsb() {
+ core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
+ unsafe { core::arch::asm!("fence", options(nostack, preserves_flags)) };
+ core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
+ }
+
+ /// Are interrupts current enabled?
+ #[inline(always)]
+ pub fn interrupts_enabled() -> bool {
+ riscv::register::mstatus::read().mie()
+ }
+}
+
+#[cfg(not(all(any(target_arch = "arm", target_arch = "riscv32"), target_os = "none")))]
+mod inner {
+ /// Placeholder function to disable interrupts
+ pub fn interrupt_disable() {}
+ /// Placeholder function to enable interrupts
+ pub fn interrupt_enable() {}
+ /// Placeholder function to check if interrupts are enabled
+ pub fn interrupts_enabled() -> bool {
+ false
+ }
+ /// Placeholder function to wait for an event
+ pub fn wfe() {}
+ /// Placeholder function to do nothing
+ pub fn nop() {}
+ /// Placeholder function to emit a data synchronisation barrier
+ pub fn dsb() {}
+ /// Placeholder function to run a closure with interrupts disabled
+ pub fn interrupt_free(f: F) -> T
+ where
+ F: FnOnce() -> T,
+ {
+ f()
+ }
+ /// Placeholder function to wait for some clock cycles
+ pub fn delay(_: u32) {}
+ /// Placeholder function to emit an event
+ pub fn sev() {}
+}
+
+pub use inner::*;
+
+/// Create a static variable which we can grab a mutable reference to exactly once.
+#[macro_export]
+macro_rules! singleton {
+ ($name:ident: $ty:ty = $expr:expr) => {{
+ static mut $name: (::core::mem::MaybeUninit<$ty>, ::core::sync::atomic::AtomicBool) =
+ (::core::mem::MaybeUninit::uninit(), ::core::sync::atomic::AtomicBool::new(false));
+
+ #[allow(unsafe_code)]
+ if unsafe { $name.1.compare_exchange(false, true, ::core::sync::atomic::Ordering::SeqCst, ::core::sync::atomic::Ordering::SeqCst).is_ok() } {
+ // If we get here, the bool was false and we were the ones who set it to true.
+ // So we have exclusive access.
+ let expr = $expr;
+ #[allow(unsafe_code)]
+ unsafe {
+ $name.0 = ::core::mem::MaybeUninit::new(expr);
+ Some(&mut *$name.0.as_mut_ptr())
+ }
+ } else {
+ None
+ }
+ }};
+ (: $ty:ty = $expr:expr) => {
+ $crate::singleton!(VAR: $ty = $expr)
+ };
+}
diff --git a/rp235x-hal/src/async_utils.rs b/rp235x-hal/src/async_utils.rs
new file mode 100644
index 000000000..3bfee7233
--- /dev/null
+++ b/rp235x-hal/src/async_utils.rs
@@ -0,0 +1,143 @@
+//! Commonly used in async implementations.
+
+use core::{marker::PhantomData, task::Poll};
+
+pub(crate) mod sealed {
+ use core::{cell::Cell, task::Waker};
+ use critical_section::Mutex;
+
+ pub trait Wakeable {
+ /// Returns the waker associated with driver instance.
+ fn waker() -> &'static IrqWaker;
+ }
+
+ /// This type wraps a `Waker` in a `Mutex>`.
+ ///
+ /// While `critical_section::Mutex` intregrates nicely with RefCell, RefCell adds a borrow
+ /// counter that is not necessary for this usecase.
+ ///
+ /// This type is kept sealed to prevent user from mistakenly messing with the waker such as
+ /// clearing it while the driver is parked.
+ pub struct IrqWaker {
+ waker: Mutex>>,
+ }
+
+ impl Default for IrqWaker {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+
+ impl IrqWaker {
+ pub const fn new() -> Self {
+ Self {
+ waker: Mutex::new(Cell::new(None)),
+ }
+ }
+ pub fn wake(&self) {
+ critical_section::with(|cs| {
+ if let Some(waker) = self.waker.borrow(cs).take() {
+ Waker::wake(waker);
+ }
+ });
+ }
+ pub fn register(&self, waker: &Waker) {
+ critical_section::with(|cs| {
+ self.waker.borrow(cs).replace(Some(waker.clone()));
+ });
+ }
+ pub fn clear(&self) {
+ critical_section::with(|cs| {
+ self.waker.borrow(cs).take();
+ });
+ }
+ }
+}
+
+/// Marks driver instances that can be bound to an interrupt to wake async tasks.
+pub trait AsyncPeripheral: sealed::Wakeable {
+ /// Signals the driver of an interrupt.
+ fn on_interrupt();
+}
+
+#[must_use = "Future do nothing unless they are polled on."]
+pub(crate) struct CancellablePollFn<'periph, Periph, PFn, EnIrqFn, CancelFn, OutputTy>
+where
+ Periph: sealed::Wakeable,
+ CancelFn: FnMut(&mut Periph),
+{
+ periph: &'periph mut Periph,
+ poll: PFn,
+ enable_irq: EnIrqFn,
+ cancel: CancelFn,
+ done: bool,
+ // captures F's return type.
+ phantom: PhantomData,
+}
+impl<'p, Periph, PFn, EnIrqFn, CancelFn, OutputTy>
+ CancellablePollFn<'p, Periph, PFn, EnIrqFn, CancelFn, OutputTy>
+where
+ Periph: sealed::Wakeable,
+ PFn: FnMut(&mut Periph) -> Poll,
+ EnIrqFn: FnMut(&mut Periph),
+ CancelFn: FnMut(&mut Periph),
+{
+ pub(crate) fn new(
+ periph: &'p mut Periph,
+ poll: PFn,
+ enable_irq: EnIrqFn,
+ cancel: CancelFn,
+ ) -> Self {
+ Self {
+ periph,
+ poll,
+ enable_irq,
+ cancel,
+ done: false,
+ phantom: PhantomData,
+ }
+ }
+}
+
+impl core::future::Future
+ for CancellablePollFn<'_, Periph, PFn, EnIrqFn, CancelFn, OutputTy>
+where
+ Periph: sealed::Wakeable,
+ PFn: FnMut(&mut Periph) -> Poll,
+ EnIrqFn: FnMut(&mut Periph),
+ CancelFn: FnMut(&mut Periph),
+{
+ type Output = OutputTy;
+
+ fn poll(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> Poll {
+ // SAFETY: We are not moving anything.
+ let Self {
+ ref mut periph,
+ poll: ref mut is_ready,
+ enable_irq: ref mut setup_flags,
+ ref mut done,
+ ..
+ } = unsafe { self.get_unchecked_mut() };
+ let r = (is_ready)(periph);
+ if r.is_pending() {
+ Periph::waker().register(cx.waker());
+ (setup_flags)(periph);
+ } else {
+ *done = true;
+ }
+ r
+ }
+}
+impl<'periph, Periph, PFn, EnIrqFn, CancelFn, OutputTy> Drop
+ for CancellablePollFn<'periph, Periph, PFn, EnIrqFn, CancelFn, OutputTy>
+where
+ Periph: sealed::Wakeable,
+ CancelFn: FnMut(&mut Periph),
+{
+ fn drop(&mut self) {
+ if !self.done {
+ Periph::waker().clear();
+ (self.cancel)(self.periph);
+ }
+ }
+}
diff --git a/rp235x-hal/src/atomic_register_access.rs b/rp235x-hal/src/atomic_register_access.rs
new file mode 100644
index 000000000..b429bab5c
--- /dev/null
+++ b/rp235x-hal/src/atomic_register_access.rs
@@ -0,0 +1,40 @@
+//! Provide atomic access to peripheral registers
+//!
+//! This feature is not available for all peripherals.
+//! See [Section 2.1.3][section_2_1_3] of the RP2350 datasheet for details.
+//!
+//! [section_2_1_3]: https://rptl.io/rp2350-datasheet#atomic-rwtype
+
+use core::ptr::write_volatile;
+
+/// Perform atomic bitmask set operation on register
+///
+/// See [Section 2.1.3][section_2_1_3] of the RP2350 datasheet for details.
+///
+/// [section_2_1_3]: https://rptl.io/rp2350-datasheet#atomic-rwtype
+///
+/// # Safety
+///
+/// In addition to the requirements of [core::ptr::write_volatile],
+/// `register` must point to a register providing atomic aliases.
+#[inline]
+pub(crate) unsafe fn write_bitmask_set(register: *mut u32, bits: u32) {
+ let alias = (register as usize + 0x2000) as *mut u32;
+ write_volatile(alias, bits);
+}
+
+/// Perform atomic bitmask clear operation on register
+///
+/// See [Section 2.1.3][section_2_1_3] of the RP2350 datasheet for details.
+///
+/// [section_2_1_3]: https://rptl.io/rp2350-datasheet#atomic-rwtype
+///
+/// # Safety
+///
+/// In addition to the requirements of [core::ptr::write_volatile],
+/// `register` must point to a register providing atomic aliases.
+#[inline]
+pub(crate) unsafe fn write_bitmask_clear(register: *mut u32, bits: u32) {
+ let alias = (register as usize + 0x3000) as *mut u32;
+ write_volatile(alias, bits);
+}
diff --git a/rp235x-hal/src/block.rs b/rp235x-hal/src/block.rs
new file mode 100644
index 000000000..7421dd7b0
--- /dev/null
+++ b/rp235x-hal/src/block.rs
@@ -0,0 +1,1092 @@
+//! Support for the RP235x Boot ROM's "Block" structures
+//!
+//! Blocks contain pointers, to form Block Loops.
+//!
+//! The `IMAGE_DEF` Block (here the `ImageDef` type) tells the ROM how to boot a
+//! firmware image. The `PARTITION_TABLE` Block (here the `PartitionTable` type)
+//! tells the ROM how to divide the flash space up into partitions.
+
+// These all have a 1 byte size
+
+/// An item ID for encoding a Vector Table address
+pub const ITEM_1BS_VECTOR_TABLE: u8 = 0x03;
+
+/// An item ID for encoding a Rolling Window Delta
+pub const ITEM_1BS_ROLLING_WINDOW_DELTA: u8 = 0x05;
+
+/// An item ID for encoding a Signature
+pub const ITEM_1BS_SIGNATURE: u8 = 0x09;
+
+/// An item ID for encoding a Salt
+pub const ITEM_1BS_SALT: u8 = 0x0c;
+
+/// An item ID for encoding an Image Type
+pub const ITEM_1BS_IMAGE_TYPE: u8 = 0x42;
+
+/// An item ID for encoding the image's Entry Point
+pub const ITEM_1BS_ENTRY_POINT: u8 = 0x44;
+
+/// An item ID for encoding the definition of a Hash
+pub const ITEM_2BS_HASH_DEF: u8 = 0x47;
+
+/// An item ID for encoding a Version
+pub const ITEM_1BS_VERSION: u8 = 0x48;
+
+/// An item ID for encoding a Hash
+pub const ITEM_1BS_HASH_VALUE: u8 = 0x4b;
+
+// These all have a 2-byte size
+
+/// An item ID for encoding a Load Map
+pub const ITEM_2BS_LOAD_MAP: u8 = 0x06;
+
+/// An item ID for encoding a Partition Table
+pub const ITEM_2BS_PARTITION_TABLE: u8 = 0x0a;
+
+/// An item ID for encoding a placeholder entry that is ignored
+///
+/// Allows a Block to not be empty.
+pub const ITEM_2BS_IGNORED: u8 = 0xfe;
+
+/// An item ID for encoding the special last item in a Block
+///
+/// It records how long the Block is.
+pub const ITEM_2BS_LAST: u8 = 0xff;
+
+// Options for ITEM_1BS_IMAGE_TYPE
+
+/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as invalid
+pub const IMAGE_TYPE_INVALID: u16 = 0x0000;
+
+/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as an executable
+pub const IMAGE_TYPE_EXE: u16 = 0x0001;
+
+/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as data
+pub const IMAGE_TYPE_DATA: u16 = 0x0002;
+
+/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as unspecified
+pub const IMAGE_TYPE_EXE_TYPE_SECURITY_UNSPECIFIED: u16 = 0x0000;
+
+/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as Non Secure
+pub const IMAGE_TYPE_EXE_TYPE_SECURITY_NS: u16 = 0x0010;
+
+/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as Non Secure
+pub const IMAGE_TYPE_EXE_TYPE_SECURITY_S: u16 = 0x0020;
+
+/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU type as Arm
+pub const IMAGE_TYPE_EXE_CPU_ARM: u16 = 0x0000;
+
+/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU type as RISC-V
+pub const IMAGE_TYPE_EXE_CPU_RISCV: u16 = 0x0100;
+
+/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU as an RP2040
+pub const IMAGE_TYPE_EXE_CHIP_RP2040: u16 = 0x0000;
+
+/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU as an RP2350
+pub const IMAGE_TYPE_EXE_CHIP_RP2350: u16 = 0x1000;
+
+/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the image as Try Before You Buy.
+///
+/// This means the image must be marked as 'Bought' with the ROM before the
+/// watchdog times out the trial period, otherwise it is erased and the previous
+/// image will be booted.
+pub const IMAGE_TYPE_TBYB: u16 = 0x8000;
+
+/// This is the magic Block Start value.
+///
+/// The Pico-SDK calls it `PICOBIN_BLOCK_MARKER_START`
+const BLOCK_MARKER_START: u32 = 0xffffded3;
+
+/// This is the magic Block END value.
+///
+/// The Pico-SDK calls it `PICOBIN_BLOCK_MARKER_END`
+const BLOCK_MARKER_END: u32 = 0xab123579;
+
+/// An Image Definition has one item in it - an [`ITEM_1BS_IMAGE_TYPE`]
+pub type ImageDef = Block<1>;
+
+/// A Block as understood by the Boot ROM.
+///
+/// This could be an Image Definition, or a Partition Table, or maybe some other
+/// kind of block.
+///
+/// It contains within the special start and end markers the Boot ROM is looking
+/// for.
+#[derive(Debug)]
+#[repr(C)]
+pub struct Block {
+ marker_start: u32,
+ items: [u32; N],
+ length: u32,
+ offset: *const u32,
+ marker_end: u32,
+}
+
+unsafe impl Sync for Block {}
+
+impl Block {
+ /// Construct a new Binary Block, with the given items.
+ ///
+ /// The length, and the Start and End markers are added automatically. The
+ /// Block Loop pointer initially points to itself.
+ pub const fn new(items: [u32; N]) -> Block {
+ Block {
+ marker_start: BLOCK_MARKER_START,
+ items,
+ length: item_last(N as u16),
+ // offset from this block to next block in loop. By default
+ // we form a Block Loop with a single Block in it.
+ offset: core::ptr::null(),
+ marker_end: BLOCK_MARKER_END,
+ }
+ }
+
+ /// Change the Block Loop offset value.
+ ///
+ /// This method isn't that useful because you can't evaluate the difference
+ /// between two pointers in a const context as the addresses aren't assigned
+ /// until long after the const evaluator has run.
+ ///
+ /// If you think you need this method, you might want to set a unique random
+ /// value here and swap it for the real offset as a post-processing step.
+ pub const fn with_offset(self, offset: *const u32) -> Block {
+ Block { offset, ..self }
+ }
+}
+
+impl Block<0> {
+ /// Construct an empty block.
+ pub const fn empty() -> Block<0> {
+ Block::new([])
+ }
+
+ /// Make the block one word larger
+ pub const fn extend(self, word: u32) -> Block<1> {
+ Block::new([word])
+ }
+}
+
+impl Block<1> {
+ /// Make the block one word larger
+ pub const fn extend(self, word: u32) -> Block<2> {
+ Block::new([self.items[0], word])
+ }
+}
+
+impl Block<2> {
+ /// Make the block one word larger
+ pub const fn extend(self, word: u32) -> Block<3> {
+ Block::new([self.items[0], self.items[1], word])
+ }
+}
+
+impl ImageDef {
+ /// Construct a new IMAGE_DEF Block, for an EXE with the given security and
+ /// architecture.
+ pub const fn arch_exe(security: Security, architecture: Architecture) -> Self {
+ Self::new([item_image_type_exe(security, architecture)])
+ }
+
+ /// Construct a new IMAGE_DEF Block, for an EXE with the given security.
+ ///
+ /// The target architecture is taken from the current build target (i.e. Arm
+ /// or RISC-V).
+ pub const fn exe(security: Security) -> Self {
+ if cfg!(all(target_arch = "riscv32", target_os = "none")) {
+ Self::arch_exe(security, Architecture::Riscv)
+ } else {
+ Self::arch_exe(security, Architecture::Arm)
+ }
+ }
+
+ /// Construct a new IMAGE_DEF Block, for a Non-Secure EXE.
+ ///
+ /// The target architecture is taken from the current build target (i.e. Arm
+ /// or RISC-V).
+ pub const fn non_secure_exe() -> Self {
+ Self::exe(Security::NonSecure)
+ }
+
+ /// Construct a new IMAGE_DEF Block, for a Secure EXE.
+ ///
+ /// The target architecture is taken from the current build target (i.e. Arm
+ /// or RISC-V).
+ pub const fn secure_exe() -> Self {
+ Self::exe(Security::Secure)
+ }
+}
+
+/// We make our partition table this fixed size.
+pub const PARTITION_TABLE_MAX_ITEMS: usize = 128;
+
+/// Describes a unpartitioned space
+#[derive(Debug, Clone, PartialEq, Eq, Default)]
+pub struct UnpartitionedSpace {
+ permissions_and_location: u32,
+ permissions_and_flags: u32,
+}
+
+impl UnpartitionedSpace {
+ /// Create a new unpartitioned space.
+ ///
+ /// It defaults to no permissions.
+ pub const fn new() -> Self {
+ Self {
+ permissions_and_location: 0,
+ permissions_and_flags: 0,
+ }
+ }
+
+ /// Create a new unpartition space from run-time values.
+ ///
+ /// Get these from the ROM function `get_partition_table_info` with an argument of `PT_INFO`.
+ pub const fn from_raw(permissions_and_location: u32, permissions_and_flags: u32) -> Self {
+ Self {
+ permissions_and_location,
+ permissions_and_flags,
+ }
+ }
+
+ /// Add a permission
+ pub const fn with_permission(self, permission: Permission) -> Self {
+ Self {
+ permissions_and_flags: self.permissions_and_flags | permission as u32,
+ permissions_and_location: self.permissions_and_location | permission as u32,
+ }
+ }
+
+ /// Set a flag
+ pub const fn with_flag(self, flag: UnpartitionedFlag) -> Self {
+ Self {
+ permissions_and_flags: self.permissions_and_flags | flag as u32,
+ ..self
+ }
+ }
+
+ /// Get the partition start and end
+ ///
+ /// The offsets are in 4 KiB sectors, inclusive.
+ pub fn get_first_last_sectors(&self) -> (u16, u16) {
+ (
+ (self.permissions_and_location & 0x0000_1FFF) as u16,
+ ((self.permissions_and_location >> 13) & 0x0000_1FFF) as u16,
+ )
+ }
+
+ /// Get the partition start and end
+ ///
+ /// The offsets are in bytes, inclusive.
+ pub fn get_first_last_bytes(&self) -> (u32, u32) {
+ let (first, last) = self.get_first_last_sectors();
+ (u32::from(first) * 4096, (u32::from(last) * 4096) + 4095)
+ }
+
+ /// Check if it has a permission
+ pub fn has_permission(&self, permission: Permission) -> bool {
+ let mask = permission as u32;
+ (self.permissions_and_flags & mask) != 0
+ }
+
+ /// Check if the partition has a flag set
+ pub fn has_flag(&self, flag: UnpartitionedFlag) -> bool {
+ let mask = flag as u32;
+ (self.permissions_and_flags & mask) != 0
+ }
+}
+
+impl core::fmt::Display for UnpartitionedSpace {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ let (first, last) = self.get_first_last_bytes();
+ write!(
+ f,
+ "{:#010x}..{:#010x} S:{}{} NS:{}{} B:{}{}",
+ first,
+ last,
+ if self.has_permission(Permission::SecureRead) {
+ 'R'
+ } else {
+ '_'
+ },
+ if self.has_permission(Permission::SecureWrite) {
+ 'W'
+ } else {
+ '_'
+ },
+ if self.has_permission(Permission::NonSecureRead) {
+ 'R'
+ } else {
+ '_'
+ },
+ if self.has_permission(Permission::NonSecureWrite) {
+ 'W'
+ } else {
+ '_'
+ },
+ if self.has_permission(Permission::BootRead) {
+ 'R'
+ } else {
+ '_'
+ },
+ if self.has_permission(Permission::BootWrite) {
+ 'W'
+ } else {
+ '_'
+ }
+ )
+ }
+}
+
+/// Describes a Partition
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Partition {
+ permissions_and_location: u32,
+ permissions_and_flags: u32,
+ id: Option,
+ extra_families: [u32; 4],
+ extra_families_len: usize,
+ name: [u8; 128],
+}
+
+impl Partition {
+ const FLAGS_HAS_ID: u32 = 0b1;
+ const FLAGS_LINK_TYPE_A_PARTITION: u32 = 0b01 << 1;
+ const FLAGS_LINK_TYPE_OWNER: u32 = 0b10 << 1;
+ const FLAGS_LINK_MASK: u32 = 0b111111 << 1;
+ const FLAGS_HAS_NAME: u32 = 0b1 << 12;
+ const FLAGS_HAS_EXTRA_FAMILIES_SHIFT: u8 = 7;
+ const FLAGS_HAS_EXTRA_FAMILIES_MASK: u32 = 0b11 << Self::FLAGS_HAS_EXTRA_FAMILIES_SHIFT;
+
+ /// Create a new partition, with the given start and end sectors.
+ ///
+ /// It defaults to no permissions.
+ pub const fn new(first_sector: u16, last_sector: u16) -> Self {
+ // 0x2000 sectors of 4 KiB is 32 MiB, which is the total XIP area
+ assert!(first_sector < 0x2000);
+ assert!(last_sector < 0x2000);
+ assert!(first_sector <= last_sector);
+ Self {
+ permissions_and_location: (last_sector as u32) << 13 | first_sector as u32,
+ permissions_and_flags: 0,
+ id: None,
+ extra_families: [0; 4],
+ extra_families_len: 0,
+ name: [0; 128],
+ }
+ }
+
+ /// Create a new partition from run-time values.
+ ///
+ /// Get these from the ROM function `get_partition_table_info` with an argument of `PARTITION_LOCATION_AND_FLAGS`.
+ pub const fn from_raw(permissions_and_location: u32, permissions_and_flags: u32) -> Self {
+ Self {
+ permissions_and_location,
+ permissions_and_flags,
+ id: None,
+ extra_families: [0; 4],
+ extra_families_len: 0,
+ name: [0; 128],
+ }
+ }
+
+ /// Add a permission
+ pub const fn with_permission(self, permission: Permission) -> Self {
+ Self {
+ permissions_and_location: self.permissions_and_location | permission as u32,
+ permissions_and_flags: self.permissions_and_flags | permission as u32,
+ ..self
+ }
+ }
+
+ /// Set the name of the partition
+ pub const fn with_name(self, name: &str) -> Self {
+ let mut new_name = [0u8; 128];
+ let name = name.as_bytes();
+ let mut idx = 0;
+ new_name[0] = name.len() as u8;
+ while idx < name.len() {
+ new_name[idx + 1] = name[idx];
+ idx += 1;
+ }
+ Self {
+ name: new_name,
+ permissions_and_flags: self.permissions_and_flags | Self::FLAGS_HAS_NAME,
+ ..self
+ }
+ }
+
+ /// Set the extra families for the partition.
+ ///
+ /// You can supply up to four.
+ pub const fn with_extra_families(self, extra_families: &[u32]) -> Self {
+ assert!(extra_families.len() <= 4);
+ let mut new_extra_families = [0u32; 4];
+ let mut idx = 0;
+ while idx < extra_families.len() {
+ new_extra_families[idx] = extra_families[idx];
+ idx += 1;
+ }
+ Self {
+ extra_families: new_extra_families,
+ extra_families_len: extra_families.len(),
+ permissions_and_flags: (self.permissions_and_flags
+ & !Self::FLAGS_HAS_EXTRA_FAMILIES_MASK)
+ | (extra_families.len() as u32) << Self::FLAGS_HAS_EXTRA_FAMILIES_SHIFT,
+ ..self
+ }
+ }
+
+ /// Set the ID
+ pub const fn with_id(self, id: u64) -> Self {
+ Self {
+ id: Some(id),
+ permissions_and_flags: self.permissions_and_flags | Self::FLAGS_HAS_ID,
+ ..self
+ }
+ }
+
+ /// Add a link
+ pub const fn with_link(self, link: Link) -> Self {
+ let mut new_flags = self.permissions_and_flags & !Self::FLAGS_LINK_MASK;
+ match link {
+ Link::Nothing => {}
+ Link::ToA { partition_idx } => {
+ assert!(partition_idx < 16);
+ new_flags |= Self::FLAGS_LINK_TYPE_A_PARTITION;
+ new_flags |= (partition_idx as u32) << 3;
+ }
+ Link::ToOwner { partition_idx } => {
+ assert!(partition_idx < 16);
+ new_flags |= Self::FLAGS_LINK_TYPE_OWNER;
+ new_flags |= (partition_idx as u32) << 3;
+ }
+ }
+ Self {
+ permissions_and_flags: new_flags,
+ ..self
+ }
+ }
+
+ /// Set a flag
+ pub const fn with_flag(self, flag: PartitionFlag) -> Self {
+ Self {
+ permissions_and_flags: self.permissions_and_flags | flag as u32,
+ ..self
+ }
+ }
+
+ /// Get the partition start and end
+ ///
+ /// The offsets are in 4 KiB sectors, inclusive.
+ pub fn get_first_last_sectors(&self) -> (u16, u16) {
+ (
+ (self.permissions_and_location & 0x0000_1FFF) as u16,
+ ((self.permissions_and_location >> 13) & 0x0000_1FFF) as u16,
+ )
+ }
+
+ /// Get the partition start and end
+ ///
+ /// The offsets are in bytes, inclusive.
+ pub fn get_first_last_bytes(&self) -> (u32, u32) {
+ let (first, last) = self.get_first_last_sectors();
+ (u32::from(first) * 4096, (u32::from(last) * 4096) + 4095)
+ }
+
+ /// Check if it has a permission
+ pub fn has_permission(&self, permission: Permission) -> bool {
+ let mask = permission as u32;
+ (self.permissions_and_flags & mask) != 0
+ }
+
+ /// Get which extra families are allowed in this partition
+ pub fn get_extra_families(&self) -> &[u32] {
+ &self.extra_families[0..self.extra_families_len]
+ }
+
+ /// Get the name of the partition
+ ///
+ /// Returns `None` if there's no name, or the name is not valid UTF-8.
+ pub fn get_name(&self) -> Option<&str> {
+ let len = self.name[0] as usize;
+ if len == 0 {
+ None
+ } else {
+ core::str::from_utf8(&self.name[1..=len]).ok()
+ }
+ }
+
+ /// Get the ID
+ pub fn get_id(&self) -> Option {
+ self.id
+ }
+
+ /// Check if this partition is linked
+ pub fn get_link(&self) -> Link {
+ if (self.permissions_and_flags & Self::FLAGS_LINK_TYPE_A_PARTITION) != 0 {
+ let partition_idx = ((self.permissions_and_flags >> 3) & 0x0F) as u8;
+ Link::ToA { partition_idx }
+ } else if (self.permissions_and_flags & Self::FLAGS_LINK_TYPE_OWNER) != 0 {
+ let partition_idx = ((self.permissions_and_flags >> 3) & 0x0F) as u8;
+ Link::ToOwner { partition_idx }
+ } else {
+ Link::Nothing
+ }
+ }
+
+ /// Check if the partition has a flag set
+ pub fn has_flag(&self, flag: PartitionFlag) -> bool {
+ let mask = flag as u32;
+ (self.permissions_and_flags & mask) != 0
+ }
+}
+
+impl core::fmt::Display for Partition {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ let (first, last) = self.get_first_last_bytes();
+ write!(
+ f,
+ "{:#010x}..{:#010x} S:{}{} NS:{}{} B:{}{}",
+ first,
+ last,
+ if self.has_permission(Permission::SecureRead) {
+ 'R'
+ } else {
+ '_'
+ },
+ if self.has_permission(Permission::SecureWrite) {
+ 'W'
+ } else {
+ '_'
+ },
+ if self.has_permission(Permission::NonSecureRead) {
+ 'R'
+ } else {
+ '_'
+ },
+ if self.has_permission(Permission::NonSecureWrite) {
+ 'W'
+ } else {
+ '_'
+ },
+ if self.has_permission(Permission::BootRead) {
+ 'R'
+ } else {
+ '_'
+ },
+ if self.has_permission(Permission::BootWrite) {
+ 'W'
+ } else {
+ '_'
+ }
+ )
+ }
+}
+
+/// Describes a partition table.
+///
+/// Don't store this as a static - make sure you convert it to a block.
+#[derive(Clone)]
+pub struct PartitionTableBlock {
+ /// This must look like a block, including the 1 word header and the 3 word footer.
+ contents: [u32; PARTITION_TABLE_MAX_ITEMS],
+ /// This value doesn't include the 1 word header or the 3 word footer
+ num_items: usize,
+}
+
+impl PartitionTableBlock {
+ /// Create an empty Block, big enough for a partition table.
+ ///
+ /// At a minimum you need to call [`Self::add_partition_item`].
+ pub const fn new() -> PartitionTableBlock {
+ let mut contents = [0; PARTITION_TABLE_MAX_ITEMS];
+ contents[0] = BLOCK_MARKER_START;
+ contents[1] = item_last(0);
+ contents[2] = 0;
+ contents[3] = BLOCK_MARKER_END;
+ PartitionTableBlock {
+ contents,
+ num_items: 0,
+ }
+ }
+
+ /// Add a partition to the partition table
+ pub const fn add_partition_item(
+ self,
+ unpartitioned: UnpartitionedSpace,
+ partitions: &[Partition],
+ ) -> Self {
+ let mut new_table = PartitionTableBlock::new();
+ let mut idx = 0;
+ // copy over old table, with the header but not the footer
+ while idx < self.num_items + 1 {
+ new_table.contents[idx] = self.contents[idx];
+ idx += 1;
+ }
+
+ // 1. add item header space (we fill this in later)
+ let header_idx = idx;
+ new_table.contents[idx] = 0;
+ idx += 1;
+
+ // 2. unpartitioned space flags
+ //
+ // (the location of unpartition space is not recorded here - it is
+ // inferred because the unpartitioned space is where the partitions are
+ // not)
+ new_table.contents[idx] = unpartitioned.permissions_and_flags;
+ idx += 1;
+
+ // 3. partition info
+
+ let mut partition_no = 0;
+ while partition_no < partitions.len() {
+ // a. permissions_and_location (4K units)
+ new_table.contents[idx] = partitions[partition_no].permissions_and_location;
+ idx += 1;
+
+ // b. permissions_and_flags
+ new_table.contents[idx] = partitions[partition_no].permissions_and_flags;
+ idx += 1;
+
+ // c. ID
+ if let Some(id) = partitions[partition_no].id {
+ new_table.contents[idx] = id as u32;
+ new_table.contents[idx + 1] = (id >> 32) as u32;
+ idx += 2;
+ }
+
+ // d. Extra Families
+ let mut extra_families_idx = 0;
+ while extra_families_idx < partitions[partition_no].extra_families_len {
+ new_table.contents[idx] =
+ partitions[partition_no].extra_families[extra_families_idx];
+ idx += 1;
+ extra_families_idx += 1;
+ }
+
+ // e. Name
+ let mut name_idx = 0;
+ while name_idx < partitions[partition_no].name[0] as usize {
+ let name_chunk = [
+ partitions[partition_no].name[name_idx],
+ partitions[partition_no].name[name_idx + 1],
+ partitions[partition_no].name[name_idx + 2],
+ partitions[partition_no].name[name_idx + 3],
+ ];
+ new_table.contents[idx] = u32::from_le_bytes(name_chunk);
+ name_idx += 4;
+ idx += 1;
+ }
+
+ partition_no += 1;
+ }
+
+ let len = idx - header_idx;
+ new_table.contents[header_idx] =
+ item_generic_2bs(partitions.len() as u8, len as u16, ITEM_2BS_PARTITION_TABLE);
+
+ // 7. New Footer
+ new_table.contents[idx] = item_last(idx as u16 - 1);
+ new_table.contents[idx + 1] = 0;
+ new_table.contents[idx + 2] = BLOCK_MARKER_END;
+
+ // ignore the header
+ new_table.num_items = idx - 1;
+ new_table
+ }
+
+ /// Add a version number to the partition table
+ pub const fn with_version(self, major: u16, minor: u16) -> Self {
+ let mut new_table = PartitionTableBlock::new();
+ let mut idx = 0;
+ // copy over old table, with the header but not the footer
+ while idx < self.num_items + 1 {
+ new_table.contents[idx] = self.contents[idx];
+ idx += 1;
+ }
+
+ // 1. add item
+ new_table.contents[idx] = item_generic_2bs(0, 2, ITEM_1BS_VERSION);
+ idx += 1;
+ new_table.contents[idx] = (major as u32) << 16 | minor as u32;
+ idx += 1;
+
+ // 2. New Footer
+ new_table.contents[idx] = item_last(idx as u16 - 1);
+ new_table.contents[idx + 1] = 0;
+ new_table.contents[idx + 2] = BLOCK_MARKER_END;
+
+ // ignore the header
+ new_table.num_items = idx - 1;
+ new_table
+ }
+
+ /// Add a a SHA256 hash of the Block
+ ///
+ /// Adds a `HASH_DEF` covering all the previous items in the Block, and a
+ /// `HASH_VALUE` with a SHA-256 hash of them.
+ pub const fn with_sha256(self) -> Self {
+ let mut new_table = PartitionTableBlock::new();
+ let mut idx = 0;
+ // copy over old table, with the header but not the footer
+ while idx < self.num_items + 1 {
+ new_table.contents[idx] = self.contents[idx];
+ idx += 1;
+ }
+
+ // 1. HASH_DEF says what is hashed
+ new_table.contents[idx] = item_generic_2bs(1, 2, ITEM_2BS_HASH_DEF);
+ idx += 1;
+ // we're hashing all the previous contents - including this line.
+ new_table.contents[idx] = (idx + 1) as u32;
+ idx += 1;
+
+ // calculate hash over prior contents
+ let input = unsafe {
+ core::slice::from_raw_parts(new_table.contents.as_ptr() as *const u8, idx * 4)
+ };
+ let hash: [u8; 32] = sha2_const_stable::Sha256::new().update(input).finalize();
+
+ // 2. HASH_VALUE contains the hash
+ new_table.contents[idx] = item_generic_2bs(0, 9, ITEM_1BS_HASH_VALUE);
+ idx += 1;
+
+ let mut hash_idx = 0;
+ while hash_idx < hash.len() {
+ new_table.contents[idx] = u32::from_le_bytes([
+ hash[hash_idx],
+ hash[hash_idx + 1],
+ hash[hash_idx + 2],
+ hash[hash_idx + 3],
+ ]);
+ idx += 1;
+ hash_idx += 4;
+ }
+
+ // 3. New Footer
+ new_table.contents[idx] = item_last(idx as u16 - 1);
+ new_table.contents[idx + 1] = 0;
+ new_table.contents[idx + 2] = BLOCK_MARKER_END;
+
+ // ignore the header
+ new_table.num_items = idx - 1;
+ new_table
+ }
+}
+
+impl Default for PartitionTableBlock {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+/// Flags that a Partition can have
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[repr(u32)]
+#[allow(missing_docs)]
+pub enum PartitionFlag {
+ NotBootableArm = 1 << 9,
+ NotBootableRiscv = 1 << 10,
+ Uf2DownloadAbNonBootableOwnerAffinity = 1 << 11,
+ Uf2DownloadNoReboot = 1 << 13,
+ AcceptsDefaultFamilyRp2040 = 1 << 14,
+ AcceptsDefaultFamilyData = 1 << 16,
+ AcceptsDefaultFamilyRp2350ArmS = 1 << 17,
+ AcceptsDefaultFamilyRp2350Riscv = 1 << 18,
+ AcceptsDefaultFamilyRp2350ArmNs = 1 << 19,
+}
+
+/// Flags that a Partition can have
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[repr(u32)]
+#[allow(missing_docs)]
+pub enum UnpartitionedFlag {
+ Uf2DownloadNoReboot = 1 << 13,
+ AcceptsDefaultFamilyRp2040 = 1 << 14,
+ AcceptsDefaultFamilyAbsolute = 1 << 15,
+ AcceptsDefaultFamilyData = 1 << 16,
+ AcceptsDefaultFamilyRp2350ArmS = 1 << 17,
+ AcceptsDefaultFamilyRp2350Riscv = 1 << 18,
+ AcceptsDefaultFamilyRp2350ArmNs = 1 << 19,
+}
+
+/// Kinds of linked partition
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Link {
+ /// Not linked to anything
+ Nothing,
+ /// This is a B partition - link to our A partition.
+ ToA {
+ /// The index of our matching A partition.
+ partition_idx: u8,
+ },
+ /// Link to the partition that owns this one.
+ ToOwner {
+ /// The idx of our owner
+ partition_idx: u8,
+ },
+}
+
+/// Permissions that a Partition can have
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[repr(u32)]
+pub enum Permission {
+ /// Can be read in Secure Mode
+ ///
+ /// Corresponds to `PERMISSION_S_R_BITS` in the Pico SDK
+ SecureRead = 1 << 26,
+ /// Can be written in Secure Mode
+ ///
+ /// Corresponds to `PERMISSION_S_W_BITS` in the Pico SDK
+ SecureWrite = 1 << 27,
+ /// Can be read in Non-Secure Mode
+ ///
+ /// Corresponds to `PERMISSION_NS_R_BITS` in the Pico SDK
+ NonSecureRead = 1 << 28,
+ /// Can be written in Non-Secure Mode
+ ///
+ /// Corresponds to `PERMISSION_NS_W_BITS` in the Pico SDK
+ NonSecureWrite = 1 << 29,
+ /// Can be read in Non-Secure Bootloader mode
+ ///
+ /// Corresponds to `PERMISSION_NSBOOT_R_BITS` in the Pico SDK
+ BootRead = 1 << 30,
+ /// Can be written in Non-Secure Bootloader mode
+ ///
+ /// Corresponds to `PERMISSION_NSBOOT_W_BITS` in the Pico SDK
+ BootWrite = 1 << 31,
+}
+
+impl Permission {
+ /// Is this permission bit set this in this bitmask?
+ pub const fn is_in(self, mask: u32) -> bool {
+ (mask & (self as u32)) != 0
+ }
+}
+
+/// The supported RP2350 CPU architectures
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Architecture {
+ /// Core is in Arm Cortex-M33 mode
+ Arm,
+ /// Core is in RISC-V / Hazard3 mode
+ Riscv,
+}
+
+/// The kinds of Secure Boot we support
+#[derive(Debug, Copy, Clone)]
+pub enum Security {
+ /// Security mode not given
+ Unspecified,
+ /// Start in Non-Secure mode
+ NonSecure,
+ /// Start in Secure mode
+ Secure,
+}
+
+/// Make an item containing a tag, 1 byte length and two extra bytes.
+///
+/// The `command` arg should contain `1BS`
+pub const fn item_generic_1bs(value: u16, length: u8, command: u8) -> u32 {
+ ((value as u32) << 16) | ((length as u32) << 8) | (command as u32)
+}
+
+/// Make an item containing a tag, 2 byte length and one extra byte.
+///
+/// The `command` arg should contain `2BS`
+pub const fn item_generic_2bs(value: u8, length: u16, command: u8) -> u32 {
+ ((value as u32) << 24) | ((length as u32) << 8) | (command as u32)
+}
+
+/// Create Image Type item, of type IGNORED.
+pub const fn item_ignored() -> u32 {
+ item_generic_2bs(0, 1, ITEM_2BS_IGNORED)
+}
+
+/// Create Image Type item, of type INVALID.
+pub const fn item_image_type_invalid() -> u32 {
+ let value = IMAGE_TYPE_INVALID;
+ item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
+}
+
+/// Create Image Type item, of type DATA.
+pub const fn item_image_type_data() -> u32 {
+ let value = IMAGE_TYPE_DATA;
+ item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
+}
+
+/// Create Image Type item, of type EXE.
+pub const fn item_image_type_exe(security: Security, arch: Architecture) -> u32 {
+ let mut value = IMAGE_TYPE_EXE | IMAGE_TYPE_EXE_CHIP_RP2350;
+
+ match arch {
+ Architecture::Arm => {
+ value |= IMAGE_TYPE_EXE_CPU_ARM;
+ }
+ Architecture::Riscv => {
+ value |= IMAGE_TYPE_EXE_CPU_RISCV;
+ }
+ }
+
+ match security {
+ Security::Unspecified => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_UNSPECIFIED,
+ Security::NonSecure => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_NS,
+ Security::Secure => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_S,
+ }
+
+ item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
+}
+
+/// Create a Block Last item.
+pub const fn item_last(length: u16) -> u32 {
+ item_generic_2bs(0, length, ITEM_2BS_LAST)
+}
+
+/// Create a Vector Table item.
+///
+/// This is only allowed on Arm systems.
+pub const fn item_vector_table(table_ptr: u32) -> [u32; 2] {
+ [item_generic_1bs(0, 2, ITEM_1BS_VECTOR_TABLE), table_ptr]
+}
+
+/// Create an Entry Point item.
+pub const fn item_entry_point(entry_point: u32, initial_sp: u32) -> [u32; 3] {
+ [
+ item_generic_1bs(0, 3, ITEM_1BS_ENTRY_POINT),
+ entry_point,
+ initial_sp,
+ ]
+}
+
+/// Create an Rolling Window item.
+///
+/// The delta is the number of bytes into the image that 0x10000000 should
+/// be mapped.
+pub const fn item_rolling_window(delta: u32) -> [u32; 2] {
+ [item_generic_1bs(0, 3, ITEM_1BS_ROLLING_WINDOW_DELTA), delta]
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ /// I used this JSON, with `picotool partition create`:
+ ///
+ /// ```json
+ /// {
+ /// "version": [1, 0],
+ /// "unpartitioned": {
+ /// "families": ["absolute"],
+ /// "permissions": {
+ /// "secure": "rw",
+ /// "nonsecure": "rw",
+ /// "bootloader": "rw"
+ /// }
+ /// },
+ /// "partitions": [
+ /// {
+ /// "name": "A",
+ /// "id": 0,
+ /// "size": "2044K",
+ /// "families": ["rp2350-arm-s", "rp2350-riscv"],
+ /// "permissions": {
+ /// "secure": "rw",
+ /// "nonsecure": "rw",
+ /// "bootloader": "rw"
+ /// }
+ /// },
+ /// {
+ /// "name": "B",
+ /// "id": 1,
+ /// "size": "2044K",
+ /// "families": ["rp2350-arm-s", "rp2350-riscv"],
+ /// "permissions": {
+ /// "secure": "rw",
+ /// "nonsecure": "rw",
+ /// "bootloader": "rw"
+ /// },
+ /// "link": ["a", 0]
+ /// }
+ /// ]
+ /// }
+ /// ```
+ #[test]
+ fn make_hashed_partition_table() {
+ let table = PartitionTableBlock::new()
+ .add_partition_item(
+ UnpartitionedSpace::new()
+ .with_permission(Permission::SecureRead)
+ .with_permission(Permission::SecureWrite)
+ .with_permission(Permission::NonSecureRead)
+ .with_permission(Permission::NonSecureWrite)
+ .with_permission(Permission::BootRead)
+ .with_permission(Permission::BootWrite)
+ .with_flag(UnpartitionedFlag::AcceptsDefaultFamilyAbsolute),
+ &[
+ Partition::new(2, 512)
+ .with_id(0)
+ .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350ArmS)
+ .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350Riscv)
+ .with_permission(Permission::SecureRead)
+ .with_permission(Permission::SecureWrite)
+ .with_permission(Permission::NonSecureRead)
+ .with_permission(Permission::NonSecureWrite)
+ .with_permission(Permission::BootRead)
+ .with_permission(Permission::BootWrite)
+ .with_name("A"),
+ Partition::new(513, 1023)
+ .with_id(1)
+ .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350ArmS)
+ .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350Riscv)
+ .with_link(Link::ToA { partition_idx: 0 })
+ .with_permission(Permission::SecureRead)
+ .with_permission(Permission::SecureWrite)
+ .with_permission(Permission::NonSecureRead)
+ .with_permission(Permission::NonSecureWrite)
+ .with_permission(Permission::BootRead)
+ .with_permission(Permission::BootWrite)
+ .with_name("B"),
+ ],
+ )
+ .with_version(1, 0)
+ .with_sha256();
+ let expected = &[
+ 0xffffded3, // start
+ 0x02000c0a, // Item = PARTITION_TABLE
+ 0xfc008000, // Unpartitioned Space - permissions_and_flags
+ 0xfc400002, // Partition 0 - permissions_and_location (512 * 4096, 2 * 4096)
+ 0xfc061001, // permissions_and_flags HAS_ID | HAS_NAME | ARM-S | RISC-V
+ 0x00000000, // ID
+ 0x00000000, // ID
+ 0x00004101, // Name ("A")
+ 0xfc7fe201, // Partition 1 - permissions_and_location (1023 * 4096, 513 * 4096)
+ 0xfc061003, // permissions_and_flags LINKA(0) | HAS_ID | HAS_NAME | ARM-S | RISC-V
+ 0x00000001, // ID
+ 0x00000000, // ID
+ 0x00004201, // Name ("B")
+ 0x00000248, // Item = Version
+ 0x00010000, // 0, 1
+ 0x01000247, // HASH_DEF with 2 words, and SHA256 hash
+ 0x00000011, // 17 words hashed
+ 0x0000094b, // HASH_VALUE with 9 words
+ 0x1945cdad, // Hash word 0
+ 0x6b5f9773, // Hash word 1
+ 0xe2bf39bd, // Hash word 2
+ 0xb243e599, // Hash word 3
+ 0xab2f0e9a, // Hash word 4
+ 0x4d5d6d0b, // Hash word 5
+ 0xf973050f, // Hash word 6
+ 0x5ab6dadb, // Hash word 7
+ 0x000019ff, // Last Item
+ 0x00000000, // Block Loop Next Offset
+ 0xab123579, // End
+ ];
+ assert_eq!(
+ &table.contents[..29],
+ expected,
+ "{:#010x?}\n != \n{:#010x?}",
+ &table.contents[0..29],
+ expected,
+ );
+ }
+}
diff --git a/rp235x-hal/src/clocks/clock_sources.rs b/rp235x-hal/src/clocks/clock_sources.rs
new file mode 100644
index 000000000..ccf1398d5
--- /dev/null
+++ b/rp235x-hal/src/clocks/clock_sources.rs
@@ -0,0 +1,118 @@
+//! Available clocks
+
+use super::*;
+use crate::{
+ gpio::{
+ bank0::{Gpio20, Gpio22},
+ FunctionClock, Pin, PullNone, PullType,
+ },
+ lposc::LowPowerOscillator,
+ pll::{Locked, PhaseLockedLoop},
+ rosc::{Enabled as RingOscillatorEnabled, RingOscillator},
+ typelevel::Sealed,
+ xosc::{CrystalOscillator, Stable},
+};
+use pac::{PLL_SYS, PLL_USB};
+
+/// System PLL.
+pub(crate) type PllSys = PhaseLockedLoop;
+impl Sealed for PllSys {}
+impl ClockSource for PllSys {
+ fn get_freq(&self) -> HertzU32 {
+ self.operating_frequency()
+ }
+}
+
+/// USB PLL.
+pub(crate) type PllUsb = PhaseLockedLoop;
+impl Sealed for PllUsb {}
+impl ClockSource for PllUsb {
+ fn get_freq(&self) -> HertzU32 {
+ self.operating_frequency()
+ }
+}
+
+// The USB Clock Generator is a clock source
+impl ClockSource for UsbClock {
+ fn get_freq(&self) -> HertzU32 {
+ self.frequency
+ }
+}
+
+// The ADC Clock Generator is a clock source
+impl ClockSource for AdcClock {
+ fn get_freq(&self) -> HertzU32 {
+ self.frequency
+ }
+}
+
+// The HSTX Clock Generator is a clock source
+impl ClockSource for HstxClock {
+ fn get_freq(&self) -> HertzU32 {
+ self.frequency
+ }
+}
+
+// The System Clock Generator is a clock source
+impl ClockSource for SystemClock {
+ fn get_freq(&self) -> HertzU32 {
+ self.frequency
+ }
+}
+
+// The Reference Clock Generator is a clock source
+impl ClockSource for ReferenceClock {
+ fn get_freq(&self) -> HertzU32 {
+ self.frequency
+ }
+}
+
+// The Peripheral Clock Generator is a clock source
+impl ClockSource for PeripheralClock {
+ fn get_freq(&self) -> HertzU32 {
+ self.frequency
+ }
+}
+
+// The Low Power Oscillator is a clock source
+pub(crate) type LpOsc = LowPowerOscillator;
+impl ClockSource for LpOsc {
+ fn get_freq(&self) -> HertzU32 {
+ 32768.Hz()
+ }
+}
+
+// The Crystal Oscillator
+pub(crate) type Xosc = CrystalOscillator;
+impl Sealed for Xosc {}
+impl ClockSource for Xosc {
+ fn get_freq(&self) -> HertzU32 {
+ self.operating_frequency()
+ }
+}
+
+// The Ring Oscillator
+pub(crate) type Rosc = RingOscillator;
+impl Sealed for Rosc {}
+// We are assuming the second output is never phase shifted (see 2.17.4)
+impl ClockSource for RingOscillator {
+ fn get_freq(&self) -> HertzU32 {
+ self.operating_frequency()
+ }
+}
+
+// GPIN0
+pub(crate) type GPin0 = Pin;
+impl ClockSource for GPin0 {
+ fn get_freq(&self) -> HertzU32 {
+ todo!()
+ }
+}
+
+// GPIN1
+pub(crate) type GPin1 = Pin;
+impl ClockSource for Pin {
+ fn get_freq(&self) -> HertzU32 {
+ todo!()
+ }
+}
diff --git a/rp235x-hal/src/clocks/macros.rs b/rp235x-hal/src/clocks/macros.rs
new file mode 100644
index 000000000..954c6240c
--- /dev/null
+++ b/rp235x-hal/src/clocks/macros.rs
@@ -0,0 +1,431 @@
+macro_rules! clocks {
+ (
+ $(
+ $(#[$attr:meta])*
+ struct $name:ident {
+ init_freq: $init_freq:expr,
+ reg: $reg:ident,
+ $(src: {$($src:ident: $src_variant:ident),*},)?
+ auxsrc: {$($auxsrc:ident: $aux_variant:ident),*}
+ $(, div: $div:tt)?
+ }
+ )*
+
+ ) => {
+
+ $crate::paste::paste!{
+ /// Abstraction layer providing Clock Management.
+ pub struct ClocksManager {
+ clocks: CLOCKS,
+ $(
+ #[doc = "`" $name "` field"]
+ pub [<$name:snake>]: $name,
+ )*
+ }
+
+ impl ClocksManager {
+ /// Exchanges CLOCKS block against Self.
+ pub fn new(mut clocks_block: CLOCKS) -> Self {
+ // Disable resus that may be enabled from previous software
+ unsafe {
+ clocks_block.clk_sys_resus_ctrl().write_with_zero(|w| w);
+ }
+
+ let shared_clocks = ShareableClocks::new(&mut clocks_block);
+ ClocksManager {
+ clocks: clocks_block,
+ $(
+ [<$name:snake>]: $name {
+ shared_dev: shared_clocks,
+ frequency: $init_freq.Hz(),
+ },
+ )*
+ }
+ }
+ }
+ }
+
+ $(
+ clock!(
+ $(#[$attr])*
+ struct $name {
+ reg: $reg,
+ $(src: {$($src: $src_variant),*},)?
+ auxsrc: {$($auxsrc: $aux_variant),*}
+ $(, div: $div )?
+ }
+ );
+ )*
+ };
+}
+
+macro_rules! clock {
+ {
+ $(#[$attr:meta])*
+ struct $name:ident {
+ reg: $reg:ident,
+ src: {$($src:ident: $src_variant:ident),*},
+ auxsrc: {$($auxsrc:ident: $aux_variant:ident),*}
+ }
+ } => {
+ base_clock!{
+ $(#[$attr])*
+ ($name, $reg, auxsrc={$($auxsrc: $aux_variant),*})
+ }
+
+ divisible_clock!($name, $reg);
+
+ $crate::paste::paste!{
+ $(impl ValidSrc<$name> for $src {
+ fn is_aux(&self) -> bool{
+ false
+ }
+ fn variant(&self) -> [<$reg:camel SrcType>] {
+ [<$reg:camel SrcType>]::Src($crate::pac::clocks::[<$reg _ctrl>]::SRC_A::$src_variant)
+ }
+ })*
+
+ impl GlitchlessClock for $name {
+ type Clock = Self;
+
+ fn await_select(&self, clock_token: &ChangingClockToken) -> nb::Result<(), Infallible> {
+ let shared_dev = unsafe { self.shared_dev.get() };
+
+ let selected = shared_dev.[<$reg _selected>]().read().bits();
+ if selected != 1 << clock_token.clock_nr {
+ return Err(nb::Error::WouldBlock);
+ }
+
+ Ok(())
+ }
+ }
+
+ #[doc = "Holds register value for ClockSource for `" $name "`"]
+ pub enum [<$reg:camel SrcType>] {
+ #[doc = "Contains a valid clock source register value that is to be used to set a clock as glitchless source for `" $name "`"]
+ Src($crate::pac::clocks::[<$reg _ctrl>]::SRC_A),
+ #[doc = "Contains a valid clock source register value that is to be used to set a clock as aux source for `" $name "`"]
+ Aux($crate::pac::clocks::[<$reg _ctrl>]::AUXSRC_A)
+ }
+
+ impl [<$reg:camel SrcType>] {
+ fn get_clock_id(&self) -> u8 {
+ match self {
+ Self::Src(v) => *v as u8,
+ Self::Aux(v) => *v as u8,
+ }
+ }
+
+ fn unwrap_src(&self) -> $crate::pac::clocks::[<$reg _ctrl>]::SRC_A{
+ match self {
+ Self::Src(v) => *v,
+ Self::Aux(_) => panic!(),
+ }
+ }
+
+ fn unwrap_aux(&self) -> $crate::pac::clocks::[<$reg _ctrl>]::AUXSRC_A {
+ match self {
+ Self::Src(_) => panic!(),
+ Self::Aux(v) => *v
+ }
+ }
+ }
+
+ impl $name {
+ /// Reset clock back to its reset source
+ pub fn reset_source_await(&mut self) -> nb::Result<(), Infallible> {
+ let shared_dev = unsafe { self.shared_dev.get() };
+
+ shared_dev.[<$reg _ctrl>]().modify(|_, w| {
+ w.src().variant(self.get_default_clock_source())
+ });
+
+ use fugit::RateExtU32;
+ self.frequency = 12.MHz(); //TODO Get actual clock source.. Most likely 12 MHz though
+
+ self.await_select(&ChangingClockToken{clock_nr:0, clock: PhantomData::})
+ }
+
+ fn set_src>(&mut self, src: &S)-> ChangingClockToken<$name> {
+ let shared_dev = unsafe { self.shared_dev.get() };
+
+ shared_dev.[<$reg _ctrl>]().modify(|_,w| {
+ w.src().variant(src.variant().unwrap_src())
+ });
+
+ ChangingClockToken {
+ clock: PhantomData::<$name>,
+ clock_nr: src.variant().get_clock_id(),
+ }
+ }
+
+ fn set_self_aux_src(&mut self) -> ChangingClockToken<$name> {
+ unsafe { self.shared_dev.get() }.[<$reg _ctrl>]().modify(|_, w| {
+ w.src().variant(self.get_aux_source())
+ });
+
+ ChangingClockToken{
+ clock: PhantomData::<$name>,
+ clock_nr: $crate::pac::clocks::clk_ref_ctrl::SRC_A::CLKSRC_CLK_REF_AUX as u8,
+ }
+ }
+ }
+
+ impl Clock for $name {
+ type Variant = [<$reg:camel SrcType>];
+
+ #[doc = "Get operating frequency for `" $name "`"]
+ fn freq(&self) -> HertzU32 {
+ self.frequency
+ }
+
+ #[doc = "Configure `" $name "`"]
+ fn configure_clock>(&mut self, src: &S, freq: HertzU32) -> Result<(), ClockError>{
+ let src_freq: HertzU32 = src.get_freq().into();
+
+ if freq.gt(&src_freq){
+ return Err(ClockError::CantIncreaseFreq);
+ }
+
+ let div = fractional_div(src_freq.to_Hz(), freq.to_Hz()).ok_or(ClockError::FrequencyTooLow)?;
+
+ // If increasing divisor, set divisor before source. Otherwise set source
+ // before divisor. This avoids a momentary overspeed when e.g. switching
+ // to a faster source and increasing divisor to compensate.
+ if div > self.get_div() {
+ self.set_div(div);
+ }
+
+
+ // Set aux mux first, and then glitchless mux if this self has one
+ let token = if src.is_aux() {
+ // If switching a glitchless slice (ref or sys) to an aux source, switch
+ // away from aux *first* to avoid passing glitches when changing aux mux.
+ // Assume (!!!) glitchless source 0 is no faster than the aux source.
+ nb::block!(self.reset_source_await()).unwrap();
+
+ self.set_aux(src);
+ self.set_self_aux_src()
+ } else {
+ self.set_src(src)
+ };
+
+ nb::block!(self.await_select(&token)).unwrap();
+
+
+ // Now that the source is configured, we can trust that the user-supplied
+ // divisor is a safe value.
+ self.set_div(div);
+
+ // Store the configured frequency
+ use fugit::RateExtU32;
+ self.frequency = fractional_div(src_freq.to_Hz(), div).ok_or(ClockError::FrequencyTooHigh)?.Hz();
+
+ Ok(())
+ }
+ }
+ }
+ };
+ {
+ $( #[$attr:meta])*
+ struct $name:ident {
+ reg: $reg:ident,
+ auxsrc: {$($auxsrc:ident: $variant:ident),*},
+ div: false
+ }
+ } => {
+ base_clock!{
+ $(#[$attr])*
+ ($name, $reg, auxsrc={$($auxsrc: $variant),*})
+ }
+
+ // Just to match proper divisible clocks so we don't have to do something special in configure function
+ impl ClockDivision for $name {
+ fn set_div(&mut self, _: u32) {}
+ fn get_div(&self) -> u32 {1}
+ }
+
+ stoppable_clock!($name, $reg);
+ };
+ {
+ $( #[$attr:meta])*
+ struct $name:ident {
+ reg: $reg:ident,
+ auxsrc: {$($auxsrc:ident: $variant:ident),*}
+ }
+ } => {
+ base_clock!{
+ $(#[$attr])*
+ ($name, $reg, auxsrc={$($auxsrc: $variant),*})
+ }
+
+ divisible_clock!($name, $reg);
+ stoppable_clock!($name, $reg);
+ };
+}
+
+macro_rules! divisible_clock {
+ ($name:ident, $reg:ident) => {
+ $crate::paste::paste! {
+ impl ClockDivision for $name {
+ fn set_div(&mut self, div: u32) {
+ unsafe { self.shared_dev.get() }.[<$reg _div>]().modify(|_, w| unsafe {
+ w.bits(div);
+ w
+ });
+ }
+ fn get_div(&self) -> u32 {
+ unsafe { self.shared_dev.get() }.[<$reg _div>]().read().bits()
+ }
+ }
+ }
+ };
+}
+
+macro_rules! stoppable_clock {
+ ($name:ident, $reg:ident) => {
+ $crate::paste::paste!{
+ #[doc = "Holds register value for ClockSource for `" $name "`"]
+ pub enum [<$reg:camel SrcType>] {
+ #[doc = "Contains a valid clock source register value that is to be used to set a clock as aux source for `" $name "`"]
+ Aux($crate::pac::clocks::[<$reg _ctrl>]::AUXSRC_A)
+ }
+
+ impl [<$reg:camel SrcType>] {
+ fn unwrap_aux(&self) -> $crate::pac::clocks::[<$reg _ctrl>]::AUXSRC_A {
+ match self {
+ Self::Aux(v) => *v
+ }
+ }
+ }
+
+ impl StoppableClock for $name {
+ /// Enable the clock
+ fn enable(&mut self) {
+ unsafe { self.shared_dev.get() }.[<$reg _ctrl>]().modify(|_, w| {
+ w.enable().set_bit()
+ });
+ }
+
+ /// Disable the clock cleanly
+ fn disable(&mut self) {
+ unsafe { self.shared_dev.get() }.[<$reg _ctrl>]().modify(|_, w| {
+ w.enable().clear_bit()
+ });
+ }
+
+ /// Disable the clock asynchronously
+ fn kill(&mut self) {
+ unsafe { self.shared_dev.get() }.[<$reg _ctrl>]().modify(|_, w| {
+ w.kill().set_bit()
+ });
+ }
+ }
+
+ impl Clock for $name {
+ type Variant = [<$reg:camel SrcType>];
+
+ #[doc = "Get operating frequency for `" $name "`"]
+ fn freq(&self) -> HertzU32 {
+ self.frequency
+ }
+
+ #[doc = "Configure `" $name "`"]
+ fn configure_clock>(&mut self, src: &S, freq: HertzU32) -> Result<(), ClockError>{
+ let src_freq: HertzU32 = src.get_freq().into();
+
+ if freq.gt(&src_freq){
+ return Err(ClockError::CantIncreaseFreq);
+ }
+
+ let div = fractional_div(src_freq.to_Hz(), freq.to_Hz()).ok_or(ClockError::FrequencyTooLow)?;
+
+ // If increasing divisor, set divisor before source. Otherwise set source
+ // before divisor. This avoids a momentary overspeed when e.g. switching
+ // to a faster source and increasing divisor to compensate.
+ if div > self.get_div() {
+ self.set_div(div);
+ }
+
+ // If no glitchless mux, cleanly stop the clock to avoid glitches
+ // propagating when changing aux mux. Note it would be a really bad idea
+ // to do this on one of the glitchless clocks (clk_sys, clk_ref).
+
+ // Disable clock. On clk_ref and clk_sys this does nothing,
+ // all other clocks have the ENABLE bit in the same position.
+ self.disable();
+ if self.frequency > HertzU32::Hz(0) {
+ // Delay for 3 cycles of the target clock, for ENABLE propagation.
+ // Note XOSC_COUNT is not helpful here because XOSC is not
+ // necessarily running, nor is timer... so, 3 cycles per loop:
+ let sys_freq = 125_000_000; // TODO get actual sys_clk frequency
+ let delay_cyc = sys_freq / self.frequency.to_Hz() + 1u32;
+ crate::arch::delay(delay_cyc);
+ }
+
+ // Set aux mux first, and then glitchless mux if this self has one
+ self.set_aux(src);
+
+ // Enable clock. On clk_ref and clk_sys this does nothing,
+ // all other clocks have the ENABLE bit in the same posi
+ self.enable();
+
+ // Now that the source is configured, we can trust that the user-supplied
+ // divisor is a safe value.
+ self.set_div(div);
+
+ // Store the configured frequency
+ use fugit::RateExtU32;
+ self.frequency = fractional_div(src_freq.to_Hz(), div).ok_or(ClockError::FrequencyTooHigh)?.Hz();
+
+ Ok(())
+ }
+ }
+ }
+ };
+}
+
+macro_rules! base_clock {
+ {
+ $(#[$attr:meta])*
+ ($name:ident, $reg:ident, auxsrc={$($auxsrc:ident: $variant:ident),*})
+ } => {
+ $crate::paste::paste!{
+
+ $(impl ValidSrc<$name> for $auxsrc {
+
+ fn is_aux(&self) -> bool{
+ true
+ }
+ fn variant(&self) -> [<$reg:camel SrcType>] {
+ [<$reg:camel SrcType>]::Aux($crate::pac::clocks::[<$reg _ctrl>]::AUXSRC_A::$variant)
+ }
+ })*
+
+ $(#[$attr])*
+ pub struct $name {
+ shared_dev: ShareableClocks,
+ frequency: HertzU32,
+ }
+
+ impl $name {
+ fn set_aux>(&mut self, src: &S) {
+ let shared_dev = unsafe { self.shared_dev.get() };
+
+ shared_dev.[<$reg _ctrl>]().modify(|_,w| {
+ w.auxsrc().variant(src.variant().unwrap_aux())
+ });
+ }
+ }
+
+ impl Sealed for $name {}
+
+ impl From<&$name> for HertzU32
+ {
+ fn from(value: &$name) -> HertzU32 {
+ value.frequency
+ }
+ }
+ }
+ };
+}
diff --git a/rp235x-hal/src/clocks/mod.rs b/rp235x-hal/src/clocks/mod.rs
new file mode 100644
index 000000000..f9d0c6534
--- /dev/null
+++ b/rp235x-hal/src/clocks/mod.rs
@@ -0,0 +1,746 @@
+//! Clocks (CLOCKS)
+//!
+//!
+//!
+//! ## Usage simple
+//! ```no_run
+//! use rp235x_hal::{self as hal, clocks::init_clocks_and_plls, watchdog::Watchdog};
+//!
+//! let mut peripherals = hal::pac::Peripherals::take().unwrap();
+//! let mut watchdog = Watchdog::new(peripherals.WATCHDOG);
+//! const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
+//! let mut clocks = init_clocks_and_plls(
+//! XOSC_CRYSTAL_FREQ,
+//! peripherals.XOSC,
+//! peripherals.CLOCKS,
+//! peripherals.PLL_SYS,
+//! peripherals.PLL_USB,
+//! &mut peripherals.RESETS,
+//! &mut watchdog,
+//! )
+//! .ok()
+//! .unwrap();
+//! ```
+//!
+//! ## Usage extended
+//! ```no_run
+//! use fugit::RateExtU32;
+//! use rp235x_hal::{clocks::{Clock, ClocksManager, ClockSource, InitError}, gpio::Pins, self as hal, pll::{common_configs::{PLL_SYS_150MHZ, PLL_USB_48MHZ}, setup_pll_blocking}, Sio, watchdog::Watchdog, xosc::setup_xosc_blocking};
+//!
+//! # fn func() -> Result<(), InitError> {
+//! let mut peripherals = hal::pac::Peripherals::take().unwrap();
+//! let mut watchdog = Watchdog::new(peripherals.WATCHDOG);
+//! const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
+//!
+//! // Enable the xosc
+//! let xosc = setup_xosc_blocking(peripherals.XOSC, XOSC_CRYSTAL_FREQ.Hz()).map_err(InitError::XoscErr)?;
+//!
+//! // Start tick in watchdog
+//! watchdog.enable_tick_generation((XOSC_CRYSTAL_FREQ / 1_000_000) as u16);
+//!
+//! let mut clocks = ClocksManager::new(peripherals.CLOCKS);
+//!
+//! // Configure PLLs
+//! // REF FBDIV VCO POSTDIV
+//! // PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 5 / 2 = 150MHz
+//! // PLL USB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz
+//! let pll_sys = setup_pll_blocking(peripherals.PLL_SYS, xosc.operating_frequency().into(), PLL_SYS_150MHZ, &mut clocks, &mut peripherals.RESETS).map_err(InitError::PllError)?;
+//! let pll_usb = setup_pll_blocking(peripherals.PLL_USB, xosc.operating_frequency().into(), PLL_USB_48MHZ, &mut clocks, &mut peripherals.RESETS).map_err(InitError::PllError)?;
+//!
+//! // Configure clocks
+//! // CLK_REF = XOSC (12MHz) / 1 = 12MHz
+//! clocks.reference_clock.configure_clock(&xosc, xosc.get_freq()).map_err(InitError::ClockError)?;
+//!
+//! // CLK SYS = PLL SYS (150MHz) / 1 = 150MHz
+//! clocks.system_clock.configure_clock(&pll_sys, pll_sys.get_freq()).map_err(InitError::ClockError)?;
+//!
+//! // CLK USB = PLL USB (48MHz) / 1 = 48MHz
+//! clocks.usb_clock.configure_clock(&pll_usb, pll_usb.get_freq()).map_err(InitError::ClockError)?;
+//!
+//! // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
+//! clocks.adc_clock.configure_clock(&pll_usb, pll_usb.get_freq()).map_err(InitError::ClockError)?;
+//!
+//! // CLK HSTX = PLL SYS (150MHz) / 1 = 150MHz
+//! clocks.hstx_clock.configure_clock(&pll_sys, pll_sys.get_freq()).map_err(InitError::ClockError)?;
+//!
+//! // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
+//! // Normally choose clk_sys or clk_usb
+//! clocks.peripheral_clock.configure_clock(&clocks.system_clock, clocks.system_clock.freq()).map_err(InitError::ClockError)?;
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! See [Chapter 8](https://rptl.io/rp2350-datasheet#section_clocks) for more details.
+
+use core::{convert::Infallible, marker::PhantomData};
+use fugit::{HertzU32, RateExtU32};
+
+use crate::{
+ pac::{self, CLOCKS, PLL_SYS, PLL_USB, RESETS, XOSC},
+ pll::{
+ common_configs::{PLL_SYS_150MHZ, PLL_USB_48MHZ},
+ setup_pll_blocking, Error as PllError, Locked, PhaseLockedLoop,
+ },
+ typelevel::Sealed,
+ watchdog::Watchdog,
+ xosc::{setup_xosc_blocking, CrystalOscillator, Error as XoscError, Stable},
+};
+
+#[macro_use]
+mod macros;
+mod clock_sources;
+
+use clock_sources::PllSys;
+
+use self::clock_sources::{GPin0, GPin1, LpOsc, PllUsb, Rosc, Xosc};
+
+bitfield::bitfield! {
+ /// Bit field mapping clock enable bits.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Default)]
+ pub struct ClockGate(u64);
+ /// Clock gate to the clock controller.
+ pub sys_clock, set_sys_clock: 0;
+ /// Clock gate the hardware access control registers
+ pub sys_accessctrl, set_sys_accessctrl: 1;
+ /// Clock gate to the adc analog logic.
+ pub adc_adc, set_adc_adc: 2;
+ /// Clock gate to the adc peripheral.
+ pub sys_adc, set_sys_adc: 3;
+ /// Clock gate the Boot RAM
+ pub sys_bootram, set_sys_bootram: 4;
+ /// Clock gate the memory bus controller.
+ pub sys_busctrl, set_sys_busctrl: 5;
+ /// Clock gate the memory bus fabric.
+ pub sys_busfabric, set_sys_busfabric: 6;
+ /// Clock gate the dma controller.
+ pub sys_dma, set_sys_dma: 7;
+ /// Clock gate the Glitch Detector peripheral
+ pub sys_glitch_detector, set_sys_glitch_detector: 8;
+ /// Clock gate the High-Speed TX peripheral
+ pub hstx, set_hstx: 9;
+ /// Clock gate the High-Speed TX peripheral
+ pub sys_hstx, set_sys_hstx: 10;
+ /// Clock gate I2C0.
+ pub sys_i2c0, set_sys_i2c0: 11;
+ /// Clock gate I2C1.
+ pub sys_i2c1, set_sys_i2c1: 12;
+ /// Clock gate the IO controller.
+ pub sys_io, set_sys_io: 13;
+ /// Clock gate the JTAG peripheral.
+ pub sys_jtag, set_sys_jtag: 14;
+ /// Clock gate the OTP (one-time programmable memory) peripheral
+ pub ref_otp, set_ref_otp: 15;
+ /// Clock gate the OTP (one-time programmable memory) peripheral
+ pub sys_otp, set_sys_otp: 16;
+ /// Clock gate pad controller.
+ pub sys_pads, set_sys_pads: 17;
+ /// Clock gate PIO0 peripheral.
+ pub sys_pio0, set_sys_pio0: 18;
+ /// Clock gate PIO1 peripheral.
+ pub sys_pio1, set_sys_pio1: 19;
+ /// Clock gate PIO2 peripheral.
+ pub sys_pio2, set_sys_pio2: 20;
+ /// Clock gate the system PLL.
+ pub sys_pll_sys, set_sys_pll_sys: 21;
+ /// Clock gate the USB PLL.
+ pub sys_pll_usb, set_sys_pll_usb: 22;
+ /// Clock gate the Power Manager
+ pub ref_powman, set_ref_powman: 23;
+ /// Clock gate the Power Manager
+ pub sys_powman, set_sys_powman: 24;
+ /// Clock gate PWM peripheral.
+ pub sys_pwm, set_sys_pwm: 25;
+ /// Clock gate the reset controller.
+ pub sys_resets, set_sys_resets: 26;
+ /// Clock gate the ROM.
+ pub sys_rom, set_sys_rom: 27;
+ /// Clock gate the ROSC controller (not the rosc itself).
+ pub sys_rosc, set_sys_rosc: 28;
+ /// Clock gate the Power State Machine
+ pub sys_psm, set_sys_psm: 29;
+ /// Clock gate the SHA256 peripheral
+ pub sys_sha256, set_sys_sha256: 30;
+ /// Clock gate the SIO controller.
+ pub sys_sio, set_sys_sio: 31;
+
+ /// Clock gate SPI0's baud generation.
+ pub peri_spi0, set_peri_spi0: 32;
+ /// Clock gate SPI0's controller..
+ pub sys_spi0, set_sys_spi0: 33;
+ /// Clock gate SPI1's baud generation.
+ pub peri_spi1, set_peri_spi1: 34;
+ /// Clock gate SPI1's controller..
+ pub sys_spi1, set_sys_spi1: 35;
+ /// Clock gate SRAM0.
+ pub sys_sram0, set_sys_sram0: 36;
+ /// Clock gate SRAM1.
+ pub sys_sram1, set_sys_sram1: 37;
+ /// Clock gate SRAM2.
+ pub sys_sram2, set_sys_sram2: 38;
+ /// Clock gate SRAM3.
+ pub sys_sram3, set_sys_sram3: 39;
+ /// Clock gate SRAM4.
+ pub sys_sram4, set_sys_sram4: 40;
+ /// Clock gate SRAM5.
+ pub sys_sram5, set_sys_sram5: 41;
+ /// Clock gate SRAM6
+ pub sys_sram6, set_sys_sram6: 42;
+ /// Clock gate SRAM7
+ pub sys_sram7, set_sys_sram7: 43;
+ /// Clock gate SRAM8
+ pub sys_sram8, set_sys_sram8: 44;
+ /// Clock gate SRAM9
+ pub sys_sram9, set_sys_sram9: 45;
+ /// Clock gate the system configuration controller.
+ pub sys_syscfg, set_sys_syscfg: 46;
+ /// Clock gate the system information peripheral.
+ pub sys_sysinfo, set_sys_sysinfo: 47;
+ /// Clock gate the test bench manager.
+ pub sys_tbman, set_sys_tbman: 48;
+ /// Clock gate the tick generator
+ pub ref_ticks, set_ref_ticks: 49;
+ /// Clock gate the tick generator
+ pub sys_ticks, set_sys_ticks: 50;
+ /// Clock gate the Timer 0 peripheral.
+ pub sys_timer0, set_sys_timer0: 51;
+ /// Clock gate the Timer 1 peripheral.
+ pub sys_timer1, set_sys_timer1: 52;
+ /// Clock gate the TrustZone Random Number Generator
+ pub sys_trng, set_sys_trng: 53;
+ /// Clock gate UART0's baud generation.
+ pub peri_uart0, set_peri_uart0: 54;
+ /// Clock gate UART0's controller.
+ pub sys_uart0, set_sys_uart0: 55;
+ /// Clock gate UART1's baud generation.
+ pub peri_uart1, set_peri_uart1: 56;
+ /// Clock gate UART1's controller.
+ pub sys_uart1, set_sys_uart1: 57;
+ /// Clock gate the USB controller.
+ pub sys_usbctrl, set_sys_usbctrl: 58;
+ /// Clock gate the USB logic.
+ pub usb, set_usb: 59;
+ /// Clock gate the Watchdog controller.
+ pub sys_watchdog, set_sys_watchdog: 60;
+ /// .Clock gate the XIP controller.
+ pub sys_xip, set_sys_xip: 61;
+ /// Clock gate the XOSC controller (not xosc itself).
+ pub sys_xosc, set_sys_xosc: 62;
+}
+
+impl core::fmt::Debug for ClockGate {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ f.debug_struct("ClockGate")
+ .field("sys_clock", &self.sys_clock())
+ .field("sys_accessctrl", &self.sys_accessctrl())
+ .field("adc_adc", &self.adc_adc())
+ .field("sys_adc", &self.sys_adc())
+ .field("sys_bootram", &self.sys_bootram())
+ .field("sys_busctrl", &self.sys_busctrl())
+ .field("sys_busfabric", &self.sys_busfabric())
+ .field("sys_dma", &self.sys_dma())
+ .field("sys_glitch_detector", &self.sys_glitch_detector())
+ .field("hstx", &self.hstx())
+ .field("sys_hstx", &self.sys_hstx())
+ .field("sys_i2c0", &self.sys_i2c0())
+ .field("sys_i2c1", &self.sys_i2c1())
+ .field("sys_io", &self.sys_io())
+ .field("sys_jtag", &self.sys_jtag())
+ .field("ref_otp", &self.ref_otp())
+ .field("sys_otp", &self.sys_otp())
+ .field("sys_pads", &self.sys_pads())
+ .field("sys_pio0", &self.sys_pio0())
+ .field("sys_pio1", &self.sys_pio1())
+ .field("sys_pio2", &self.sys_pio2())
+ .field("sys_pll_sys", &self.sys_pll_sys())
+ .field("sys_pll_usb", &self.sys_pll_usb())
+ .field("ref_powman", &self.ref_powman())
+ .field("sys_powman", &self.sys_powman())
+ .field("sys_pwm", &self.sys_pwm())
+ .field("sys_resets", &self.sys_resets())
+ .field("sys_rom", &self.sys_rom())
+ .field("sys_rosc", &self.sys_rosc())
+ .field("sys_psm", &self.sys_psm())
+ .field("sys_sha256", &self.sys_sha256())
+ .field("sys_sio", &self.sys_sio())
+ .field("peri_spi0", &self.peri_spi0())
+ .field("sys_spi0", &self.sys_spi0())
+ .field("peri_spi1", &self.peri_spi1())
+ .field("sys_spi1", &self.sys_spi1())
+ .field("sys_sram0", &self.sys_sram0())
+ .field("sys_sram1", &self.sys_sram1())
+ .field("sys_sram2", &self.sys_sram2())
+ .field("sys_sram3", &self.sys_sram3())
+ .field("sys_sram4", &self.sys_sram4())
+ .field("sys_sram5", &self.sys_sram5())
+ .field("sys_sram6", &self.sys_sram6())
+ .field("sys_sram7", &self.sys_sram7())
+ .field("sys_sram8", &self.sys_sram8())
+ .field("sys_sram9", &self.sys_sram9())
+ .field("sys_syscfg", &self.sys_syscfg())
+ .field("sys_sysinfo", &self.sys_sysinfo())
+ .field("sys_tbman", &self.sys_tbman())
+ .field("ref_ticks", &self.ref_ticks())
+ .field("sys_ticks", &self.sys_ticks())
+ .field("sys_timer0", &self.sys_timer0())
+ .field("sys_timer1", &self.sys_timer1())
+ .field("sys_trng", &self.sys_trng())
+ .field("peri_uart0", &self.peri_uart0())
+ .field("sys_uart0", &self.sys_uart0())
+ .field("peri_uart1", &self.peri_uart1())
+ .field("sys_uart1", &self.sys_uart1())
+ .field("sys_usbctrl", &self.sys_usbctrl())
+ .field("usb", &self.usb())
+ .field("sys_watchdog", &self.sys_watchdog())
+ .field("sys_xip", &self.sys_xip())
+ .field("sys_xosc", &self.sys_xosc())
+ .finish()
+ }
+}
+
+#[derive(Copy, Clone)]
+/// Provides refs to the CLOCKS block.
+struct ShareableClocks {
+ _internal: (),
+}
+
+impl ShareableClocks {
+ fn new(_clocks: &mut CLOCKS) -> Self {
+ ShareableClocks { _internal: () }
+ }
+
+ unsafe fn get(&self) -> &pac::clocks::RegisterBlock {
+ &*CLOCKS::ptr()
+ }
+}
+
+/// Something when wrong setting up the clock
+#[non_exhaustive]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum ClockError {
+ /// The frequency desired is higher than the source frequency
+ CantIncreaseFreq,
+ /// The desired frequency is to high (would overflow an u32)
+ FrequencyTooHigh,
+ /// The desired frequency is too low (divider can't reach the desired value)
+ FrequencyTooLow,
+}
+
+/// For clocks
+pub trait Clock: Sealed + Sized {
+ /// Enum with valid source clocks register values for `Clock`
+ type Variant;
+
+ /// Get operating frequency
+ fn freq(&self) -> HertzU32;
+
+ /// Configure this clock based on a clock source and desired frequency
+ fn configure_clock>(
+ &mut self,
+ src: &S,
+ freq: HertzU32,
+ ) -> Result<(), ClockError>;
+}
+
+/// For clocks with a divider
+trait ClockDivision {
+ /// Set integer divider value.
+ fn set_div(&mut self, div: u32);
+ /// Get integer diveder value.
+ fn get_div(&self) -> u32;
+}
+
+/// Clock with glitchless source
+trait GlitchlessClock {
+ /// Self type to hand to ChangingClockToken
+ type Clock: Clock;
+
+ /// Await switching clock sources without glitches. Needs a token that is returned when setting
+ fn await_select(
+ &self,
+ clock_token: &ChangingClockToken,
+ ) -> nb::Result<(), Infallible>;
+}
+
+/// Token which can be used to await the glitchless switch
+pub struct ChangingClockToken {
+ clock_nr: u8,
+ clock: PhantomData,
+}
+
+/// For clocks that can be disabled
+pub trait StoppableClock: Sealed {
+ /// Enables the clock.
+ fn enable(&mut self);
+
+ /// Disables the clock.
+ fn disable(&mut self);
+
+ /// Kills the clock.
+ fn kill(&mut self);
+}
+
+/// Trait for things that can be used as clock source
+pub trait ClockSource: Sealed {
+ /// Get the operating frequency for this source
+ ///
+ /// Used to determine the divisor
+ fn get_freq(&self) -> HertzU32;
+}
+
+/// Trait to contrain which ClockSource is valid for which Clock
+pub trait ValidSrc: Sealed + ClockSource {
+ /// Is this a ClockSource for src or aux?
+ fn is_aux(&self) -> bool;
+ /// Get register value for this ClockSource
+ fn variant(&self) -> C::Variant;
+}
+
+clocks! {
+ /// GPIO Output 0 Clock Generator
+ ///
+ /// Clock output to GPIO. Can be used to clock external devices or debug on
+ /// chip clocks with a logic analyser or oscilloscope.
+ struct GpioOutput0Clock {
+ init_freq: 0,
+ reg: clk_gpout0,
+ auxsrc: {
+ PllSys: CLKSRC_PLL_SYS,
+ GPin0: CLKSRC_GPIN0,
+ GPin1: CLKSRC_GPIN1,
+ PllUsb: CLKSRC_PLL_USB,
+ /* CLKSRC_PLL_USB_PRIMARY_REF_OPCG */
+ Rosc: ROSC_CLKSRC,
+ Xosc: XOSC_CLKSRC,
+ LpOsc: LPOSC_CLKSRC,
+ SystemClock: CLK_SYS,
+ UsbClock: CLK_USB,
+ AdcClock: CLK_ADC,
+ ReferenceClock: CLK_REF,
+ PeripheralClock: CLK_PERI,
+ HstxClock: CLK_HSTX
+ /* OTP_CLK2FC */
+ }
+ }
+ /// GPIO Output 1 Clock Generator
+ ///
+ /// Clock output to GPIO. Can be used to clock external devices or debug on
+ /// chip clocks with a logic analyser or oscilloscope.
+ struct GpioOutput1Clock {
+ init_freq: 0,
+ reg: clk_gpout1,
+ auxsrc: {
+ PllSys: CLKSRC_PLL_SYS,
+ GPin0: CLKSRC_GPIN0,
+ GPin1: CLKSRC_GPIN1,
+ PllUsb: CLKSRC_PLL_USB,
+ /* CLKSRC_PLL_USB_PRIMARY_REF_OPCG */
+ Rosc: ROSC_CLKSRC,
+ Xosc: XOSC_CLKSRC,
+ LpOsc: LPOSC_CLKSRC,
+ SystemClock: CLK_SYS,
+ UsbClock: CLK_USB,
+ AdcClock: CLK_ADC,
+ ReferenceClock: CLK_REF,
+ PeripheralClock: CLK_PERI,
+ HstxClock: CLK_HSTX
+ /* OTP_CLK2FC */
+ }
+ }
+ /// GPIO Output 2 Clock Generator
+ ///
+ /// Clock output to GPIO. Can be used to clock external devices or debug on
+ /// chip clocks with a logic analyser or oscilloscope.
+ struct GpioOutput2Clock {
+ init_freq: 0,
+ reg: clk_gpout2,
+ auxsrc: {
+ PllSys: CLKSRC_PLL_SYS,
+ GPin0: CLKSRC_GPIN0,
+ GPin1: CLKSRC_GPIN1,
+ PllUsb: CLKSRC_PLL_USB,
+ /* CLKSRC_PLL_USB_PRIMARY_REF_OPCG */
+ Rosc: ROSC_CLKSRC_PH,
+ Xosc: XOSC_CLKSRC,
+ LpOsc: LPOSC_CLKSRC,
+ SystemClock: CLK_SYS,
+ UsbClock: CLK_USB,
+ AdcClock: CLK_ADC,
+ ReferenceClock: CLK_REF,
+ PeripheralClock: CLK_PERI,
+ HstxClock: CLK_HSTX
+ /* OTP_CLK2FC */
+ }
+ }
+ /// GPIO Output 3 Clock Generator
+ ///
+ /// Clock output to GPIO. Can be used to clock external devices or debug on
+ /// chip clocks with a logic analyser or oscilloscope.
+ struct GpioOutput3Clock {
+ init_freq: 0,
+ reg: clk_gpout3,
+ auxsrc: {
+ PllSys: CLKSRC_PLL_SYS,
+ GPin0: CLKSRC_GPIN0,
+ GPin1: CLKSRC_GPIN1,
+ PllUsb: CLKSRC_PLL_USB,
+ /* CLKSRC_PLL_USB_PRIMARY_REF_OPCG */
+ Rosc: ROSC_CLKSRC_PH,
+ Xosc: XOSC_CLKSRC,
+ LpOsc: LPOSC_CLKSRC,
+ SystemClock: CLK_SYS,
+ UsbClock: CLK_USB,
+ AdcClock: CLK_ADC,
+ ReferenceClock: CLK_REF,
+ PeripheralClock: CLK_PERI,
+ HstxClock: CLK_HSTX
+ /* OTP_CLK2FC */
+ }
+ }
+ /// Reference clock that is always running 6 - 12MHz unless in DORMANT mode.
+ ///
+ /// Runs from Ring Oscillator (ROSC) at power-up but can be switched to
+ /// Crystal Oscillator (XOSC) for more accuracy.
+ struct ReferenceClock {
+ init_freq: 12_000_000,
+ // Starts from ROSC which actually varies with input voltage etc,
+ // but 12 MHz seems to be a good value
+ reg: clk_ref,
+ src: {
+ Rosc: ROSC_CLKSRC_PH,
+ /* Aux: CLKSRC_CLK_REF_AUX, */
+ Xosc: XOSC_CLKSRC,
+ LpOsc: LPOSC_CLKSRC
+ },
+ auxsrc: {
+ PllUsb:CLKSRC_PLL_USB,
+ GPin0:CLKSRC_GPIN0,
+ GPin1:CLKSRC_GPIN1
+ /* CLKSRC_PLL_USB_PRIMARY_REF_OPCG */
+ }
+ }
+ /// System clock that is always running unless in DORMANT mode.
+ ///
+ /// Runs from clk_ref at power-up but is typically switched to a PLL.
+ struct SystemClock {
+ init_freq: 12_000_000, // ref_clk is 12 MHz
+ reg: clk_sys,
+ src: {
+ ReferenceClock: CLK_REF,
+ SystemClock: CLKSRC_CLK_SYS_AUX
+ },
+ auxsrc: {
+ PllSys: CLKSRC_PLL_SYS,
+ PllUsb: CLKSRC_PLL_USB,
+ Rosc: ROSC_CLKSRC,
+ Xosc: XOSC_CLKSRC,
+ GPin0: CLKSRC_GPIN0,
+ GPin1: CLKSRC_GPIN1
+ }
+ }
+ /// Peripheral clock.
+ ///
+ /// Typically runs from 12 - 150MHz clk_sys but allows peripherals to run at
+ /// a consistent speed if clk_sys is changed by software.
+ struct PeripheralClock {
+ init_freq: 12_000_000, // sys_clk is 12 MHz
+ reg: clk_peri,
+ auxsrc: {
+ SystemClock: CLK_SYS,
+ PllSys: CLKSRC_PLL_SYS,
+ PllUsb:CLKSRC_PLL_USB,
+ Rosc: ROSC_CLKSRC_PH,
+ Xosc: XOSC_CLKSRC,
+ GPin0: CLKSRC_GPIN0,
+ GPin1: CLKSRC_GPIN1
+ },
+ div: false
+ }
+ /// HSTX (High-Speed Transmitter) Clock
+ struct HstxClock {
+ init_freq: 0,
+ reg: clk_hstx,
+ auxsrc: {
+ SystemClock: CLK_SYS,
+ PllUsb: CLKSRC_PLL_USB,
+ PllSys: CLKSRC_PLL_SYS,
+ GPin0: CLKSRC_GPIN0,
+ GPin1: CLKSRC_GPIN1
+ }
+ }
+ /// USB reference clock.
+ ///
+ /// Must be 48MHz.
+ struct UsbClock {
+ init_freq: 0,
+ reg: clk_usb,
+ auxsrc: {
+ PllUsb: CLKSRC_PLL_USB,
+ PllSys: CLKSRC_PLL_SYS,
+ Rosc: ROSC_CLKSRC_PH,
+ Xosc: XOSC_CLKSRC,
+ GPin0: CLKSRC_GPIN0,
+ GPin1: CLKSRC_GPIN1
+ }
+ }
+ /// ADC reference clock.
+ ///
+ /// Must be 48MHz.
+ struct AdcClock {
+ init_freq: 0,
+ reg: clk_adc,
+ auxsrc: {
+ PllUsb: CLKSRC_PLL_USB,
+ PllSys: CLKSRC_PLL_SYS,
+ Rosc: ROSC_CLKSRC_PH,
+ Xosc: XOSC_CLKSRC,
+ GPin0: CLKSRC_GPIN0,
+ GPin1: CLKSRC_GPIN1
+ }
+ }
+}
+
+impl SystemClock {
+ fn get_default_clock_source(&self) -> pac::clocks::clk_sys_ctrl::SRC_A {
+ pac::clocks::clk_sys_ctrl::SRC_A::CLK_REF
+ }
+
+ fn get_aux_source(&self) -> pac::clocks::clk_sys_ctrl::SRC_A {
+ pac::clocks::clk_sys_ctrl::SRC_A::CLKSRC_CLK_SYS_AUX
+ }
+}
+
+impl ReferenceClock {
+ fn get_default_clock_source(&self) -> pac::clocks::clk_ref_ctrl::SRC_A {
+ pac::clocks::clk_ref_ctrl::SRC_A::ROSC_CLKSRC_PH
+ }
+
+ fn get_aux_source(&self) -> pac::clocks::clk_ref_ctrl::SRC_A {
+ pac::clocks::clk_ref_ctrl::SRC_A::CLKSRC_CLK_REF_AUX
+ }
+}
+
+impl ClocksManager {
+ /// Initialize the clocks to a sane default
+ pub fn init_default(
+ &mut self,
+
+ xosc: &CrystalOscillator,
+ pll_sys: &PhaseLockedLoop,
+ pll_usb: &PhaseLockedLoop,
+ ) -> Result<(), ClockError> {
+ // Configure clocks
+ // CLK_REF = XOSC (12MHz) / 1 = 12MHz
+ self.reference_clock
+ .configure_clock(xosc, xosc.get_freq())?;
+
+ // CLK SYS = PLL SYS (150MHz) / 1 = 150MHz
+ self.system_clock
+ .configure_clock(pll_sys, pll_sys.get_freq())?;
+
+ // CLK USB = PLL USB (48MHz) / 1 = 48MHz
+ self.usb_clock
+ .configure_clock(pll_usb, pll_usb.get_freq())?;
+
+ // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
+ self.adc_clock
+ .configure_clock(pll_usb, pll_usb.get_freq())?;
+
+ // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
+ // Normally choose clk_sys or clk_usb
+ self.peripheral_clock
+ .configure_clock(&self.system_clock, self.system_clock.freq())
+ }
+
+ /// Configure the clocks staying ON during deep-sleep.
+ pub fn configure_sleep_enable(&mut self, clock_gate: ClockGate) {
+ self.clocks
+ .sleep_en0()
+ .write(|w| unsafe { w.bits(clock_gate.0 as u32) });
+ self.clocks
+ .sleep_en1()
+ .write(|w| unsafe { w.bits((clock_gate.0 >> 32) as u32) });
+ }
+
+ /// Read the clock gate configuration while the device is in its (deep) sleep state.
+ pub fn sleep_enable(&self) -> ClockGate {
+ ClockGate(
+ (u64::from(self.clocks.sleep_en1().read().bits()) << 32)
+ | u64::from(self.clocks.sleep_en0().read().bits()),
+ )
+ }
+
+ /// Read the clock gate configuration while the device is in its wake state.
+ pub fn wake_enable(&self) -> ClockGate {
+ ClockGate(
+ (u64::from(self.clocks.wake_en1().read().bits()) << 32)
+ | u64::from(self.clocks.wake_en0().read().bits()),
+ )
+ }
+
+ /// Releases the CLOCKS block
+ pub fn free(self) -> CLOCKS {
+ self.clocks
+ }
+}
+
+/// Possible init errors
+#[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum InitError {
+ /// Something went wrong setting up the Xosc
+ XoscErr(XoscError),
+ /// Something went wrong setting up the Pll
+ PllError(PllError),
+ /// Something went wrong setting up the Clocks
+ ClockError(ClockError),
+}
+
+/// Initialize the clocks and plls according to the reference implementation
+pub fn init_clocks_and_plls(
+ xosc_crystal_freq: u32,
+ xosc_dev: XOSC,
+ clocks_dev: CLOCKS,
+ pll_sys_dev: PLL_SYS,
+ pll_usb_dev: PLL_USB,
+ resets: &mut RESETS,
+ watchdog: &mut Watchdog,
+) -> Result {
+ let xosc = setup_xosc_blocking(xosc_dev, xosc_crystal_freq.Hz()).map_err(InitError::XoscErr)?;
+
+ // Configure watchdog tick generation to tick over every microsecond
+ watchdog.enable_tick_generation((xosc_crystal_freq / 1_000_000) as u16);
+
+ let mut clocks = ClocksManager::new(clocks_dev);
+
+ let pll_sys = setup_pll_blocking(
+ pll_sys_dev,
+ xosc.operating_frequency(),
+ PLL_SYS_150MHZ,
+ &mut clocks,
+ resets,
+ )
+ .map_err(InitError::PllError)?;
+ let pll_usb = setup_pll_blocking(
+ pll_usb_dev,
+ xosc.operating_frequency(),
+ PLL_USB_48MHZ,
+ &mut clocks,
+ resets,
+ )
+ .map_err(InitError::PllError)?;
+
+ clocks
+ .init_default(&xosc, &pll_sys, &pll_usb)
+ .map_err(InitError::ClockError)?;
+ Ok(clocks)
+}
+
+// Calculates (numerator<<16)/denominator.
+fn fractional_div(numerator: u32, denominator: u32) -> Option {
+ let numerator: u64 = u64::from(numerator) * 65536;
+ let denominator: u64 = u64::from(denominator);
+ let result = numerator / denominator;
+ result.try_into().ok()
+}
diff --git a/rp235x-hal/src/critical_section_impl.rs b/rp235x-hal/src/critical_section_impl.rs
new file mode 100644
index 000000000..abe0a8e83
--- /dev/null
+++ b/rp235x-hal/src/critical_section_impl.rs
@@ -0,0 +1,91 @@
+use core::sync::atomic::{AtomicU8, Ordering};
+
+struct RpSpinlockCs;
+critical_section::set_impl!(RpSpinlockCs);
+
+/// Marker value to indicate no-one has the lock.
+///
+/// Initialising `LOCK_OWNER` to 0 means cheaper static initialisation so it's the best choice
+const LOCK_UNOWNED: u8 = 0;
+
+/// Indicates which core owns the lock so that we can call critical_section recursively.
+///
+/// 0 = no one has the lock, 1 = core0 has the lock, 2 = core1 has the lock
+static LOCK_OWNER: AtomicU8 = AtomicU8::new(LOCK_UNOWNED);
+
+/// Marker value to indicate that we already owned the lock when we started the `critical_section`.
+///
+/// Since we can't take the spinlock when we already have it, we need some other way to keep track of `critical_section` ownership.
+/// `critical_section` provides a token for communicating between `acquire` and `release` so we use that.
+/// If we're the outermost call to `critical_section` we use the values 0 and 1 to indicate we should release the spinlock and set the interrupts back to disabled and enabled, respectively.
+/// The value 2 indicates that we aren't the outermost call, and should not release the spinlock or re-enable interrupts in `release`
+const LOCK_ALREADY_OWNED: u8 = 2;
+
+unsafe impl critical_section::Impl for RpSpinlockCs {
+ unsafe fn acquire() -> u8 {
+ RpSpinlockCs::acquire()
+ }
+
+ unsafe fn release(token: u8) {
+ RpSpinlockCs::release(token);
+ }
+}
+
+impl RpSpinlockCs {
+ unsafe fn acquire() -> u8 {
+ // Store the initial interrupt state and current core id in stack variables
+ let interrupts_active = crate::arch::interrupts_enabled();
+ // We reserved 0 as our `LOCK_UNOWNED` value, so add 1 to core_id so we get 1 for core0, 2 for core1.
+ let core = crate::Sio::core() as u8 + 1_u8;
+ // Do we already own the spinlock?
+ if LOCK_OWNER.load(Ordering::Acquire) == core {
+ // We already own the lock, so we must have called acquire within a critical_section.
+ // Return the magic inner-loop value so that we know not to re-enable interrupts in release()
+ LOCK_ALREADY_OWNED
+ } else {
+ // Spin until we get the lock
+ loop {
+ // Need to disable interrupts to ensure that we will not deadlock
+ // if an interrupt enters critical_section::Impl after we acquire the lock
+ crate::arch::interrupt_disable();
+ // Ensure the compiler doesn't re-order accesses and violate safety here
+ core::sync::atomic::compiler_fence(Ordering::SeqCst);
+ // Read the spinlock reserved for `critical_section`
+ if let Some(lock) = crate::sio::Spinlock31::try_claim() {
+ // We just acquired the lock.
+ // 1. Forget it, so we don't immediately unlock
+ core::mem::forget(lock);
+ // 2. Store which core we are so we can tell if we're called recursively
+ LOCK_OWNER.store(core, Ordering::Relaxed);
+ break;
+ }
+ // We didn't get the lock, enable interrupts if they were enabled before we started
+ if interrupts_active {
+ crate::arch::interrupt_enable();
+ }
+ }
+ // If we broke out of the loop we have just acquired the lock
+ // As the outermost loop, we want to return the interrupt status to restore later
+ interrupts_active as _
+ }
+ }
+
+ unsafe fn release(token: u8) {
+ // Did we already own the lock at the start of the `critical_section`?
+ if token != LOCK_ALREADY_OWNED {
+ // No, it wasn't owned at the start of this `critical_section`, so this core no longer owns it.
+ // Set `LOCK_OWNER` back to `LOCK_UNOWNED` to ensure the next critical section tries to obtain the spinlock instead
+ LOCK_OWNER.store(LOCK_UNOWNED, Ordering::Relaxed);
+ // Ensure the compiler doesn't re-order accesses and violate safety here
+ core::sync::atomic::compiler_fence(Ordering::SeqCst);
+ // Release the spinlock to allow others to enter critical_section again
+ crate::sio::Spinlock31::release();
+ // Re-enable interrupts if they were enabled when we first called acquire()
+ // We only do this on the outermost `critical_section` to ensure interrupts stay disabled
+ // for the whole time that we have the lock
+ if token != 0 {
+ crate::arch::interrupt_enable();
+ }
+ }
+ }
+}
diff --git a/rp235x-hal/src/dcp.rs b/rp235x-hal/src/dcp.rs
new file mode 100644
index 000000000..0abe04cac
--- /dev/null
+++ b/rp235x-hal/src/dcp.rs
@@ -0,0 +1,159 @@
+//! Double-Co-Pro Support
+//!
+//! The Double-Co-Pro is a very simple sort-of-FPU. It doesn't execute Arm FPU
+//! instructions like the Cortex-M33F's built-in FPU does. But it can perform
+//! some basic operations that can greatly speed up a pure-software FPU library.
+//! We can:
+//!
+//! * copy 64-bit values in to the co-pro with an `mrrc` instruction
+//! * copy 64-bit values out of the co-pro with an `mcrr` instruction
+//! * trigger the co-pro to do a thing with a `cdp` instruction
+//!
+//! We provide some functions here which can do accelerated FPU operations. The
+//! assembly is largely take from the Raspberry Pi Pico SDK.
+//!
+//! If you enable the `dcp-fast-f64` feature, we will replace the standard
+//! `__aeabi_dadd` and `__aeabi_dmul` functions with versions that complete
+//! around 3x to 6x faster.
+
+/// Perform a fast add of two f64 values, using the DCP.
+pub fn dadd(x: f64, y: f64) -> f64 {
+ unsafe { _aapcs_dcp_add(x, y) }
+}
+
+#[cfg(feature = "dcp-fast-f64")]
+#[no_mangle]
+/// Replace the built-in `__aeabi_dadd` function with one that uses the DCP.
+extern "aapcs" fn __aeabi_dadd(x: f64, y: f64) -> f64 {
+ unsafe { _aapcs_dcp_add(x, y) }
+}
+
+/// Perform a fast multiply of two f64 values, using the DCP.
+pub fn dmul(x: f64, y: f64) -> f64 {
+ unsafe { _aapcs_dcp_mul(x, y) }
+}
+
+#[cfg(feature = "dcp-fast-f64")]
+#[no_mangle]
+/// Replace the built-in `__aeabi_dmul` function with one that uses the DCP.
+extern "aapcs" fn __aeabi_dmul(x: f64, y: f64) -> f64 {
+ unsafe { _aapcs_dcp_mul(x, y) }
+}
+
+extern "aapcs" {
+ fn _aapcs_dcp_mul(x: f64, y: f64) -> f64;
+ fn _aapcs_dcp_add(x: f64, y: f64) -> f64;
+}
+
+core::arch::global_asm!(
+ r#"
+ .syntax unified
+ .cpu cortex-m33
+ .cfi_sections .debug_frame
+ .thumb
+ "#,
+ // Do a fast f64 multiply on the DCP.
+ //
+ // Assumes the DCP has been enabled in the CPACR register.
+ //
+ // * First f64 is in r0,r1
+ // * Second f64 is in r2,r3
+ // * Result is in r0,r1
+ //
+ // This function is Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
+ // SPDX-License-Identifier: BSD-3-Clause
+ r#"
+ .global _aapcs_dcp_mul
+ .type _aapcs_dcp_mul,%function
+ .cfi_startproc
+ .p2align 2
+ 1:
+ push {{lr}} // Save LR
+ bl generic_save_state // Run the save/restore routine
+ b 1f // Jump forward to run the routine
+ _aapcs_dcp_mul:
+ mrc2 p4,#0,apsr_nzcv,c0,c0,#1 // PCMP apsr_nzcv - If DCP is running, set the Negative flag
+ bmi 1b // Branch back if minus (i.e. negative flag is set)
+ 1: // Usually we fall right through, which is fast
+ push {{r4,r14}} // Save the callee-save temp registers we are using
+ mcrr p4,#1,r0,r1,c0 // WXUP r0,r1 - write r1|r0 to xm and xe
+ mcrr p4,#1,r2,r3,c1 // WYUP r2,r3 - write r3|r2 to ym and ye
+ mrrc p4,#0,r0,r1,c4 // RXMS r0,r1,0 - read xm Q62-s
+ mrrc p4,#0,r2,r3,c5 // RYMS r2,r3,0 - read xy Q62-s
+ umull r4,r12,r0,r2 // Unsigned Long Multiply r12|r4 := r0*r2
+ movs r14,#0 // r14 = 0
+ umlal r12,r14,r0,r3 // Unsigned Long Multiply with Accumulate r14|r12 += r0*r3
+ umlal r12,r14,r1,r2 // Unsigned Long Multiply with Accumulate r14|r12 += r1*r2
+ mcrr p4,#2,r4,r12,c0 // WXMS r4,r12 - set xm bit 0 if r12|r4 is 0, or 1 if r12|r4 is non-zero
+ movs r4,#0 // r4 = 0
+ umlal r14,r4,r1,r3 // Unsigned Long Multiply with Accumulate r4|r14 += r1*r3
+ mcrr p4,#3,r14,r4,c0 // WXMO r14,r4 - write xm direct OR into b0, add exponents, XOR signs
+ cdp p4,#8,c0,c0,c0,#1 // NRDD - normalise and round double-precision
+ mrrc p4,#5,r0,r1,c0 // RDDM r0,r1 - read DMUL result packed from xm and xe
+ pop {{r4,lr}} // Restore the callee-save temp registers we are using
+ bx lr // Return to caller
+ .cfi_endproc
+ .size _aapcs_dcp_mul, . - _aapcs_dcp_mul
+ "#,
+ // Do a fast f64 add on the DCP.
+ //
+ // Assumes the DCP has been enabled in the CPACR register.
+ //
+ // * First f64 is in r0,r1
+ // * Second f64 is in r2,r3
+ // * Result is in r0,r1
+ //
+ // This function is Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
+ // SPDX-License-Identifier: BSD-3-Clause
+ r#"
+ .global _aapcs_dcp_add
+ .type _aapcs_dcp_add,%function
+ .cfi_startproc
+ .p2align 2
+ 1:
+ push {{lr}} // Save LR
+ bl generic_save_state // Run the save/restore routine
+ b 1f // Jump forward to run the routine
+ _aapcs_dcp_add:
+ mrc2 p4,#0,apsr_nzcv,c0,c0,#1 // PCMP apsr_nzcv - If DCP is running, set the Negative flag
+ bmi 1b // Branch back if minus (i.e. negative flag is set)
+ 1: // Usually we fall right through, which is fast
+ mcrr p4,#1,r0,r1,c0 // WXUP r0,r1 - write r1|r0 to xm and xe
+ mcrr p4,#1,r2,r3,c1 // WYUP r2,r3 - write r3|r2 to ym and ye
+ cdp p4,#0,c0,c0,c1,#0 // ADD0 - compare X-Y, set status
+ cdp p4,#1,c0,c0,c1,#0 // ADD1 - xm := ±xm+±ym>>s or ±ym+±xm>>s
+ cdp p4,#8,c0,c0,c0,#1 // NRDD - normalise and round double-precision
+ mrrc p4,#1,r0,r1,c0 // RDDA r0,r1 - read DADD result packed from xm and xe
+ bx lr // Return to caller (or to state restore routine)
+ .cfi_endproc
+ .size _aapcs_dcp_add, . - _aapcs_dcp_add
+ "#,
+ // This code grabs the DCP state and pushes it to the stack.
+ // It then does a subroutine call back to where it came from
+ // and when that subroutine finishes (the original function we were
+ // trying to call), then we return here and restore the DCP state before
+ // actually returning to the caller.
+ r#"
+ .thumb_func
+ generic_save_state:
+ sub sp, #24
+ push {{r0, r1}}
+ // save DCP state here
+ mrrc2 p4,#0,r0,r1,c8 // PXMD r0, r1
+ strd r0, r1, [sp, #8 + 0]
+ mrrc2 p4,#0,r0,r1,c9 // PYMD r0, r1
+ strd r0, r1, [sp, #8 + 8]
+ mrrc p4,#0,r0,r1,c10 // REFD r0, r1
+ strd r0, r1, [sp, #8 + 16]
+ pop {{r0, r1}}
+ blx lr // briefly visit the function we're wrapping
+ // now restore the DCP state from the stack
+ pop {{r12, r14}}
+ mcrr p4,#0,r12,r14,c0 // WXMD r12, r14
+ pop {{r12, r14}}
+ mcrr p4,#0,r12,r14,c1 // WYMD r12, r14
+ pop {{r12, r14}}
+ mcrr p4,#0,r12,r14,c2 // WEFD r12, r14
+ pop {{pc}}
+ "#,
+);
diff --git a/rp235x-hal/src/dma/bidirectional.rs b/rp235x-hal/src/dma/bidirectional.rs
new file mode 100644
index 000000000..f6894d446
--- /dev/null
+++ b/rp235x-hal/src/dma/bidirectional.rs
@@ -0,0 +1,160 @@
+//! Bidirectional DMA transfers
+
+use core::sync::atomic::{compiler_fence, Ordering};
+
+use super::{
+ single_channel::{ChannelConfig, SingleChannel},
+ Pace, ReadTarget, WriteTarget,
+};
+
+/// DMA configuration for sending and receiving data simultaneously
+pub struct Config
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget,
+ BIDI: ReadTarget + WriteTarget,
+ TO: WriteTarget,
+{
+ ch: (CH1, CH2),
+ from: FROM,
+ bidi: BIDI,
+ to: TO,
+ from_pace: Pace,
+ to_pace: Pace,
+ bswap: bool,
+}
+
+impl Config
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget,
+ BIDI: ReadTarget + WriteTarget,
+ TO: WriteTarget,
+{
+ /// Create a DMA configuration for sending and receiving data simultaneously
+ pub fn new(ch: (CH1, CH2), from: FROM, bidi: BIDI, to: TO) -> Config {
+ Config {
+ ch,
+ from,
+ bidi,
+ to,
+ bswap: false,
+ from_pace: Pace::PreferSink,
+ to_pace: Pace::PreferSink,
+ }
+ }
+
+ #[allow(clippy::wrong_self_convention)]
+ /// Set the transfer pacing for the DMA transfer from the source
+ pub fn from_pace(&mut self, pace: Pace) {
+ self.from_pace = pace;
+ }
+
+ #[allow(clippy::wrong_self_convention)]
+ /// Set the transfer pacing for the DMA transfer to the target
+ pub fn to_pace(&mut self, pace: Pace) {
+ self.to_pace = pace;
+ }
+
+ /// Enable/disable byteswapping for the DMA transfers, default value is false.
+ ///
+ /// For byte data, this has no effect. For halfword data, the two bytes of
+ /// each halfword are swapped. For word data, the four bytes of each word
+ /// are swapped to reverse order.
+ ///
+ /// This is a convenient way to change the (half-)words' byte endianness on the fly.
+ pub fn bswap(&mut self, bswap: bool) {
+ self.bswap = bswap;
+ }
+
+ /// Start the DMA transfer
+ pub fn start(mut self) -> Transfer {
+ crate::arch::dsb();
+ compiler_fence(Ordering::SeqCst);
+
+ // Configure the DMA channel and start it.
+ self.ch.0.config(
+ &self.from,
+ &mut self.bidi,
+ self.from_pace,
+ self.bswap,
+ None,
+ false,
+ );
+ self.ch.1.config(
+ &self.bidi,
+ &mut self.to,
+ self.to_pace,
+ self.bswap,
+ None,
+ false,
+ );
+ self.ch.0.start_both(&mut self.ch.1);
+
+ Transfer {
+ ch: self.ch,
+ from: self.from,
+ bidi: self.bidi,
+ to: self.to,
+ }
+ }
+}
+
+/// Instance of a bidirectional DMA transfer
+pub struct Transfer
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget,
+ BIDI: ReadTarget + WriteTarget,
+ TO: WriteTarget,
+{
+ ch: (CH1, CH2),
+ from: FROM,
+ bidi: BIDI,
+ to: TO,
+}
+
+impl Transfer
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget,
+ BIDI: ReadTarget + WriteTarget,
+ TO: WriteTarget,
+{
+ /// Check if an interrupt is pending for either channel and clear the corresponding pending bit
+ pub fn check_irq0(&mut self) -> bool {
+ let a = self.ch.0.check_irq0();
+ let b = self.ch.1.check_irq0();
+ a | b
+ }
+
+ /// Check if an interrupt is pending for either channel and clear the corresponding pending bit
+ pub fn check_irq1(&mut self) -> bool {
+ let a = self.ch.0.check_irq1();
+ let b = self.ch.1.check_irq1();
+ a | b
+ }
+
+ /// Check if the transfer is completed
+ pub fn is_done(&self) -> bool {
+ let a = self.ch.1.ch().ch_ctrl_trig().read().busy().bit_is_set();
+ let b = self.ch.0.ch().ch_ctrl_trig().read().busy().bit_is_set();
+ !(a | b)
+ }
+
+ /// Block until transfer is complete
+ pub fn wait(self) -> ((CH1, CH2), FROM, BIDI, TO) {
+ while !self.is_done() {}
+
+ // Make sure that memory contents reflect what the user intended.
+ crate::arch::dsb();
+ compiler_fence(Ordering::SeqCst);
+
+ // TODO: Use a tuple type?
+ ((self.ch.0, self.ch.1), self.from, self.bidi, self.to)
+ }
+}
diff --git a/rp235x-hal/src/dma/double_buffer.rs b/rp235x-hal/src/dma/double_buffer.rs
new file mode 100644
index 000000000..17f2933e9
--- /dev/null
+++ b/rp235x-hal/src/dma/double_buffer.rs
@@ -0,0 +1,316 @@
+//! Double-buffered DMA Transfers
+
+use core::sync::atomic::{compiler_fence, Ordering};
+
+use super::{
+ single_channel::ChannelConfig, single_channel::SingleChannel, EndlessReadTarget,
+ EndlessWriteTarget, Pace, ReadTarget, WriteTarget,
+};
+
+/// Configuration for double-buffered DMA transfer
+pub struct Config {
+ ch: (CH1, CH2),
+ from: FROM,
+ to: TO,
+ bswap: bool,
+ pace: Pace,
+}
+
+impl Config
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget,
+ TO: WriteTarget,
+{
+ /// Create a new configuration for double-buffered DMA transfer
+ pub fn new(ch: (CH1, CH2), from: FROM, to: TO) -> Config {
+ Config {
+ ch,
+ from,
+ to,
+ bswap: false,
+ pace: Pace::PreferSource,
+ }
+ }
+
+ /// Sets the (preferred) pace for the DMA transfers.
+ ///
+ /// Usually, the code will automatically configure the correct pace, but
+ /// peripheral-to-peripheral transfers require the user to manually select whether the source
+ /// or the sink shall be queried for the pace signal.
+ pub fn pace(&mut self, pace: Pace) {
+ self.pace = pace;
+ }
+
+ /// Enable/disable byteswapping for the DMA transfers, default value is false.
+ ///
+ /// For byte data, this has no effect. For halfword data, the two bytes of
+ /// each halfword are swapped. For word data, the four bytes of each word
+ /// are swapped to reverse order.
+ ///
+ /// This is a convenient way to change the (half-)words' byte endianness on the fly.
+ pub fn bswap(&mut self, bswap: bool) {
+ self.bswap = bswap;
+ }
+
+ /// Start the DMA transfer
+ pub fn start(mut self) -> Transfer {
+ // TODO: Do we want to call any callbacks to configure source/sink?
+
+ // Make sure that memory contents reflect what the user intended.
+ // TODO: How much of the following is necessary?
+ crate::arch::dsb();
+ compiler_fence(Ordering::SeqCst);
+
+ // Configure the DMA channel and start it.
+ self.ch
+ .0
+ .config(&self.from, &mut self.to, self.pace, self.bswap, None, true);
+
+ Transfer {
+ ch: self.ch,
+ from: self.from,
+ to: self.to,
+ pace: self.pace,
+ bswap: self.bswap,
+ state: (),
+ second_ch: false,
+ }
+ }
+}
+
+/// State for a double-buffered read
+pub struct ReadNext(BUF);
+/// State for a double-buffered write
+pub struct WriteNext(BUF);
+
+/// Instance of a double-buffered DMA transfer
+pub struct Transfer
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget,
+ TO: WriteTarget,
+{
+ ch: (CH1, CH2),
+ from: FROM,
+ to: TO,
+ pace: Pace,
+ bswap: bool,
+ state: STATE,
+ second_ch: bool,
+}
+
+impl Transfer
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget,
+ TO: WriteTarget,
+{
+ /// Check if an interrupt is pending for the active channel and clear the corresponding pending bit
+ pub fn check_irq0(&mut self) -> bool {
+ if self.second_ch {
+ self.ch.1.check_irq0()
+ } else {
+ self.ch.0.check_irq0()
+ }
+ }
+
+ /// Check if an interrupt is pending for the active channel and clear the corresponding pending bit
+ pub fn check_irq1(&mut self) -> bool {
+ if self.second_ch {
+ self.ch.1.check_irq1()
+ } else {
+ self.ch.0.check_irq1()
+ }
+ }
+
+ /// Check if the transfer is completed
+ pub fn is_done(&self) -> bool {
+ if self.second_ch {
+ !self.ch.1.ch().ch_ctrl_trig().read().busy().bit_is_set()
+ } else {
+ !self.ch.0.ch().ch_ctrl_trig().read().busy().bit_is_set()
+ }
+ }
+}
+
+impl Transfer
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget,
+ TO: WriteTarget + EndlessWriteTarget,
+{
+ /// Block until transfer completed
+ pub fn wait(self) -> (CH1, CH2, FROM, TO) {
+ while !self.is_done() {}
+
+ // Make sure that memory contents reflect what the user intended.
+ crate::arch::dsb();
+ compiler_fence(Ordering::SeqCst);
+
+ // TODO: Use a tuple type?
+ (self.ch.0, self.ch.1, self.from, self.to)
+ }
+}
+
+impl Transfer
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget,
+ TO: WriteTarget + EndlessWriteTarget,
+{
+ /// Perform the next read of a double-buffered sequence
+ pub fn read_next>(
+ mut self,
+ buf: BUF,
+ ) -> Transfer> {
+ // Make sure that memory contents reflect what the user intended.
+ // TODO: How much of the following is necessary?
+ crate::arch::dsb();
+ compiler_fence(Ordering::SeqCst);
+
+ // Configure the _other_ DMA channel, but do not start it yet.
+ if self.second_ch {
+ self.ch
+ .0
+ .config(&buf, &mut self.to, self.pace, self.bswap, None, false);
+ } else {
+ self.ch
+ .1
+ .config(&buf, &mut self.to, self.pace, self.bswap, None, false);
+ }
+
+ // Chain the first channel to the second.
+ if self.second_ch {
+ self.ch.1.set_chain_to_enabled(&mut self.ch.0);
+ } else {
+ self.ch.0.set_chain_to_enabled(&mut self.ch.1);
+ }
+
+ Transfer {
+ ch: self.ch,
+ from: self.from,
+ to: self.to,
+ pace: self.pace,
+ bswap: self.bswap,
+ state: ReadNext(buf),
+ second_ch: self.second_ch,
+ }
+ }
+}
+
+impl Transfer
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget + EndlessReadTarget,
+ TO: WriteTarget,
+{
+ /// Perform the next write of a double-buffered sequence
+ pub fn write_next>(
+ mut self,
+ mut buf: BUF,
+ ) -> Transfer> {
+ // Make sure that memory contents reflect what the user intended.
+ // TODO: How much of the following is necessary?
+ crate::arch::dsb();
+ compiler_fence(Ordering::SeqCst);
+
+ // Configure the _other_ DMA channel, but do not start it yet.
+ if self.second_ch {
+ self.ch
+ .0
+ .config(&self.from, &mut buf, self.pace, self.bswap, None, false);
+ } else {
+ self.ch
+ .1
+ .config(&self.from, &mut buf, self.pace, self.bswap, None, false);
+ }
+
+ // Chain the first channel to the second.
+ if self.second_ch {
+ self.ch.1.set_chain_to_enabled(&mut self.ch.0);
+ } else {
+ self.ch.0.set_chain_to_enabled(&mut self.ch.1);
+ }
+
+ Transfer {
+ ch: self.ch,
+ from: self.from,
+ to: self.to,
+ pace: self.pace,
+ bswap: self.bswap,
+ state: WriteNext(buf),
+ second_ch: self.second_ch,
+ }
+ }
+}
+
+impl Transfer>
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget,
+ TO: WriteTarget + EndlessWriteTarget,
+ NEXT: ReadTarget,
+{
+ /// Block until the the transfer is complete
+ pub fn wait(self) -> (FROM, Transfer) {
+ while !self.is_done() {}
+
+ // Make sure that memory contents reflect what the user intended.
+ crate::arch::dsb();
+ compiler_fence(Ordering::SeqCst);
+
+ // Invert second_ch as now the other channel is the "active" channel.
+ (
+ self.from,
+ Transfer {
+ ch: self.ch,
+ from: self.state.0,
+ to: self.to,
+ pace: self.pace,
+ bswap: self.bswap,
+ state: (),
+ second_ch: !self.second_ch,
+ },
+ )
+ }
+}
+
+impl Transfer>
+where
+ CH1: SingleChannel,
+ CH2: SingleChannel,
+ FROM: ReadTarget + EndlessReadTarget,
+ TO: WriteTarget,
+ NEXT: WriteTarget,
+{
+ /// Block until transfer is complete
+ pub fn wait(self) -> (TO, Transfer) {
+ while !self.is_done() {}
+
+ // Make sure that memory contents reflect what the user intended.
+ crate::arch::dsb();
+ compiler_fence(Ordering::SeqCst);
+
+ // Invert second_ch as now the other channel is the "active" channel.
+ (
+ self.to,
+ Transfer {
+ ch: self.ch,
+ from: self.from,
+ to: self.state.0,
+ pace: self.pace,
+ bswap: self.bswap,
+ state: (),
+ second_ch: !self.second_ch,
+ },
+ )
+ }
+}
diff --git a/rp235x-hal/src/dma/mod.rs b/rp235x-hal/src/dma/mod.rs
new file mode 100644
index 000000000..f87918968
--- /dev/null
+++ b/rp235x-hal/src/dma/mod.rs
@@ -0,0 +1,327 @@
+//! Direct memory access (DMA).
+//!
+//! The DMA unit of the rp235x seems very simplistic at first when compared to other MCUs. For
+//! example, the individual DMA channels do not support chaining multiple buffers. However, within
+//! certain limits, the DMA engine supports a wide range of transfer and buffer types, often by
+//! combining multiple DMA channels:
+//!
+//! * Simple RX/TX transfers filling a single buffer or transferring data from one peripheral to
+//! another.
+//! * RX/TX transfers that use multiple chained buffers: These transfers require two channels to
+//! be combined, where the first DMA channel configures the second DMA channel. An example for
+//! this transfer type can be found in the datasheet.
+//! * Repeated transfers from/to a set of buffers: By allocating one channel per buffer and
+//! chaining the channels together, continuous transfers to a set of ring buffers can be
+//! achieved. Note, however, that the MCU manually needs to reconfigure the DMA units unless the
+//! buffer addresses and sizes are aligned, in which case the ring buffer functionality of the
+//! DMA engine can be used. Even then, however, at least two DMA channels are required as a
+//! channel cannot be chained to itself.
+//!
+//! This API tries to provide three types of buffers: Single buffers, double-buffered transfers
+//! where the user can specify the next buffer while the previous is being transferred, and
+//! automatic continuous ring buffers consisting of two aligned buffers being read or written
+//! alternatingly.
+
+use core::marker::PhantomData;
+use embedded_dma::{ReadBuffer, WriteBuffer};
+
+use crate::{
+ pac::{self, DMA},
+ resets::SubsystemReset,
+ typelevel::Sealed,
+};
+// Export these types for easier use by external code
+pub use crate::dma::single_channel::SingleChannel;
+
+// Bring in our submodules
+pub mod bidirectional;
+pub mod double_buffer;
+pub mod single_buffer;
+mod single_channel;
+
+/// DMA unit.
+pub trait DMAExt: Sealed {
+ /// Splits the DMA unit into its individual channels.
+ fn split(self, resets: &mut pac::RESETS) -> Channels;
+ /// Splits the DMA unit into its individual channels with runtime ownership
+ fn dyn_split(self, resets: &mut pac::RESETS) -> DynChannels;
+}
+
+/// DMA channel.
+pub struct Channel {
+ _phantom: PhantomData,
+}
+
+/// DMA channel identifier.
+pub trait ChannelIndex: Sealed {
+ /// Numerical index of the DMA channel (0..11).
+ fn id() -> u8;
+}
+
+macro_rules! channels {
+ (
+ $($CHX:ident: ($chX:ident, $x:expr),)+
+ ) => {
+ impl DMAExt for DMA {
+ fn split(self, resets: &mut pac::RESETS) -> Channels {
+ self.reset_bring_down(resets);
+ self.reset_bring_up(resets);
+
+ Channels {
+ $(
+ $chX: Channel {
+ _phantom: PhantomData,
+ },
+ )+
+ }
+ }
+
+ fn dyn_split(self, resets: &mut pac::RESETS) -> DynChannels{
+ self.reset_bring_down(resets);
+ self.reset_bring_up(resets);
+
+ DynChannels {
+ $(
+ $chX: Some(Channel {
+ _phantom: PhantomData,
+ }),
+ )+
+ }
+ }
+ }
+
+ impl Sealed for DMA {}
+
+ /// Set of DMA channels.
+ pub struct Channels {
+ $(
+ /// DMA channel.
+ pub $chX: Channel<$CHX>,
+ )+
+ }
+ $(
+ /// DMA channel identifier.
+ pub struct $CHX;
+ impl ChannelIndex for $CHX {
+ fn id() -> u8 {
+ $x
+ }
+ }
+
+ impl Sealed for $CHX {}
+ )+
+
+ /// Set of DMA channels with runtime ownership.
+ pub struct DynChannels {
+ $(
+ /// DMA channel.
+ pub $chX: Option>,
+ )+
+ }
+ }
+}
+
+channels! {
+ CH0: (ch0, 0),
+ CH1: (ch1, 1),
+ CH2: (ch2, 2),
+ CH3: (ch3, 3),
+ CH4: (ch4, 4),
+ CH5: (ch5, 5),
+ CH6: (ch6, 6),
+ CH7: (ch7, 7),
+ CH8: (ch8, 8),
+ CH9: (ch9, 9),
+ CH10:(ch10, 10),
+ CH11:(ch11, 11),
+}
+
+trait ChannelRegs {
+ unsafe fn ptr() -> *const pac::dma::CH;
+ fn regs(&self) -> &pac::dma::CH;
+}
+
+impl ChannelRegs for Channel {
+ unsafe fn ptr() -> *const pac::dma::CH {
+ (*pac::DMA::ptr()).ch(CH::id() as usize)
+ }
+
+ fn regs(&self) -> &pac::dma::CH {
+ unsafe { &*Self::ptr() }
+ }
+}
+
+/// Trait which is implemented by anything that can be read via DMA.
+///
+/// # Safety
+///
+/// The implementing type must be safe to use for DMA reads. This means:
+///
+/// - The range returned by rx_address_count must point to a valid address,
+/// and if rx_increment is true, count must fit into the allocated buffer.
+/// - As long as no `&mut self` method is called on the implementing object:
+/// - `rx_address_count` must always return the same value, if called multiple
+/// times.
+/// - The memory specified by the pointer and size returned by `rx_address_count`
+/// must not be freed during the transfer it is used in as long as `self` is not dropped.
+pub unsafe trait ReadTarget {
+ /// Type which is transferred in a single DMA transfer.
+ type ReceivedWord;
+
+ /// Returns the DREQ number for this data source (`None` for memory buffers).
+ fn rx_treq() -> Option;
+
+ /// Returns the address and the maximum number of words that can be transferred from this data
+ /// source in a single DMA operation.
+ ///
+ /// For peripherals, the count should likely be u32::MAX. If a data source implements
+ /// EndlessReadTarget, it is suitable for infinite transfers from or to ring buffers. Note that
+ /// ring buffers designated for endless transfers, but with a finite buffer size, should return
+ /// the size of their individual buffers here.
+ ///
+ /// # Safety
+ ///
+ /// This function has the same safety guarantees as `ReadBuffer::read_buffer`.
+ fn rx_address_count(&self) -> (u32, u32);
+
+ /// Returns whether the address shall be incremented after each transfer.
+ fn rx_increment(&self) -> bool;
+}
+
+/// Marker which signals that `rx_address_count()` can be called multiple times.
+///
+/// The DMA code will never call `rx_address_count()` to request more than two buffers to configure
+/// two DMA channels. In the case of peripherals, the function can always return the same values.
+pub trait EndlessReadTarget: ReadTarget {}
+
+/// Safety: ReadBuffer and ReadTarget have the same safety requirements.
+unsafe impl ReadTarget for B {
+ type ReceivedWord = ::Word;
+
+ fn rx_treq() -> Option {
+ None
+ }
+
+ fn rx_address_count(&self) -> (u32, u32) {
+ let (ptr, len) = unsafe { self.read_buffer() };
+ (ptr as u32, len as u32)
+ }
+
+ fn rx_increment(&self) -> bool {
+ true
+ }
+}
+
+/// Trait which is implemented by anything that can be written via DMA.
+///
+/// # Safety
+///
+/// The implementing type must be safe to use for DMA writes. This means:
+///
+/// - The range returned by tx_address_count must point to a valid address,
+/// and if tx_increment is true, count must fit into the allocated buffer.
+/// - As long as no other `&mut self` method is called on the implementing object:
+/// - `tx_address_count` must always return the same value, if called multiple
+/// times.
+/// - The memory specified by the pointer and size returned by `tx_address_count`
+/// must not be freed during the transfer it is used in as long as `self` is not dropped.
+pub unsafe trait WriteTarget {
+ /// Type which is transferred in a single DMA transfer.
+ type TransmittedWord;
+
+ /// Returns the DREQ number for this data sink (`None` for memory buffers).
+ fn tx_treq() -> Option;
+
+ /// Returns the address and the maximum number of words that can be transferred from this data
+ /// source in a single DMA operation.
+ ///
+ /// See `ReadTarget::rx_address_count` for a complete description of the semantics of this
+ /// function.
+ fn tx_address_count(&mut self) -> (u32, u32);
+
+ /// Returns whether the address shall be incremented after each transfer.
+ fn tx_increment(&self) -> bool;
+}
+
+/// Marker which signals that `tx_address_count()` can be called multiple times.
+///
+/// The DMA code will never call `tx_address_count()` to request more than two buffers to configure
+/// two DMA channels. In the case of peripherals, the function can always return the same values.
+pub trait EndlessWriteTarget: WriteTarget {}
+
+/// Safety: WriteBuffer and WriteTarget have the same safety requirements.
+unsafe impl WriteTarget for B {
+ type TransmittedWord = ::Word;
+
+ fn tx_treq() -> Option {
+ None
+ }
+
+ fn tx_address_count(&mut self) -> (u32, u32) {
+ let (ptr, len) = unsafe { self.write_buffer() };
+ (ptr as u32, len as u32)
+ }
+
+ fn tx_increment(&self) -> bool {
+ true
+ }
+}
+
+/// Pacing for DMA transfers.
+///
+/// Generally, while memory-to-memory DMA transfers can operate at maximum possible throughput,
+/// transfers involving peripherals commonly have to wait for data to be available or for available
+/// space in write queues. This type defines whether the sink or the source shall pace the transfer
+/// for peripheral-to-peripheral transfers.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum Pace {
+ /// The DREQ signal from the source is used, if available. If not, the sink's DREQ signal is
+ /// used.
+ PreferSource,
+ /// The DREQ signal from the sink is used, if available. If not, the source's DREQ signal is
+ /// used.
+ PreferSink,
+ // TODO: Timers?
+}
+
+/// Error during DMA configuration.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum DMAError {
+ /// Buffers were not aligned to their size even though they needed to be.
+ Alignment,
+ /// An illegal configuration (i.e., buffer sizes not suitable for a memory-to-memory transfer)
+ /// was specified.
+ IllegalConfig,
+}
+
+/// Constraint on transfer size types
+pub trait TransferSize: Sealed {
+ /// Actual type of transfer
+ type Type;
+}
+
+/// DMA transfer in bytes (u8)
+#[derive(Debug, Copy, Clone)]
+pub struct Byte;
+/// DMA transfer in half words (u16)
+#[derive(Debug, Copy, Clone)]
+pub struct HalfWord;
+/// DMA transfer in words (u32)
+#[derive(Debug, Copy, Clone)]
+pub struct Word;
+
+impl Sealed for Byte {}
+impl Sealed for HalfWord {}
+impl Sealed for Word {}
+
+impl TransferSize for Byte {
+ type Type = u8;
+}
+impl TransferSize for HalfWord {
+ type Type = u16;
+}
+impl TransferSize for Word {
+ type Type = u32;
+}
diff --git a/rp235x-hal/src/dma/single_buffer.rs b/rp235x-hal/src/dma/single_buffer.rs
new file mode 100644
index 000000000..a4c657ecf
--- /dev/null
+++ b/rp235x-hal/src/dma/single_buffer.rs
@@ -0,0 +1,149 @@
+//! Single-buffered or peripheral-peripheral DMA Transfers
+
+use core::sync::atomic::{compiler_fence, Ordering};
+
+use super::{
+ single_channel::ChannelConfig, single_channel::SingleChannel, Pace, ReadTarget, WriteTarget,
+};
+
+/// Configuration for single-buffered DMA transfer
+pub struct Config {
+ ch: CH,
+ from: FROM,
+ to: TO,
+ pace: Pace,
+ bswap: bool,
+}
+
+impl Config
+where
+ CH: SingleChannel,
+ FROM: ReadTarget,
+ TO: WriteTarget,
+{
+ /// Create a new configuration for single-buffered DMA transfer
+ pub fn new(ch: CH, from: FROM, to: TO) -> Config {
+ Config {
+ ch,
+ from,
+ to,
+ pace: Pace::PreferSource,
+ bswap: false,
+ }
+ }
+
+ /// Sets the (preferred) pace for the DMA transfers.
+ ///
+ /// Usually, the code will automatically configure the correct pace, but
+ /// peripheral-to-peripheral transfers require the user to manually select whether the source
+ /// or the sink shall be queried for the pace signal.
+ pub fn pace(&mut self, pace: Pace) {
+ self.pace = pace;
+ }
+
+ /// Enable/disable byteswapping for the DMA transfers, default value is false.
+ ///
+ /// For byte data, this has no effect. For halfword data, the two bytes of
+ /// each halfword are swapped. For word data, the four bytes of each word
+ /// are swapped to reverse order.
+ ///
+ /// This is a convenient way to change the (half-)words' byte endianness on the fly.
+ pub fn bswap(&mut self, bswap: bool) {
+ self.bswap = bswap;
+ }
+
+ /// Start the DMA transfer
+ pub fn start(mut self) -> Transfer {
+ // TODO: Do we want to call any callbacks to configure source/sink?
+
+ // Make sure that memory contents reflect what the user intended.
+ // TODO: How much of the following is necessary?
+ crate::arch::dsb();
+ compiler_fence(Ordering::SeqCst);
+
+ // Configure the DMA channel and start it.
+ self.ch
+ .config(&self.from, &mut self.to, self.pace, self.bswap, None, true);
+
+ Transfer {
+ ch: self.ch,
+ from: self.from,
+ to: self.to,
+ }
+ }
+}
+
+// TODO: Drop for most of these structs
+/// Instance of a single-buffered DMA transfer
+pub struct Transfer {
+ ch: CH,
+ from: FROM,
+ to: TO,
+}
+
+impl Transfer
+where
+ CH: SingleChannel,
+ FROM: ReadTarget,
+ TO: WriteTarget,
+{
+ /// Check if an interrupt is pending for this channel and clear the corresponding pending bit
+ pub fn check_irq0(&mut self) -> bool {
+ self.ch.check_irq0()
+ }
+
+ /// Check if an interrupt is pending for this channel and clear the corresponding pending bit
+ pub fn check_irq1(&mut self) -> bool {
+ self.ch.check_irq1()
+ }
+
+ /// Check if the transfer has completed.
+ pub fn is_done(&self) -> bool {
+ !self.ch.ch().ch_ctrl_trig().read().busy().bit_is_set()
+ }
+
+ /// Block until the transfer is complete, returning the channel and targets
+ pub fn wait(self) -> (CH, FROM, TO) {
+ while !self.is_done() {}
+
+ // Make sure that memory contents reflect what the user intended.
+ crate::arch::dsb();
+ compiler_fence(Ordering::SeqCst);
+
+ (self.ch, self.from, self.to)
+ }
+
+ /// Aborts the current transfer, returning the channel and targets
+ pub fn abort(mut self) -> (CH, FROM, TO) {
+ let irq0_was_enabled = self.ch.is_enabled_irq0();
+ let irq1_was_enabled = self.ch.is_enabled_irq1();
+ self.ch.disable_irq0();
+ self.ch.disable_irq1();
+
+ let chan_abort = unsafe { &*crate::pac::DMA::ptr() }.chan_abort();
+ let abort_mask = (1 << self.ch.id()) as u16;
+
+ chan_abort.write(|w| unsafe { w.chan_abort().bits(abort_mask) });
+
+ while chan_abort.read().bits() != 0 {}
+
+ while !self.is_done() {}
+
+ self.ch.check_irq0();
+ self.ch.check_irq1();
+
+ if irq0_was_enabled {
+ self.ch.enable_irq0();
+ }
+
+ if irq1_was_enabled {
+ self.ch.enable_irq1();
+ }
+
+ // Make sure that memory contents reflect what the user intended.
+ crate::arch::dsb();
+ compiler_fence(Ordering::SeqCst);
+
+ (self.ch, self.from, self.to)
+ }
+}
diff --git a/rp235x-hal/src/dma/single_channel.rs b/rp235x-hal/src/dma/single_channel.rs
new file mode 100644
index 000000000..664fb1958
--- /dev/null
+++ b/rp235x-hal/src/dma/single_channel.rs
@@ -0,0 +1,263 @@
+use crate::pac::DMA;
+
+use super::{Channel, ChannelIndex, Pace, ReadTarget, WriteTarget};
+use crate::{
+ atomic_register_access::{write_bitmask_clear, write_bitmask_set},
+ dma::ChannelRegs,
+ typelevel::Sealed,
+};
+use core::mem;
+
+/// Trait which implements low-level functionality for transfers using a single DMA channel.
+pub trait SingleChannel: Sealed {
+ /// Returns the registers associated with this DMA channel.
+ ///
+ /// In the case of channel pairs, this returns the first channel.
+ fn ch(&self) -> &crate::pac::dma::CH;
+ /// Returns the index of the DMA channel.
+ fn id(&self) -> u8;
+
+ #[deprecated(note = "Renamed to enable_irq0")]
+ /// Enables the DMA_IRQ_0 signal for this channel.
+ fn listen_irq0(&mut self) {
+ self.enable_irq0();
+ }
+
+ /// Enables the DMA_IRQ_0 signal for this channel.
+ fn enable_irq0(&mut self) {
+ // Safety: We only use the atomic alias of the register.
+ unsafe {
+ write_bitmask_set((*DMA::ptr()).inte0().as_ptr(), 1 << self.id());
+ }
+ }
+
+ /// Check if the DMA_IRQ_0 signal for this channel is enabled.
+ fn is_enabled_irq0(&mut self) -> bool {
+ unsafe { ((*DMA::ptr()).inte0().read().bits() & (1 << self.id())) != 0 }
+ }
+
+ #[deprecated(note = "Renamed to disable_irq0")]
+ /// Disables the DMA_IRQ_0 signal for this channel.
+ fn unlisten_irq0(&mut self) {
+ self.disable_irq0();
+ }
+
+ /// Disables the DMA_IRQ_0 signal for this channel.
+ fn disable_irq0(&mut self) {
+ // Safety: We only use the atomic alias of the register.
+ unsafe {
+ write_bitmask_clear((*DMA::ptr()).inte0().as_ptr(), 1 << self.id());
+ }
+ }
+
+ /// Check if an interrupt is pending for this channel and clear the corresponding pending bit
+ fn check_irq0(&mut self) -> bool {
+ // Safety: The following is race-free as we only ever clear the bit for this channel.
+ // Nobody else modifies that bit.
+ unsafe {
+ let status = (*DMA::ptr()).ints0().read().bits();
+ if (status & (1 << self.id())) != 0 {
+ // Clear the interrupt.
+ (*DMA::ptr()).ints0().write(|w| w.bits(1 << self.id()));
+ true
+ } else {
+ false
+ }
+ }
+ }
+
+ #[deprecated(note = "Renamed to enable_irq1")]
+ /// Enables the DMA_IRQ_1 signal for this channel.
+ fn listen_irq1(&mut self) {
+ self.enable_irq1();
+ }
+
+ /// Enables the DMA_IRQ_1 signal for this channel.
+ fn enable_irq1(&mut self) {
+ // Safety: We only use the atomic alias of the register.
+ unsafe {
+ write_bitmask_set((*DMA::ptr()).inte1().as_ptr(), 1 << self.id());
+ }
+ }
+
+ /// Check if the DMA_IRQ_1 signal for this channel is enabled.
+ fn is_enabled_irq1(&mut self) -> bool {
+ unsafe { ((*DMA::ptr()).inte1().read().bits() & (1 << self.id())) != 0 }
+ }
+
+ #[deprecated(note = "Renamed to disable_irq1")]
+ /// Disables the DMA_IRQ_1 signal for this channel.
+ fn unlisten_irq1(&mut self) {
+ self.disable_irq1();
+ }
+
+ /// Disables the DMA_IRQ_1 signal for this channel.
+ fn disable_irq1(&mut self) {
+ // Safety: We only use the atomic alias of the register.
+ unsafe {
+ write_bitmask_clear((*DMA::ptr()).inte1().as_ptr(), 1 << self.id());
+ }
+ }
+
+ /// Check if an interrupt is pending for this channel and clear the corresponding pending bit
+ fn check_irq1(&mut self) -> bool {
+ // Safety: The following is race-free as we only ever clear the bit for this channel.
+ // Nobody else modifies that bit.
+ unsafe {
+ let status = (*DMA::ptr()).ints1().read().bits();
+ if (status & (1 << self.id())) != 0 {
+ // Clear the interrupt.
+ (*DMA::ptr()).ints1().write(|w| w.bits(1 << self.id()));
+ true
+ } else {
+ false
+ }
+ }
+ }
+}
+
+impl SingleChannel for Channel {
+ fn ch(&self) -> &crate::pac::dma::CH {
+ self.regs()
+ }
+
+ fn id(&self) -> u8 {
+ CH::id()
+ }
+}
+
+impl Sealed for Channel {}
+
+impl SingleChannel for (Channel, Channel) {
+ fn ch(&self) -> &crate::pac::dma::CH {
+ self.0.regs()
+ }
+
+ fn id(&self) -> u8 {
+ CH1::id()
+ }
+}
+
+pub(crate) trait ChannelConfig {
+ fn config(
+ &mut self,
+ from: &FROM,
+ to: &mut TO,
+ pace: Pace,
+ bswap: bool,
+ chain_to: Option,
+ start: bool,
+ ) where
+ FROM: ReadTarget,
+ TO: WriteTarget;
+
+ fn set_chain_to_enabled(&mut self, other: &mut CH);
+ fn start(&mut self);
+ fn start_both(&mut self, other: &mut CH);
+}
+
+// rp235x's DMA engine only works with certain word sizes. Make sure that other
+// word sizes will fail to compile.
+struct IsValidWordSize | |