diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000000..3c75bc0e0e6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: ["bug", "status:needs-attention"] +assignees: '' + +--- + +## Bug description + + + +- Would you like to work on a fix? [y/n] + +## To Reproduce + + +1. ... +2. ... + + + + + +## Expected behavior + + + +## Environment + +- Target device: [e.g. ESP32-S3] +- Crate name and version: [e.g. esp-hal 0.20.0] \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..3e5f69b0766 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Ask a question (Matrix Channel) + url: https://github.com/esp-rs/esp-hal/discussions/new + about: Ask questions in our Matrix channel. + - name: Ask a question (GitHub Discussions) + url: https://github.com/esp-rs/esp-hal/discussions/new + about: Ask questions and discuss via GitHub Discussions. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000000..7ccb7759c06 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,28 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: ["enhancement", "status:needs-attention"] +assignees: '' + +--- + +## Motivations + + + +- Would you like to implement this feature? [y/n] + +## Solution + + + +## Alternatives + + + +## Additional context + + \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 86ee7fb67d1..8ddc5827c73 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,10 +7,11 @@ To help us review it efficiently, please ensure you've gone through the followin - [ ] I have updated existing examples or added new ones (if applicable). - [ ] I have used `cargo xtask fmt-packages` command to ensure that all changed code is formatted correctly. - [ ] My changes were added to the [`CHANGELOG.md`](https://github.com/esp-rs/esp-hal/blob/main/esp-hal/CHANGELOG.md) in the **_proper_** section. -- [ ] My changes are in accordance to the [esp-rs API guidelines](https://github.com/esp-rs/esp-hal/blob/main/API-GUIDELINES.md) +- [ ] I have added necessary changes to user code to the [Migration Guide](https://github.com/esp-rs/esp-hal/blob/main/esp-hal/MIGRATING-0.21.md). +- [ ] My changes are in accordance to the [esp-rs API guidelines](https://github.com/esp-rs/esp-hal/blob/main/documentation/API-GUIDELINES.md) #### Extra: -- [ ] I have read the [CONTRIBUTING.md guide](https://github.com/esp-rs/esp-hal/blob/main/CONTRIBUTING.md) and followed its instructions. +- [ ] I have read the [CONTRIBUTING.md guide](https://github.com/esp-rs/esp-hal/blob/main/documentation/CONTRIBUTING.md) and followed its instructions. ### Pull Request Details 📖 diff --git a/.github/actions/check-esp-hal/action.yml b/.github/actions/check-esp-hal/action.yml index d532c7e2acf..65991955d29 100644 --- a/.github/actions/check-esp-hal/action.yml +++ b/.github/actions/check-esp-hal/action.yml @@ -57,5 +57,7 @@ runs: --target=${{ inputs.target }} \ esp-hal - name: Build (examples) + env: + CI: 1 shell: bash - run: cargo +${{ inputs.toolchain }} xtask build-examples esp-hal ${{ inputs.device }} + run: cargo +${{ inputs.toolchain }} xtask build-examples esp-hal ${{ inputs.device }} --debug diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index adbfcb6cceb..645a8ae5e36 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -47,6 +47,8 @@ jobs: - 'esp-println/**' esp-riscv-rt: - 'esp-riscv-rt/**' + xtensa-lx-rt: + - 'xtensa-lx-rt/**' esp-storage: - 'esp-storage/**' esp-wifi: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9e142ae2a1..683e4ceebf6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,13 +16,14 @@ on: push: branches-ignore: - "gh-readonly-queue/**" + - "main" merge_group: workflow_dispatch: env: CARGO_TERM_COLOR: always GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - MSRV: "1.76.0" + MSRV: "1.77.0" RUSTDOCFLAGS: -Dwarnings # Cancel any currently running workflows from the same PR, branch, or @@ -133,13 +134,26 @@ jobs: cargo xtask build-package --features=esp32c6,ci --target=riscv32imac-unknown-none-elf esp-hal cargo xtask build-package --features=esp32h2,ci --target=riscv32imac-unknown-none-elf esp-hal - # Verify the MSRV for all Xtensa chips: + - name: msrv RISCV (esp-wifi) + run: | + cargo xtask build-package --features=esp32c2,wifi,ble,async --target=riscv32imc-unknown-none-elf esp-wifi + cargo xtask build-package --features=esp32c3,wifi,ble,async --target=riscv32imc-unknown-none-elf esp-wifi + cargo xtask build-package --features=esp32c6,wifi,ble,async --target=riscv32imac-unknown-none-elf esp-wifi + cargo xtask build-package --features=esp32h2,ble,async --target=riscv32imac-unknown-none-elf esp-wifi + + # Verify the MSRV for all Xtensa chips: - name: msrv Xtensa (esp-hal) run: | cargo xtask build-package --toolchain=esp --features=esp32,ci --target=xtensa-esp32-none-elf esp-hal cargo xtask build-package --toolchain=esp --features=esp32s2,ci --target=xtensa-esp32s2-none-elf esp-hal cargo xtask build-package --toolchain=esp --features=esp32s3,ci --target=xtensa-esp32s3-none-elf esp-hal + - name: msrv Xtensa (esp-wifi) + run: | + cargo xtask build-package --toolchain=esp --features=esp32,wifi,ble,async --target=xtensa-esp32-none-elf esp-wifi + cargo xtask build-package --toolchain=esp --features=esp32s2,wifi,async --target=xtensa-esp32s2-none-elf esp-wifi + cargo xtask build-package --toolchain=esp --features=esp32s3,wifi,ble,async --target=xtensa-esp32s3-none-elf esp-wifi + - name: msrv (esp-lp-hal) run: | cargo xtask build-package --features=esp32c6 --target=riscv32imac-unknown-none-elf esp-lp-hal @@ -164,48 +178,3 @@ jobs: # Check the formatting of all packages: - run: cargo xtask fmt-packages --check - - # -------------------------------------------------------------------------- - # Tests - - hil: - name: HIL Test | ${{ matrix.target.soc }} - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - target: - # RISC-V devices: - - soc: esp32c2 - rust-target: riscv32imc-unknown-none-elf - - soc: esp32c3 - rust-target: riscv32imc-unknown-none-elf - - soc: esp32c6 - rust-target: riscv32imac-unknown-none-elf - - soc: esp32h2 - rust-target: riscv32imac-unknown-none-elf - # Xtensa devices: - - soc: esp32 - - soc: esp32s2 - - soc: esp32s3 - - steps: - - uses: actions/checkout@v4 - - # Install the Rust toolchain for RISC-V devices: - - if: ${{ !contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.target.soc) }} - uses: dtolnay/rust-toolchain@v1 - with: - target: ${{ matrix.target.rust-target }} - toolchain: stable - components: rust-src - # Install the Rust toolchain for Xtensa devices: - - if: contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.target.soc) - uses: esp-rs/xtensa-toolchain@v1.5 - with: - buildtargets: ${{ matrix.target.soc }} - ldproxy: false - - - uses: Swatinem/rust-cache@v2 - - run: cargo xtask build-tests ${{ matrix.target.soc }} diff --git a/.github/workflows/hil.yml b/.github/workflows/hil.yml index fc797b40f88..72bd68a7c9b 100644 --- a/.github/workflows/hil.yml +++ b/.github/workflows/hil.yml @@ -1,6 +1,8 @@ name: HIL on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] merge_group: workflow_dispatch: inputs: @@ -13,13 +15,61 @@ on: required: true default: "main" +# Cancel any currently running workflows from the same PR, branch, or +# tag when a new workflow is triggered. +# +# https://stackoverflow.com/a/66336834 +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + env: CARGO_TERM_COLOR: always GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: + build-xtasks: + name: Build xtasks | ${{ matrix.host.arch }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + host: + - arch: armv7 + rust-target: armv7-unknown-linux-gnueabihf + - arch: aarch64 + rust-target: aarch64-unknown-linux-gnu + + steps: + - uses: actions/checkout@v4 + if: github.event_name != 'workflow_dispatch' + - uses: actions/checkout@v4 + if: github.event_name == 'workflow_dispatch' + with: + repository: ${{ github.event.inputs.repository }} + ref: ${{ github.event.inputs.branch }} + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@v1 + with: + toolchain: stable + components: rust-src + + - name: Install cross + run: cargo install cross + + - name: Build xtasks + run: cross build --release --target ${{ matrix.host.rust-target }} -p xtask + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: xtask-${{ matrix.host.arch }} + path: target/${{ matrix.host.rust-target }}/release/xtask + build-tests: - name: HIL Test | ${{ matrix.target.soc }} + name: Build HIL Tests | ${{ matrix.target.soc }} runs-on: ubuntu-latest strategy: @@ -36,6 +86,8 @@ jobs: - soc: esp32h2 rust-target: riscv32imac-unknown-none-elf # # Xtensa devices: + - soc: esp32 + rust-target: xtensa-esp32-none-elf - soc: esp32s2 rust-target: xtensa-esp32s2-none-elf - soc: esp32s3 @@ -92,8 +144,8 @@ jobs: overwrite: true hil: - name: HIL Test | ${{ matrix.target.soc }} - needs: build-tests + name: Run HIL Tests | ${{ matrix.target.soc }} + needs: [build-tests, build-xtasks] runs-on: labels: [self-hosted, "${{ matrix.target.runner }}"] strategy: @@ -103,46 +155,75 @@ jobs: # RISC-V devices: - soc: esp32c2 runner: esp32c2-jtag - usb: USB2 + host: aarch64 + hubs: "1 3" - soc: esp32c3 runner: esp32c3-usb - usb: ACM0 + host: armv7 + hubs: "1-1" - soc: esp32c6 runner: esp32c6-usb - usb: ACM0 + host: armv7 + hubs: "1-1" - soc: esp32h2 runner: esp32h2-usb - usb: USB0 + host: armv7 + hubs: "1-1" # Xtensa devices: + - soc: esp32 + runner: esp32-jtag + host: aarch64 + hubs: "1 3" - soc: esp32s2 runner: esp32s2-jtag - usb: USB1 + host: armv7 + hubs: "1-1" - soc: esp32s3 runner: esp32s3-usb - usb: USB0 + host: armv7 + hubs: "1-1" steps: - - uses: actions/checkout@v4 - if: github.event_name != 'workflow_dispatch' - - uses: actions/checkout@v4 - if: github.event_name == 'workflow_dispatch' - with: - repository: ${{ github.event.inputs.repository }} - ref: ${{ github.event.inputs.branch }} - - uses: actions/download-artifact@v4 with: name: tests-${{ matrix.target.soc }} path: tests-${{ matrix.target.soc }} + + - uses: actions/download-artifact@v4 + with: + name: xtask-${{ matrix.target.host }} + + - name: Cycle USB ports + run: | + export PATH=$PATH:/home/espressif/.cargo/bin + for i in {1..10}; do + # Disable all used hubs + for hub in ${{ matrix.target.hubs }}; do + sudo uhubctl -a off -l $hub + done + + sleep 0.5 + + # Enable all used hubs + for hub in ${{ matrix.target.hubs }}; do + sudo uhubctl -a on -l $hub + done + + sleep 0.5 + + if probe-rs list | grep -q "\[0\]:"; then + break + fi + done + - name: Run Tests id: run-tests run: | export PATH=$PATH:/home/espressif/.cargo/bin - cargo xtask run-elfs ${{ matrix.target.soc }} tests-${{ matrix.target.soc }} + chmod +x xtask + ./xtask run-elfs ${{ matrix.target.soc }} tests-${{ matrix.target.soc }} - - name: Erase Flash on Failure - if: ${{ failure() && steps.run-tests.conclusion == 'failure' }} - env: - ESPFLASH_PORT: /dev/tty${{ matrix.target.usb }} + - name: Clean up + if: always() run: | - export PATH=$PATH:/home/espressif/.cargo/bin - espflash erase-flash + rm -rf tests-${{ matrix.target.soc }} + rm -f xtask diff --git a/.github/workflows/issue_handler.yml b/.github/workflows/issue_handler.yml index 978e80428ce..5b4e5070f74 100644 --- a/.github/workflows/issue_handler.yml +++ b/.github/workflows/issue_handler.yml @@ -13,4 +13,4 @@ jobs: - uses: actions/add-to-project@v0.5.0 with: project-url: https://github.com/orgs/esp-rs/projects/2 - github-token: ${{ secrets.PAT }} + github-token: ${{ secrets.PAT }} \ No newline at end of file diff --git a/README.md b/README.md index f78c7771323..aa4c7c319c9 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ If you have any questions, comments, or concerns, please [open an issue], [start > [!NOTE] > -> This project is still in the relatively early stages of development, and as such there should be no expectation of API stability. A significant number of peripherals currently have drivers implemented but have varying levels of functionality. For most basic tasks, this should be usable already, however some more advanced or uncommon features may not yet be implemented. +> This project is still in the relatively early stages of development, and as such there should be no expectation of API stability. A significant number of peripherals currently have drivers implemented but have varying levels of functionality. For most tasks, this should be usable already, however some more advanced or uncommon features may not yet be implemented. [esp-lp-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp-lp-hal [esp-idf-svc]: https://github.com/esp-rs/esp-idf-svc @@ -49,6 +49,13 @@ For information about the HAL and how to use it in your own projects, please ref This repository is home to a number of different packages; for more information regarding a particular package, please refer to its `README.md` and/or documentation. +## Contributing + +We have a number of living documents to aid contributing to the project, please give these a read before modifying code: + +- [API-GUIDELINES](https://github.com/esp-rs/esp-hal/blob/main/documentation/API-GUIDELINES.md) +- [CONTRIBUTING-GUIDE](https://github.com/esp-rs/esp-hal/blob/main/documentation/CONTRIBUTING.md) + ## License Licensed under either of: @@ -58,10 +65,8 @@ Licensed under either of: at your option. -### Contribution +### Contribution 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. - -If you consider contributing, please make sure you have read and understood our [contributing guide](./CONTRIBUTING.md) and [API guidelines](https://github.com/esp-rs/esp-hal/blob/main/API-GUIDELINES.md). diff --git a/API-GUIDELINES.md b/documentation/API-GUIDELINES.md similarity index 90% rename from API-GUIDELINES.md rename to documentation/API-GUIDELINES.md index 093ac5a2775..0eca26d9b75 100644 --- a/API-GUIDELINES.md +++ b/documentation/API-GUIDELINES.md @@ -15,16 +15,16 @@ The following paragraphs contain additional recommendations. ## Construction and Destruction of Drivers -- Drivers take peripherals and pins via the `PeripheralRef` pattern - they don't consume peripherals/pins -- Consider adding a `Drop` implementation resetting the peripheral to idle state -- Consider using a builder-like pattern for configuration which must be done during initialization +- Drivers take peripherals and pins via the `PeripheralRef` pattern - they don't consume peripherals/pins. +- Consider adding a `Drop` implementation resetting the peripheral to idle state. +- Consider using a builder-like pattern for configuration which must be done during initialization. ## Interoperability -- `cfg` gated `defmt` derives and impls are added to new structs and enums +- `cfg` gated `defmt` derives and impls are added to new structs and enums. - see [this example](https://github.com/esp-rs/esp-hal/blob/df2b7bd8472cc1d18db0d9441156575570f59bb3/esp-hal/src/spi/mod.rs#L15) - e.g. `#[cfg_attr(feature = "defmt", derive(defmt::Format))]` -- Don't use `log::XXX!` macros directly - use the wrappers in `fmt.rs` (e.g. just `info!` instead of `log::info!` or importing `log::*`) +- Don't use `log::XXX!` macros directly - use the wrappers in `fmt.rs` (e.g. just `info!` instead of `log::info!` or importing `log::*`)! ## API Surface @@ -44,6 +44,7 @@ The following paragraphs contain additional recommendations. - Common cases of useless type info is storing pin information - this is usually not required after configuring the pins and will bloat the complexity of the type massively. When following the `PeripheralRef` pattern it's not needed in order to keep users from re-using the pin while in use - Avoiding `&mut self` when `&self` is safe to use. `&self` is generally easier to use as an API. Typical applications of this are where the methods just do writes to registers which don't have side effects. - For example starting a timer is fine for `&self`, worst case a timer will be started twice if two parts of the program call it. You can see a real example of this [here](https://github.com/esp-rs/esp-hal/pull/1500#pullrequestreview-2015911974) +- Maintain order consistency in the API, such as in the case of pairs like RX/TX. ## Maintainability @@ -52,6 +53,8 @@ The following paragraphs contain additional recommendations. - If you are porting code from ESP-IDF (or anything else), please include a link WITH the commit hash in it, and please highlight the relevant line(s) of code - If necessary provide further context as comments (consider linking to code, PRs, TRM - make sure to use permanent links, e.g. include the hash when linking to a Git repository, include the revision, page number etc. when linking to TRMs) - Generally, follow common "good practices" and idiomatic Rust style +- All `Future` objects (public or private) must be marked with ``#[must_use = "futures do nothing unless you `.await` or poll them"]``. +- Prefer `cfg_if!` over multiple exclusive `#[cfg]` attributes. `cfg_if!` visually divides the options, often results in simpler conditions and simplifies adding new branches in the future. ## Modules Documentation @@ -77,7 +80,7 @@ Modules should have the following documentation format: //! ``` //! //! ## Implementation State -//! List unsuported features +//! List unsupported features ``` - If any of the headers is empty, remove it - When possible, use ESP-IDF docs and TRM as references and include links if possible. diff --git a/CONTRIBUTING.md b/documentation/CONTRIBUTING.md similarity index 96% rename from CONTRIBUTING.md rename to documentation/CONTRIBUTING.md index d0c009718b0..0ce91dd0dcf 100644 --- a/CONTRIBUTING.md +++ b/documentation/CONTRIBUTING.md @@ -41,7 +41,7 @@ Before adding or changing code, review the [esp-rs API guidelines]. [Setting Up Git]: https://docs.github.com/en/get-started/quickstart/set-up-git [GitHub Flow]: https://docs.github.com/en/get-started/quickstart/github-flow [Pull Requests]: https://docs.github.com/en/github/collaborating-with-pull-requests -[esp-rs API guidelines]: ./API-GUIDELINES.md +[esp-rs API guidelines]: ./documentation/API-GUIDELINES.md ## Getting Started @@ -84,11 +84,11 @@ By taking these extra steps to test your contributions, you help maintain the hi Ensuring the quality and reliability of `esp-hal` is a shared responsibility, and testing plays a critical role in this process. Our GitHub CI automatically checks the buildability of all examples and drivers within the project. However, automated tests can't catch everything, especially when it comes to the nuanced behavior of hardware interactions. So make sure that the example affected by your change works as expected. -Further steps that can (or should) be taken in testing: +Further steps that can (or should) be taken in testing: -* Using [xtask], build examples for the specified chip. +* Using [xtask], build examples for the specified chip. * Build the documentation and run the doctests if they have been modified using the `build-documentation` and `run-doc-test` commands in [xtask]. -* Run the [HIL] tests locally if changes have been made to them. +* Run the [HIL] tests locally if changes have been made to them. [xtask]: https://github.com/esp-rs/esp-hal/tree/main/xtask @@ -122,6 +122,7 @@ This will use `rustfmt` to ensure that all source code is formatted correctly pr * [Link your PR] to any relevant issues it addresses. * [Allow edits from maintainers] so the branch can be updated for a merge. Once you submit your PR, a Docs team member will review your proposal. We may ask questions or request additional information. * Make sure you add an entry with your changes to the [Changelog]. Also make sure that it is in the appropriate section of the document. +* Make sure you add your changes to the current [migration guide]. * We may ask for changes to be made before a PR can be merged, either using [suggested changes] or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch. * As you update your PR and apply changes, mark each conversation as [resolved]. * Resolve merge conflicts if they arise, using resources like [this git tutorial] for help. @@ -129,6 +130,7 @@ This will use `rustfmt` to ensure that all source code is formatted correctly pr [Link your PR]: https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue [Allow edits from maintainers]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-forkmember [Changelog]: esp-hal/CHANGELOG.md +[migration guide]: esp-hal/MIGRATING-0.20.md [suggested changes]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/incorporating-feedback-in-your-pull-request [resolved]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request#resolving-conversations [this git tutorial]: https://github.com/skills/resolve-merge-conflicts diff --git a/esp-alloc/CHANGELOG.md b/esp-alloc/CHANGELOG.md index 85894f3a362..4ce4d03b19c 100644 --- a/esp-alloc/CHANGELOG.md +++ b/esp-alloc/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- a global allocator is created in esp-alloc, now you need to add individual memory regions (up to 3) to the allocator (#2099) + ### Fixed ### Removed diff --git a/esp-alloc/Cargo.toml b/esp-alloc/Cargo.toml index d6beab49b8a..09d1bd2f238 100644 --- a/esp-alloc/Cargo.toml +++ b/esp-alloc/Cargo.toml @@ -23,7 +23,8 @@ default-target = "riscv32imc-unknown-none-elf" features = ["nightly"] [dependencies] -critical-section = "1.1.2" +critical-section = "1.1.3" +enumset = "1.1.5" linked_list_allocator = { version = "0.10.5", default-features = false, features = ["const_mut_refs"] } [features] diff --git a/esp-alloc/src/lib.rs b/esp-alloc/src/lib.rs index 3d4bce93961..c3c6c7f0009 100644 --- a/esp-alloc/src/lib.rs +++ b/esp-alloc/src/lib.rs @@ -1,33 +1,35 @@ -//! A simple `no_std` heap allocator for RISC-V and Xtensa processors from +//! A `no_std` heap allocator for RISC-V and Xtensa processors from //! Espressif. Supports all currently available ESP32 devices. //! //! **NOTE:** using this as your global allocator requires using Rust 1.68 or //! greater, or the `nightly` release channel. //! //! # Using this as your Global Allocator -//! To use EspHeap as your global allocator, you need at least Rust 1.68 or -//! nightly. //! //! ```rust -//! #[global_allocator] -//! static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); +//! use esp_alloc as _; //! //! fn init_heap() { //! const HEAP_SIZE: usize = 32 * 1024; //! static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit(); //! //! unsafe { -//! ALLOCATOR.init(HEAP.as_mut_ptr() as *mut u8, HEAP_SIZE); +//! esp_alloc::INSTANCE.add_region(esp_alloc::HeapRegion::new( +//! HEAP.as_mut_ptr() as *mut u8, +//! HEAP_SIZE, +//! esp_alloc::MemoryCapability::Internal.into(), +//! )); //! } //! } //! ``` //! //! # Using this with the nightly `allocator_api`-feature -//! Sometimes you want to have single allocations in PSRAM, instead of an esp's -//! DRAM. For that, it's convenient to use the nightly `allocator_api`-feature, +//! Sometimes you want to have more control over allocations. +//! +//! For that, it's convenient to use the nightly `allocator_api`-feature, //! which allows you to specify an allocator for single allocations. //! -//! **NOTE:** To use this, you have to enable the create's `nightly` feature +//! **NOTE:** To use this, you have to enable the crate's `nightly` feature //! flag. //! //! Create and initialize an allocator to use in single allocations: @@ -36,7 +38,11 @@ //! //! fn init_psram_heap() { //! unsafe { -//! PSRAM_ALLOCATOR.init(psram::psram_vaddr_start() as *mut u8, psram::PSRAM_BYTES); +//! PSRAM_ALLOCATOR.add_region(esp_alloc::HeapRegion::new( +//! psram::psram_vaddr_start() as *mut u8, +//! psram::PSRAM_BYTES, +//! esp_alloc::MemoryCapability::Internal.into(), +//! )); //! } //! } //! ``` @@ -50,7 +56,7 @@ #![cfg_attr(feature = "nightly", feature(allocator_api))] #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")] -pub mod macros; +mod macros; #[cfg(feature = "nightly")] use core::alloc::{AllocError, Allocator}; @@ -61,35 +67,81 @@ use core::{ }; use critical_section::Mutex; +use enumset::{EnumSet, EnumSetType}; use linked_list_allocator::Heap; +/// The global allocator instance +#[global_allocator] +pub static HEAP: EspHeap = EspHeap::empty(); + +const NON_REGION: Option = None; + +#[derive(EnumSetType)] +/// Describes the properties of a memory region +pub enum MemoryCapability { + /// Memory must be internal; specifically it should not disappear when + /// flash/spiram cache is switched off + Internal, + /// Memory must be in SPI RAM + External, +} + +/// A memory region to be used as heap memory +pub struct HeapRegion { + heap: Heap, + capabilities: EnumSet, +} + +impl HeapRegion { + /// Create a new [HeapRegion] with the given capabilities + /// + /// # Safety + /// + /// - The supplied memory region must be available for the entire program + /// (`'static`). + /// - The supplied memory region must be exclusively available to the heap + /// only, no aliasing. + /// - `size > 0`. + pub unsafe fn new( + heap_bottom: *mut u8, + size: usize, + capabilities: EnumSet, + ) -> Self { + let mut heap = Heap::empty(); + heap.init(heap_bottom, size); + + Self { heap, capabilities } + } +} + +/// A memory allocator +/// +/// In addition to what Rust's memory allocator can do it allows to allocate +/// memory in regions satisfying specific needs. pub struct EspHeap { - heap: Mutex>, + heap: Mutex; 3]>>, } impl EspHeap { /// Crate a new UNINITIALIZED heap allocator - /// - /// You must initialize this heap using the - /// [`init`](struct.EspHeap.html#method.init) method before using the - /// allocator. - pub const fn empty() -> EspHeap { + pub const fn empty() -> Self { EspHeap { - heap: Mutex::new(RefCell::new(Heap::empty())), + heap: Mutex::new(RefCell::new([NON_REGION; 3])), } } - /// Initializes the heap - /// - /// This function must be called BEFORE you run any code that makes use of - /// the allocator. + /// Add a memory region to the heap /// /// `heap_bottom` is a pointer to the location of the bottom of the heap. /// /// `size` is the size of the heap in bytes. /// + /// You can add up to three regions per allocator. + /// /// Note that: /// + /// - Memory is allocated from the first suitable memory region first + /// /// - The heap grows "upwards", towards larger addresses. Thus `end_addr` /// must be larger than `start_addr` /// @@ -102,59 +154,147 @@ impl EspHeap { /// `'static` lifetime). /// - The supplied memory region must be exclusively available to the heap /// only, no aliasing. - /// - This function must be called exactly ONCE. /// - `size > 0`. - pub unsafe fn init(&self, heap_bottom: *mut u8, size: usize) { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().init(heap_bottom, size)); + pub unsafe fn add_region(&self, region: HeapRegion) { + critical_section::with(|cs| { + let mut regions = self.heap.borrow_ref_mut(cs); + let free = regions + .iter() + .enumerate() + .find(|v| v.1.is_none()) + .map(|v| v.0); + + if let Some(free) = free { + regions[free] = Some(region); + } else { + panic!( + "Exceeded the maximum of {} heap memory regions", + regions.len() + ); + } + }); } - /// Returns an estimate of the amount of bytes in use. + /// Returns an estimate of the amount of bytes in use in all memory regions. pub fn used(&self) -> usize { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().used()) + critical_section::with(|cs| { + let regions = self.heap.borrow_ref(cs); + let mut used = 0; + for region in regions.iter() { + if let Some(region) = region.as_ref() { + used += region.heap.used(); + } + } + used + }) } /// Returns an estimate of the amount of bytes available. pub fn free(&self) -> usize { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free()) + self.free_caps(EnumSet::empty()) + } + + /// The free heap satisfying the given requirements + pub fn free_caps(&self, capabilities: EnumSet) -> usize { + critical_section::with(|cs| { + let regions = self.heap.borrow_ref(cs); + let mut free = 0; + for region in regions.iter().filter(|region| { + if region.is_some() { + region + .as_ref() + .unwrap() + .capabilities + .is_superset(capabilities) + } else { + false + } + }) { + if let Some(region) = region.as_ref() { + free += region.heap.free(); + } + } + free + }) + } + + /// Allocate memory in a region satisfying the given requirements. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure that `layout` has non-zero size. + /// + /// The allocated block of memory may or may not be initialized. + pub unsafe fn alloc_caps( + &self, + capabilities: EnumSet, + layout: Layout, + ) -> *mut u8 { + critical_section::with(|cs| { + let mut regions = self.heap.borrow_ref_mut(cs); + let mut iter = (*regions).iter_mut().filter(|region| { + if region.is_some() { + region + .as_ref() + .unwrap() + .capabilities + .is_superset(capabilities) + } else { + false + } + }); + + let res = loop { + if let Some(Some(region)) = iter.next() { + let res = region.heap.allocate_first_fit(layout); + if let Ok(res) = res { + break Some(res); + } + } else { + break None; + } + }; + + res.map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) + }) } } unsafe impl GlobalAlloc for EspHeap { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .allocate_first_fit(layout) - .ok() - .map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) - }) + self.alloc_caps(EnumSet::empty(), layout) } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + if ptr.is_null() { + return; + } + critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .deallocate(NonNull::new_unchecked(ptr), layout) - }); + let mut regions = self.heap.borrow_ref_mut(cs); + let mut iter = (*regions).iter_mut(); + + while let Some(Some(region)) = iter.next() { + if region.heap.bottom() <= ptr && region.heap.top() >= ptr { + region.heap.deallocate(NonNull::new_unchecked(ptr), layout); + } + } + }) } } #[cfg(feature = "nightly")] unsafe impl Allocator for EspHeap { fn allocate(&self, layout: Layout) -> Result, AllocError> { - critical_section::with(|cs| { - let raw_ptr = self - .heap - .borrow(cs) - .borrow_mut() - .allocate_first_fit(layout) - .map_err(|_| AllocError)? - .as_ptr(); - let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; - Ok(NonNull::slice_from_raw_parts(ptr, layout.size())) - }) + let raw_ptr = unsafe { self.alloc(layout) }; + + if raw_ptr.is_null() { + return Err(AllocError); + } + + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; + Ok(NonNull::slice_from_raw_parts(ptr, layout.size())) } unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { diff --git a/esp-alloc/src/macros.rs b/esp-alloc/src/macros.rs index 17eec9662b9..47d9c454fa4 100644 --- a/esp-alloc/src/macros.rs +++ b/esp-alloc/src/macros.rs @@ -1,24 +1,25 @@ //! Macros provided for convenience -/// Create a heap allocator providing a heap of the given size in bytes -/// -/// You can only have ONE allocator at most +/// Initialize a global heap allocator providing a heap of the given size in +/// bytes #[macro_export] macro_rules! heap_allocator { ($size:expr) => {{ - #[global_allocator] - static ALLOCATOR: $crate::EspHeap = $crate::EspHeap::empty(); static mut HEAP: core::mem::MaybeUninit<[u8; $size]> = core::mem::MaybeUninit::uninit(); unsafe { - ALLOCATOR.init(HEAP.as_mut_ptr() as *mut u8, $size); + $crate::HEAP.add_region($crate::HeapRegion::new( + HEAP.as_mut_ptr() as *mut u8, + $size, + $crate::MemoryCapability::Internal.into(), + )); } }}; } -/// Create a heap allocator backed by PSRAM +/// Initialize a global heap allocator backed by PSRAM /// -/// You can only have ONE allocator at most. You need a SoC which supports PSRAM +/// You need a SoC which supports PSRAM /// and activate the feature to enable it. You need to pass the PSRAM peripheral /// and the psram module path. /// @@ -29,13 +30,14 @@ macro_rules! heap_allocator { #[macro_export] macro_rules! psram_allocator { ($peripheral:expr,$psram_module:path) => {{ - #[global_allocator] - static ALLOCATOR: $crate::EspHeap = $crate::EspHeap::empty(); - use $psram_module as _psram; _psram::init_psram($peripheral); unsafe { - ALLOCATOR.init(_psram::psram_vaddr_start() as *mut u8, _psram::PSRAM_BYTES); + $crate::HEAP.add_region($crate::HeapRegion::new( + _psram::psram_vaddr_start() as *mut u8, + _psram::PSRAM_BYTES, + $crate::MemoryCapability::External.into(), + )); } }}; } diff --git a/esp-backtrace/CHANGELOG.md b/esp-backtrace/CHANGELOG.md index 11a5c8f1848..8bf2a9764af 100644 --- a/esp-backtrace/CHANGELOG.md +++ b/esp-backtrace/CHANGELOG.md @@ -8,16 +8,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added -- add custom-pre-backtrace feature (#1822) ### Changed -- Improve panic message printing (#1823) +### Fixed + +### Removed + +## 0.14.1 - 2024-09-06 + +### Added + +### Changed +- Print a more helpful message in case of a `Cp0Disabled` exception (#2061) ### Fixed ### Removed +## 0.14.0 - 2024-08-29 + +### Added + +- Add custom-pre-backtrace feature (#1822) + +### Changed + +- Improve panic message printing (#1823) + ## 0.13.0 - 2024-07-16 No changes - published to avoid conflicts with `esp-println` diff --git a/esp-backtrace/Cargo.toml b/esp-backtrace/Cargo.toml index 77373a65417..969380a3435 100644 --- a/esp-backtrace/Cargo.toml +++ b/esp-backtrace/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "esp-backtrace" -version = "0.13.0" +version = "0.14.1" edition = "2021" rust-version = "1.76.0" description = "Bare-metal backtrace support for Espressif devices" @@ -13,8 +13,8 @@ features = ["esp32c3", "panic-handler", "exception-handler", "println", "e [dependencies] defmt = { version = "0.3.8", optional = true } -esp-println = { version = "0.10.0", optional = true, default-features = false, path = "../esp-println" } -semihosting = { version = "0.1.12", optional = true } +esp-println = { version = "0.11.0", optional = true, default-features = false, path = "../esp-println" } +semihosting = { version = "0.1.14", optional = true } [build-dependencies] esp-build = { version = "0.1.0", path = "../esp-build" } diff --git a/esp-backtrace/src/lib.rs b/esp-backtrace/src/lib.rs index 0a9a5f273b2..f023e605aab 100644 --- a/esp-backtrace/src/lib.rs +++ b/esp-backtrace/src/lib.rs @@ -100,10 +100,10 @@ unsafe fn __user_exception(cause: arch::ExceptionCause, context: arch::Context) // Unfortunately, a different formatter string is used #[cfg(not(feature = "defmt"))] - esp_println::println!("\n\nException occured '{:?}'", cause); + esp_println::println!("\n\nException occurred '{}'", cause); #[cfg(feature = "defmt")] - defmt::error!("\n\nException occured '{}'", cause); + defmt::error!("\n\nException occurred '{}'", cause); println!("{:?}", context); diff --git a/esp-backtrace/src/riscv.rs b/esp-backtrace/src/riscv.rs index 06d666174ee..9c20924849f 100644 --- a/esp-backtrace/src/riscv.rs +++ b/esp-backtrace/src/riscv.rs @@ -12,48 +12,98 @@ pub(super) const RA_OFFSET: usize = 4; /// Registers saved in trap handler #[doc(hidden)] -#[allow(missing_docs)] #[derive(Default, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] +#[cfg(feature = "exception-handler")] pub(crate) struct TrapFrame { + /// Return address, stores the address to return to after a function call or + /// interrupt. pub ra: usize, + /// Temporary register t0, used for intermediate values. pub t0: usize, + /// Temporary register t1, used for intermediate values. pub t1: usize, + /// Temporary register t2, used for intermediate values. pub t2: usize, + /// Temporary register t3, used for intermediate values. pub t3: usize, + /// Temporary register t4, used for intermediate values. pub t4: usize, + /// Temporary register t5, used for intermediate values. pub t5: usize, + /// Temporary register t6, used for intermediate values. pub t6: usize, + /// Argument register a0, typically used to pass the first argument to a + /// function. pub a0: usize, + /// Argument register a1, typically used to pass the second argument to a + /// function. pub a1: usize, + /// Argument register a2, typically used to pass the third argument to a + /// function. pub a2: usize, + /// Argument register a3, typically used to pass the fourth argument to a + /// function. pub a3: usize, + /// Argument register a4, typically used to pass the fifth argument to a + /// function. pub a4: usize, + /// Argument register a5, typically used to pass the sixth argument to a + /// function. pub a5: usize, + /// Argument register a6, typically used to pass the seventh argument to a + /// function. pub a6: usize, + /// Argument register a7, typically used to pass the eighth argument to a + /// function. pub a7: usize, + /// Saved register s0, used to hold values across function calls. pub s0: usize, + /// Saved register s1, used to hold values across function calls. pub s1: usize, + /// Saved register s2, used to hold values across function calls. pub s2: usize, + /// Saved register s3, used to hold values across function calls. pub s3: usize, + /// Saved register s4, used to hold values across function calls. pub s4: usize, + /// Saved register s5, used to hold values across function calls. pub s5: usize, + /// Saved register s6, used to hold values across function calls. pub s6: usize, + /// Saved register s7, used to hold values across function calls. pub s7: usize, + /// Saved register s8, used to hold values across function calls. pub s8: usize, + /// Saved register s9, used to hold values across function calls. pub s9: usize, + /// Saved register s10, used to hold values across function calls. pub s10: usize, + /// Saved register s11, used to hold values across function calls. pub s11: usize, + /// Global pointer register, holds the address of the global data area. pub gp: usize, + /// Thread pointer register, holds the address of the thread-local storage + /// area. pub tp: usize, + /// Stack pointer register, holds the address of the top of the stack. pub sp: usize, + /// Program counter, stores the address of the next instruction to be + /// executed. pub pc: usize, + /// Machine status register, holds the current status of the processor, + /// including interrupt enable bits and privilege mode. pub mstatus: usize, + /// Machine cause register, contains the reason for the trap (e.g., + /// exception or interrupt number). pub mcause: usize, + /// Machine trap value register, contains additional information about the + /// trap (e.g., faulting address). pub mtval: usize, } +#[cfg(feature = "exception-handler")] impl core::fmt::Debug for TrapFrame { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { write!( diff --git a/esp-backtrace/src/xtensa.rs b/esp-backtrace/src/xtensa.rs index 64b8287ef6d..eb0c4425c43 100644 --- a/esp-backtrace/src/xtensa.rs +++ b/esp-backtrace/src/xtensa.rs @@ -1,4 +1,4 @@ -use core::arch::asm; +use core::{arch::asm, fmt::Display}; use crate::MAX_BACKTRACE_ADDRESSES; @@ -9,9 +9,9 @@ use crate::MAX_BACKTRACE_ADDRESSES; #[cfg(feature = "panic-handler")] pub(super) const RA_OFFSET: usize = 3; +/// Exception Cause #[doc(hidden)] -#[allow(missing_docs)] -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub enum ExceptionCause { @@ -95,89 +95,175 @@ pub enum ExceptionCause { Cp6Disabled = 38, /// Access To Coprocessor 7 When Disabled Cp7Disabled = 39, - + /// None None = 255, } +impl Display for ExceptionCause { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + if *self == Self::Cp0Disabled { + write!(f, "Cp0Disabled (Access to the floating point coprocessor is not allowed. You may want to enable the `float-save-restore` feature of the `xtensa-lx-rt` crate.)") + } else { + write!(f, "{:?}", self) + } + } +} + #[doc(hidden)] -#[allow(missing_docs, non_snake_case)] +#[allow(non_snake_case)] #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub struct Context { + /// Program counter, stores the address of the next instruction to be + /// executed. pub PC: u32, + /// Processor status, holds various status flags for the CPU. pub PS: u32, + /// General-purpose register A0 used for data storage and manipulation. pub A0: u32, + /// General-purpose register A1 used for data storage and manipulation. pub A1: u32, + /// General-purpose register A2 used for data storage and manipulation. pub A2: u32, + /// General-purpose register A3 used for data storage and manipulation. pub A3: u32, + /// General-purpose register A4 used for data storage and manipulation. pub A4: u32, + /// General-purpose register A5 used for data storage and manipulation. pub A5: u32, + /// General-purpose register A6 used for data storage and manipulation. pub A6: u32, + /// General-purpose register A7 used for data storage and manipulation. pub A7: u32, + /// General-purpose register A8 used for data storage and manipulation. pub A8: u32, + /// General-purpose register A9 used for data storage and manipulation. pub A9: u32, + /// General-purpose register A10 used for data storage and manipulation. pub A10: u32, + /// General-purpose register A11 used for data storage and manipulation. pub A11: u32, + /// General-purpose register A12 used for data storage and manipulation. pub A12: u32, + /// General-purpose register A13 used for data storage and manipulation. pub A13: u32, + /// General-purpose register A14 used for data storage and manipulation. pub A14: u32, + /// General-purpose register A15 used for data storage and manipulation. pub A15: u32, + /// Shift amount register, used for shift and rotate instructions. pub SAR: u32, + /// Exception cause, indicates the reason for the last exception. pub EXCCAUSE: u32, + /// Exception address, holds the address related to the exception. pub EXCVADDR: u32, + /// Loop start address, used in loop instructions. pub LBEG: u32, + /// Loop end address, used in loop instructions. pub LEND: u32, + /// Loop counter, used to count iterations in loop instructions. pub LCOUNT: u32, + /// Thread pointer, used for thread-local storage. pub THREADPTR: u32, + /// Compare register, used for certain compare instructions. pub SCOMPARE1: u32, + /// Break register, used for breakpoint-related operations. pub BR: u32, + /// Accumulator low register, used for extended arithmetic operations. pub ACCLO: u32, + /// Accumulator high register, used for extended arithmetic operations. pub ACCHI: u32, + /// Additional register M0 used for special operations. pub M0: u32, + /// Additional register M1 used for special operations. pub M1: u32, + /// Additional register M2 used for special operations. pub M2: u32, + /// Additional register M3 used for special operations. pub M3: u32, + /// 64-bit floating-point register (low part), available if the + /// `print-float-registers` feature is enabled. #[cfg(feature = "print-float-registers")] pub F64R_LO: u32, + /// 64-bit floating-point register (high part), available if the + /// `print-float-registers` feature is enabled. #[cfg(feature = "print-float-registers")] pub F64R_HI: u32, + /// Floating-point status register, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F64S: u32, + /// Floating-point control register, available if the + /// `print-float-registers` feature is enabled. #[cfg(feature = "print-float-registers")] pub FCR: u32, + /// Floating-point status register, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub FSR: u32, + /// Floating-point register F0, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F0: u32, + /// Floating-point register F1, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F1: u32, + /// Floating-point register F2, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F2: u32, + /// Floating-point register F3, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F3: u32, + /// Floating-point register F4, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F4: u32, + /// Floating-point register F5, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F5: u32, + /// Floating-point register F6, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F6: u32, + /// Floating-point register F7, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F7: u32, + /// Floating-point register F8, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F8: u32, + /// Floating-point register F9, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F9: u32, + /// Floating-point register F10, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F10: u32, + /// Floating-point register F11, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F11: u32, + /// Floating-point register F12, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F12: u32, + /// Floating-point register F13, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F13: u32, + /// Floating-point register F14, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F14: u32, + /// Floating-point register F15, available if the `print-float-registers` + /// feature is enabled. #[cfg(feature = "print-float-registers")] pub F15: u32, } diff --git a/esp-build/src/lib.rs b/esp-build/src/lib.rs index 3de49b6ac3d..4e97602da0a 100644 --- a/esp-build/src/lib.rs +++ b/esp-build/src/lib.rs @@ -5,6 +5,7 @@ use std::{io::Write as _, process}; use proc_macro::TokenStream; +use quote::ToTokens; use syn::{parse_macro_input, punctuated::Punctuated, LitStr, Token}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; @@ -58,22 +59,10 @@ pub fn assert_unique_features(input: TokenStream) -> TokenStream { .into_iter() .collect::>(); - let pairs = unique_pairs(&features); - let unique_cfgs = pairs - .iter() - .map(|(a, b)| quote::quote! { all(feature = #a, feature = #b) }); - - let message = format!( - r#" -ERROR: expected exactly zero or one enabled feature from feature group: - {:?} -"#, - features.iter().map(|lit| lit.value()).collect::>(), - ); + let unique = impl_unique_features(&features, "exactly zero or one"); quote::quote! { - #[cfg(any(#(#unique_cfgs),*))] - ::esp_build::error! { #message } + #unique } .into() } @@ -91,17 +80,10 @@ pub fn assert_used_features(input: TokenStream) -> TokenStream { .into_iter() .collect::>(); - let message = format!( - r#" -ERROR: expected at least one enabled feature from feature group: - {:?} - "#, - features.iter().map(|lit| lit.value()).collect::>() - ); + let used = impl_used_features(&features, "at least one"); quote::quote! { - #[cfg(not(any(#(feature = #features),*)))] - ::esp_build::error! { #message } + #used } .into() } @@ -118,29 +100,54 @@ pub fn assert_unique_used_features(input: TokenStream) -> TokenStream { .into_iter() .collect::>(); - let pairs = unique_pairs(&features); + let unique = impl_unique_features(&features, "exactly one"); + let used = impl_used_features(&features, "exactly one"); + + quote::quote! { + #unique + #used + } + .into() +} + +// ---------------------------------------------------------------------------- +// Helper Functions + +fn impl_unique_features(features: &[LitStr], expectation: &str) -> impl ToTokens { + let pairs = unique_pairs(features); let unique_cfgs = pairs .iter() .map(|(a, b)| quote::quote! { all(feature = #a, feature = #b) }); let message = format!( r#" -ERROR: expected exactly one enabled feature from feature group: +ERROR: expected {expectation} enabled feature from feature group: + {:?} +"#, + features.iter().map(|lit| lit.value()).collect::>(), + ); + + quote::quote! { + #[cfg(any(#(#unique_cfgs),*))] + ::esp_build::error! { #message } + } +} + +fn impl_used_features(features: &[LitStr], expectation: &str) -> impl ToTokens { + let message = format!( + r#" +ERROR: expected {expectation} enabled feature from feature group: {:?} "#, features.iter().map(|lit| lit.value()).collect::>() ); quote::quote! { - #[cfg(any(any(#(#unique_cfgs),*), not(any(#(feature = #features),*))))] + #[cfg(not(any(#(feature = #features),*)))] ::esp_build::error! { #message } } - .into() } -// ---------------------------------------------------------------------------- -// Helper Functions - // Adapted from: // https://github.com/dtolnay/build-alert/blob/49d060e/src/lib.rs#L54-L93 fn do_alert(color: Color, input: TokenStream) -> TokenStream { diff --git a/esp-hal-embassy/CHANGELOG.md b/esp-hal-embassy/CHANGELOG.md index 63019c77cfe..741ba7a3d30 100644 --- a/esp-hal-embassy/CHANGELOG.md +++ b/esp-hal-embassy/CHANGELOG.md @@ -9,15 +9,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +### Changed + +### Fixed + +### Removed + +- Removed the `clocks` parameter from `esp_hal_embassy::init`. (#1999) + +## 0.3.0 - 2024-08-29 + +### Added + - This package now re-exports the `esp_hal_procmacros::main` macro (#1828) ### Changed +- Updated to latest release (`0.6.0`) for `embassy-executor` (#1942) +- Changed `init` to accept timers of multiple types (#1957, #2012) + ### Fixed - Fixed a bug where the timeout was huge whenever the timestamp at the time of scheduling was already in the past (#1875) - -### Removed +- Fixed interrupt executors looping endlessly when `integrated-timers` is used. (#1936) ## 0.2.0 - 2024-07-15 diff --git a/esp-hal-embassy/Cargo.toml b/esp-hal-embassy/Cargo.toml index aed8bc5e6e2..c080278ca41 100644 --- a/esp-hal-embassy/Cargo.toml +++ b/esp-hal-embassy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "esp-hal-embassy" -version = "0.2.0" +version = "0.3.0" edition = "2021" rust-version = "1.76.0" description = "Embassy support for esp-hal" @@ -12,20 +12,21 @@ default-target = "riscv32imac-unknown-none-elf" features = ["esp32c6"] [dependencies] -critical-section = "1.1.2" +critical-section = "1.1.3" defmt = { version = "0.3.8", optional = true } document-features = "0.2.10" -embassy-executor = { version = "0.5.0", optional = true } +embassy-executor = { version = "0.6.0", optional = true } embassy-time-driver = { version = "0.1.0", features = [ "tick-hz-1_000_000" ] } -esp-hal = { version = "0.19.0", path = "../esp-hal" } +esp-hal = { version = "0.20.0", path = "../esp-hal" } log = { version = "0.4.22", optional = true } -macros = { version = "0.12.0", features = ["embassy"], package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } -portable-atomic = "1.6.0" +macros = { version = "0.13.0", features = ["embassy"], package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } +portable-atomic = "1.7.0" +static_cell = "2.1.0" [build-dependencies] cfg-if = "1.0.0" esp-build = { version = "0.1.0", path = "../esp-build" } -esp-metadata = { version = "0.2.0", path = "../esp-metadata" } +esp-metadata = { version = "0.3.0", path = "../esp-metadata" } [features] default = ["executors"] @@ -43,7 +44,7 @@ defmt = ["dep:defmt", "embassy-executor?/defmt", "esp-hal/defmt"] ## Enable logging via the log crate log = ["dep:log"] ## Provide `Executor` and `InterruptExecutor` -executors = ["dep:embassy-executor"] +executors = ["dep:embassy-executor", "esp-hal/__esp_hal_embassy"] ## Use the executor-integrated `embassy-time` timer queue. integrated-timers = ["embassy-executor?/integrated-timers"] diff --git a/esp-hal-embassy/MIGRATING-0.3.md b/esp-hal-embassy/MIGRATING-0.3.md new file mode 100644 index 00000000000..be65c5db9b7 --- /dev/null +++ b/esp-hal-embassy/MIGRATING-0.3.md @@ -0,0 +1,30 @@ +Migration Guide from 0.3.x to vNext +==================================== + +Initialsation +------------- + +You no longer have to set up clocks and pass them to `esp_hal_embassy::init`. + +```diff + use esp_hal::{ +- clock::ClockControl, +- peripherals::Peripherals, + prelude::*, +- system::SystemControl, + }; + + #[esp_hal_embassy::main] + async fn main(_spawner: Spawner) -> ! { +- let peripherals = Peripherals::take(); +- let system = SystemControl::new(peripherals.SYSTEM); +- let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); ++ let peripherals = esp_hal::init(esp_hal::Config::default()); + + let timg0 = TimerGroup::new(peripherals.TIMG0); +- esp_hal_embassy::init(&clocks, timg0); ++ esp_hal_embassy::init(timg0); + + // ... + } +``` diff --git a/esp-hal-embassy/src/executor/interrupt.rs b/esp-hal-embassy/src/executor/interrupt.rs index 89369a81c8a..8dcc343e9fd 100644 --- a/esp-hal-embassy/src/executor/interrupt.rs +++ b/esp-hal-embassy/src/executor/interrupt.rs @@ -5,8 +5,7 @@ use core::{cell::UnsafeCell, mem::MaybeUninit}; use embassy_executor::{raw, SendSpawner}; use esp_hal::{ get_core, - interrupt::{self, InterruptHandler}, - system::SoftwareInterrupt, + interrupt::{self, software::SoftwareInterrupt, InterruptHandler}, }; use portable_atomic::{AtomicUsize, Ordering}; diff --git a/esp-hal-embassy/src/executor/mod.rs b/esp-hal-embassy/src/executor/mod.rs index f82bf3f6c43..51e43f34e79 100644 --- a/esp-hal-embassy/src/executor/mod.rs +++ b/esp-hal-embassy/src/executor/mod.rs @@ -5,7 +5,7 @@ mod thread; #[export_name = "__pender"] fn __pender(context: *mut ()) { - use esp_hal::system::SoftwareInterrupt; + use esp_hal::interrupt::software::SoftwareInterrupt; let context = (context as usize).to_le_bytes(); diff --git a/esp-hal-embassy/src/executor/thread.rs b/esp-hal-embassy/src/executor/thread.rs index 7ac8807ae20..5050a3793c1 100644 --- a/esp-hal-embassy/src/executor/thread.rs +++ b/esp-hal-embassy/src/executor/thread.rs @@ -71,14 +71,13 @@ impl Executor { #[cfg_attr( multi_core, doc = r#" - This will use software-interrupt 3 which isn't - available for anything else to wake the other core(s). - "# + +This will use software-interrupt 3 which isn't available for anything else to wake the other core(s)."# )] pub fn new() -> Self { #[cfg(multi_core)] unsafe { - esp_hal::system::SoftwareInterrupt::<3>::steal() + esp_hal::interrupt::software::SoftwareInterrupt::<3>::steal() .set_interrupt_handler(software3_interrupt) } diff --git a/esp-hal-embassy/src/lib.rs b/esp-hal-embassy/src/lib.rs index 93b6a1114bf..eae0a3a4ee3 100644 --- a/esp-hal-embassy/src/lib.rs +++ b/esp-hal-embassy/src/lib.rs @@ -36,7 +36,9 @@ // MUST be the first module mod fmt; -use esp_hal::clock::Clocks; +#[cfg(not(feature = "esp32"))] +use esp_hal::timer::systimer::Alarm; +use esp_hal::timer::{timg::Timer as TimgTimer, ErasedTimer}; pub use macros::main; #[cfg(feature = "executors")] @@ -47,7 +49,118 @@ use self::time_driver::{EmbassyTimer, Timer}; mod executor; mod time_driver; -/// Initialize embassy -pub fn init(clocks: &Clocks, time_driver: &'static mut [Timer]) { - EmbassyTimer::init(clocks, time_driver) +macro_rules! mk_static { + ($t:ty,$val:expr) => {{ + static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); + #[deny(unused_attributes)] + let x = STATIC_CELL.uninit().write(($val)); + x + }}; +} + +/// A trait to allow better UX for initializing the timers. +/// +/// This trait is meant to be used only for the `init` function. +/// Calling `timers()` multiple times may panic. +pub trait TimerCollection { + /// Returns the timers as a slice. + fn timers(self) -> &'static mut [Timer]; +} + +/// Helper trait to reduce boilerplate. +/// +/// We can't blanket-implement for `Into` because of possible +/// conflicting implementations. +trait IntoErasedTimer: Into {} + +impl IntoErasedTimer for ErasedTimer {} + +impl IntoErasedTimer for TimgTimer +where + DM: esp_hal::Mode, + Self: Into, +{ +} + +#[cfg(not(feature = "esp32"))] +impl IntoErasedTimer for Alarm<'_, T, DM, COMP, UNIT> +where + DM: esp_hal::Mode, + Self: Into, +{ +} + +impl TimerCollection for T +where + T: IntoErasedTimer, +{ + fn timers(self) -> &'static mut [Timer] { + Timer::new(self.into()).timers() + } +} + +impl TimerCollection for Timer { + fn timers(self) -> &'static mut [Timer] { + let timers = mk_static!([Timer; 1], [self]); + timers.timers() + } +} + +impl TimerCollection for &'static mut [Timer] { + fn timers(self) -> &'static mut [Timer] { + self + } +} + +impl TimerCollection for &'static mut [Timer; N] { + fn timers(self) -> &'static mut [Timer] { + self.as_mut() + } +} + +macro_rules! impl_array { + ($n:literal) => { + impl TimerCollection for [T; $n] + where + T: IntoErasedTimer, + { + fn timers(self) -> &'static mut [Timer] { + mk_static!([Timer; $n], self.map(|t| Timer::new(t.into()))) + } + } + }; +} + +impl_array!(2); +impl_array!(3); +impl_array!(4); + +/// Initialize embassy. +/// +/// Call this as soon as possible, before the first timer-related operation. +/// +/// The time driver can be one of a number of different options: +/// +/// - A timg `Timer` instance +/// - A systimer `Alarm` instance +/// - An `ErasedTimer` instance +/// - A `OneShotTimer` instance +/// - A mutable static slice of `OneShotTimer` instances +/// - A mutable static array of `OneShotTimer` instances +/// - A 2, 3, 4 element array of `ErasedTimer` instances +/// +/// # Examples +/// +/// ```rust, no_run +#[doc = esp_hal::before_snippet!()] +/// use esp_hal::timg::TimerGroup; +/// +/// let timg0 = TimerGroup::new(peripherals.TIMG0); +/// esp_hal_embassy::init(timg0.timer0); +/// +/// // ... now you can spawn embassy tasks or use `Timer::after` etc. +/// # } +/// ``` +pub fn init(time_driver: impl TimerCollection) { + EmbassyTimer::init(time_driver.timers()) } diff --git a/esp-hal-embassy/src/time_driver.rs b/esp-hal-embassy/src/time_driver.rs index 365cfe3933e..4303e56af4d 100644 --- a/esp-hal-embassy/src/time_driver.rs +++ b/esp-hal-embassy/src/time_driver.rs @@ -3,10 +3,9 @@ use core::cell::{Cell, RefCell}; use critical_section::Mutex; use embassy_time_driver::{AlarmHandle, Driver}; use esp_hal::{ - clock::Clocks, interrupt::{InterruptHandler, Priority}, prelude::*, - time::current_time, + time::now, timer::{ErasedTimer, OneShotTimer}, }; @@ -45,7 +44,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: EmbassyTimer = EmbassyTime }); impl EmbassyTimer { - pub(super) fn init(_clocks: &Clocks, timers: &'static mut [Timer]) { + pub(super) fn init(timers: &'static mut [Timer]) { if timers.len() > MAX_SUPPORTED_ALARM_COUNT { panic!( "Maximum of {} timers can be used.", @@ -120,7 +119,7 @@ impl EmbassyTimer { } fn arm(timer: &mut Timer, timestamp: u64) { - let now = current_time().duration_since_epoch(); + let now = now().duration_since_epoch(); let ts = timestamp.micros(); // if the TS is already in the past make the timer fire immediately let timeout = if ts > now { ts - now } else { 0.micros() }; @@ -131,7 +130,7 @@ impl EmbassyTimer { impl Driver for EmbassyTimer { fn now(&self) -> u64 { - current_time().ticks() + now().ticks() } unsafe fn allocate_alarm(&self) -> Option { @@ -156,10 +155,11 @@ impl Driver for EmbassyTimer { } fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { - // we sometimes get called with `u64::MAX` for apparently not yet initialized - // timers which would fail later on + // If `embassy-executor/integrated-timers` is enabled and there are no pending + // timers, embassy still calls `set_alarm` with `u64::MAX`. By returning + // `true` we signal that no re-polling is necessary. if timestamp == u64::MAX { - return false; + return true; } // The hardware fires the alarm even if timestamp is lower than the current diff --git a/esp-hal-procmacros/Cargo.toml b/esp-hal-procmacros/Cargo.toml index d50f67ed39c..ab1067e78c8 100644 --- a/esp-hal-procmacros/Cargo.toml +++ b/esp-hal-procmacros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "esp-hal-procmacros" -version = "0.12.0" +version = "0.13.0" edition = "2021" rust-version = "1.76.0" description = "Procedural macros for esp-hal" @@ -17,12 +17,12 @@ proc-macro = true darling = "0.20.10" document-features = "0.2.10" litrs = "0.4.1" -object = { version = "0.36.1", optional = true } -proc-macro-crate = "3.1.0" -proc-macro-error = "1.0.4" +object = { version = "0.36.4", optional = true, default-features = false, features = ["read_core", "elf"] } +proc-macro-crate = "3.2.0" +proc-macro-error2 = "2.0.0" proc-macro2 = "1.0.86" -quote = "1.0.36" -syn = { version = "2.0.71", features = ["extra-traits", "full"] } +quote = "1.0.37" +syn = { version = "2.0.76", features = ["extra-traits", "full"] } [features] ## Provide a `#[main]` procmacro to mark the entry point for Embassy applications. diff --git a/esp-hal-procmacros/src/embassy.rs b/esp-hal-procmacros/src/embassy.rs index fdd44ff863c..9d1dc5c0144 100644 --- a/esp-hal-procmacros/src/embassy.rs +++ b/esp-hal-procmacros/src/embassy.rs @@ -46,13 +46,13 @@ pub(crate) mod main { if !f.sig.generics.params.is_empty() { ctxt.error_spanned_by(&f.sig, "main function must not be generic"); } - if !f.sig.generics.where_clause.is_none() { + if f.sig.generics.where_clause.is_some() { ctxt.error_spanned_by(&f.sig, "main function must not have `where` clauses"); } - if !f.sig.abi.is_none() { + if f.sig.abi.is_some() { ctxt.error_spanned_by(&f.sig, "main function must not have an ABI qualifier"); } - if !f.sig.variadic.is_none() { + if f.sig.variadic.is_some() { ctxt.error_spanned_by(&f.sig, "main function must not be variadic"); } match &f.sig.output { diff --git a/esp-hal-procmacros/src/enum_dispatch.rs b/esp-hal-procmacros/src/enum_dispatch.rs deleted file mode 100644 index 2372be88426..00000000000 --- a/esp-hal-procmacros/src/enum_dispatch.rs +++ /dev/null @@ -1,80 +0,0 @@ -use proc_macro2::{Group, TokenStream, TokenTree}; -use quote::{format_ident, quote}; -use syn::{ - parse::{Parse, ParseStream, Result}, - Ident, -}; - -#[derive(Debug)] -pub(crate) struct MakeGpioEnumDispatchMacro { - pub name: String, - pub filter: Vec, - pub elements: Vec<(String, usize)>, -} - -impl Parse for MakeGpioEnumDispatchMacro { - fn parse(input: ParseStream) -> Result { - let name = input.parse::()?.to_string(); - let filter = input - .parse::()? - .stream() - .into_iter() - .map(|v| match v { - TokenTree::Group(_) => String::new(), - TokenTree::Ident(ident) => ident.to_string(), - TokenTree::Punct(_) => String::new(), - TokenTree::Literal(_) => String::new(), - }) - .filter(|p| !p.is_empty()) - .collect(); - - let mut elements = vec![]; - - let mut stream = input.parse::()?.stream().into_iter(); - let mut element_name = String::new(); - loop { - match stream.next() { - Some(v) => match v { - TokenTree::Ident(ident) => { - element_name = ident.to_string(); - } - TokenTree::Literal(lit) => { - let index = lit.to_string().parse().unwrap(); - elements.push((element_name.clone(), index)); - } - _ => (), - }, - None => break, - } - } - - Ok(MakeGpioEnumDispatchMacro { - name, - filter, - elements, - }) - } -} - -pub(crate) fn build_match_arms(input: MakeGpioEnumDispatchMacro) -> Vec { - let mut arms = Vec::new(); - for (gpio_type, num) in input.elements { - let enum_name = format_ident!("ErasedPin"); - let variant_name = format_ident!("Gpio{}", num); - - if input.filter.contains(&gpio_type) { - arms.push({ - quote! { #enum_name::#variant_name($target) => $body } - }); - } else { - arms.push({ - quote! { - #[allow(unused)] - #enum_name::#variant_name($target) => { panic!("Unsupported") } - } - }); - } - } - - arms -} diff --git a/esp-hal-procmacros/src/interrupt.rs b/esp-hal-procmacros/src/interrupt.rs index 385dbb3c06d..f84acbfcbc5 100644 --- a/esp-hal-procmacros/src/interrupt.rs +++ b/esp-hal-procmacros/src/interrupt.rs @@ -24,7 +24,7 @@ pub(crate) fn check_attr_whitelist( 'o: for attr in attrs { for val in whitelist { - if eq(&attr, &val) { + if eq(attr, val) { continue 'o; } } @@ -35,7 +35,7 @@ pub(crate) fn check_attr_whitelist( } }; - return Err(Error::new(attr.span(), &err_str).to_compile_error().into()); + return Err(Error::new(attr.span(), err_str).to_compile_error().into()); } Ok(()) diff --git a/esp-hal-procmacros/src/lib.rs b/esp-hal-procmacros/src/lib.rs index 6bec2d198f1..c3a2afcdce9 100644 --- a/esp-hal-procmacros/src/lib.rs +++ b/esp-hal-procmacros/src/lib.rs @@ -52,8 +52,6 @@ use proc_macro::TokenStream; #[cfg(feature = "embassy")] mod embassy; -#[cfg(feature = "enum-dispatch")] -mod enum_dispatch; #[cfg(feature = "interrupt")] mod interrupt; #[cfg(any( @@ -130,11 +128,11 @@ struct RamArgs { /// [`bytemuck::Zeroable`]: https://docs.rs/bytemuck/1.9.0/bytemuck/trait.Zeroable.html #[cfg(feature = "ram")] #[proc_macro_attribute] -#[proc_macro_error::proc_macro_error] +#[proc_macro_error2::proc_macro_error] pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream { use darling::{ast::NestedMeta, Error, FromMeta}; use proc_macro::Span; - use proc_macro_error::abort; + use proc_macro_error2::abort; use syn::{parse, Item}; let attr_args = match NestedMeta::parse_meta_list(args.into()) { @@ -210,7 +208,7 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream { let hal = proc_macro2::Ident::new( if let Ok(FoundCrate::Name(ref name)) = crate_name("esp-hal") { - &name + name } else { "crate" }, @@ -243,7 +241,7 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream { /// /// If no priority is given, `Priority::min()` is assumed #[cfg(feature = "interrupt")] -#[proc_macro_error::proc_macro_error] +#[proc_macro_error2::proc_macro_error] #[proc_macro_attribute] pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream { use darling::{ast::NestedMeta, FromMeta}; @@ -278,7 +276,7 @@ pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream { let root = Ident::new( if let Ok(FoundCrate::Name(ref name)) = crate_name("esp-hal") { - &name + name } else { "crate" }, @@ -339,37 +337,6 @@ pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream { .into() } -/// Create an enum for erased GPIO pins, using the enum-dispatch pattern -/// -/// Only used internally -#[cfg(feature = "enum-dispatch")] -#[proc_macro] -pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream { - use quote::{format_ident, quote}; - - use self::enum_dispatch::{build_match_arms, MakeGpioEnumDispatchMacro}; - - let input = syn::parse_macro_input!(input as MakeGpioEnumDispatchMacro); - - let macro_name = format_ident!("{}", input.name); - let arms = build_match_arms(input); - - quote! { - #[doc(hidden)] - #[macro_export] - macro_rules! #macro_name { - ($m:ident, $target:ident, $body:block) => { - match $m { - #(#arms)* - } - } - } - - pub(crate) use #macro_name; - } - .into() -} - /// Load code to be run on the LP/ULP core. /// /// ## Example @@ -385,7 +352,7 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { /// Marks the entry function of a LP core / ULP program. #[cfg(any(feature = "is-lp-core", feature = "is-ulp-core"))] -#[proc_macro_error::proc_macro_error] +#[proc_macro_error2::proc_macro_error] #[proc_macro_attribute] pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { lp_core::entry(args, input) diff --git a/esp-hal-procmacros/src/lp_core.rs b/esp-hal-procmacros/src/lp_core.rs index 31baca8eee4..5fa639d7e92 100644 --- a/esp-hal-procmacros/src/lp_core.rs +++ b/esp-hal-procmacros/src/lp_core.rs @@ -23,19 +23,19 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let mut res = String::from("__ULP_MAGIC_"); for &a in args { let t = &a.ty; - let quoted = to_string(&t); + let quoted = to_string(t); res.push_str("ed); - res.push_str("$"); + res.push('$'); } res } pub(crate) fn get_simplename(t: &Type) -> String { - String::from(match t { - Type::Path(p) => String::from(&p.path.segments.last().unwrap().ident.to_string()), + match t { + Type::Path(p) => p.path.segments.last().unwrap().ident.to_string(), _ => String::new(), - }) + } } pub(crate) fn extract_pin(ty: &Type) -> u8 { @@ -49,7 +49,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { res = extract_pin(t); } GenericArgument::Const(c) => { - res = ("e! { #c }.to_string()).parse().unwrap(); + res = quote! { #c }.to_string().parse().unwrap(); } _ => (), } @@ -68,11 +68,11 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { res.push_str(&segment.ident.to_string()); if let PathArguments::AngleBracketed(g) = &segment.arguments { - res.push_str("<"); + res.push('<'); let mut pushed = false; for arg in &g.args { if pushed { - res.push_str(","); + res.push(','); } match arg { @@ -87,7 +87,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { _ => (), } } - res.push_str(">"); + res.push('>'); } } @@ -219,7 +219,7 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { }; let hal_crate = if let Ok(FoundCrate::Name(ref name)) = hal_crate { - let ident = Ident::new(&name, Span::call_site().into()); + let ident = Ident::new(name, Span::call_site().into()); quote!( #ident ) } else { quote!(crate) @@ -261,12 +261,14 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { let mut sections: Vec
= sections .into_iter() - .filter(|section| match section.kind() { - SectionKind::Text - | SectionKind::ReadOnlyData - | SectionKind::Data - | SectionKind::UninitializedData => true, - _ => false, + .filter(|section| { + matches!( + section.kind(), + SectionKind::Text + | SectionKind::ReadOnlyData + | SectionKind::Data + | SectionKind::UninitializedData + ) }) .collect(); sections.sort_by(|a, b| a.address().partial_cmp(&b.address()).unwrap()); @@ -280,9 +282,8 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { for section in sections { if section.address() > last_address { - for _ in 0..(section.address() - last_address) { - binary.push(0); - } + let fill = section.address() - last_address; + binary.extend(std::iter::repeat(0).take(fill as usize)); } binary.extend_from_slice(section.data().unwrap()); @@ -293,21 +294,20 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { .symbols() .find(|s| s.name().unwrap().starts_with("__ULP_MAGIC_")); - if let None = magic_symbol { + let magic_symbol = if let Some(magic_symbol) = magic_symbol { + magic_symbol.name().unwrap() + } else { return Error::new( Span::call_site().into(), "Given file doesn't seem to be an LP/ULP core application.", ) .to_compile_error() .into(); - } - - let magic_symbol = magic_symbol.unwrap().name().unwrap(); + }; let magic_symbol = magic_symbol.trim_start_matches("__ULP_MAGIC_"); let args: Vec = magic_symbol .split("$") - .into_iter() .map(|t| { let t = if t.contains("OutputOpenDrain") { t.replace("OutputOpenDrain", "LowPowerOutputOpenDrain") diff --git a/esp-hal-smartled/CHANGELOG.md b/esp-hal-smartled/CHANGELOG.md index c0e35d7fe1f..b56517e9cb3 100644 --- a/esp-hal-smartled/CHANGELOG.md +++ b/esp-hal-smartled/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +- Removed the `clocks` parameter from `SmartLedsAdapter::new` (#1999) + +## 0.13.0 - 2024-08-29 + ## 0.12.0 - 2024-07-15 ## 0.11.0 - 2024-06-04 diff --git a/esp-hal-smartled/Cargo.toml b/esp-hal-smartled/Cargo.toml index 6d50b4b4fae..11593624700 100644 --- a/esp-hal-smartled/Cargo.toml +++ b/esp-hal-smartled/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "esp-hal-smartled" -version = "0.12.0" +version = "0.13.0" edition = "2021" rust-version = "1.76.0" description = "RMT adapter for smartleds" @@ -14,7 +14,7 @@ targets = ["riscv32imc-unknown-none-elf"] [dependencies] defmt = { version = "0.3.8", optional = true } document-features = "0.2.10" -esp-hal = { version = "0.19.0", path = "../esp-hal" } +esp-hal = { version = "0.20.0", path = "../esp-hal" } fugit = "0.3.7" smart-leds-trait = "0.3.0" diff --git a/esp-hal-smartled/src/lib.rs b/esp-hal-smartled/src/lib.rs index 981647793bb..ff3016aa6b8 100644 --- a/esp-hal-smartled/src/lib.rs +++ b/esp-hal-smartled/src/lib.rs @@ -12,10 +12,10 @@ //! //! ```rust,ignore //! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! let rmt = Rmt::new(peripherals.RMT, 80.MHz(), &clocks, None).unwrap(); +//! let rmt = Rmt::new(peripherals.RMT, 80.MHz(), None).unwrap(); //! //! let rmt_buffer = smartLedBuffer!(1); -//! let mut led = SmartLedsAdapter::new(rmt.channel0, io.pins.gpio2, rmt_buffer, &clocks); +//! let mut led = SmartLedsAdapter::new(rmt.channel0, io.pins.gpio2, rmt_buffer); //! ``` //! //! ## Feature Flags @@ -89,7 +89,6 @@ where channel: C, pin: impl Peripheral

+ 'd, rmt_buffer: [u32; BUFFER_SIZE], - clocks: &Clocks, ) -> SmartLedsAdapter where O: OutputPin + 'd, @@ -107,6 +106,7 @@ where let channel = channel.configure(pin, config).unwrap(); // Assume the RMT peripheral is set up to use the APB clock + let clocks = Clocks::get(); let src_clock = clocks.apb_clock.to_MHz(); Self { diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 57628edb878..9aba2306a2d 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -7,19 +7,81 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Bump MSRV to 1.77.0 (#1971) + ### Added +- Implement `embedded-hal` output pin traits for `DummyPin` (#2019) +- Added `esp_hal::init` to simplify HAL initialisation (#1970, #1999) +- Added GpioPin::degrade to create ErasePins easily. Same for AnyPin by accident. (#2075) +- Added missing functions to `Flex`: `unlisten`, `is_interrupt_set`, `wakeup_enable`, `wait_for_high`, `wait_for_low`, `wait_for_rising_edge`, `wait_for_falling_edge`, `wait_for_any_edge`. (#2075) +- `Flex` now implements `Wait`. (#2075) +- Added sleep and wakeup support for esp32c2 (#1922) +- `Input`, `Output`, `OutputOpenDrain` and `Flex` now implement `Peripheral`. (#2094) +- Previously unavailable memory is available via `.dram2_uninit` section (#2079) + +### Changed + +- Make saving and restoring SHA digest state an explicit operation (#2049) +- Reordered RX-TX pairs in all APIs to be consistent (#2074) +- Make saving and restoring SHA digest state an explicit operation (#2049) +- `Delay::new()` is now a `const` function (#1999) +- You can now create an `AnyPin` out of an `ErasedPin`. (#2072) +- `Input`, `Output`, `OutputOpenDrain` and `Flex` are now type-erased by default. Use the new `new_typed` constructor to keep using the ZST pin types. (#2075) +- To avoid confusion with the `Rtc::current_time` wall clock time APIs, we've renamed `esp_hal::time::current_time` to `esp_hal::time::now`. (#2091) +- Renamed `touch::Continous` to `touch::Continuous`. (#2094) +- The (previously undocumented) `ErasedPin` enum has been replaced with the `ErasedPin` struct. (#2094) + +### Fixed + +- SHA driver can now be safely used in multiple contexts concurrently (#2049) +- Fixed an issue with DMA transfers potentially not waking up the correct async task (#2065) +- Fixed an issue with LCD_CAM i8080 where it would send double the clocks in 16bit mode (#2085) +- Fix i2c embedded-hal transaction (#2028) + +### Removed + +- Removed `digest::Digest` implementation from SHA (#2049) +- Removed `NoPinType` in favour of `DummyPin`. (#2068) +- Removed the `async`, `embedded-hal-02`, `embedded-hal`, `embedded-io`, `embedded-io-async`, and `ufmt` features (#2070) +- Removed the `GpioN` type aliasses. Use `GpioPin` instead. (#2073) +- Removed `Peripherals::take`. Use `esp_hal::init` to obtain `Peripherals` (#1999) +- Removed `AnyInputOnlyPin` in favour of `AnyPin`. (#2071) +- Removed the following functions from `GpioPin`: `is_high`, `is_low`, `set_high`, `set_low`, `set_state`, `is_set_high`, `is_set_low`, `toggle`. (#2094) + +## [0.20.1] - 2024-08-30 + +### Fixed + +- A build issue when including doc comment prelude (#2040) + +## [0.20.0] - 2024-08-29 + +### Added + +- Introduce DMA buffer objects (#1856, #1985) - Added new `Io::new_no_bind_interrupt` constructor (#1861) -- Added touch pad support for esp32 (#1873) +- Added touch pad support for esp32 (#1873, #1956) - Allow configuration of period updating method for MCPWM timers (#1898) +- Add self-testing mode for TWAI peripheral. (#1929) +- Added a `PeripheralClockControl::reset` to the driver constructors where missing (#1893) +- Added `digest::Digest` implementation to SHA (#1908) +- Added `debugger::debugger_connected`. (#1961) +- DMA: don't require `Sealed` to implement `ReadBuffer` and `WriteBuffer` (#1921) +- Allow DMA to/from psram for esp32s3 (#1827) +- Added missing methods to `SpiDmaBus` (#2016). +- PARL_IO use ReadBuffer and WriteBuffer for Async DMA (#1996) ### Changed - Peripheral driver constructors don't take `InterruptHandler`s anymore. Use `set_interrupt_handler` to explicitly set the interrupt handler now. (#1819) +- Migrate SPI driver to use DMA buffer objects (#1856, #1985) - Use the peripheral ref pattern for `OneShotTimer` and `PeriodicTimer` (#1855) - -- Allow DMA to/from psram for esp32s3 (#1827) -- DMA buffers now don't require a static lifetime. Make sure to never `mem::forget` an in-progress DMA transfer (consider using `#[deny(clippy::mem_forget)]`) (#1837) +- Improve SYSTIMER API (#1871) +- SHA driver now use specific structs for the hashing algorithm instead of a parameter. (#1908) +- Remove `fn free(self)` in HMAC which goes against esp-hal API guidelines (#1972) +- `AnyPin`, `AnyInputOnyPin` and `DummyPin` are now accessible from `gpio` module (#1918) +- Changed the RSA modular multiplication API to be consistent across devices (#2002) ### Fixed @@ -29,11 +91,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SPI: Clear DMA interrupts before (not after) DMA starts (#1859) - SPI: disable and re-enable MISO and MOSI in `start_transfer_dma`, `start_read_bytes_dma` and `start_write_bytes_dma` accordingly (#1894) - TWAI: GPIO pins are not configured as input and output (#1906) +- ESP32C6: Make ADC usable after TRNG deinicialization (#1945) +- We should no longer generate 1GB .elf files for ESP32C2 and ESP32C3 (#1962) +- Reset peripherals in driver constructors where missing (#1893, #1961) +- Fixed ESP32-S2 systimer interrupts (#1979) +- Software interrupt 3 is no longer available when it is required by `esp-hal-embassy`. (#2011) +- ESP32: Fixed async RSA (#2002) ### Removed - This package no longer re-exports the `esp_hal_procmacros::main` macro (#1828) - The `AesFlavour` trait no longer has the `ENCRYPT_MODE`/`DECRYPT_MODE` associated constants (#1849) +- Removed `FlashSafeDma` (#1856) +- Remove redundant WithDmaSpi traits (#1975) +- `IsFullDuplex` and `IsHalfDuplex` traits (#1985) ## [0.19.0] - 2024-07-15 @@ -272,6 +343,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Auto detect crystal frequency based on `RtcClock::estimate_xtal_frequency()` (#1165) - ESP32-S3: Configure 32k ICACHE (#1169) - Lift the minimal buffer size requirement for I2S (#1189) +- Replaced `SystemTimer::TICKS_PER_SEC` with `SystemTimer::ticks_per_sec()` (#1981) ### Removed @@ -666,7 +738,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.1.0] - 2022-08-05 -[Unreleased]: https://github.com/esp-rs/esp-hal/compare/v0.19.0...HEAD +[Unreleased]: https://github.com/esp-rs/esp-hal/compare/v0.20.1...HEAD +[0.20.1]: https://github.com/esp-rs/esp-hal/compare/v0.20.0...v0.20.1 +[0.20.0]: https://github.com/esp-rs/esp-hal/compare/v0.19.0...v0.20.0 [0.19.0]: https://github.com/esp-rs/esp-hal/compare/v0.18.0...v0.19.0 [0.18.0]: https://github.com/esp-rs/esp-hal/compare/v0.17.0...v0.18.0 [0.17.0]: https://github.com/esp-rs/esp-hal/compare/v0.16.1...v0.17.0 diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 9c2c41a9afe..ce9ba0ec8ff 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "esp-hal" -version = "0.19.0" +version = "0.20.1" edition = "2021" -rust-version = "1.76.0" +rust-version = "1.77.0" description = "Bare-metal HAL for Espressif devices" documentation = "https://docs.esp-rs.org/esp-hal/" repository = "https://github.com/esp-rs/esp-hal" @@ -11,78 +11,81 @@ links = "esp-hal" [package.metadata.docs.rs] default-target = "riscv32imac-unknown-none-elf" -features = ["embedded-hal", "esp32c6"] +features = ["esp32c6"] rustdoc-args = ["--cfg", "docsrs"] [dependencies] bitflags = "2.6.0" -bytemuck = "1.16.1" -bitfield = "0.15.0" +bytemuck = "1.17.1" +bitfield = "0.16.1" cfg-if = "1.0.0" -critical-section = "1.1.2" +critical-section = "1.1.3" defmt = { version = "0.3.8", optional = true } delegate = "0.12.0" +digest = { version = "0.10.7", default-features = false, optional = true } document-features = "0.2.10" -embassy-futures = { version = "0.1.1", optional = true } -embassy-sync = { version = "0.6.0", optional = true } +embassy-futures = "0.1.1" +embassy-sync = "0.6.0" embassy-usb-driver = { version = "0.1.0", optional = true } embassy-usb-synopsys-otg = { version = "0.1.0", optional = true } -embedded-can = { version = "0.4.1", optional = true } -embedded-hal-02 = { version = "0.2.7", optional = true, features = ["unproven"], package = "embedded-hal" } -embedded-hal = { version = "1.0.0", optional = true } -embedded-hal-async = { version = "1.0.0", optional = true } -embedded-hal-nb = { version = "1.0.0", optional = true } -embedded-io = { version = "0.6.1", optional = true } -embedded-io-async = { version = "0.6.1", optional = true } -enumset = "1.1.3" +embedded-can = "0.4.1" +embedded-hal-02 = { version = "0.2.7", features = ["unproven"], package = "embedded-hal" } +embedded-hal = "1.0.0" +embedded-hal-async = "1.0.0" +embedded-hal-nb = "1.0.0" +embedded-io = "0.6.1" +embedded-io-async = "0.6.1" +enumset = "1.1.5" esp-synopsys-usb-otg = { version = "0.4.2", optional = true, features = ["fs", "esp32sx"] } fugit = "0.3.7" log = { version = "0.4.22", optional = true } nb = "1.1.0" paste = "1.0.15" -portable-atomic = { version = "1.6.0", default-features = false } -procmacros = { version = "0.12.0", features = ["enum-dispatch", "interrupt", "ram"], package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } +portable-atomic = { version = "1.7.0", default-features = false } +procmacros = { version = "0.13.0", features = ["enum-dispatch", "interrupt", "ram"], package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } riscv = { version = "0.11.1", optional = true } strum = { version = "0.26.3", default-features = false, features = ["derive"] } void = { version = "1.0.2", default-features = false } usb-device = { version = "0.3.2", optional = true } rand_core = "0.6.4" -ufmt-write = { version = "0.1.0", optional = true } +ufmt-write = "0.1.0" xtensa-lx = { version = "0.9.0", optional = true } # IMPORTANT: # Each supported device MUST have its PAC included below along with a # corresponding feature. -esp32 = { git = "https://github.com/esp-rs/esp-pacs", rev = "27dd7e5", features = ["critical-section", "rt"], optional = true } -esp32c2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "27dd7e5", features = ["critical-section", "rt"], optional = true } -esp32c3 = { git = "https://github.com/esp-rs/esp-pacs", rev = "27dd7e5", features = ["critical-section", "rt"], optional = true } -esp32c6 = { git = "https://github.com/esp-rs/esp-pacs", rev = "27dd7e5", features = ["critical-section", "rt"], optional = true } -esp32h2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "27dd7e5", features = ["critical-section", "rt"], optional = true } -esp32s2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "27dd7e5", features = ["critical-section", "rt"], optional = true } -esp32s3 = { git = "https://github.com/esp-rs/esp-pacs", rev = "27dd7e5", features = ["critical-section", "rt"], optional = true } +esp32 = { version = "0.33.0", features = ["critical-section", "rt"], optional = true } +esp32c2 = { version = "0.22.0", features = ["critical-section", "rt"], optional = true } +esp32c3 = { version = "0.25.0", features = ["critical-section", "rt"], optional = true } +esp32c6 = { version = "0.16.0", features = ["critical-section", "rt"], optional = true } +esp32h2 = { version = "0.12.0", features = ["critical-section", "rt"], optional = true } +esp32s2 = { version = "0.24.0", features = ["critical-section", "rt"], optional = true } +esp32s3 = { version = "0.28.0", features = ["critical-section", "rt"], optional = true } [target.'cfg(target_arch = "riscv32")'.dependencies] esp-riscv-rt = { version = "0.9.0", path = "../esp-riscv-rt" } [target.'cfg(target_arch = "xtensa")'.dependencies] -xtensa-lx-rt = "0.16.0" +xtensa-lx-rt = { version = "0.17.0", path = "../xtensa-lx-rt" } [build-dependencies] basic-toml = "0.1.9" cfg-if = "1.0.0" esp-build = { version = "0.1.0", path = "../esp-build" } -esp-metadata = { version = "0.2.0", path = "../esp-metadata" } -serde = { version = "1.0.204", features = ["derive"] } +esp-metadata = { version = "0.3.0", path = "../esp-metadata" } +serde = { version = "1.0.209", features = ["derive"] } [features] -default = ["embedded-hal"] +default = [] riscv = ["dep:riscv", "critical-section/restore-state-u8"] xtensa = ["dep:xtensa-lx", "critical-section/restore-state-u32"] bluetooth = [] -usb-otg = ["esp-synopsys-usb-otg", "usb-device"] +usb-otg = ["dep:embassy-usb-driver", "dep:embassy-usb-synopsys-otg", "dep:esp-synopsys-usb-otg", "dep:usb-device"] + +__esp_hal_embassy = [] ## Enable debug features in the HAL (used for development). debug = [ @@ -121,27 +124,14 @@ esp32s3 = ["dep:esp32s3", "xtensa", "procmacros/has-ulp-core", "xtensa-lx/spin", flip-link = ["esp-riscv-rt/fix-sp"] #! ### Trait Implementation Feature Flags -## Enable support for asynchronous operation, with interfaces provided by -## `embedded-hal-async` and `embedded-io-async`. -## Also enables `embassy-usb` support for ESP32-S2 and ESP32-S3. -async = [ - "embedded-hal", - "embedded-hal-async", - "embedded-io", - "embedded-io-async", - "embassy-sync", - "embassy-futures", - "embassy-usb-driver", - "embassy-usb-synopsys-otg" -] ## Implement `defmt::Format` on certain types. defmt = [ "dep:defmt", - "embassy-futures?/defmt", - "embassy-sync?/defmt", - "embedded-hal?/defmt-03", + "embassy-futures/defmt", + "embassy-sync/defmt", + "embedded-hal/defmt-03", "embedded-io/defmt-03", - "embedded-io-async?/defmt-03", + "embedded-io-async/defmt-03", "esp32?/defmt", "esp32c2?/defmt", "esp32c3?/defmt", @@ -149,16 +139,8 @@ defmt = [ "esp32h2?/defmt", "esp32s2?/defmt", "esp32s3?/defmt", + "fugit/defmt", ] -## Implement the traits defined in the `1.0.0` releases of `embedded-hal` and -## `embedded-hal-nb` for the relevant peripherals. -embedded-hal = ["dep:embedded-hal", "dep:embedded-hal-nb", "dep:embedded-can"] -## Implement the traits defined in the `0.2.x` release of `embedded-hal`. -embedded-hal-02 = ["dep:embedded-hal-02"] -## Implement the traits defined in `embedded-io` for certain peripherals. -embedded-io = ["dep:embedded-io"] -## Implement the `ufmt_write::uWrite` trait for certain peripherals. -ufmt = ["dep:ufmt-write"] #! ### PSRAM Feature Flags ## Use externally connected PSRAM (2MB). @@ -181,10 +163,10 @@ opsram-8m = [] opsram-16m = [] # This feature is intended for testing; you probably don't want to enable it: -ci = ["async", "embedded-hal-02", "embedded-io", "ufmt", "defmt", "bluetooth", "place-spi-driver-in-ram"] +ci = ["defmt", "bluetooth", "place-spi-driver-in-ram"] [lints.clippy] mixed_attributes_style = "allow" [lints.rust] -unexpected_cfgs = "allow" +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(host_os, values("windows"))'] } diff --git a/esp-hal/MIGRATING-0.20.md b/esp-hal/MIGRATING-0.20.md new file mode 100644 index 00000000000..b5fb14d45ab --- /dev/null +++ b/esp-hal/MIGRATING-0.20.md @@ -0,0 +1,82 @@ +# Migration Guide from 0.20.x to vNext + +## Cargo Features + +A number of trait implementations which were previously feature-gated are now implemented by default. The following Cargo features have been removed: + +- `async` +- `embedded-hal-02` +- `embedded-hal` +- `embedded-io` +- `embedded-io-async` +- `ufmt` + +If your project enables any of these features, simply remove them from your Cargo manifest and things should continue to work as expected. + +## HAL Initialisation + +Instead of manually grabbing peripherals and setting up clocks, you should now call `esp_hal::init`. + +```diff + use esp_hal::{ +- clock::ClockControl, +- peripherals::Peripherals, + prelude::*, +- system::SystemControl, + }; + + #[entry] + fn main() -> ! { +- let peripherals = Peripherals::take(); +- let system = SystemControl::new(peripherals.SYSTEM); +- let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); ++ let peripherals = esp_hal::init(esp_hal::Config::default()); + + // ... + } +``` + +## GPIO changes + + - The `GpioN` type aliasses are no longer available. You can use `GpioPin` instead. + - The `AnyInputOnlyPin` has been removed. Replace any use with `AnyPin`. + - The `NoPinType` has been removed. You can use `DummyPin` in its place. + +### Type-erased GPIO drivers + +You no longer have to spell out the GPIO pin type for `Input`, `Output`, `OutputOpenDrain` or `Flex`. +However, if you want to, you can keep using their typed form! + +```rust +let pin = Input::new(io.gpio0); // pin will have the type `Input<'some>` (or `Input<'some, ErasedPin>` if you want to be explicit about it) +let pin = Input::new_typed(io.gpio0); // pin will have the type `Input<'some, GpioPin<0>>` +``` + +## `esp_hal::time::current_time` rename + +To avoid confusion with the `Rtc::current_time` wall clock time APIs, we've renamed `esp_hal::time::current_time` to `esp_hal::time::now()`. + +```diff +- use esp_hal::time::current_time; ++ use esp_hal::time::now; +``` + +## RX/TX Order + +Previously, our API was pretty inconsitent with the RX/TX ordering, and different peripherals had different order. Now, all +the peripherals use rx-tx. Make sure your methods are expecting the rigth RX/TX order, for example an SPI DMA app should be updated to: + +```diff +- let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(4); ++ let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(4); +let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); +let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + +... + + let transfer = spi +- .dma_transfer(dma_tx_buf, dma_rx_buf) ++ .dma_transfer(dma_rx_buf, dma_tx_buf) + .map_err(|e| e.0) + .unwrap(); +``` diff --git a/esp-hal/build.rs b/esp-hal/build.rs index 53d29277066..d6c28894540 100644 --- a/esp-hal/build.rs +++ b/esp-hal/build.rs @@ -67,9 +67,9 @@ fn main() -> Result<(), Box> { config.define_symbols(); #[allow(unused_mut)] - let mut config_symbols = config.all(); + let mut config_symbols = config.all().collect::>(); #[cfg(feature = "flip-link")] - config_symbols.push("flip-link".to_owned()); + config_symbols.push("flip-link"); // Place all linker scripts in `OUT_DIR`, and instruct Cargo how to find these // files: @@ -117,12 +117,6 @@ fn main() -> Result<(), Box> { )?; } - // needed for before_snippet! macro - let host = env::var_os("HOST").expect("HOST not set"); - if let Some("windows") = host.to_str().unwrap().split('-').nth(2) { - println!("cargo:rustc-cfg=host_os=\"windows\""); - } - // With the architecture-specific linker scripts taken care of, we can copy all // remaining linker scripts which are common to all devices: copy_dir_all(&config_symbols, "ld/sections", &out)?; @@ -135,7 +129,7 @@ fn main() -> Result<(), Box> { // Helper Functions fn copy_dir_all( - config_symbols: &Vec, + config_symbols: &[&str], src: impl AsRef, dst: impl AsRef, ) -> std::io::Result<()> { @@ -162,7 +156,7 @@ fn copy_dir_all( /// A naive pre-processor for linker scripts fn preprocess_file( - config: &[String], + config: &[&str], src: impl AsRef, dst: impl AsRef, ) -> std::io::Result<()> { @@ -176,8 +170,7 @@ fn preprocess_file( let line = line?; let trimmed = line.trim(); - if let Some(stripped) = trimmed.strip_prefix("#IF ") { - let condition = stripped.to_string(); + if let Some(condition) = trimmed.strip_prefix("#IF ") { let should_take = take.iter().all(|v| *v); let should_take = should_take && config.contains(&condition); take.push(should_take); diff --git a/esp-hal/doc-helper/before b/esp-hal/doc-helper/before deleted file mode 100644 index 7781574e3d0..00000000000 --- a/esp-hal/doc-helper/before +++ /dev/null @@ -1,12 +0,0 @@ -# #![no_std] -# use esp_hal::peripherals::Peripherals; -# use esp_hal::clock::ClockControl; -# use esp_hal::system::SystemControl; -# #[panic_handler] -# fn panic(_ : &core::panic::PanicInfo) -> ! { -# loop {} -# } -# fn main() { -# let peripherals = Peripherals::take(); -# let system = SystemControl::new(peripherals.SYSTEM); -# let mut clocks = ClockControl::boot_defaults(system.clock_control).freeze(); \ No newline at end of file diff --git a/esp-hal/ld/esp32/esp32.x b/esp-hal/ld/esp32/esp32.x index e3c639b6112..5d9ffec825a 100644 --- a/esp-hal/ld/esp32/esp32.x +++ b/esp-hal/ld/esp32/esp32.x @@ -26,15 +26,9 @@ INCLUDE "rwdata.x" INCLUDE "rtc_fast.x" INCLUDE "rtc_slow.x" INCLUDE "stack.x" +INCLUDE "dram2.x" /* End of Shared sections */ -/* an uninitialized section for use as the wifi-heap in esp-wifi */ -SECTIONS { - .dram2_uninit (NOLOAD) : ALIGN(4) { - *(.dram2_uninit) - } > dram2_seg -} - EXTERN(DefaultHandler); EXTERN(WIFI_EVENT); /* Force inclusion of WiFi libraries */ diff --git a/esp-hal/ld/esp32c2/esp32c2.x b/esp-hal/ld/esp32c2/esp32c2.x index dd800c29363..8324eb4c31e 100644 --- a/esp-hal/ld/esp32c2/esp32c2.x +++ b/esp-hal/ld/esp32c2/esp32c2.x @@ -36,14 +36,6 @@ PROVIDE(_mp_hook = default_mp_hook); PROVIDE(_start_trap = default_start_trap); /* esp32c2 fixups */ -SECTIONS { - .text.dummy (NOLOAD) : - { - /* This section is intended to make _stext address work */ - . = ABSOLUTE(_stext); - } > ROTEXT -} -INSERT BEFORE .text; SECTIONS { .trap : ALIGN(4) @@ -93,6 +85,7 @@ INCLUDE "rodata.x" INCLUDE "rwtext.x" INCLUDE "rwdata.x" INCLUDE "stack.x" +INCLUDE "dram2.x" /* End of Shared sections */ INCLUDE "debug.x" diff --git a/esp-hal/ld/esp32c2/memory.x b/esp-hal/ld/esp32c2/memory.x index 0a46d9b1066..4f3f9460e9a 100644 --- a/esp-hal/ld/esp32c2/memory.x +++ b/esp-hal/ld/esp32c2/memory.x @@ -12,12 +12,14 @@ MEMORY [0x4037C000, 0x403C0000, "IRAM"]] */ - /* 272K of on soc RAM, 16K reserved for cache */ - ICACHE : ORIGIN = 0x4037C000, LENGTH = 16K + ICACHE : ORIGIN = 0x4037C000, LENGTH = 16K /* Instruction RAM */ - IRAM : ORIGIN = 0x4037C000 + 16K, LENGTH = 272K - 16K + IRAM : ORIGIN = 0x4037C000 + LENGTH(ICACHE), LENGTH = 186k /* Data RAM */ - DRAM : ORIGIN = 0x3FCA0000, LENGTH = 0x30000 + DRAM : ORIGIN = 0x3FCA0000, LENGTH = 186k + + /* memory available after the 2nd stage bootloader is finished */ + dram2_seg ( RW ) : ORIGIN = ORIGIN(DRAM) + LENGTH(DRAM), len = 0x3fcdeb70 - (ORIGIN(DRAM) + LENGTH(DRAM)) /* External flash */ /* Instruction ROM */ diff --git a/esp-hal/ld/esp32c3/esp32c3.x b/esp-hal/ld/esp32c3/esp32c3.x index 2584e7a6418..4d83f420e98 100644 --- a/esp-hal/ld/esp32c3/esp32c3.x +++ b/esp-hal/ld/esp32c3/esp32c3.x @@ -36,14 +36,6 @@ PROVIDE(_mp_hook = default_mp_hook); PROVIDE(_start_trap = default_start_trap); /* esp32c3 fixups */ -SECTIONS { - .text.dummy (NOLOAD) : - { - /* This section is intended to make _stext address work */ - . = ABSOLUTE(_stext); - } > ROTEXT -} -INSERT BEFORE .text; SECTIONS { .trap : ALIGN(4) @@ -94,6 +86,7 @@ INCLUDE "rwtext.x" INCLUDE "rwdata.x" INCLUDE "rtc_fast.x" INCLUDE "stack.x" +INCLUDE "dram2.x" /* End of Shared sections */ INCLUDE "debug.x" diff --git a/esp-hal/ld/esp32c3/memory.x b/esp-hal/ld/esp32c3/memory.x index 88d2c44f331..078d9fcd1b4 100644 --- a/esp-hal/ld/esp32c3/memory.x +++ b/esp-hal/ld/esp32c3/memory.x @@ -13,14 +13,15 @@ MEMORY [0x50000000, 0x50002000, "RTC_IRAM"], [0x50000000, 0x50002000, "RTC_DRAM"], [0x600FE000, 0x60100000, "MEM_INTERNAL2"]] - */ - /* 400K of on soc RAM, 16K reserved for cache */ + ICACHE : ORIGIN = 0x4037C000, LENGTH = 0x4000 /* Instruction RAM */ - IRAM : ORIGIN = 0x4037C000 + 0x4000, LENGTH = 400K - 0x4000 + IRAM : ORIGIN = 0x4037C000 + 0x4000, LENGTH = 313K - 0x4000 /* Data RAM */ - DRAM : ORIGIN = 0x3FC80000, LENGTH = 0x50000 + DRAM : ORIGIN = 0x3FC80000, LENGTH = 313K + /* memory available after the 2nd stage bootloader is finished */ + dram2_seg ( RW ) : ORIGIN = ORIGIN(DRAM) + LENGTH(DRAM), len = 0x3fcde710 - (ORIGIN(DRAM) + LENGTH(DRAM)) /* External flash */ /* Instruction ROM */ diff --git a/esp-hal/ld/esp32c6/esp32c6.x b/esp-hal/ld/esp32c6/esp32c6.x index 52282c7796b..4a3cc337aa3 100644 --- a/esp-hal/ld/esp32c6/esp32c6.x +++ b/esp-hal/ld/esp32c6/esp32c6.x @@ -78,6 +78,7 @@ INCLUDE "rodata.x" INCLUDE "rwdata.x" INCLUDE "rtc_fast.x" INCLUDE "stack.x" +INCLUDE "dram2.x" /* End of Shared sections */ INCLUDE "debug.x" \ No newline at end of file diff --git a/esp-hal/ld/esp32c6/memory.x b/esp-hal/ld/esp32c6/memory.x index d039fd030f8..c2e459a28eb 100644 --- a/esp-hal/ld/esp32c6/memory.x +++ b/esp-hal/ld/esp32c6/memory.x @@ -21,6 +21,9 @@ MEMORY */ RAM : ORIGIN = 0x40800000 , LENGTH = 0x6E610 + /* memory available after the 2nd stage bootloader is finished */ + dram2_seg ( RW ) : ORIGIN = ORIGIN(RAM) + LENGTH(RAM), len = 0x4087e610 - (ORIGIN(RAM) + LENGTH(RAM)) + /* External flash */ /* Instruction and Data ROM */ ROM : ORIGIN = 0x42000000 + 0x20, LENGTH = 0x400000 - 0x20 diff --git a/esp-hal/ld/esp32h2/esp32h2.x b/esp-hal/ld/esp32h2/esp32h2.x index 8565d94e3aa..635338e1c24 100644 --- a/esp-hal/ld/esp32h2/esp32h2.x +++ b/esp-hal/ld/esp32h2/esp32h2.x @@ -71,6 +71,7 @@ INCLUDE "rodata.x" INCLUDE "rwdata.x" INCLUDE "rtc_fast.x" INCLUDE "stack.x" +INCLUDE "dram2.x" /* End of Shared sections */ INCLUDE "debug.x" diff --git a/esp-hal/ld/esp32h2/memory.x b/esp-hal/ld/esp32h2/memory.x index 0625ed58a4f..bf1e81832ba 100644 --- a/esp-hal/ld/esp32h2/memory.x +++ b/esp-hal/ld/esp32h2/memory.x @@ -17,11 +17,14 @@ MEMORY /* 320K of on soc RAM, 16K reserved for cache */ /* Instruction and Data RAM - 0x4086E610 = 2nd stage bootloader iram_loader_seg start address + 0x4083EFD0 = 2nd stage bootloader iram_loader_seg start address see https://github.com/espressif/esp-idf/blob/03414a15508036c8fc0f51642aed7a264e9527df/components/esp_system/ld/esp32h2/memory.ld.in#L26 */ RAM : ORIGIN = 0x40800000, LENGTH = 0x3EFD0 + /* memory available after the 2nd stage bootloader is finished */ + dram2_seg ( RW ) : ORIGIN = ORIGIN(RAM) + LENGTH(RAM), len = 0x4084fee0 - (ORIGIN(RAM) + LENGTH(RAM)) + /* External flash */ /* Instruction and Data ROM */ ROM : ORIGIN = 0x42000000, LENGTH = 0x400000 diff --git a/esp-hal/ld/esp32s2/esp32s2.x b/esp-hal/ld/esp32s2/esp32s2.x index dc2e704197c..29602c0695f 100644 --- a/esp-hal/ld/esp32s2/esp32s2.x +++ b/esp-hal/ld/esp32s2/esp32s2.x @@ -34,6 +34,7 @@ INCLUDE "rwdata.x" INCLUDE "rtc_fast.x" INCLUDE "rtc_slow.x" INCLUDE "stack.x" +INCLUDE "dram2.x" /* End of Shared sections */ EXTERN(DefaultHandler); diff --git a/esp-hal/ld/esp32s2/memory.x b/esp-hal/ld/esp32s2/memory.x index 77669550621..3615b93db7b 100644 --- a/esp-hal/ld/esp32s2/memory.x +++ b/esp-hal/ld/esp32s2/memory.x @@ -15,11 +15,14 @@ VECTORS_SIZE = 0x400; /* Specify main memory areas */ MEMORY { - vectors_seg ( RX ) : ORIGIN = 0x40020000 + RESERVE_CACHES, len = VECTORS_SIZE /* SRAM0 */ - iram_seg ( RX ) : ORIGIN = 0x40020000 + RESERVE_CACHES + VECTORS_SIZE, len = 192k - RESERVE_CACHES - VECTORS_SIZE /* SRAM0 */ + vectors_seg ( RX ) : ORIGIN = 0x40020000 + RESERVE_CACHES, len = VECTORS_SIZE + iram_seg ( RX ) : ORIGIN = 0x40020000 + RESERVE_CACHES + VECTORS_SIZE, len = 188k - RESERVE_CACHES - VECTORS_SIZE dram_seg ( RW ) : ORIGIN = 0x3FFB0000 + RESERVE_CACHES + VECTORS_SIZE, len = 188k - RESERVE_CACHES - VECTORS_SIZE + /* memory available after the 2nd stage bootloader is finished */ + dram2_seg ( RW ) : ORIGIN = ORIGIN(dram_seg) + LENGTH(dram_seg), len = 0x3ffffa10 - (ORIGIN(dram_seg) + LENGTH(dram_seg)) + /* external flash The 0x20 offset is a convenience for the app binary image generation. Flash cache has 64KB pages. The .bin file which is flashed to the chip diff --git a/esp-hal/ld/esp32s3/esp32s3.x b/esp-hal/ld/esp32s3/esp32s3.x index 085b23b134d..6f333cc8042 100644 --- a/esp-hal/ld/esp32s3/esp32s3.x +++ b/esp-hal/ld/esp32s3/esp32s3.x @@ -48,6 +48,7 @@ INCLUDE "rwdata.x" INCLUDE "rtc_fast.x" INCLUDE "rtc_slow.x" INCLUDE "stack.x" +INCLUDE "dram2.x" /* End of Shared sections */ EXTERN(DefaultHandler); diff --git a/esp-hal/ld/esp32s3/memory.x b/esp-hal/ld/esp32s3/memory.x index 3b493fbcfee..eac8f14e71c 100644 --- a/esp-hal/ld/esp32s3/memory.x +++ b/esp-hal/ld/esp32s3/memory.x @@ -24,6 +24,9 @@ MEMORY iram_seg ( RX ) : ORIGIN = 0x40370000 + RESERVE_ICACHE + VECTORS_SIZE, len = 328k - VECTORS_SIZE - RESERVE_ICACHE dram_seg ( RW ) : ORIGIN = 0x3FC88000 , len = 345856 + /* memory available after the 2nd stage bootloader is finished */ + dram2_seg ( RW ) : ORIGIN = ORIGIN(dram_seg) + LENGTH(dram_seg), len = 0x3fced710 - (ORIGIN(dram_seg) + LENGTH(dram_seg)) + /* external flash The 0x20 offset is a convenience for the app binary image generation. Flash cache has 64KB pages. The .bin file which is flashed to the chip diff --git a/esp-hal/ld/sections/dram2.x b/esp-hal/ld/sections/dram2.x new file mode 100644 index 00000000000..1289f781fe9 --- /dev/null +++ b/esp-hal/ld/sections/dram2.x @@ -0,0 +1,6 @@ +/* an uninitialized section of RAM otherwise not useable */ +SECTIONS { + .dram2_uninit (NOLOAD) : ALIGN(4) { + *(.dram2_uninit) + } > dram2_seg +} diff --git a/esp-hal/src/aes/mod.rs b/esp-hal/src/aes/mod.rs index c955c51e5fd..b9427eac700 100644 --- a/esp-hal/src/aes/mod.rs +++ b/esp-hal/src/aes/mod.rs @@ -1,12 +1,14 @@ //! # Advanced Encryption Standard (AES). //! //! ## Overview +//! //! The AES accelerator is a hardware device that speeds up computation //! using AES algorithm significantly, compared to AES algorithms implemented //! solely in software. The AES accelerator has two working modes, which are //! Typical AES and AES-DMA. //! //! ## Configuration +//! //! The AES peripheral can be configured to encrypt or decrypt data using //! different encryption/decryption modes. //! @@ -14,40 +16,45 @@ //! cipher modes such as ECB, CBC, OFB, CTR, CFB8, and CFB128. //! //! ## Examples -//! ### Encrypting and Decrypting a Message +//! +//! ### Encrypting and decrypting a message +//! //! Simple example of encrypting and decrypting a message using AES-128: +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::aes::{Aes, Mode}; -//! # let keytext = "SUp4SeCp@sSw0rd".as_bytes(); -//! # let plaintext = "message".as_bytes(); +//! # let keytext = b"SUp4SeCp@sSw0rd"; +//! # let plaintext = b"message"; //! # let mut keybuf = [0_u8; 16]; //! # keybuf[..keytext.len()].copy_from_slice(keytext); -//! let mut block_buf = [0_u8; 16]; -//! block_buf[..plaintext.len()].copy_from_slice(plaintext); -//! let mut block = block_buf.clone(); +//! # +//! let mut block = [0_u8; 16]; +//! block[..plaintext.len()].copy_from_slice(plaintext); //! //! let mut aes = Aes::new(peripherals.AES); //! aes.process(&mut block, Mode::Encryption128, keybuf); -//! let hw_encrypted = block.clone(); +//! +//! // The encryption happens in-place, so the ciphertext is in `block` //! //! aes.process(&mut block, Mode::Decryption128, keybuf); -//! let hw_decrypted = block; +//! +//! // The decryption happens in-place, so the plaintext is in `block` //! # } //! ``` //! //! ### AES-DMA +//! //! Visit the [AES-DMA] test for a more advanced example of using AES-DMA //! mode. //! //! [AES-DMA]: https://github.com/esp-rs/esp-hal/blob/main/hil-test/tests/aes_dma.rs //! //! ## Implementation State +//! //! * AES-DMA mode is currently not supported on ESP32 and ESP32S2 //! * AES-DMA Initialization Vector (IV) is currently not supported -#![deny(missing_docs)] - use crate::{ peripheral::{Peripheral, PeripheralRef}, peripherals::AES, @@ -135,6 +142,10 @@ impl<'d> Aes<'d> { /// Constructs a new `Aes` instance. pub fn new(aes: impl Peripheral

+ 'd) -> Self { crate::into_ref!(aes); + + crate::system::PeripheralClockControl::reset(crate::system::Peripheral::Aes); + crate::system::PeripheralClockControl::enable(crate::system::Peripheral::Aes); + let mut ret = Self { aes, alignment_helper: AlignmentHelper::native_endianess(), @@ -232,7 +243,7 @@ pub mod dma { DmaChannel, DmaDescriptor, DmaPeripheral, - DmaTransferTxRx, + DmaTransferRxTx, ReadBuffer, RxPrivate, TxPrivate, @@ -268,8 +279,8 @@ pub mod dma { pub aes: super::Aes<'d>, pub(crate) channel: Channel<'d, C, crate::Blocking>, - tx_chain: DescriptorChain, rx_chain: DescriptorChain, + tx_chain: DescriptorChain, } /// Functionality for using AES with DMA. @@ -282,8 +293,8 @@ pub mod dma { fn with_dma( self, channel: Channel<'d, C, crate::Blocking>, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], ) -> AesDma<'d, C>; } @@ -295,16 +306,16 @@ pub mod dma { fn with_dma( self, mut channel: Channel<'d, C, crate::Blocking>, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], ) -> AesDma<'d, C> { channel.tx.init_channel(); // no need to call this for both, TX and RX AesDma { aes: self, channel, - tx_chain: DescriptorChain::new(tx_descriptors), rx_chain: DescriptorChain::new(rx_descriptors), + tx_chain: DescriptorChain::new(tx_descriptors), } } } @@ -324,7 +335,7 @@ pub mod dma { C: DmaChannel, C::P: AesPeripheral, { - fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { while self.aes.aes.state().read().state().bits() != 2 // DMA status DONE == 2 && !self.channel.tx.is_done() { @@ -397,7 +408,7 @@ pub mod dma { /// Perform a DMA transfer. /// - /// This will return a [DmaTransferTxRx]. The maximum amount of data to + /// This will return a [DmaTransferRxTx]. The maximum amount of data to /// be sent/received is 32736 bytes. pub fn process<'t, K, TXBUF, RXBUF>( &'t mut self, @@ -406,7 +417,7 @@ pub mod dma { mode: Mode, cipher_mode: CipherMode, key: K, - ) -> Result, crate::dma::DmaError> + ) -> Result, crate::dma::DmaError> where K: Into, TXBUF: ReadBuffer, @@ -416,25 +427,25 @@ pub mod dma { let (read_ptr, read_len) = unsafe { read_buffer.write_buffer() }; self.start_transfer_dma( - write_ptr, - write_len, read_ptr, read_len, + write_ptr, + write_len, mode, cipher_mode, key.into(), )?; - Ok(DmaTransferTxRx::new(self)) + Ok(DmaTransferRxTx::new(self)) } #[allow(clippy::too_many_arguments)] fn start_transfer_dma( &mut self, - write_buffer_ptr: *const u8, - write_buffer_len: usize, read_buffer_ptr: *mut u8, read_buffer_len: usize, + write_buffer_ptr: *const u8, + write_buffer_len: usize, mode: Mode, cipher_mode: CipherMode, key: K, diff --git a/esp-hal/src/analog/adc/esp32.rs b/esp-hal/src/analog/adc/esp32.rs index ec6bdac107e..4bacea253d0 100644 --- a/esp-hal/src/analog/adc/esp32.rs +++ b/esp-hal/src/analog/adc/esp32.rs @@ -343,7 +343,6 @@ impl<'d, ADC1> Adc<'d, ADC1> { } } -#[cfg(feature = "embedded-hal-02")] impl<'d, ADCI, PIN> embedded_hal_02::adc::OneShot> for Adc<'d, ADCI> where @@ -360,29 +359,29 @@ where mod adc_implementation { crate::analog::adc::impl_adc_interface! { ADC1 [ - (Gpio36, 0), // Alt. name: SENSOR_VP - (Gpio37, 1), // Alt. name: SENSOR_CAPP - (Gpio38, 2), // Alt. name: SENSOR_CAPN - (Gpio39, 3), // Alt. name: SENSOR_VN - (Gpio33, 4), // Alt. name: 32K_XP - (Gpio32, 5), // Alt. name: 32K_XN - (Gpio34, 6), // Alt. name: VDET_1 - (Gpio35, 7), // Alt. name: VDET_2 + (GpioPin<36>, 0), // Alt. name: SENSOR_VP + (GpioPin<37>, 1), // Alt. name: SENSOR_CAPP + (GpioPin<38>, 2), // Alt. name: SENSOR_CAPN + (GpioPin<39>, 3), // Alt. name: SENSOR_VN + (GpioPin<33>, 4), // Alt. name: 32K_XP + (GpioPin<32>, 5), // Alt. name: 32K_XN + (GpioPin<34>, 6), // Alt. name: VDET_1 + (GpioPin<35>, 7), // Alt. name: VDET_2 ] } crate::analog::adc::impl_adc_interface! { ADC2 [ - (Gpio4, 0), - (Gpio0, 1), - (Gpio2, 2), - (Gpio15, 3), // Alt. name: MTDO - (Gpio13, 4), // Alt. name: MTCK - (Gpio12, 5), // Alt. name: MTDI - (Gpio14, 6), // Alt. name: MTMS - (Gpio27, 7), - (Gpio25, 8), - (Gpio26, 9), + (GpioPin<4>, 0), + (GpioPin<0>, 1), + (GpioPin<2>, 2), + (GpioPin<15>, 3), // Alt. name: MTDO + (GpioPin<13>, 4), // Alt. name: MTCK + (GpioPin<12>, 5), // Alt. name: MTDI + (GpioPin<14>, 6), // Alt. name: MTMS + (GpioPin<27>, 7), + (GpioPin<25>, 8), + (GpioPin<26>, 9), ] } } diff --git a/esp-hal/src/analog/adc/mod.rs b/esp-hal/src/analog/adc/mod.rs index ff9981d62be..b7d4e58d2a8 100644 --- a/esp-hal/src/analog/adc/mod.rs +++ b/esp-hal/src/analog/adc/mod.rs @@ -1,11 +1,13 @@ //! # Analog to Digital Converter (ADC) //! //! ## Overview +//! //! The ADC is integrated on the chip, and is capable of measuring analog //! signals from specific analog I/O pins. One or more ADC units are available, //! depending on the device being used. //! //! ## Configuration +//! //! The ADC can be configured to measure analog signals from specific pins. The //! configuration includes the resolution of the ADC, the attenuation of the //! input signal, and the pins to be measured. @@ -15,10 +17,13 @@ //! schemes can be used to improve the accuracy of the ADC readings. //! //! ## Usage +//! //! The ADC driver implements the `embedded-hal@0.2.x` ADC traits. //! -//! ## Examples -//! #### Read an analog signal from a pin +//! ## Example +//! +//! ### Read an analog signal from a pin +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::analog::adc::AdcConfig; @@ -27,8 +32,6 @@ //! # use esp_hal::analog::adc::Adc; //! # use esp_hal::delay::Delay; //! # use esp_hal::gpio::Io; -//! # use core::result::Result::Err; -//! //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); #![cfg_attr(esp32, doc = "let analog_pin = io.pins.gpio32;")] #![cfg_attr(any(esp32s2, esp32s3), doc = "let analog_pin = io.pins.gpio3;")] @@ -37,11 +40,13 @@ doc = "let analog_pin = io.pins.gpio2;" )] //! let mut adc1_config = AdcConfig::new(); -//! let mut pin = adc1_config.enable_pin(analog_pin, -//! Attenuation::Attenuation11dB); +//! let mut pin = adc1_config.enable_pin( +//! analog_pin, +//! Attenuation::Attenuation11dB, +//! ); //! let mut adc1 = Adc::new(peripherals.ADC1, adc1_config); //! -//! let mut delay = Delay::new(&clocks); +//! let mut delay = Delay::new(); //! //! loop { //! let pin_value: u16 = nb::block!(adc1.read_oneshot(&mut pin)).unwrap(); @@ -50,8 +55,10 @@ //! } //! # } //! ``` +//! //! ## Implementation State -//! - [ADC calibration is not implemented for all targets]. +//! +//! - [ADC calibration is not implemented for all targets]. //! //! [ADC calibration is not implemented for all targets]: https://github.com/esp-rs/esp-hal/issues/326 use core::marker::PhantomData; @@ -103,7 +110,6 @@ pub struct AdcPin { _phantom: PhantomData, } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::adc::Channel for AdcPin where PIN: embedded_hal_02::adc::Channel, @@ -246,15 +252,14 @@ trait AdcCalEfuse { macro_rules! impl_adc_interface { ($adc:ident [ - $( ($pin:ident, $channel:expr) ,)+ + $( (GpioPin<$pin:literal>, $channel:expr) ,)+ ]) => { $( - impl $crate::analog::adc::AdcChannel for crate::gpio::$pin { + impl $crate::analog::adc::AdcChannel for crate::gpio::GpioPin<$pin> { const CHANNEL: u8 = $channel; } - #[cfg(feature = "embedded-hal-02")] - impl embedded_hal_02::adc::Channel for crate::gpio::$pin { + impl embedded_hal_02::adc::Channel for crate::gpio::GpioPin<$pin> { type ID = u8; fn channel() -> u8 { $channel } diff --git a/esp-hal/src/analog/adc/riscv.rs b/esp-hal/src/analog/adc/riscv.rs index 428acea3327..b25eaa2fc86 100644 --- a/esp-hal/src/analog/adc/riscv.rs +++ b/esp-hal/src/analog/adc/riscv.rs @@ -408,6 +408,7 @@ where adc_instance: impl crate::peripheral::Peripheral

+ 'd, config: AdcConfig, ) -> Self { + PeripheralClockControl::reset(Peripheral::ApbSarAdc); PeripheralClockControl::enable(Peripheral::ApbSarAdc); unsafe { &*APB_SARADC::PTR }.ctrl().modify(|_, w| unsafe { @@ -529,7 +530,6 @@ impl super::AdcCalEfuse for crate::peripherals::ADC2 { } } -#[cfg(feature = "embedded-hal-02")] impl<'d, ADCI, PIN, CS> embedded_hal_02::adc::OneShot> for Adc<'d, ADCI> where @@ -548,11 +548,11 @@ where mod adc_implementation { crate::analog::adc::impl_adc_interface! { ADC1 [ - (Gpio0, 0), - (Gpio1, 1), - (Gpio2, 2), - (Gpio3, 3), - (Gpio4, 4), + (GpioPin<0>, 0), + (GpioPin<1>, 1), + (GpioPin<2>, 2), + (GpioPin<3>, 3), + (GpioPin<4>, 4), ] } } @@ -561,17 +561,17 @@ mod adc_implementation { mod adc_implementation { crate::analog::adc::impl_adc_interface! { ADC1 [ - (Gpio0, 0), - (Gpio1, 1), - (Gpio2, 2), - (Gpio3, 3), - (Gpio4, 4), + (GpioPin<0>, 0), + (GpioPin<1>, 1), + (GpioPin<2>, 2), + (GpioPin<3>, 3), + (GpioPin<4>, 4), ] } crate::analog::adc::impl_adc_interface! { ADC2 [ - (Gpio5, 0), + (GpioPin<5>, 0), ] } } @@ -580,13 +580,13 @@ mod adc_implementation { mod adc_implementation { crate::analog::adc::impl_adc_interface! { ADC1 [ - (Gpio0, 0), - (Gpio1, 1), - (Gpio2, 2), - (Gpio3, 3), - (Gpio4, 4), - (Gpio5, 5), - (Gpio6, 6), + (GpioPin<0>, 0), + (GpioPin<1>, 1), + (GpioPin<2>, 2), + (GpioPin<3>, 3), + (GpioPin<4>, 4), + (GpioPin<5>, 5), + (GpioPin<6>, 6), ] } } @@ -595,11 +595,11 @@ mod adc_implementation { mod adc_implementation { crate::analog::adc::impl_adc_interface! { ADC1 [ - (Gpio1, 0), - (Gpio2, 1), - (Gpio3, 2), - (Gpio4, 3), - (Gpio5, 4), + (GpioPin<1>, 0), + (GpioPin<2>, 1), + (GpioPin<3>, 2), + (GpioPin<4>, 3), + (GpioPin<5>, 4), ] } } diff --git a/esp-hal/src/analog/adc/xtensa.rs b/esp-hal/src/analog/adc/xtensa.rs index 65e51d75ff9..bc9d6ef3202 100644 --- a/esp-hal/src/analog/adc/xtensa.rs +++ b/esp-hal/src/analog/adc/xtensa.rs @@ -6,6 +6,7 @@ use crate::efuse::Efuse; use crate::{ peripheral::PeripheralRef, peripherals::{APB_SARADC, SENS}, + system::{Peripheral, PeripheralClockControl}, }; mod calibration; @@ -400,6 +401,9 @@ where adc_instance: impl crate::peripheral::Peripheral

+ 'd, config: AdcConfig, ) -> Self { + PeripheralClockControl::reset(Peripheral::ApbSarAdc); + PeripheralClockControl::enable(Peripheral::ApbSarAdc); + let sensors = unsafe { &*SENS::ptr() }; // Set attenuation for pins @@ -586,7 +590,6 @@ impl super::AdcCalEfuse for crate::peripherals::ADC2 { } } -#[cfg(feature = "embedded-hal-02")] impl<'d, ADCI, PIN, CS> embedded_hal_02::adc::OneShot> for Adc<'d, ADCI> where @@ -604,31 +607,31 @@ where mod adc_implementation { crate::analog::adc::impl_adc_interface! { ADC1 [ - (Gpio1, 0), - (Gpio2, 1), - (Gpio3, 2), - (Gpio4, 3), - (Gpio5, 4), - (Gpio6, 5), - (Gpio7, 6), - (Gpio8, 7), - (Gpio9, 8), - (Gpio10, 9), + (GpioPin<1>, 0), + (GpioPin<2>, 1), + (GpioPin<3>, 2), + (GpioPin<4>, 3), + (GpioPin<5>, 4), + (GpioPin<6>, 5), + (GpioPin<7>, 6), + (GpioPin<8>, 7), + (GpioPin<9>, 8), + (GpioPin<10>, 9), ] } crate::analog::adc::impl_adc_interface! { ADC2 [ - (Gpio11, 0), - (Gpio12, 1), - (Gpio13, 2), - (Gpio14, 3), - (Gpio15, 4), - (Gpio16, 5), - (Gpio17, 6), - (Gpio18, 7), - (Gpio19, 8), - (Gpio20, 9), + (GpioPin<11>, 0), + (GpioPin<12>, 1), + (GpioPin<13>, 2), + (GpioPin<14>, 3), + (GpioPin<15>, 4), + (GpioPin<16>, 5), + (GpioPin<17>, 6), + (GpioPin<18>, 7), + (GpioPin<19>, 8), + (GpioPin<20>, 9), ] } } diff --git a/esp-hal/src/analog/dac.rs b/esp-hal/src/analog/dac.rs index d0244c4ba9a..8cb1e859db3 100644 --- a/esp-hal/src/analog/dac.rs +++ b/esp-hal/src/analog/dac.rs @@ -27,7 +27,7 @@ #![cfg_attr(esp32s2, doc = "let dac1_pin = io.pins.gpio17;")] //! let mut dac1 = Dac::new(peripherals.DAC1, dac1_pin); //! -//! let mut delay = Delay::new(&clocks); +//! let mut delay = Delay::new(); //! //! let mut voltage_dac1 = 200u8; //! @@ -51,11 +51,11 @@ use crate::{ // reason, we will type alias the pins for ease of use later in this module: cfg_if::cfg_if! { if #[cfg(esp32)] { - type Dac1Gpio = gpio::Gpio25; - type Dac2Gpio = gpio::Gpio26; + type Dac1Gpio = gpio::GpioPin<25>; + type Dac2Gpio = gpio::GpioPin<26>; } else if #[cfg(esp32s2)] { - type Dac1Gpio = gpio::Gpio17; - type Dac2Gpio = gpio::Gpio18; + type Dac1Gpio = gpio::GpioPin<17>; + type Dac2Gpio = gpio::GpioPin<18>; } } diff --git a/esp-hal/src/analog/mod.rs b/esp-hal/src/analog/mod.rs index 714642f7db9..cac201b4c04 100644 --- a/esp-hal/src/analog/mod.rs +++ b/esp-hal/src/analog/mod.rs @@ -5,8 +5,6 @@ //! available on the device. For more information about a peripheral driver, //! please refer to the relevant module documentation. -#![deny(missing_docs)] - #[cfg(adc)] pub mod adc; #[cfg(dac)] diff --git a/esp-hal/src/assist_debug.rs b/esp-hal/src/assist_debug.rs index 91128407b7f..4e5fe1e8c90 100644 --- a/esp-hal/src/assist_debug.rs +++ b/esp-hal/src/assist_debug.rs @@ -68,7 +68,7 @@ impl<'d> InterruptConfigurable for DebugAssist<'d> { #[cfg(assist_debug_sp_monitor)] impl<'d> DebugAssist<'d> { /// Enable SP monitoring on main core. When the SP exceeds the - /// `lower_bound` or `upper_bound` treshold, the module will record the PC + /// `lower_bound` or `upper_bound` threshold, the module will record the PC /// pointer and generate an interrupt. pub fn enable_sp_monitor(&mut self, lower_bound: u32, upper_bound: u32) { self.debug_assist @@ -150,8 +150,8 @@ impl<'d> DebugAssist<'d> { #[cfg(all(assist_debug_sp_monitor, multi_core))] impl<'d> DebugAssist<'d> { - /// Enable SP monitoring on secondondary core. When the SP exceeds the - /// `lower_bound` or `upper_bound` treshold, the module will record the PC + /// Enable SP monitoring on secondary core. When the SP exceeds the + /// `lower_bound` or `upper_bound` threshold, the module will record the PC /// pointer and generate an interrupt. pub fn enable_core1_sp_monitor(&mut self, lower_bound: u32, upper_bound: u32) { self.debug_assist @@ -384,7 +384,7 @@ impl<'d> DebugAssist<'d> { .bit_is_set() } - /// Get region monotoring PC value on main core. + /// Get region monitoring PC value on main core. pub fn get_region_monitor_pc(&self) -> u32 { self.debug_assist .core_0_area_pc() @@ -550,7 +550,7 @@ impl<'d> DebugAssist<'d> { .bit_is_set() } - /// Get region monotoring PC value on secondary core. + /// Get region monitoring PC value on secondary core. pub fn get_core1_region_monitor_pc(&self) -> u32 { self.debug_assist .core_1_area_pc() diff --git a/esp-hal/src/clock/mod.rs b/esp-hal/src/clock/mod.rs index af1f19d0a46..1ad30294322 100644 --- a/esp-hal/src/clock/mod.rs +++ b/esp-hal/src/clock/mod.rs @@ -1,6 +1,7 @@ -//! # Clock Control +//! # CPU Clock Control //! //! ## Overview +//! //! Clocks are mainly sourced from oscillator (OSC), RC, and PLL circuits, and //! then processed by the dividers or selectors, which allows most functional //! modules to select their working clock according to their power consumption @@ -12,20 +13,14 @@ //! module clocks. //! //! ## Configuration -//! Proper clock configuration is essential for the correct functioning of the -//! microcontroller and its peripherals. -//! -//! The `Clock` driver supports configuring multiple clocks, including: -//! * CPU clock -//! * APB (Advanced Peripheral Bus) clock -//! * XTAL (External Crystal) clock -//! * PLL (Phase Lock Loop) clock //! -//! and other specific clocks based on the ESP microcontroller's architecture. +//! During HAL initialization, specify a CPU clock speed to configure the +//! desired clock frequencies. //! //! The `CPU clock` is responsible for defining the speed at which the central //! processing unit (CPU) operates. This driver provides predefined options for -//! different CPU clock speeds, such +//! different CPU clock speeds, such as +//! //! * 80 MHz //! * 96 MHz //! * 120 MHz @@ -34,52 +29,52 @@ //! //! and others, depending on the microcontroller model. //! -//! ### Clock Control -//! The `ClockControl` struct allows users to configure the desired clock -//! frequencies before applying them. It offers flexibility in selecting -//! appropriate clock frequencies based on specific application requirements. -//! //! ### Frozen Clock Frequencies -//! Once the clock configuration is applied using the `freeze` function of the -//! ClockControl struct, the clock frequencies become `frozen` and cannot be -//! changed. The `Clocks` struct is returned after freezing, providing read-only -//! access to the configured clock frequencies. +//! +//! Once the clock configuration is applied, the clock frequencies become +//! `frozen` and cannot be changed. The `Clocks` struct is returned as part of +//! the `System` struct, providing read-only access to the configured clock +//! frequencies. //! //! ## Examples +//! //! ### Initialize With Different Clock Frequencies //! ```rust, no_run //! # #![no_std] -//! # use esp_hal::peripherals::Peripherals; -//! # use esp_hal::clock::ClockControl; -//! # use esp_hal::system::SystemControl; +//! # use esp_hal::prelude::*; //! # #[panic_handler] //! # fn panic(_ : &core::panic::PanicInfo) -> ! { //! # loop {} //! # } //! # fn main() { -//! # let peripherals = Peripherals::take(); -//! # let system = SystemControl::new(peripherals.SYSTEM); //! // Initialize with the highest possible frequency for this chip -//! let clocks = ClockControl::max(system.clock_control).freeze(); +//! let peripherals = esp_hal::init({ +//! let mut config = esp_hal::Config::default(); +//! config.cpu_clock = CpuClock::max(); +//! config +//! }); //! //! // Initialize with custom clock frequency -//! // let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock160MHz).freeze(); +//! // let peripherals = esp_hal::init({ +//! // let mut config = esp_hal::Config::default(); +#![cfg_attr( + not(any(esp32c2, esp32h2)), + doc = "// config.cpu_clock = CpuClock::Clock160MHz;" +)] +#![cfg_attr(esp32c2, doc = "// config.cpu_clock = CpuClock::Clock120MHz;")] +#![cfg_attr(esp32h2, doc = "// config.cpu_clock = CpuClock::Clock96MHz;")] +//! // config +//! // }); //! // //! // Initialize with default clock frequency for this chip -//! // let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); +//! // let peripherals = esp_hal::init(esp_hal::Config::default()); //! # } //! ``` -#![deny(missing_docs)] - use fugit::HertzU32; #[cfg(any(esp32, esp32c2))] use crate::rtc_cntl::RtcClock; -use crate::{ - peripheral::{Peripheral, PeripheralRef}, - system::SystemClockControl, -}; #[cfg_attr(esp32, path = "clocks_ll/esp32.rs")] #[cfg_attr(esp32c2, path = "clocks_ll/esp32c2.rs")] @@ -107,40 +102,63 @@ pub trait Clock { } /// CPU clock speed -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CpuClock { /// 80MHz CPU clock #[cfg(not(esp32h2))] - Clock80MHz, + Clock80MHz = 80, + /// 96MHz CPU clock #[cfg(esp32h2)] - Clock96MHz, + Clock96MHz = 96, + /// 120MHz CPU clock #[cfg(esp32c2)] - Clock120MHz, + Clock120MHz = 120, + /// 160MHz CPU clock #[cfg(not(any(esp32c2, esp32h2)))] - Clock160MHz, + Clock160MHz = 160, + /// 240MHz CPU clock #[cfg(xtensa)] - Clock240MHz, + Clock240MHz = 240, +} + +impl Default for CpuClock { + fn default() -> Self { + cfg_if::cfg_if! { + if #[cfg(esp32h2)] { + Self::Clock96MHz + } else { + // FIXME: I don't think this is correct in general? + Self::Clock80MHz + } + } + } +} + +impl CpuClock { + /// Use the highest possible frequency for a particular chip. + pub const fn max() -> Self { + cfg_if::cfg_if! { + if #[cfg(esp32c2)] { + Self::Clock120MHz + } else if #[cfg(any(esp32c3, esp32c6))] { + Self::Clock160MHz + } else if #[cfg(esp32h2)] { + Self::Clock96MHz + } else { + Self::Clock240MHz + } + } + } } -#[allow(dead_code)] impl Clock for CpuClock { fn frequency(&self) -> HertzU32 { - match self { - #[cfg(not(esp32h2))] - CpuClock::Clock80MHz => HertzU32::MHz(80), - #[cfg(esp32h2)] - CpuClock::Clock96MHz => HertzU32::MHz(96), - #[cfg(esp32c2)] - CpuClock::Clock120MHz => HertzU32::MHz(120), - #[cfg(not(any(esp32c2, esp32h2)))] - CpuClock::Clock160MHz => HertzU32::MHz(160), - #[cfg(xtensa)] - CpuClock::Clock240MHz => HertzU32::MHz(240), - } + HertzU32::MHz(*self as u32) } } @@ -253,481 +271,313 @@ impl Clock for ApbClock { } } -/// Frozen clock frequencies -/// -/// The instantiation of this type indicates that the clock configuration can no -/// longer be changed -pub struct Clocks<'d> { - _private: PeripheralRef<'d, SystemClockControl>, +/// Clock frequencies. +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct Clocks { /// CPU clock frequency pub cpu_clock: HertzU32, + /// APB clock frequency pub apb_clock: HertzU32, + /// XTAL clock frequency pub xtal_clock: HertzU32, + /// I2C clock frequency #[cfg(esp32)] pub i2c_clock: HertzU32, + /// PWM clock frequency #[cfg(esp32)] pub pwm_clock: HertzU32, + /// Crypto PWM clock frequency #[cfg(esp32s3)] pub crypto_pwm_clock: HertzU32, + /// Crypto clock frequency #[cfg(any(esp32c6, esp32h2))] pub crypto_clock: HertzU32, + /// PLL 48M clock frequency (fixed) #[cfg(esp32h2)] pub pll_48m_clock: HertzU32, + /// PLL 96M clock frequency (fixed) #[cfg(esp32h2)] pub pll_96m_clock: HertzU32, } -#[doc(hidden)] -impl<'d> Clocks<'d> { - /// This should not be used in user code. - /// The whole point this exists is make it possible to have other crates - /// (i.e. esp-wifi) create `Clocks` - #[doc(hidden)] - pub fn from_raw_clocks( - system_clock_control: PeripheralRef<'d, SystemClockControl>, - raw_clocks: RawClocks, - ) -> Clocks<'d> { - Self { - _private: system_clock_control, - cpu_clock: raw_clocks.cpu_clock, - apb_clock: raw_clocks.apb_clock, - xtal_clock: raw_clocks.xtal_clock, - #[cfg(esp32)] - i2c_clock: raw_clocks.i2c_clock, - #[cfg(esp32)] - pwm_clock: raw_clocks.pwm_clock, - #[cfg(esp32s3)] - crypto_pwm_clock: raw_clocks.crypto_pwm_clock, - #[cfg(any(esp32c6, esp32h2))] - crypto_clock: raw_clocks.crypto_clock, - #[cfg(esp32h2)] - pll_48m_clock: raw_clocks.pll_48m_clock, - #[cfg(esp32h2)] - pll_96m_clock: raw_clocks.pll_96m_clock, - } - } -} +static mut ACTIVE_CLOCKS: Option = None; -#[doc(hidden)] -pub struct RawClocks { - pub cpu_clock: HertzU32, - pub apb_clock: HertzU32, - pub xtal_clock: HertzU32, - #[cfg(esp32)] - pub i2c_clock: HertzU32, - #[cfg(esp32)] - pub pwm_clock: HertzU32, - #[cfg(esp32s3)] - pub crypto_pwm_clock: HertzU32, - #[cfg(any(esp32c6, esp32h2))] - pub crypto_clock: HertzU32, - #[cfg(esp32h2)] - pub pll_48m_clock: HertzU32, - #[cfg(esp32h2)] - pub pll_96m_clock: HertzU32, -} +impl Clocks { + pub(crate) fn init(cpu_clock_speed: CpuClock) { + critical_section::with(|_| { + unsafe { ACTIVE_CLOCKS = Some(Self::configure(cpu_clock_speed)) }; + }) + } -/// Used to configure the frequencies of the clocks present in the chip. -/// -/// After setting all frequencies, call the freeze function to apply the -/// configuration. -pub struct ClockControl<'d> { - _private: PeripheralRef<'d, SystemClockControl>, - desired_rates: RawClocks, -} + fn try_get() -> Option<&'static Clocks> { + unsafe { + // Safety: ACTIVE_CLOCKS is only set in `init` and never modified after that. + ACTIVE_CLOCKS.as_ref() + } + } -impl<'d> ClockControl<'d> { - /// Applies the clock configuration and returns a Clocks struct that - /// signifies that the clocks are frozen, and contains the frequencies - /// used. After this function is called, the clocks can not change - pub fn freeze(self) -> Clocks<'d> { - Clocks::from_raw_clocks(self._private, self.desired_rates) + /// Get the active clock configuration. + pub fn get() -> &'static Clocks { + unwrap!(Self::try_get()) } -} -#[cfg(esp32)] -impl<'d> ClockControl<'d> { - /// Use what is considered the default settings after boot. - pub fn boot_defaults( - clock_control: impl Peripheral

+ 'd, - ) -> ClockControl<'d> { - let xtal_freq = if RtcClock::estimate_xtal_frequency() > 33 { - 40 + /// Returns the xtal frequency. + /// + /// This function will run the frequency estimation if called before + /// [`crate::init()`]. + pub fn xtal_freq() -> HertzU32 { + if let Some(clocks) = Self::try_get() { + clocks.xtal_clock } else { - 26 - }; - - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: HertzU32::MHz(80), - apb_clock: HertzU32::MHz(80), - xtal_clock: HertzU32::MHz(xtal_freq), - i2c_clock: HertzU32::MHz(80), - pwm_clock: HertzU32::MHz(160), - }, + Self::measure_xtal_frequency().frequency() } } +} - /// Configure the CPU clock speed. - pub fn configure( - clock_control: impl Peripheral

+ 'd, - cpu_clock_speed: CpuClock, - ) -> ClockControl<'d> { - let xtal_freq = if RtcClock::estimate_xtal_frequency() > 33 { +#[cfg(esp32)] +impl Clocks { + fn measure_xtal_frequency() -> XtalClock { + if RtcClock::estimate_xtal_frequency() > 33 { XtalClock::RtcXtalFreq40M } else { XtalClock::RtcXtalFreq26M - }; - - let pll_freq = match cpu_clock_speed { - CpuClock::Clock80MHz => PllClock::Pll320MHz, - CpuClock::Clock160MHz => PllClock::Pll320MHz, - CpuClock::Clock240MHz => PllClock::Pll480MHz, - }; - - clocks_ll::esp32_rtc_update_to_xtal(xtal_freq, 1); - clocks_ll::esp32_rtc_bbpll_enable(); - clocks_ll::esp32_rtc_bbpll_configure(xtal_freq, pll_freq); - clocks_ll::set_cpu_freq(cpu_clock_speed); - - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: cpu_clock_speed.frequency(), - apb_clock: HertzU32::MHz(80), - xtal_clock: HertzU32::MHz(40), - i2c_clock: HertzU32::MHz(80), - // The docs are unclear here. pwm_clock seems to be tied to clocks.apb_clock - // while simultaneously being fixed at 160 MHz. - // Testing showed 160 MHz to be correct for current clock configurations. - pwm_clock: HertzU32::MHz(160), - }, } } - /// Use the highest possible frequency for a particular chip - pub fn max(clock_control: impl Peripheral

+ 'd) -> ClockControl<'d> { - Self::configure(clock_control, CpuClock::Clock240MHz) + /// Configure the CPU clock speed. + pub(crate) fn configure(cpu_clock_speed: CpuClock) -> Self { + let xtal_freq = Self::measure_xtal_frequency(); + + if cpu_clock_speed != CpuClock::default() { + let pll_freq = match cpu_clock_speed { + CpuClock::Clock80MHz => PllClock::Pll320MHz, + CpuClock::Clock160MHz => PllClock::Pll320MHz, + CpuClock::Clock240MHz => PllClock::Pll480MHz, + }; + + clocks_ll::esp32_rtc_update_to_xtal(xtal_freq, 1); + clocks_ll::esp32_rtc_bbpll_enable(); + clocks_ll::esp32_rtc_bbpll_configure(xtal_freq, pll_freq); + clocks_ll::set_cpu_freq(cpu_clock_speed); + } + + Self { + cpu_clock: cpu_clock_speed.frequency(), + apb_clock: HertzU32::MHz(80), + xtal_clock: HertzU32::MHz(xtal_freq.mhz()), + i2c_clock: HertzU32::MHz(80), + // The docs are unclear here. pwm_clock seems to be tied to clocks.apb_clock + // while simultaneously being fixed at 160 MHz. + // Testing showed 160 MHz to be correct for current clock configurations. + pwm_clock: HertzU32::MHz(160), + } } } #[cfg(esp32c2)] -impl<'d> ClockControl<'d> { - /// Use what is considered the default settings after boot. - pub fn boot_defaults( - clock_control: impl Peripheral

+ 'd, - ) -> ClockControl<'d> { - let xtal_freq = if RtcClock::estimate_xtal_frequency() > 33 { - 40 +impl Clocks { + fn measure_xtal_frequency() -> XtalClock { + if RtcClock::estimate_xtal_frequency() > 33 { + XtalClock::RtcXtalFreq40M } else { - 26 - }; - - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: HertzU32::MHz(80), - apb_clock: HertzU32::MHz(40), - xtal_clock: HertzU32::MHz(xtal_freq), - }, + XtalClock::RtcXtalFreq26M } } /// Configure the CPU clock speed. - pub fn configure( - clock_control: impl Peripheral

+ 'd, - cpu_clock_speed: CpuClock, - ) -> ClockControl<'d> { - let apb_freq; - - let xtal_freq = if RtcClock::estimate_xtal_frequency() > 33 { - XtalClock::RtcXtalFreq40M - } else { - XtalClock::RtcXtalFreq26M - }; - - let pll_freq = PllClock::Pll480MHz; + pub(crate) fn configure(cpu_clock_speed: CpuClock) -> Self { + let xtal_freq = Self::measure_xtal_frequency(); - if cpu_clock_speed.mhz() <= xtal_freq.mhz() { - apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz()); - clocks_ll::esp32c2_rtc_update_to_xtal(xtal_freq, 1); - clocks_ll::esp32c2_rtc_apb_freq_update(apb_freq); + let apb_freq; + if cpu_clock_speed != CpuClock::default() { + let pll_freq = PllClock::Pll480MHz; + + if cpu_clock_speed.mhz() <= xtal_freq.mhz() { + apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz()); + clocks_ll::esp32c2_rtc_update_to_xtal(xtal_freq, 1); + clocks_ll::esp32c2_rtc_apb_freq_update(apb_freq); + } else { + apb_freq = ApbClock::ApbFreq40MHz; + clocks_ll::esp32c2_rtc_bbpll_enable(); + clocks_ll::esp32c2_rtc_bbpll_configure(xtal_freq, pll_freq); + clocks_ll::esp32c2_rtc_freq_to_pll_mhz(cpu_clock_speed); + clocks_ll::esp32c2_rtc_apb_freq_update(apb_freq); + } } else { apb_freq = ApbClock::ApbFreq40MHz; - clocks_ll::esp32c2_rtc_bbpll_enable(); - clocks_ll::esp32c2_rtc_bbpll_configure(xtal_freq, pll_freq); - clocks_ll::esp32c2_rtc_freq_to_pll_mhz(cpu_clock_speed); - clocks_ll::esp32c2_rtc_apb_freq_update(apb_freq); } - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: cpu_clock_speed.frequency(), - apb_clock: apb_freq.frequency(), - xtal_clock: xtal_freq.frequency(), - }, + Self { + cpu_clock: cpu_clock_speed.frequency(), + apb_clock: apb_freq.frequency(), + xtal_clock: xtal_freq.frequency(), } } - - /// Use the highest possible frequency for a particular chip - pub fn max(clock_control: impl Peripheral

+ 'd) -> ClockControl<'d> { - Self::configure(clock_control, CpuClock::Clock120MHz) - } } #[cfg(esp32c3)] -impl<'d> ClockControl<'d> { - /// Use what is considered the default settings after boot. - pub fn boot_defaults( - clock_control: impl Peripheral

+ 'd, - ) -> ClockControl<'d> { - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: HertzU32::MHz(80), - apb_clock: HertzU32::MHz(80), - xtal_clock: HertzU32::MHz(40), - }, - } +impl Clocks { + fn measure_xtal_frequency() -> XtalClock { + XtalClock::RtcXtalFreq40M } /// Configure the CPU clock speed. - pub fn configure( - clock_control: impl Peripheral

+ 'd, - cpu_clock_speed: CpuClock, - ) -> ClockControl<'d> { - let apb_freq; - let xtal_freq = XtalClock::RtcXtalFreq40M; - let pll_freq = PllClock::Pll480MHz; + pub(crate) fn configure(cpu_clock_speed: CpuClock) -> Self { + let xtal_freq = Self::measure_xtal_frequency(); - if cpu_clock_speed.mhz() <= xtal_freq.mhz() { - apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz()); - clocks_ll::esp32c3_rtc_update_to_xtal(xtal_freq, 1); - clocks_ll::esp32c3_rtc_apb_freq_update(apb_freq); + let apb_freq; + if cpu_clock_speed != CpuClock::default() { + if cpu_clock_speed.mhz() <= xtal_freq.mhz() { + apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz()); + clocks_ll::esp32c3_rtc_update_to_xtal(xtal_freq, 1); + clocks_ll::esp32c3_rtc_apb_freq_update(apb_freq); + } else { + let pll_freq = PllClock::Pll480MHz; + apb_freq = ApbClock::ApbFreq80MHz; + clocks_ll::esp32c3_rtc_bbpll_enable(); + clocks_ll::esp32c3_rtc_bbpll_configure(xtal_freq, pll_freq); + clocks_ll::esp32c3_rtc_freq_to_pll_mhz(cpu_clock_speed); + clocks_ll::esp32c3_rtc_apb_freq_update(apb_freq); + } } else { apb_freq = ApbClock::ApbFreq80MHz; - clocks_ll::esp32c3_rtc_bbpll_enable(); - clocks_ll::esp32c3_rtc_bbpll_configure(xtal_freq, pll_freq); - clocks_ll::esp32c3_rtc_freq_to_pll_mhz(cpu_clock_speed); - clocks_ll::esp32c3_rtc_apb_freq_update(apb_freq); } - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: cpu_clock_speed.frequency(), - apb_clock: apb_freq.frequency(), - xtal_clock: xtal_freq.frequency(), - }, + Self { + cpu_clock: cpu_clock_speed.frequency(), + apb_clock: apb_freq.frequency(), + xtal_clock: xtal_freq.frequency(), } } - - /// Use the highest possible frequency for a particular chip - pub fn max(clock_control: impl Peripheral

+ 'd) -> ClockControl<'d> { - Self::configure(clock_control, CpuClock::Clock160MHz) - } } #[cfg(esp32c6)] -impl<'d> ClockControl<'d> { - /// Use what is considered the default settings after boot. - pub fn boot_defaults( - clock_control: impl Peripheral

+ 'd, - ) -> ClockControl<'d> { - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: HertzU32::MHz(80), - apb_clock: HertzU32::MHz(80), - xtal_clock: HertzU32::MHz(40), - crypto_clock: HertzU32::MHz(160), - }, - } +impl Clocks { + fn measure_xtal_frequency() -> XtalClock { + XtalClock::RtcXtalFreq40M } /// Configure the CPU clock speed. - pub fn configure( - clock_control: impl Peripheral

+ 'd, - cpu_clock_speed: CpuClock, - ) -> ClockControl<'d> { - let apb_freq; - let xtal_freq = XtalClock::RtcXtalFreq40M; - let pll_freq = PllClock::Pll480MHz; + pub(crate) fn configure(cpu_clock_speed: CpuClock) -> Self { + let xtal_freq = Self::measure_xtal_frequency(); - if cpu_clock_speed.mhz() <= xtal_freq.mhz() { - apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz()); - clocks_ll::esp32c6_rtc_update_to_xtal(xtal_freq, 1); - clocks_ll::esp32c6_rtc_apb_freq_update(apb_freq); + let apb_freq; + if cpu_clock_speed != CpuClock::default() { + if cpu_clock_speed.mhz() <= xtal_freq.mhz() { + apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz()); + clocks_ll::esp32c6_rtc_update_to_xtal(xtal_freq, 1); + clocks_ll::esp32c6_rtc_apb_freq_update(apb_freq); + } else { + let pll_freq = PllClock::Pll480MHz; + apb_freq = ApbClock::ApbFreq80MHz; + clocks_ll::esp32c6_rtc_bbpll_enable(); + clocks_ll::esp32c6_rtc_bbpll_configure(xtal_freq, pll_freq); + clocks_ll::esp32c6_rtc_freq_to_pll_mhz(cpu_clock_speed); + clocks_ll::esp32c6_rtc_apb_freq_update(apb_freq); + } } else { apb_freq = ApbClock::ApbFreq80MHz; - clocks_ll::esp32c6_rtc_bbpll_enable(); - clocks_ll::esp32c6_rtc_bbpll_configure(xtal_freq, pll_freq); - clocks_ll::esp32c6_rtc_freq_to_pll_mhz(cpu_clock_speed); - clocks_ll::esp32c6_rtc_apb_freq_update(apb_freq); } - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: cpu_clock_speed.frequency(), - apb_clock: apb_freq.frequency(), - xtal_clock: xtal_freq.frequency(), - crypto_clock: HertzU32::MHz(160), - }, + Self { + cpu_clock: cpu_clock_speed.frequency(), + apb_clock: apb_freq.frequency(), + xtal_clock: xtal_freq.frequency(), + crypto_clock: HertzU32::MHz(160), } } - - /// Use the highest possible frequency for a particular chip - pub fn max(clock_control: impl Peripheral

+ 'd) -> ClockControl<'d> { - Self::configure(clock_control, CpuClock::Clock160MHz) - } } #[cfg(esp32h2)] -impl<'d> ClockControl<'d> { - /// Use what is considered the default settings after boot. - pub fn boot_defaults( - clock_control: impl Peripheral

+ 'd, - ) -> ClockControl<'d> { - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: HertzU32::MHz(96), - apb_clock: HertzU32::MHz(32), - xtal_clock: HertzU32::MHz(32), - pll_48m_clock: HertzU32::MHz(48), - crypto_clock: HertzU32::MHz(96), - pll_96m_clock: HertzU32::MHz(96), - }, - } +impl Clocks { + fn measure_xtal_frequency() -> XtalClock { + XtalClock::RtcXtalFreq32M } /// Configure the CPU clock speed. - pub fn configure( - clock_control: impl Peripheral

+ 'd, - cpu_clock_speed: CpuClock, - ) -> ClockControl<'d> { - let apb_freq; - let xtal_freq = XtalClock::RtcXtalFreq32M; - let pll_freq = PllClock::Pll96MHz; + pub(crate) fn configure(cpu_clock_speed: CpuClock) -> Self { + let xtal_freq = Self::measure_xtal_frequency(); - if cpu_clock_speed.mhz() <= xtal_freq.mhz() { - apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz()); - clocks_ll::esp32h2_rtc_update_to_xtal(xtal_freq, 1); - clocks_ll::esp32h2_rtc_apb_freq_update(apb_freq); + let apb_freq; + if cpu_clock_speed != CpuClock::default() { + if cpu_clock_speed.mhz() <= xtal_freq.mhz() { + apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz()); + clocks_ll::esp32h2_rtc_update_to_xtal(xtal_freq, 1); + clocks_ll::esp32h2_rtc_apb_freq_update(apb_freq); + } else { + let pll_freq = PllClock::Pll96MHz; + apb_freq = ApbClock::ApbFreq32MHz; + clocks_ll::esp32h2_rtc_bbpll_enable(); + clocks_ll::esp32h2_rtc_bbpll_configure(xtal_freq, pll_freq); + clocks_ll::esp32h2_rtc_freq_to_pll_mhz(cpu_clock_speed); + clocks_ll::esp32h2_rtc_apb_freq_update(apb_freq); + } } else { apb_freq = ApbClock::ApbFreq32MHz; - clocks_ll::esp32h2_rtc_bbpll_enable(); - clocks_ll::esp32h2_rtc_bbpll_configure(xtal_freq, pll_freq); - clocks_ll::esp32h2_rtc_freq_to_pll_mhz(cpu_clock_speed); - clocks_ll::esp32h2_rtc_apb_freq_update(apb_freq); } - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: cpu_clock_speed.frequency(), - apb_clock: apb_freq.frequency(), - xtal_clock: xtal_freq.frequency(), - pll_48m_clock: HertzU32::MHz(48), - crypto_clock: HertzU32::MHz(96), - pll_96m_clock: HertzU32::MHz(96), - }, + Self { + cpu_clock: cpu_clock_speed.frequency(), + apb_clock: apb_freq.frequency(), + xtal_clock: xtal_freq.frequency(), + pll_48m_clock: HertzU32::MHz(48), + crypto_clock: HertzU32::MHz(96), + pll_96m_clock: HertzU32::MHz(96), } } - - /// Use the highest possible frequency for a particular chip - pub fn max(clock_control: impl Peripheral

+ 'd) -> ClockControl<'d> { - Self::configure(clock_control, CpuClock::Clock96MHz) - } } #[cfg(esp32s2)] -impl<'d> ClockControl<'d> { - /// Use what is considered the default settings after boot. - pub fn boot_defaults( - clock_control: impl Peripheral

+ 'd, - ) -> ClockControl<'d> { - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: HertzU32::MHz(80), - apb_clock: HertzU32::MHz(80), - xtal_clock: HertzU32::MHz(40), - }, - } +impl Clocks { + fn measure_xtal_frequency() -> XtalClock { + XtalClock::RtcXtalFreq40M } /// Configure the CPU clock speed. - pub fn configure( - clock_control: impl Peripheral

+ 'd, - cpu_clock_speed: CpuClock, - ) -> ClockControl<'d> { - clocks_ll::set_cpu_clock(cpu_clock_speed); - - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: cpu_clock_speed.frequency(), - apb_clock: HertzU32::MHz(80), - xtal_clock: HertzU32::MHz(40), - }, + pub(crate) fn configure(cpu_clock_speed: CpuClock) -> Self { + let xtal_freq = Self::measure_xtal_frequency(); + + if cpu_clock_speed != CpuClock::default() { + clocks_ll::set_cpu_clock(cpu_clock_speed); } - } - /// Use the highest possible frequency for a particular chip - pub fn max(clock_control: impl Peripheral

+ 'd) -> ClockControl<'d> { - Self::configure(clock_control, CpuClock::Clock240MHz) + Self { + cpu_clock: cpu_clock_speed.frequency(), + apb_clock: HertzU32::MHz(80), + xtal_clock: xtal_freq.frequency(), + } } } #[cfg(esp32s3)] -impl<'d> ClockControl<'d> { - /// Use what is considered the default settings after boot. - pub fn boot_defaults( - clock_control: impl Peripheral

+ 'd, - ) -> ClockControl<'d> { - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: HertzU32::MHz(80), - apb_clock: HertzU32::MHz(80), - xtal_clock: HertzU32::MHz(40), - crypto_pwm_clock: HertzU32::MHz(160), - }, - } +impl Clocks { + fn measure_xtal_frequency() -> XtalClock { + XtalClock::RtcXtalFreq40M } /// Configure the CPU clock speed. - pub fn configure( - clock_control: impl Peripheral

+ 'd, - cpu_clock_speed: CpuClock, - ) -> ClockControl<'d> { - clocks_ll::set_cpu_clock(cpu_clock_speed); - - ClockControl { - _private: clock_control.into_ref(), - desired_rates: RawClocks { - cpu_clock: cpu_clock_speed.frequency(), - apb_clock: HertzU32::MHz(80), - xtal_clock: HertzU32::MHz(40), - crypto_pwm_clock: HertzU32::MHz(160), - }, + pub(crate) fn configure(cpu_clock_speed: CpuClock) -> Self { + let xtal_freq = Self::measure_xtal_frequency(); + + if cpu_clock_speed != CpuClock::default() { + clocks_ll::set_cpu_clock(cpu_clock_speed); } - } - /// Use the highest possible frequency for a particular chip - pub fn max(clock_control: impl Peripheral

+ 'd) -> ClockControl<'d> { - Self::configure(clock_control, CpuClock::Clock240MHz) + Self { + cpu_clock: cpu_clock_speed.frequency(), + apb_clock: HertzU32::MHz(80), + xtal_clock: xtal_freq.frequency(), + crypto_pwm_clock: HertzU32::MHz(160), + } } } diff --git a/esp-hal/src/debugger.rs b/esp-hal/src/debugger.rs new file mode 100644 index 00000000000..3e4ab7043e8 --- /dev/null +++ b/esp-hal/src/debugger.rs @@ -0,0 +1,25 @@ +//! Debugger utilities + +/// Checks if a debugger is connected. +pub fn debugger_connected() -> bool { + #[cfg(xtensa)] + { + xtensa_lx::is_debugger_attached() + } + + #[cfg(riscv)] + { + use crate::peripherals::ASSIST_DEBUG; + let assist_debug = unsafe { &*ASSIST_DEBUG::ptr() }; + assist_debug + .core_0_debug_mode() + .read() + .core_0_debug_module_active() + .bit_is_set() + } + + #[cfg(not(any(xtensa, riscv)))] + { + false + } +} diff --git a/esp-hal/src/delay.rs b/esp-hal/src/delay.rs index 2febb2202e3..d1c562b01d3 100644 --- a/esp-hal/src/delay.rs +++ b/esp-hal/src/delay.rs @@ -1,16 +1,18 @@ //! # Delay //! //! ## Overview +//! //! The Delay driver provides blocking delay functionalities using the -//! `SYSTIMER` peripheral for RISC-V devices and the built-in Xtensa timer for -//! Xtensa devices. +//! [now] function. //! //! ## Configuration +//! //! The delays are implemented in a "best-effort" way, meaning that the CPU will //! block for at least the amount of time specified, but accuracy can be //! affected by many factors, including interrupt usage. //! //! ## Usage +//! //! This module implements the blocking [DelayMs] and [DelayUs] traits from //! [embedded-hal], both 0.2.x and 1.x.x. //! @@ -20,7 +22,7 @@ #![doc = crate::before_snippet!()] //! # use esp_hal::delay::Delay; //! # use embedded_hal::delay::DelayNs; -//! let mut delay = Delay::new(&clocks); +//! let mut delay = Delay::new(); //! //! delay.delay_ms(1000 as u32); //! # } @@ -29,41 +31,27 @@ //! [DelayMs]: embedded_hal_02::blocking::delay::DelayMs //! [DelayUs]: embedded_hal_02::blocking::delay::DelayUs //! [embedded-hal]: https://docs.rs/embedded-hal/1.0.0/embedded_hal/delay/index.html +//! [now]: crate::time::now -use fugit::HertzU64; pub use fugit::MicrosDurationU64; /// Delay driver /// /// Uses the `SYSTIMER` peripheral internally for RISC-V devices, and the /// built-in Xtensa timer for Xtensa devices. -#[derive(Clone, Copy)] -pub struct Delay { - freq: HertzU64, -} - -impl Delay { - /// Delay for the specified number of milliseconds - pub fn delay_millis(&self, ms: u32) { - for _ in 0..ms { - self.delay_micros(1000); - } - } -} +#[derive(Clone, Copy, Default)] +#[non_exhaustive] +pub struct Delay; -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::blocking::delay::DelayMs for Delay where T: Into, { fn delay_ms(&mut self, ms: T) { - for _ in 0..ms.into() { - self.delay_micros(1000); - } + self.delay_millis(ms.into()); } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::blocking::delay::DelayUs for Delay where T: Into, @@ -73,90 +61,53 @@ where } } -#[cfg(feature = "embedded-hal")] impl embedded_hal::delay::DelayNs for Delay { fn delay_ns(&mut self, ns: u32) { self.delay_nanos(ns); } } -#[cfg(riscv)] -mod implementation { - use super::*; - use crate::{clock::Clocks, timer::systimer::SystemTimer}; - - impl Delay { - /// Create a new `Delay` instance - pub fn new(clocks: &Clocks<'_>) -> Self { - // The counters and comparators are driven using `XTAL_CLK`. - // The average clock frequency is fXTAL_CLK/2.5, which is 16 MHz. - // The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle. - Self { - #[cfg(not(esp32h2))] - freq: HertzU64::MHz(clocks.xtal_clock.to_MHz() as u64 * 10 / 25), - #[cfg(esp32h2)] - // esp32h2 TRM, section 11.2 Clock Source Selection - freq: HertzU64::MHz(clocks.xtal_clock.to_MHz() as u64 * 10 / 20), - } - } - - /// Delay for the specified time - pub fn delay(&self, time: MicrosDurationU64) { - let t0 = SystemTimer::now(); - let rate: HertzU64 = MicrosDurationU64::from_ticks(1).into_rate(); - let clocks = time.ticks() * (self.freq / rate); +impl Delay { + /// Creates a new `Delay` instance. + pub const fn new() -> Self { + Self {} + } - while SystemTimer::now().wrapping_sub(t0) & SystemTimer::BIT_MASK <= clocks {} - } + /// Delay for the specified time + pub fn delay(&self, delay: MicrosDurationU64) { + let start = crate::time::now(); - /// Delay for the specified number of microseconds - pub fn delay_micros(&self, us: u32) { - let t0 = SystemTimer::now(); - let clocks = us as u64 * (self.freq / HertzU64::MHz(1)); + while elapsed_since(start) < delay {} + } - while SystemTimer::now().wrapping_sub(t0) & SystemTimer::BIT_MASK <= clocks {} + /// Delay for the specified number of milliseconds + pub fn delay_millis(&self, ms: u32) { + for _ in 0..ms { + self.delay_micros(1000); } + } - /// Delay for the specified number of nanoseconds - pub fn delay_nanos(&self, ns: u32) { - let t0 = SystemTimer::now(); - let clocks = ns as u64 * (self.freq / HertzU64::MHz(1)) / 1000; + /// Delay for the specified number of microseconds + pub fn delay_micros(&self, us: u32) { + let delay = MicrosDurationU64::micros(us as u64); + self.delay(delay); + } - while SystemTimer::now().wrapping_sub(t0) & SystemTimer::BIT_MASK <= clocks {} - } + /// Delay for the specified number of nanoseconds + pub fn delay_nanos(&self, ns: u32) { + let delay = MicrosDurationU64::nanos(ns as u64); + self.delay(delay); } } -#[cfg(xtensa)] -mod implementation { - use super::*; - use crate::clock::Clocks; +fn elapsed_since(start: fugit::Instant) -> MicrosDurationU64 { + let now = crate::time::now(); - impl Delay { - /// Create a new `Delay` instance - pub fn new(clocks: &Clocks<'_>) -> Self { - Self { - freq: clocks.cpu_clock.into(), - } - } - - /// Delay for the specified time - pub fn delay(&self, time: MicrosDurationU64) { - let rate: HertzU64 = MicrosDurationU64::from_ticks(1).into_rate(); - let clocks = time.ticks() * (self.freq / rate); - xtensa_lx::timer::delay(clocks as u32); - } - - /// Delay for the specified number of microseconds - pub fn delay_micros(&self, us: u32) { - let clocks = us as u64 * (self.freq / HertzU64::MHz(1)); - xtensa_lx::timer::delay(clocks as u32); - } - - /// Delay for the specified number of nanoseconds - pub fn delay_nanos(&self, ns: u32) { - let clocks = ns as u64 * (self.freq / HertzU64::MHz(1)) / 1000; - xtensa_lx::timer::delay(clocks as u32); - } + if start.ticks() <= now.ticks() { + now - start + } else { + // now specifies at least 7 happy years, let's ignore this issue for + // now. + panic!("Time has wrapped around, which we currently don't handle"); } } diff --git a/esp-hal/src/dma/gdma.rs b/esp-hal/src/dma/gdma.rs index 39f24a5862c..4c1152a9f3e 100644 --- a/esp-hal/src/dma/gdma.rs +++ b/esp-hal/src/dma/gdma.rs @@ -420,14 +420,20 @@ impl RegisterAccess for Channel { #[doc(hidden)] pub struct ChannelTxImpl {} +use embassy_sync::waitqueue::AtomicWaker; + +#[allow(clippy::declare_interior_mutable_const)] +const INIT: AtomicWaker = AtomicWaker::new(); + +static TX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [INIT; CHANNEL_COUNT]; + +static RX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [INIT; CHANNEL_COUNT]; + impl crate::private::Sealed for ChannelTxImpl {} impl TxChannel> for ChannelTxImpl { - #[cfg(feature = "async")] - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - static WAKER: embassy_sync::waitqueue::AtomicWaker = - embassy_sync::waitqueue::AtomicWaker::new(); - &WAKER + fn waker() -> &'static AtomicWaker { + &TX_WAKERS[N as usize] } } @@ -438,11 +444,8 @@ pub struct ChannelRxImpl {} impl crate::private::Sealed for ChannelRxImpl {} impl RxChannel> for ChannelRxImpl { - #[cfg(feature = "async")] - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - static WAKER: embassy_sync::waitqueue::AtomicWaker = - embassy_sync::waitqueue::AtomicWaker::new(); - &WAKER + fn waker() -> &'static AtomicWaker { + &RX_WAKERS[N as usize] } } @@ -542,7 +545,6 @@ macro_rules! impl_channel { /// /// Descriptors should be sized as `(CHUNK_SIZE + 4091) / 4092`. I.e., to /// transfer buffers of size `1..=4092`, you need 1 descriptor. - #[cfg(feature = "async")] pub fn configure_for_async<'a>( self, burst_mode: bool, @@ -569,16 +571,20 @@ macro_rules! impl_channel { cfg_if::cfg_if! { if #[cfg(esp32c2)] { + const CHANNEL_COUNT: usize = 1; impl_channel!(0, super::asynch::interrupt::interrupt_handler_ch0, DMA_CH0); } else if #[cfg(esp32c3)] { + const CHANNEL_COUNT: usize = 3; impl_channel!(0, super::asynch::interrupt::interrupt_handler_ch0, DMA_CH0); impl_channel!(1, super::asynch::interrupt::interrupt_handler_ch1, DMA_CH1); impl_channel!(2, super::asynch::interrupt::interrupt_handler_ch2, DMA_CH2); } else if #[cfg(any(esp32c6, esp32h2))] { + const CHANNEL_COUNT: usize = 3; impl_channel!(0, super::asynch::interrupt::interrupt_handler_ch0, DMA_IN_CH0, DMA_OUT_CH0); impl_channel!(1, super::asynch::interrupt::interrupt_handler_ch1, DMA_IN_CH1, DMA_OUT_CH1); impl_channel!(2, super::asynch::interrupt::interrupt_handler_ch2, DMA_IN_CH2, DMA_OUT_CH2); - } else if #[cfg(esp32s3)] { + } else if #[cfg(esp32s3)] { + const CHANNEL_COUNT: usize = 5; impl_channel!(0, super::asynch::interrupt::interrupt_handler_ch0, DMA_IN_CH0, DMA_OUT_CH0); impl_channel!(1, super::asynch::interrupt::interrupt_handler_ch1, DMA_IN_CH1, DMA_OUT_CH1); impl_channel!(2, super::asynch::interrupt::interrupt_handler_ch2, DMA_IN_CH2, DMA_OUT_CH2); @@ -668,8 +674,8 @@ mod m2m { MODE: crate::Mode, { channel: Channel<'d, C, MODE>, - tx_chain: DescriptorChain, rx_chain: DescriptorChain, + tx_chain: DescriptorChain, peripheral: DmaPeripheral, } @@ -682,15 +688,15 @@ mod m2m { pub fn new( channel: Channel<'d, C, MODE>, peripheral: impl DmaEligible, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], ) -> Result { unsafe { Self::new_unsafe( channel, peripheral.dma_peripheral(), - tx_descriptors, rx_descriptors, + tx_descriptors, crate::dma::CHUNK_SIZE, ) } @@ -700,16 +706,16 @@ mod m2m { pub fn new_with_chunk_size( channel: Channel<'d, C, MODE>, peripheral: impl DmaEligible, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], chunk_size: usize, ) -> Result { unsafe { Self::new_unsafe( channel, peripheral.dma_peripheral(), - tx_descriptors, rx_descriptors, + tx_descriptors, chunk_size, ) } @@ -724,8 +730,8 @@ mod m2m { pub unsafe fn new_unsafe( mut channel: Channel<'d, C, MODE>, peripheral: DmaPeripheral, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], chunk_size: usize, ) -> Result { if !(1..=4092).contains(&chunk_size) { @@ -739,16 +745,16 @@ mod m2m { Ok(Mem2Mem { channel, peripheral, - tx_chain: DescriptorChain::new_with_chunk_size(tx_descriptors, chunk_size), rx_chain: DescriptorChain::new_with_chunk_size(rx_descriptors, chunk_size), + tx_chain: DescriptorChain::new_with_chunk_size(tx_descriptors, chunk_size), }) } /// Start a memory to memory transfer. pub fn start_transfer<'t, TXBUF, RXBUF>( &mut self, - tx_buffer: &'t TXBUF, rx_buffer: &'t mut RXBUF, + tx_buffer: &'t TXBUF, ) -> Result, DmaError> where TXBUF: ReadBuffer, @@ -793,7 +799,7 @@ mod m2m { C: DmaChannel, MODE: crate::Mode, { - fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { while !self.channel.rx.is_done() {} } diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 38df8650737..5e5bce6d8ba 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -1,6 +1,7 @@ //! # Direct Memory Access (DMA) //! //! ## Overview +//! //! The DMA driver provides an interface to efficiently transfer data between //! different memory regions and peripherals within the ESP microcontroller //! without involving the CPU. The DMA controller is responsible for managing @@ -10,15 +11,16 @@ //! `ESP32-S2` are using older `PDMA` controller, whenever other chips are using //! newer `GDMA` controller. //! -//! ## Examples -//! ### Initialize and Utilize DMA Controller in `SPI` +//! ## Example +//! +//! ### Initialize and utilize DMA controller in `SPI` +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::dma_buffers; //! # use esp_hal::gpio::Io; -//! # use esp_hal::spi::{master::{Spi, prelude::*}, SpiMode}; +//! # use esp_hal::spi::{master::Spi, SpiMode}; //! # use esp_hal::dma::{Dma, DmaPriority}; -//! # use crate::esp_hal::prelude::_fugit_RateExtU32; //! let dma = Dma::new(peripherals.DMA); #![cfg_attr(any(esp32, esp32s2), doc = "let dma_channel = dma.spi2channel;")] #![cfg_attr(not(any(esp32, esp32s2)), doc = "let dma_channel = dma.channel0;")] @@ -28,31 +30,36 @@ //! let mosi = io.pins.gpio4; //! let cs = io.pins.gpio5; //! -//! let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = -//! dma_buffers!(32000); -//! -//! let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) +//! let mut spi = Spi::new( +//! peripherals.SPI2, +//! 100.kHz(), +//! SpiMode::Mode0, +//! ) //! .with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs)) //! .with_dma(dma_channel.configure( //! false, //! DmaPriority::Priority0, -//! ), tx_descriptors, rx_descriptors); +//! )); //! # } //! ``` //! //! ⚠️ Note: Descriptors should be sized as `(max_transfer_size + CHUNK_SIZE - 1) / CHUNK_SIZE`. //! I.e., to transfer buffers of size `1..=CHUNK_SIZE`, you need 1 descriptor. //! -//! ⚠️ Note: For chips that support DMA to/from PSRAM (esp32s3) DMA transfers to/from PSRAM +//! ⚠️ Note: For chips that support DMA to/from PSRAM (ESP32-S3) DMA transfers to/from PSRAM //! have extra alignment requirements. The address and size of the buffer pointed to by //! each descriptor must be a multiple of the cache line (block) size. This is 32 bytes -//! on esp32s3. +//! on ESP32-S3. //! //! For convenience you can use the [crate::dma_buffers] macro. -#![deny(missing_docs)] - -use core::{fmt::Debug, marker::PhantomData, ptr::addr_of_mut, sync::atomic::compiler_fence}; +use core::{ + cmp::min, + fmt::Debug, + marker::PhantomData, + ptr::addr_of_mut, + sync::atomic::compiler_fence, +}; trait Word: crate::private::Sealed {} @@ -79,7 +86,13 @@ impl crate::private::Sealed for &[W] where W: Word {} impl crate::private::Sealed for &mut [W] where W: Word {} /// Trait for buffers that can be given to DMA for reading. -pub trait ReadBuffer: crate::private::Sealed { +/// +/// # Safety +/// +/// Once the `read_buffer` method has been called, it is unsafe to call any +/// `&mut self` methods on this object as long as the returned value is in use +/// (by DMA). +pub unsafe trait ReadBuffer { /// Provide a buffer usable for DMA reads. /// /// The return value is: @@ -94,7 +107,7 @@ pub trait ReadBuffer: crate::private::Sealed { unsafe fn read_buffer(&self) -> (*const u8, usize); } -impl ReadBuffer for [W; S] +unsafe impl ReadBuffer for [W; S] where W: Word, { @@ -103,7 +116,7 @@ where } } -impl ReadBuffer for &[W; S] +unsafe impl ReadBuffer for &[W; S] where W: Word, { @@ -112,7 +125,7 @@ where } } -impl ReadBuffer for &mut [W; S] +unsafe impl ReadBuffer for &mut [W; S] where W: Word, { @@ -121,7 +134,7 @@ where } } -impl ReadBuffer for &[W] +unsafe impl ReadBuffer for &[W] where W: Word, { @@ -130,7 +143,7 @@ where } } -impl ReadBuffer for &mut [W] +unsafe impl ReadBuffer for &mut [W] where W: Word, { @@ -140,7 +153,13 @@ where } /// Trait for buffers that can be given to DMA for writing. -pub trait WriteBuffer: crate::private::Sealed { +/// +/// # Safety +/// +/// Once the `write_buffer` method has been called, it is unsafe to call any +/// `&mut self` methods, except for `write_buffer`, on this object as long as +/// the returned value is in use (by DMA). +pub unsafe trait WriteBuffer { /// Provide a buffer usable for DMA writes. /// /// The return value is: @@ -152,11 +171,11 @@ pub trait WriteBuffer: crate::private::Sealed { /// /// Once this method has been called, it is unsafe to call any `&mut self` /// methods, except for `write_buffer`, on this object as long as the - /// returned value is in use (by DMA). + /// returned value is in use (by DMA). unsafe fn write_buffer(&mut self) -> (*mut u8, usize); } -impl WriteBuffer for [W; S] +unsafe impl WriteBuffer for [W; S] where W: Word, { @@ -165,7 +184,7 @@ where } } -impl WriteBuffer for &mut [W; S] +unsafe impl WriteBuffer for &mut [W; S] where W: Word, { @@ -174,7 +193,7 @@ where } } -impl WriteBuffer for &mut [W] +unsafe impl WriteBuffer for &mut [W] where W: Word, { @@ -265,54 +284,65 @@ use enumset::{EnumSet, EnumSetType}; pub use self::gdma::*; #[cfg(pdma)] pub use self::pdma::*; -use crate::{interrupt::InterruptHandler, Mode}; +use crate::{interrupt::InterruptHandler, soc::is_slice_in_dram, Mode}; #[cfg(gdma)] mod gdma; #[cfg(pdma)] mod pdma; -/// Kinds of interrupt to listen to +/// Kinds of interrupt to listen to. #[derive(EnumSetType)] pub enum DmaInterrupt { - /// TX is done - TxDone, /// RX is done RxDone, + /// TX is done + TxDone, } -/// The default CHUNK_SIZE used for DMA transfers +/// The default chunk size used for DMA transfers. pub const CHUNK_SIZE: usize = 4092; -/// Convenience macro to create DMA buffers and descriptors +/// Convenience macro to create DMA buffers and descriptors. /// /// ## Usage -/// ```rust,ignore -/// // TX and RX buffers are 32000 bytes - passing only one parameter makes TX and RX the same size -/// let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(32000, 32000); +/// ```rust,no_run +#[doc = crate::before_snippet!()] +/// use esp_hal::dma_buffers; +/// +/// // RX and TX buffers are 32000 bytes - passing only one parameter makes RX +/// // and TX the same size. +/// let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = +/// dma_buffers!(32000, 32000); +/// # } /// ``` #[macro_export] macro_rules! dma_buffers { - ($tx_size:expr, $rx_size:expr) => { - $crate::dma_buffers_chunk_size!($tx_size, $rx_size, $crate::dma::CHUNK_SIZE) + ($rx_size:expr, $tx_size:expr) => { + $crate::dma_buffers_chunk_size!($rx_size, $tx_size, $crate::dma::CHUNK_SIZE) }; ($size:expr) => { $crate::dma_buffers_chunk_size!($size, $crate::dma::CHUNK_SIZE) }; } -/// Convenience macro to create circular DMA buffers and descriptors +/// Convenience macro to create circular DMA buffers and descriptors. /// /// ## Usage -/// ```rust,ignore -/// // TX and RX buffers are 32000 bytes - passing only one parameter makes TX and RX the same size -/// let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = +/// ```rust,no_run +#[doc = crate::before_snippet!()] +/// use esp_hal::dma_circular_buffers; +/// +/// // RX and TX buffers are 32000 bytes - passing only one parameter makes RX +/// // and TX the same size. +/// let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = /// dma_circular_buffers!(32000, 32000); +/// # } /// ``` #[macro_export] macro_rules! dma_circular_buffers { - ($tx_size:expr, $rx_size:expr) => { - $crate::dma_circular_buffers_chunk_size!($tx_size, $rx_size, $crate::dma::CHUNK_SIZE) + ($rx_size:expr, $tx_size:expr) => { + $crate::dma_circular_buffers_chunk_size!($rx_size, $tx_size, $crate::dma::CHUNK_SIZE) }; ($size:expr) => { @@ -320,17 +350,22 @@ macro_rules! dma_circular_buffers { }; } -/// Convenience macro to create DMA descriptors +/// Convenience macro to create DMA descriptors. /// /// ## Usage -/// ```rust,ignore -/// // Create TX and RX descriptors for transactions up to 32000 bytes - passing only one parameter assumes TX and RX are the same size -/// let (tx_descriptors, rx_descriptors) = dma_descriptors!(32000, 32000); +/// ```rust,no_run +#[doc = crate::before_snippet!()] +/// use esp_hal::dma_descriptors; +/// +/// // Create RX and TX descriptors for transactions up to 32000 bytes - passing +/// // only one parameter assumes RX and TX are the same size. +/// let (rx_descriptors, tx_descriptors) = dma_descriptors!(32000, 32000); +/// # } /// ``` #[macro_export] macro_rules! dma_descriptors { - ($tx_size:expr, $rx_size:expr) => { - $crate::dma_descriptors_chunk_size!($tx_size, $rx_size, $crate::dma::CHUNK_SIZE) + ($rx_size:expr, $tx_size:expr) => { + $crate::dma_descriptors_chunk_size!($rx_size, $tx_size, $crate::dma::CHUNK_SIZE) }; ($size:expr) => { @@ -338,17 +373,23 @@ macro_rules! dma_descriptors { }; } -/// Convenience macro to create circular DMA descriptors +/// Convenience macro to create circular DMA descriptors. /// /// ## Usage -/// ```rust,ignore -/// // Create TX and RX descriptors for transactions up to 32000 bytes - passing only one parameter assumes TX and RX are the same size -/// let (tx_descriptors, rx_descriptors) = dma_circular_descriptors!(32000, 32000); +/// ```rust,no_run +#[doc = crate::before_snippet!()] +/// use esp_hal::dma_circular_descriptors; +/// +/// // Create RX and TX descriptors for transactions up to 32000 +/// // bytes - passing only one parameter assumes RX and TX are the same size. +/// let (rx_descriptors, tx_descriptors) = +/// dma_circular_descriptors!(32000, 32000); +/// # } /// ``` #[macro_export] macro_rules! dma_circular_descriptors { - ($tx_size:expr, $rx_size:expr) => { - $crate::dma_circular_descriptors_chunk_size!($tx_size, $rx_size, $crate::dma::CHUNK_SIZE) + ($rx_size:expr, $tx_size:expr) => { + $crate::dma_circular_descriptors_chunk_size!($rx_size, $tx_size, $crate::dma::CHUNK_SIZE) }; ($size:expr) => { @@ -357,26 +398,32 @@ macro_rules! dma_circular_descriptors { } /// Convenience macro to create DMA buffers and descriptors with specific chunk -/// size +/// size. /// /// ## Usage -/// ```rust,ignore -/// // TX and RX buffers are 32000 bytes - passing only one parameter makes TX and RX the same size -/// let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(32000, 32000, 4032); +/// ```rust,no_run +#[doc = crate::before_snippet!()] +/// use esp_hal::dma_buffers_chunk_size; +/// +/// // TX and RX buffers are 32000 bytes - passing only one parameter makes TX +/// // and RX the same size. +/// let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = +/// dma_buffers_chunk_size!(32000, 32000, 4032); +/// # } /// ``` #[macro_export] macro_rules! dma_buffers_chunk_size { - ($tx_size:expr, $rx_size:expr, $chunk_size:expr) => {{ - static mut TX_BUFFER: [u8; $tx_size] = [0u8; $tx_size]; + ($rx_size:expr, $tx_size:expr, $chunk_size:expr) => {{ static mut RX_BUFFER: [u8; $rx_size] = [0u8; $rx_size]; - let (mut tx_descriptors, mut rx_descriptors) = - $crate::dma_descriptors_chunk_size!($tx_size, $rx_size, $chunk_size); + static mut TX_BUFFER: [u8; $tx_size] = [0u8; $tx_size]; + let (mut rx_descriptors, mut tx_descriptors) = + $crate::dma_descriptors_chunk_size!($rx_size, $tx_size, $chunk_size); unsafe { ( - &mut TX_BUFFER, - tx_descriptors, &mut RX_BUFFER, rx_descriptors, + &mut TX_BUFFER, + tx_descriptors, ) } }}; @@ -387,27 +434,32 @@ macro_rules! dma_buffers_chunk_size { } /// Convenience macro to create circular DMA buffers and descriptors with -/// specific chunk size +/// specific chunk size. /// /// ## Usage -/// ```rust,ignore -/// // TX and RX buffers are 32000 bytes - passing only one parameter makes TX and RX the same size -/// let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = -/// dma_circular_buffers!(32000, 32000, 4032); +/// ```rust,no_run +#[doc = crate::before_snippet!()] +/// use esp_hal::dma_circular_buffers_chunk_size; +/// +/// // RX and TX buffers are 32000 bytes - passing only one parameter makes RX +/// // and TX the same size. +/// let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = +/// dma_circular_buffers_chunk_size!(32000, 32000, 4032); +/// # } /// ``` #[macro_export] macro_rules! dma_circular_buffers_chunk_size { - ($tx_size:expr, $rx_size:expr, $chunk_size:expr) => {{ - static mut TX_BUFFER: [u8; $tx_size] = [0u8; $tx_size]; + ($rx_size:expr, $tx_size:expr, $chunk_size:expr) => {{ static mut RX_BUFFER: [u8; $rx_size] = [0u8; $rx_size]; - let (mut tx_descriptors, mut rx_descriptors) = - $crate::dma_circular_descriptors_chunk_size!($tx_size, $rx_size, $chunk_size); + static mut TX_BUFFER: [u8; $tx_size] = [0u8; $tx_size]; + let (mut rx_descriptors, mut tx_descriptors) = + $crate::dma_circular_descriptors_chunk_size!($rx_size, $tx_size, $chunk_size); unsafe { ( - &mut TX_BUFFER, - tx_descriptors, &mut RX_BUFFER, rx_descriptors, + &mut TX_BUFFER, + tx_descriptors, ) } }}; @@ -420,24 +472,30 @@ macro_rules! dma_circular_buffers_chunk_size { /// Convenience macro to create DMA descriptors with specific chunk size /// /// ## Usage -/// ```rust,ignore -/// // Create TX and RX descriptors for transactions up to 32000 bytes - passing only one parameter assumes TX and RX are the same size -/// let (tx_descriptors, rx_descriptors) = dma_descriptors_chunk_size!(32000, 32000, 4032); +/// ```rust,no_run +#[doc = crate::before_snippet!()] +/// use esp_hal::dma_descriptors_chunk_size; +/// +/// // Create RX and TX descriptors for transactions up to 32000 bytes - passing +/// // only one parameter assumes RX and TX are the same size. +/// let (rx_descriptors, tx_descriptors) = +/// dma_descriptors_chunk_size!(32000, 32000, 4032); +/// # } /// ``` #[macro_export] macro_rules! dma_descriptors_chunk_size { - ($tx_size:expr, $rx_size:expr, $chunk_size:expr) => {{ + ($rx_size:expr, $tx_size:expr, $chunk_size:expr) => {{ // these will check for size at compile time - const _: () = assert!($chunk_size <= 4092, "chunk size must be <= 4092"); - const _: () = assert!($chunk_size > 0, "chunk size must be > 0"); + const _: () = ::core::assert!($chunk_size <= 4092, "chunk size must be <= 4092"); + const _: () = ::core::assert!($chunk_size > 0, "chunk size must be > 0"); - static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; - ($tx_size + $chunk_size - 1) / $chunk_size] = - [$crate::dma::DmaDescriptor::EMPTY; ($tx_size + $chunk_size - 1) / $chunk_size]; static mut RX_DESCRIPTORS: [$crate::dma::DmaDescriptor; ($rx_size + $chunk_size - 1) / $chunk_size] = [$crate::dma::DmaDescriptor::EMPTY; ($rx_size + $chunk_size - 1) / $chunk_size]; - unsafe { (&mut TX_DESCRIPTORS, &mut RX_DESCRIPTORS) } + static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; + ($tx_size + $chunk_size - 1) / $chunk_size] = + [$crate::dma::DmaDescriptor::EMPTY; ($tx_size + $chunk_size - 1) / $chunk_size]; + unsafe { (&mut RX_DESCRIPTORS, &mut TX_DESCRIPTORS) } }}; ($size:expr, $chunk_size:expr) => { @@ -449,34 +507,40 @@ macro_rules! dma_descriptors_chunk_size { /// size /// /// ## Usage -/// ```rust,ignore -/// // Create TX and RX descriptors for transactions up to 32000 bytes - passing only one parameter assumes TX and RX are the same size -/// let (tx_descriptors, rx_descriptors) = dma_circular_descriptors!(32000, 32000, 4032); +/// ```rust,no_run +#[doc = crate::before_snippet!()] +/// use esp_hal::dma_circular_descriptors_chunk_size; +/// +/// // Create RX and TX descriptors for transactions up to 32000 bytes - passing +/// // only one parameter assumes RX and TX are the same size. +/// let (rx_descriptors, tx_descriptors) = +/// dma_circular_descriptors_chunk_size!(32000, 32000, 4032); +/// # } /// ``` #[macro_export] macro_rules! dma_circular_descriptors_chunk_size { - ($tx_size:expr, $rx_size:expr, $chunk_size:expr) => {{ + ($rx_size:expr, $tx_size:expr, $chunk_size:expr) => {{ // these will check for size at compile time - const _: () = assert!($chunk_size <= 4092, "chunk size must be <= 4092"); - const _: () = assert!($chunk_size > 0, "chunk size must be > 0"); + const _: () = ::core::assert!($chunk_size <= 4092, "chunk size must be <= 4092"); + const _: () = ::core::assert!($chunk_size > 0, "chunk size must be > 0"); - const tx_descriptor_len: usize = if $tx_size > $chunk_size * 2 { - ($tx_size + $chunk_size - 1) / $chunk_size + const rx_descriptor_len: usize = if $rx_size > $chunk_size * 2 { + ($rx_size + $chunk_size - 1) / $chunk_size } else { 3 }; - const rx_descriptor_len: usize = if $rx_size > $chunk_size * 2 { - ($rx_size + $chunk_size - 1) / $chunk_size + const tx_descriptor_len: usize = if $tx_size > $chunk_size * 2 { + ($tx_size + $chunk_size - 1) / $chunk_size } else { 3 }; - static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; tx_descriptor_len] = - [$crate::dma::DmaDescriptor::EMPTY; tx_descriptor_len]; static mut RX_DESCRIPTORS: [$crate::dma::DmaDescriptor; rx_descriptor_len] = [$crate::dma::DmaDescriptor::EMPTY; rx_descriptor_len]; - unsafe { (&mut TX_DESCRIPTORS, &mut RX_DESCRIPTORS) } + static mut TX_DESCRIPTORS: [$crate::dma::DmaDescriptor; tx_descriptor_len] = + [$crate::dma::DmaDescriptor::EMPTY; tx_descriptor_len]; + unsafe { (&mut RX_DESCRIPTORS, &mut TX_DESCRIPTORS) } }}; ($size:expr, $chunk_size:expr) => { @@ -494,8 +558,7 @@ pub enum DmaError { OutOfDescriptors, /// DescriptorError the DMA rejected the descriptor configuration. This /// could be because the source address of the data is not in RAM. Ensure - /// your source data is in a valid address space, or try using - /// [`crate::FlashSafeDma`] wrapper. + /// your source data is in a valid address space. DescriptorError, /// The available free buffer is less than the amount of data to push Overflow, @@ -511,17 +574,26 @@ pub enum DmaError { #[cfg(gdma)] #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] pub enum DmaPriority { + /// The lowest priority level (Priority 0). Priority0 = 0, + /// Priority level 1. Priority1 = 1, + /// Priority level 2. Priority2 = 2, + /// Priority level 3. Priority3 = 3, + /// Priority level 4. Priority4 = 4, + /// Priority level 5. Priority5 = 5, + /// Priority level 6. Priority6 = 6, + /// Priority level 7. Priority7 = 7, + /// Priority level 8. Priority8 = 8, + /// The highest priority level (Priority 9). Priority9 = 9, } @@ -530,8 +602,8 @@ pub enum DmaPriority { #[cfg(pdma)] #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] pub enum DmaPriority { + /// The lowest priority level (Priority 0). Priority0 = 0, } @@ -848,10 +920,12 @@ impl DescriptorChain { /// Block size for transfers to/from psram #[derive(Copy, Clone, Debug, PartialEq)] -#[allow(missing_docs)] pub enum DmaExtMemBKSize { + /// External memory block size of 16 bytes. Size16 = 0, + /// External memory block size of 32 bytes. Size32 = 1, + /// External memory block size of 64 bytes. Size64 = 2, } @@ -1113,6 +1187,12 @@ pub trait RxPrivate: crate::private::Sealed { chain: &DescriptorChain, ) -> Result<(), DmaError>; + unsafe fn prepare_transfer( + &mut self, + peri: DmaPeripheral, + first_desc: *mut DmaDescriptor, + ) -> Result<(), DmaError>; + fn start_transfer(&mut self) -> Result<(), DmaError>; #[cfg(esp32s3)] @@ -1168,7 +1248,6 @@ pub trait RxPrivate: crate::private::Sealed { fn clear_interrupts(&self); - #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; } @@ -1188,14 +1267,14 @@ where unsafe fn prepare_transfer_without_start( &mut self, - descriptors: &DescriptorChain, + first_desc: *mut DmaDescriptor, peri: DmaPeripheral, ) -> Result<(), DmaError> { compiler_fence(core::sync::atomic::Ordering::SeqCst); R::clear_in_interrupts(); R::reset_in(); - R::set_in_descriptors(descriptors.first() as u32); + R::set_in_descriptors(first_desc as u32); R::set_in_peripheral(peri as u8); Ok(()) @@ -1215,7 +1294,6 @@ where R::is_in_done() } - #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; } @@ -1289,7 +1367,22 @@ where } } - self.rx_impl.prepare_transfer_without_start(chain, peri) + self.rx_impl + .prepare_transfer_without_start(chain.first() as _, peri) + } + + unsafe fn prepare_transfer( + &mut self, + peri: DmaPeripheral, + first_desc: *mut DmaDescriptor, + ) -> Result<(), DmaError> { + // TODO: Figure out burst mode for DmaBuf. + if self.burst_mode { + return Err(DmaError::InvalidAlignment); + } + + self.rx_impl + .prepare_transfer_without_start(first_desc, peri) } fn start_transfer(&mut self) -> Result<(), DmaError> { @@ -1398,7 +1491,6 @@ where CH::Channel::clear_in_interrupts(); } - #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { CH::Rx::waker() } @@ -1417,6 +1509,12 @@ pub trait TxPrivate: crate::private::Sealed { chain: &DescriptorChain, ) -> Result<(), DmaError>; + unsafe fn prepare_transfer( + &mut self, + peri: DmaPeripheral, + desc: *mut DmaDescriptor, + ) -> Result<(), DmaError>; + fn start_transfer(&mut self) -> Result<(), DmaError>; #[cfg(esp32s3)] @@ -1450,7 +1548,6 @@ pub trait TxPrivate: crate::private::Sealed { fn clear_interrupts(&self); - #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; fn descriptors_handled(&self) -> bool; @@ -1472,14 +1569,14 @@ where unsafe fn prepare_transfer_without_start( &mut self, - descriptors: &DescriptorChain, + first_desc: *mut DmaDescriptor, peri: DmaPeripheral, ) -> Result<(), DmaError> { compiler_fence(core::sync::atomic::Ordering::SeqCst); R::clear_out_interrupts(); R::reset_out(); - R::set_out_descriptors(descriptors.first() as u32); + R::set_out_descriptors(first_desc as u32); R::set_out_peripheral(peri as u8); Ok(()) @@ -1531,7 +1628,6 @@ where R::last_out_dscr_address() } - #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; } @@ -1596,7 +1692,16 @@ where crate::soc::cache_writeback_addr(des.buffer as u32, des.size() as u32); } } - self.tx_impl.prepare_transfer_without_start(chain, peri) + self.tx_impl + .prepare_transfer_without_start(chain.first() as _, peri) + } + + unsafe fn prepare_transfer( + &mut self, + peri: DmaPeripheral, + desc: *mut DmaDescriptor, + ) -> Result<(), DmaError> { + self.tx_impl.prepare_transfer_without_start(desc, peri) } fn start_transfer(&mut self) -> Result<(), DmaError> { @@ -1648,7 +1753,6 @@ where CH::Channel::has_out_descriptor_error() } - #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { CH::Tx::waker() } @@ -1768,10 +1872,10 @@ where CH: DmaChannel, MODE: Mode, { - /// TX half of the channel - pub tx: ChannelTx<'d, CH>, /// RX half of the channel pub rx: ChannelRx<'d, CH>, + /// TX half of the channel + pub tx: ChannelTx<'d, CH>, phantom: PhantomData, } @@ -1779,7 +1883,7 @@ impl<'d, C> Channel<'d, C, crate::Blocking> where C: DmaChannel, { - /// Sets the interrupt handler for TX and RX interrupts, enables them + /// Sets the interrupt handler for RX and TX interrupts, enables them /// with [crate::interrupt::Priority::max()] /// /// Interrupts are not enabled at the peripheral level here. @@ -1791,8 +1895,8 @@ where pub fn listen(&mut self, interrupts: EnumSet) { for interrupt in interrupts { match interrupt { - DmaInterrupt::TxDone => self.tx.listen_ch_out_done(), DmaInterrupt::RxDone => self.rx.listen_ch_in_done(), + DmaInterrupt::TxDone => self.tx.listen_ch_out_done(), } } } @@ -1801,8 +1905,8 @@ where pub fn unlisten(&mut self, interrupts: EnumSet) { for interrupt in interrupts { match interrupt { - DmaInterrupt::TxDone => self.tx.unlisten_ch_out_done(), DmaInterrupt::RxDone => self.rx.unlisten_ch_in_done(), + DmaInterrupt::TxDone => self.tx.unlisten_ch_out_done(), } } } @@ -1810,12 +1914,12 @@ where /// Gets asserted interrupts pub fn interrupts(&mut self) -> EnumSet { let mut res = EnumSet::new(); - if self.tx.is_done() { - res.insert(DmaInterrupt::TxDone); - } if self.rx.is_done() { res.insert(DmaInterrupt::RxDone); } + if self.tx.is_done() { + res.insert(DmaInterrupt::TxDone); + } res } @@ -1823,10 +1927,523 @@ where pub fn clear_interrupts(&mut self, interrupts: EnumSet) { for interrupt in interrupts { match interrupt { - DmaInterrupt::TxDone => self.tx.clear_ch_out_done(), DmaInterrupt::RxDone => self.rx.clear_ch_in_done(), + DmaInterrupt::TxDone => self.tx.clear_ch_out_done(), + } + } + } +} + +/// Error returned from Dma[Rx|Tx|RxTx]Buf operations. +#[derive(Debug)] +pub enum DmaBufError { + /// More descriptors are needed for the buffer size + InsufficientDescriptors, + /// Descriptors or buffers are not located in a supported memory region + UnsupportedMemoryRegion, +} + +/// DMA transmit buffer +/// +/// This is a contiguous buffer linked together by DMA descriptors of length +/// 4092. It can only be used for transmitting data to a peripheral's FIFO. +/// See [DmaRxBuf] for receiving data. +#[derive(Debug)] +pub struct DmaTxBuf { + descriptors: &'static mut [DmaDescriptor], + buffer: &'static mut [u8], +} + +impl DmaTxBuf { + /// Creates a new [DmaTxBuf] from some descriptors and a buffer. + /// + /// There must be enough descriptors for the provided buffer. + /// Each descriptor can handle 4092 bytes worth of buffer. + /// + /// Both the descriptors and buffer must be in DMA-capable memory. + /// Only DRAM is supported. + pub fn new( + descriptors: &'static mut [DmaDescriptor], + buffer: &'static mut [u8], + ) -> Result { + let min_descriptors = buffer.len().div_ceil(CHUNK_SIZE); + if descriptors.len() < min_descriptors { + return Err(DmaBufError::InsufficientDescriptors); + } + + if !is_slice_in_dram(descriptors) || !is_slice_in_dram(buffer) { + return Err(DmaBufError::UnsupportedMemoryRegion); + } + + // Setup size and buffer pointer as these will not change for the remainder of + // this object's lifetime + let chunk_iter = descriptors.iter_mut().zip(buffer.chunks_mut(CHUNK_SIZE)); + for (desc, chunk) in chunk_iter { + desc.set_size(chunk.len()); + desc.buffer = chunk.as_mut_ptr(); + } + + let mut buf = Self { + descriptors, + buffer, + }; + buf.set_length(buf.capacity()); + + Ok(buf) + } + + /// Consume the buf, returning the descriptors and buffer. + pub fn split(self) -> (&'static mut [DmaDescriptor], &'static mut [u8]) { + (self.descriptors, self.buffer) + } + + /// Returns the size of the underlying buffer + pub fn capacity(&self) -> usize { + self.buffer.len() + } + + /// Return the number of bytes that would be transmitted by this buf. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + let mut result = 0; + for desc in self.descriptors.iter() { + result += desc.len(); + if desc.next.is_null() { + break; + } + } + result + } + + /// Reset the descriptors to only transmit `len` amount of bytes from this + /// buf. + /// + /// The number of bytes in data must be less than or equal to the buffer + /// size. + pub fn set_length(&mut self, len: usize) { + assert!(len <= self.buffer.len()); + + // Get the minimum number of descriptors needed for this length of data. + let descriptor_count = len.div_ceil(CHUNK_SIZE).max(1); + let required_descriptors = &mut self.descriptors[0..descriptor_count]; + + // Link up the relevant descriptors. + let mut next = core::ptr::null_mut(); + for desc in required_descriptors.iter_mut().rev() { + desc.next = next; + next = desc; + } + + let mut remaining_length = len; + for desc in required_descriptors.iter_mut() { + // As this is a simple dma buffer implementation we won't + // be making use of this feature. + desc.set_suc_eof(false); + + // This isn't strictly needed for this simple implementation, + // but it is useful for debugging. + desc.set_owner(Owner::Dma); + + let chunk_size = min(remaining_length, desc.flags.size() as usize); + desc.set_length(chunk_size); + remaining_length -= chunk_size; + } + debug_assert_eq!(remaining_length, 0); + + required_descriptors.last_mut().unwrap().set_suc_eof(true); + } + + /// Fills the TX buffer with the bytes provided in `data` and reset the + /// descriptors to only cover the filled section. + /// + /// The number of bytes in data must be less than or equal to the buffer + /// size. + pub fn fill(&mut self, data: &[u8]) { + self.set_length(data.len()); + self.as_mut_slice()[..data.len()].copy_from_slice(data); + } + + /// Returns the buf as a mutable slice than can be written. + pub fn as_mut_slice(&mut self) -> &mut [u8] { + &mut self.buffer[..] + } + + /// Returns the buf as a slice than can be read. + pub fn as_slice(&self) -> &[u8] { + self.buffer + } + + pub(crate) fn first(&self) -> *mut DmaDescriptor { + self.descriptors.as_ptr() as _ + } +} + +/// DMA receive buffer +/// +/// This is a contiguous buffer linked together by DMA descriptors of length +/// 4092. It can only be used for receiving data from a peripheral's FIFO. +/// See [DmaTxBuf] for transmitting data. +pub struct DmaRxBuf { + descriptors: &'static mut [DmaDescriptor], + buffer: &'static mut [u8], +} + +impl DmaRxBuf { + /// Creates a new [DmaRxBuf] from some descriptors and a buffer. + /// + /// There must be enough descriptors for the provided buffer. + /// Each descriptor can handle 4092 bytes worth of buffer. + /// + /// Both the descriptors and buffer must be in DMA-capable memory. + /// Only DRAM is supported. + pub fn new( + descriptors: &'static mut [DmaDescriptor], + buffer: &'static mut [u8], + ) -> Result { + let min_descriptors = buffer.len().div_ceil(CHUNK_SIZE); + if descriptors.len() < min_descriptors { + return Err(DmaBufError::InsufficientDescriptors); + } + + if !is_slice_in_dram(descriptors) || !is_slice_in_dram(buffer) { + return Err(DmaBufError::UnsupportedMemoryRegion); + } + + // Setup size and buffer pointer as these will not change for the remainder of + // this object's lifetime + let chunk_iter = descriptors.iter_mut().zip(buffer.chunks_mut(CHUNK_SIZE)); + for (desc, chunk) in chunk_iter { + desc.set_size(chunk.len()); + desc.buffer = chunk.as_mut_ptr(); + } + + let mut buf = Self { + descriptors, + buffer, + }; + + buf.set_length(buf.capacity()); + + Ok(buf) + } + + /// Consume the buf, returning the descriptors and buffer. + pub fn split(self) -> (&'static mut [DmaDescriptor], &'static mut [u8]) { + (self.descriptors, self.buffer) + } + + /// Returns the size of the underlying buffer + pub fn capacity(&self) -> usize { + self.buffer.len() + } + + /// Returns the maximum number of bytes that this buf has been configured to + /// receive. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + let mut result = 0; + for desc in self.descriptors.iter() { + result += desc.flags.size() as usize; + if desc.next.is_null() { + break; + } + } + result + } + + /// Reset the descriptors to only receive `len` amount of bytes into this + /// buf. + /// + /// The number of bytes in data must be less than or equal to the buffer + /// size. + pub fn set_length(&mut self, len: usize) { + assert!(len <= self.buffer.len()); + + // Get the minimum number of descriptors needed for this length of data. + let descriptor_count = len.div_ceil(CHUNK_SIZE).max(1); + let required_descriptors = &mut self.descriptors[..descriptor_count]; + + // Link up the relevant descriptors. + let mut next = core::ptr::null_mut(); + for desc in required_descriptors.iter_mut().rev() { + desc.next = next; + next = desc; + } + + // Get required part of the buffer. + let mut remaining_length = len; + for desc in required_descriptors.iter_mut() { + // Clear this to allow hardware to set it when the peripheral returns an EOF + // bit. + desc.set_suc_eof(false); + + // This isn't strictly needed for this simple implementation, + // but it is useful for debugging. + desc.set_owner(Owner::Dma); + + // Clear this to allow hardware to set it when it's + // done receiving data for this descriptor. + desc.set_length(0); + + let chunk_size = min(CHUNK_SIZE, remaining_length); + desc.set_size(chunk_size); + remaining_length -= chunk_size; + } + debug_assert_eq!(remaining_length, 0); + } + + /// Returns the entire underlying buffer as a slice than can be read. + pub fn as_slice(&self) -> &[u8] { + self.buffer + } + + /// Returns the entire underlying buffer as a slice than can be written. + pub fn as_mut_slice(&mut self) -> &mut [u8] { + &mut self.buffer[..] + } + + /// Return the number of bytes that was received by this buf. + pub fn number_of_received_bytes(&self) -> usize { + let mut result = 0; + for desc in self.descriptors.iter() { + result += desc.len(); + if desc.next.is_null() { + break; + } + } + result + } + + /// Reads the received data into the provided `buf`. + /// + /// If `buf.len()` is less than the amount of received data then only the + /// first `buf.len()` bytes of received data is written into `buf`. + /// + /// Returns the number of bytes in written to `buf`. + pub fn read_received_data(&self, buf: &mut [u8]) -> usize { + let mut remaining = &mut buf[..]; + + let mut buffer_offset = 0; + for desc in self.descriptors.iter() { + if remaining.is_empty() { + break; + } + + let amount_to_copy = min(desc.len(), remaining.len()); + + let (to_fill, to_remain) = remaining.split_at_mut(amount_to_copy); + to_fill.copy_from_slice(&self.buffer[buffer_offset..][..amount_to_copy]); + remaining = to_remain; + + if desc.next.is_null() { + break; } + buffer_offset += desc.flags.size() as usize; } + + let remaining_bytes = remaining.len(); + buf.len() - remaining_bytes + } + + /// Returns the received data as an iterator of slices. + pub fn received_data(&self) -> impl Iterator { + let mut descriptors = self.descriptors.iter(); + #[allow(clippy::redundant_slicing)] // Clippy can't see why this is needed. + let mut buf = &self.buffer[..]; + + core::iter::from_fn(move || { + let mut chunk_size = 0; + let mut skip_size = 0; + while let Some(desc) = descriptors.next() { + chunk_size += desc.len(); + skip_size += desc.flags.size() as usize; + + // If this is the end of the linked list, we can skip the remaining descriptors. + if desc.next.is_null() { + while descriptors.next().is_some() { + // Drain the iterator so the next call to from_fn return + // None. + } + break; + } + + // This typically happens when the DMA gets an EOF bit from the peripheral. + // It can also happen if the DMA is restarted. + if desc.len() < desc.flags.size() as usize { + break; + } + } + + if chunk_size == 0 { + return None; + } + + let chunk = &buf[..chunk_size]; + buf = &buf[skip_size..]; + Some(chunk) + }) + } + + pub(crate) fn first(&self) -> *mut DmaDescriptor { + self.descriptors.as_ptr() as _ + } +} + +/// DMA transmit and receive buffer. +/// +/// This is a (single) contiguous buffer linked together by two sets of DMA +/// descriptors of length 4092 each. +/// It can be used for simultaneously transmitting to and receiving from a +/// peripheral's FIFO. These are typically full-duplex transfers. +pub struct DmaRxTxBuf { + rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], + buffer: &'static mut [u8], +} + +impl DmaRxTxBuf { + /// Creates a new [DmaRxTxBuf] from some descriptors and a buffer. + /// + /// There must be enough descriptors for the provided buffer. + /// Each descriptor can handle 4092 bytes worth of buffer. + /// + /// Both the descriptors and buffer must be in DMA-capable memory. + /// Only DRAM is supported. + pub fn new( + rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], + buffer: &'static mut [u8], + ) -> Result { + let min_descriptors = buffer.len().div_ceil(CHUNK_SIZE); + if rx_descriptors.len() < min_descriptors { + return Err(DmaBufError::InsufficientDescriptors); + } + if tx_descriptors.len() < min_descriptors { + return Err(DmaBufError::InsufficientDescriptors); + } + + if !is_slice_in_dram(rx_descriptors) + || !is_slice_in_dram(tx_descriptors) + || !is_slice_in_dram(buffer) + { + return Err(DmaBufError::UnsupportedMemoryRegion); + } + + // Reset the provided descriptors + rx_descriptors.fill(DmaDescriptor::EMPTY); + tx_descriptors.fill(DmaDescriptor::EMPTY); + + let descriptors = tx_descriptors.iter_mut().zip(rx_descriptors.iter_mut()); + let chunks = buffer.chunks_mut(CHUNK_SIZE); + + for ((rx_desc, tx_desc), chunk) in descriptors.zip(chunks) { + rx_desc.set_size(chunk.len()); + rx_desc.buffer = chunk.as_mut_ptr(); + tx_desc.set_size(chunk.len()); + tx_desc.buffer = chunk.as_mut_ptr(); + } + + let mut buf = Self { + rx_descriptors, + tx_descriptors, + buffer, + }; + buf.set_length(buf.capacity()); + + Ok(buf) + } + + /// Consume the buf, returning the rx descriptors, tx descriptors and + /// buffer. + pub fn split( + self, + ) -> ( + &'static mut [DmaDescriptor], + &'static mut [DmaDescriptor], + &'static mut [u8], + ) { + (self.rx_descriptors, self.tx_descriptors, self.buffer) + } + + /// Return the size of the underlying buffer. + pub fn capacity(&self) -> usize { + self.buffer.len() + } + + /// Returns the entire buf as a slice than can be read. + pub fn as_slice(&self) -> &[u8] { + self.buffer + } + + /// Returns the entire buf as a slice than can be written. + pub fn as_slice_mut(&mut self) -> &mut [u8] { + &mut self.buffer[..] + } + + /// Reset the descriptors to only transmit/receive `len` amount of bytes + /// with this buf. + /// + /// `len` must be less than or equal to the buffer size. + pub fn set_length(&mut self, len: usize) { + assert!(len <= self.buffer.len()); + + // Get the minimum number of descriptors needed for this length of data. + let descriptor_count = len.div_ceil(CHUNK_SIZE).max(1); + + let relevant_rx_descriptors = &mut self.rx_descriptors[..descriptor_count]; + let relevant_tx_descriptors = &mut self.tx_descriptors[..descriptor_count]; + + // Link up the relevant descriptors. + for descriptors in [ + &mut relevant_rx_descriptors[..], + &mut relevant_tx_descriptors[..], + ] { + let mut next = core::ptr::null_mut(); + for desc in descriptors.iter_mut().rev() { + desc.next = next; + next = desc; + } + } + + let mut remaining_length = len; + for desc in relevant_tx_descriptors.iter_mut() { + // As this is a simple dma buffer implementation we won't + // be making use of this feature. + desc.set_suc_eof(false); + + // This isn't strictly needed for this simple implementation, + // but it is useful for debugging. + desc.set_owner(Owner::Dma); + + let chunk_size = min(desc.size(), remaining_length); + desc.set_length(chunk_size); + remaining_length -= chunk_size; + } + debug_assert_eq!(remaining_length, 0); + relevant_tx_descriptors + .last_mut() + .unwrap() + .set_suc_eof(true); + + let mut remaining_length = len; + for desc in relevant_rx_descriptors.iter_mut() { + // Clear this to allow hardware to set it when the peripheral returns an EOF + // bit. + desc.set_suc_eof(false); + + // This isn't strictly needed for this simple implementation, + // but it is useful for debugging. + desc.set_owner(Owner::Dma); + + // Clear this to allow hardware to set it when it is + // done receiving data for this descriptor. + desc.set_length(0); + + let chunk_size = min(CHUNK_SIZE, remaining_length); + desc.set_size(chunk_size); + remaining_length -= chunk_size; + } + debug_assert_eq!(remaining_length, 0); } } @@ -1844,7 +2461,7 @@ pub(crate) mod dma_private { /// /// Please note: This is called in the transfer's `wait` function _and_ /// by it's [Drop] implementation. - fn peripheral_wait_dma(&mut self, is_tx: bool, is_rx: bool); + fn peripheral_wait_dma(&mut self, is_rx: bool, is_tx: bool); /// Only used by circular DMA transfers in both, the `stop` function /// _and_ it's [Drop] implementation @@ -1892,7 +2509,7 @@ where /// Wait for the transfer to finish. pub fn wait(self) -> Result<(), DmaError> { - self.instance.peripheral_wait_dma(true, false); + self.instance.peripheral_wait_dma(false, true); if self.instance.tx().has_error() { Err(DmaError::DescriptorError) @@ -1940,7 +2557,7 @@ where /// Wait for the transfer to finish. pub fn wait(self) -> Result<(), DmaError> { - self.instance.peripheral_wait_dma(false, true); + self.instance.peripheral_wait_dma(true, false); if self.instance.rx().has_error() { Err(DmaError::DescriptorError) @@ -1960,7 +2577,7 @@ where I: dma_private::DmaSupportRx, { fn drop(&mut self) { - self.instance.peripheral_wait_dma(false, true); + self.instance.peripheral_wait_dma(true, false); } } @@ -1971,17 +2588,18 @@ where /// Never use [core::mem::forget] on an in-progress transfer #[non_exhaustive] #[must_use] -pub struct DmaTransferTxRx<'a, I> +pub struct DmaTransferRxTx<'a, I> where I: dma_private::DmaSupportTx + dma_private::DmaSupportRx, { instance: &'a mut I, } -impl<'a, I> DmaTransferTxRx<'a, I> +impl<'a, I> DmaTransferRxTx<'a, I> where I: dma_private::DmaSupportTx + dma_private::DmaSupportRx, { + #[allow(dead_code)] pub(crate) fn new(instance: &'a mut I) -> Self { Self { instance } } @@ -2003,241 +2621,9 @@ where } } -impl<'a, I> Drop for DmaTransferTxRx<'a, I> -where - I: dma_private::DmaSupportTx + dma_private::DmaSupportRx, -{ - fn drop(&mut self) { - self.instance.peripheral_wait_dma(true, true); - } -} - -/// DMA transaction for TX transfers with moved-in/moved-out peripheral and -/// buffer -/// -/// # Safety -/// -/// Never use [core::mem::forget] on an in-progress transfer -#[non_exhaustive] -#[must_use] -pub struct DmaTransferTxOwned -where - I: dma_private::DmaSupportTx, - T: ReadBuffer, -{ - instance: I, - tx_buffer: T, -} - -impl DmaTransferTxOwned -where - I: dma_private::DmaSupportTx, - T: ReadBuffer, -{ - pub(crate) fn new(instance: I, tx_buffer: T) -> Self { - Self { - instance, - tx_buffer, - } - } - - /// Wait for the transfer to finish and return the peripheral and the - /// buffer. - pub fn wait(mut self) -> Result<(I, T), (DmaError, I, T)> { - self.instance.peripheral_wait_dma(true, false); - - let err = self.instance.tx().has_error(); - - // We need to have a `Drop` implementation, because we accept - // managed buffers that can free their memory on drop. Because of that - // we can't move out of the `Transfer`'s fields, so we use `ptr::read` - // and `mem::forget`. - // - // NOTE(unsafe) There is no panic branch between getting the resources - // and forgetting `self`. - - let (instance, tx_buffer) = unsafe { - let instance = core::ptr::read(&self.instance); - let tx_buffer = core::ptr::read(&self.tx_buffer); - core::mem::forget(self); - - (instance, tx_buffer) - }; - - if err { - Err((DmaError::DescriptorError, instance, tx_buffer)) - } else { - Ok((instance, tx_buffer)) - } - } - - /// Check if the transfer is finished. - pub fn is_done(&mut self) -> bool { - self.instance.tx().is_done() - } -} - -impl Drop for DmaTransferTxOwned -where - I: dma_private::DmaSupportTx, - T: ReadBuffer, -{ - fn drop(&mut self) { - self.instance.peripheral_wait_dma(true, false); - } -} - -/// DMA transaction for RX transfers with moved-in/moved-out peripheral and -/// buffer -/// -/// # Safety -/// -/// Never use [core::mem::forget] on an in-progress transfer -#[non_exhaustive] -#[must_use] -pub struct DmaTransferRxOwned -where - I: dma_private::DmaSupportRx, - R: WriteBuffer, -{ - instance: I, - rx_buffer: R, -} - -impl DmaTransferRxOwned -where - I: dma_private::DmaSupportRx, - R: WriteBuffer, -{ - pub(crate) fn new(instance: I, rx_buffer: R) -> Self { - Self { - instance, - rx_buffer, - } - } - - /// Wait for the transfer to finish and return the peripheral and the - /// buffers. - pub fn wait(mut self) -> Result<(I, R), (DmaError, I, R)> { - self.instance.peripheral_wait_dma(false, true); - - let err = self.instance.rx().has_error(); - - // We need to have a `Drop` implementation, because we accept - // managed buffers that can free their memory on drop. Because of that - // we can't move out of the `Transfer`'s fields, so we use `ptr::read` - // and `mem::forget`. - // - // NOTE(unsafe) There is no panic branch between getting the resources - // and forgetting `self`. - - let (instance, rx_buffer) = unsafe { - let instance = core::ptr::read(&self.instance); - let rx_buffer = core::ptr::read(&self.rx_buffer); - core::mem::forget(self); - - (instance, rx_buffer) - }; - - if err { - Err((DmaError::DescriptorError, instance, rx_buffer)) - } else { - Ok((instance, rx_buffer)) - } - } - - /// Check if the transfer is finished. - pub fn is_done(&mut self) -> bool { - self.instance.rx().is_done() - } -} - -impl Drop for DmaTransferRxOwned -where - I: dma_private::DmaSupportRx, - R: WriteBuffer, -{ - fn drop(&mut self) { - self.instance.peripheral_wait_dma(false, true); - } -} - -/// DMA transaction for TX+RX transfers with moved-in/moved-out peripheral and -/// buffers -/// -/// # Safety -/// -/// Never use [core::mem::forget] on an in-progress transfer -#[non_exhaustive] -#[must_use] -pub struct DmaTransferTxRxOwned -where - I: dma_private::DmaSupportTx + dma_private::DmaSupportRx, - T: ReadBuffer, - R: WriteBuffer, -{ - instance: I, - tx_buffer: T, - rx_buffer: R, -} - -impl DmaTransferTxRxOwned -where - I: dma_private::DmaSupportTx + dma_private::DmaSupportRx, - T: ReadBuffer, - R: WriteBuffer, -{ - pub(crate) fn new(instance: I, tx_buffer: T, rx_buffer: R) -> Self { - Self { - instance, - tx_buffer, - rx_buffer, - } - } - - /// Wait for the transfer to finish and return the peripheral and the - /// buffers. - #[allow(clippy::type_complexity)] - pub fn wait(mut self) -> Result<(I, T, R), (DmaError, I, T, R)> { - self.instance.peripheral_wait_dma(true, true); - - let err = self.instance.tx().has_error() || self.instance.rx().has_error(); - - // We need to have a `Drop` implementation, because we accept - // managed buffers that can free their memory on drop. Because of that - // we can't move out of the `Transfer`'s fields, so we use `ptr::read` - // and `mem::forget`. - // - // NOTE(unsafe) There is no panic branch between getting the resources - // and forgetting `self`. - - let (instance, tx_buffer, rx_buffer) = unsafe { - let instance = core::ptr::read(&self.instance); - let tx_buffer = core::ptr::read(&self.tx_buffer); - let rx_buffer = core::ptr::read(&self.rx_buffer); - core::mem::forget(self); - - (instance, tx_buffer, rx_buffer) - }; - - if err { - Err((DmaError::DescriptorError, instance, tx_buffer, rx_buffer)) - } else { - Ok((instance, tx_buffer, rx_buffer)) - } - } - - /// Check if the transfer is finished. - pub fn is_done(&mut self) -> bool { - self.instance.tx().is_done() && self.instance.rx().is_done() - } -} - -impl Drop for DmaTransferTxRxOwned +impl<'a, I> Drop for DmaTransferRxTx<'a, I> where I: dma_private::DmaSupportTx + dma_private::DmaSupportRx, - T: ReadBuffer, - R: WriteBuffer, { fn drop(&mut self) { self.instance.peripheral_wait_dma(true, true); @@ -2369,18 +2755,17 @@ where } } -#[cfg(feature = "async")] pub(crate) mod asynch { use core::task::Poll; use super::*; + #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct DmaTxFuture<'a, TX> where TX: Tx, { pub(crate) tx: &'a mut TX, - _a: (), } impl<'a, TX> DmaTxFuture<'a, TX> @@ -2388,11 +2773,7 @@ pub(crate) mod asynch { TX: Tx, { pub fn new(tx: &'a mut TX) -> Self { - Self { tx, _a: () } - } - - pub fn tx(&mut self) -> &mut TX { - self.tx + Self { tx } } } @@ -2431,12 +2812,12 @@ pub(crate) mod asynch { } } + #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct DmaRxFuture<'a, RX> where RX: Rx, { pub(crate) rx: &'a mut RX, - _a: (), } impl<'a, RX> DmaRxFuture<'a, RX> @@ -2444,9 +2825,10 @@ pub(crate) mod asynch { RX: Rx, { pub fn new(rx: &'a mut RX) -> Self { - Self { rx, _a: () } + Self { rx } } + #[allow(dead_code)] // Dead on the C2 pub fn rx(&mut self) -> &mut RX { self.rx } diff --git a/esp-hal/src/dma/pdma.rs b/esp-hal/src/dma/pdma.rs index e4f702bbabd..051ab529d7f 100644 --- a/esp-hal/src/dma/pdma.rs +++ b/esp-hal/src/dma/pdma.rs @@ -356,7 +356,6 @@ macro_rules! ImplSpiChannel { impl $crate::private::Sealed for [] {} impl<'a> TxChannel<[]> for [] { - #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); &WAKER @@ -370,7 +369,6 @@ macro_rules! ImplSpiChannel { impl $crate::private::Sealed for [] {} impl<'a> RxChannel<[]> for [] { - #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); &WAKER @@ -408,7 +406,6 @@ macro_rules! ImplSpiChannel { /// /// Descriptors should be sized as `(CHUNK_SIZE + 4091) / 4092`. I.e., to /// transfer buffers of size `1..=4092`, you need 1 descriptor. - #[cfg(feature = "async")] pub fn configure_for_async<'a>( self, burst_mode: bool, @@ -752,7 +749,6 @@ macro_rules! ImplI2sChannel { impl $crate::private::Sealed for [] {} impl<'a> TxChannel<[]> for [] { - #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); &WAKER @@ -765,7 +761,6 @@ macro_rules! ImplI2sChannel { impl $crate::private::Sealed for [] {} impl<'a> RxChannel<[]> for [] { - #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); &WAKER @@ -802,7 +797,6 @@ macro_rules! ImplI2sChannel { /// /// Descriptors should be sized as `(CHUNK_SIZE + 4091) / 4092`. I.e., to /// transfer buffers of size `1..=4092`, you need 1 descriptor. - #[cfg(feature = "async")] pub fn configure_for_async<'a>( self, burst_mode: bool, diff --git a/esp-hal/src/ecc.rs b/esp-hal/src/ecc.rs index 43424bad9e6..10d0a17fac5 100644 --- a/esp-hal/src/ecc.rs +++ b/esp-hal/src/ecc.rs @@ -53,30 +53,47 @@ pub enum Error { PointNotOnSelectedCurve, } +/// Represents supported elliptic curves for cryptographic operations. pub enum EllipticCurve { + /// The P-192 elliptic curve, a 192-bit curve. P192 = 0, + /// The P-256 elliptic curve. a 256-bit curve. P256 = 1, } #[derive(Clone)] +/// Represents the operational modes for elliptic curve or modular arithmetic +/// computations. pub enum WorkMode { + /// Point multiplication mode. PointMultiMode = 0, #[cfg(esp32c2)] + /// Division mode. DivisionMode = 1, + /// Point verification mode. PointVerif = 2, + /// Point verification and multiplication mode. PointVerifMulti = 3, + /// Jacobian point multiplication mode. JacobianPointMulti = 4, #[cfg(esp32h2)] + /// Point addition mode. PointAdd = 5, + /// Jacobian point verification mode. JacobianPointVerif = 6, + /// Point verification and multiplication in Jacobian coordinates. PointVerifJacobianMulti = 7, #[cfg(esp32h2)] + /// Modular addition mode. ModAdd = 8, #[cfg(esp32h2)] + /// Modular subtraction mode. ModSub = 9, #[cfg(esp32h2)] + /// Modular multiplication mode. ModMulti = 10, #[cfg(esp32h2)] + /// Modular division mode. ModDiv = 11, } @@ -85,6 +102,7 @@ impl<'d> Ecc<'d, crate::Blocking> { pub fn new(ecc: impl Peripheral

+ 'd) -> Self { crate::into_ref!(ecc); + PeripheralClockControl::reset(PeripheralEnable::Ecc); PeripheralClockControl::enable(PeripheralEnable::Ecc); Self { @@ -108,6 +126,7 @@ impl<'d> InterruptConfigurable for Ecc<'d, crate::Blocking> { } impl<'d, DM: crate::Mode> Ecc<'d, DM> { + /// Resets the ECC peripheral. pub fn reset(&mut self) { self.ecc.mult_conf().reset() } diff --git a/esp-hal/src/etm.rs b/esp-hal/src/etm.rs index ea35a9cc70e..1e8322c1852 100644 --- a/esp-hal/src/etm.rs +++ b/esp-hal/src/etm.rs @@ -142,13 +142,18 @@ macro_rules! create_etm { /// Provides access to all the [EtmChannel] pub struct Etm<'d> { _peripheral: PeripheralRef<'d, crate::peripherals::SOC_ETM>, - $(pub [< channel $num >]: EtmChannel<$num>,)+ + $( + /// An individual ETM channel, identified by its index number. + pub [< channel $num >]: EtmChannel<$num>, + )+ } impl<'d> Etm<'d> { + /// Creates a new `Etm` instance. pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { crate::into_ref!(peripheral); + PeripheralClockControl::reset(crate::system::Peripheral::Etm); PeripheralClockControl::enable(crate::system::Peripheral::Etm); Self { diff --git a/esp-hal/src/gpio/any_pin.rs b/esp-hal/src/gpio/any_pin.rs index d5d58e95b4a..93c59da4d93 100644 --- a/esp-hal/src/gpio/any_pin.rs +++ b/esp-hal/src/gpio/any_pin.rs @@ -1,45 +1,28 @@ -//! Type-erased wrappers for GPIO pins. -//! These are useful to pass them into peripheral drivers. -//! -//! If you want a generic pin for GPIO input/output look into -//! [Output],[OutputOpenDrain], [Input] and [Flex]. - use super::*; -#[derive(Clone, Copy)] -enum Inverted { - NonInverted, - Inverted, -} - -impl Inverted { - fn is_inverted(&self) -> bool { - match self { - Inverted::NonInverted => false, - Inverted::Inverted => true, - } - } -} - -/// Generic pin wrapper for pins which can be Output or Input. +/// A type-erased GPIO pin, with additional configuration options. +/// +/// Note that accessing unsupported pin functions (e.g. trying to use an +/// input-only pin as output) will panic. pub struct AnyPin<'d> { pin: ErasedPin, - inverted: Inverted, + is_inverted: bool, _phantom: PhantomData<&'d ()>, } impl<'d> AnyPin<'d> { /// Create wrapper for the given pin. #[inline] - pub fn new( - pin: impl crate::peripheral::Peripheral

+ 'd, - ) -> Self { + pub fn new

(pin: impl crate::peripheral::Peripheral

+ 'd) -> Self + where + P: Pin, + { crate::into_ref!(pin); - let pin = pin.erased_pin(private::Internal); + let pin = pin.degrade_internal(private::Internal); Self { pin, - inverted: Inverted::NonInverted, + is_inverted: false, _phantom: PhantomData, } } @@ -47,15 +30,16 @@ impl<'d> AnyPin<'d> { /// Create wrapper for the given pin. The peripheral signal will be /// inverted. #[inline] - pub fn new_inverted( - pin: impl crate::peripheral::Peripheral

+ 'd, - ) -> Self { + pub fn new_inverted

(pin: impl crate::peripheral::Peripheral

+ 'd) -> Self + where + P: Pin, + { crate::into_ref!(pin); - let pin = pin.erased_pin(private::Internal); + let pin = pin.degrade_internal(private::Internal); Self { pin, - inverted: Inverted::Inverted, + is_inverted: true, _phantom: PhantomData, } } @@ -67,7 +51,7 @@ impl<'d> crate::peripheral::Peripheral for AnyPin<'d> { unsafe fn clone_unchecked(&mut self) -> Self::P { Self { pin: unsafe { self.pin.clone_unchecked() }, - inverted: self.inverted, + is_inverted: self.is_inverted, _phantom: PhantomData, } } @@ -79,6 +63,7 @@ impl<'d> Pin for AnyPin<'d> { delegate::delegate! { to self.pin { fn number(&self, _internal: private::Internal) -> u8; + fn degrade_internal(&self, _internal: private::Internal) -> ErasedPin; fn sleep_mode(&mut self, on: bool, _internal: private::Internal); fn set_alternate_function(&mut self, alternate: AlternateFunction, _internal: private::Internal); fn is_listening(&self, _internal: private::Internal) -> bool; @@ -93,7 +78,6 @@ impl<'d> Pin for AnyPin<'d> { fn unlisten(&mut self, _internal: private::Internal); fn is_interrupt_set(&self, _internal: private::Internal) -> bool; fn clear_interrupt(&mut self, _internal: private::Internal); - fn wakeup_enable(&mut self, enable: bool, event: WakeEvent, _internal: private::Internal); } } } @@ -117,17 +101,6 @@ impl<'d> OutputPin for AnyPin<'d> { } } - fn connect_peripheral_to_output(&mut self, signal: OutputSignal, _internal: private::Internal) { - self.pin.connect_peripheral_to_output_with_options( - signal, - self.inverted.is_inverted(), - false, - false, - self.inverted.is_inverted(), - private::Internal, - ); - } - fn connect_peripheral_to_output_with_options( &mut self, signal: OutputSignal, @@ -137,7 +110,7 @@ impl<'d> OutputPin for AnyPin<'d> { force_via_gpio_mux: bool, _internal: private::Internal, ) { - if self.inverted.is_inverted() { + if self.is_inverted { self.pin.connect_peripheral_to_output_with_options( signal, true, @@ -163,7 +136,6 @@ impl<'d> InputPin for AnyPin<'d> { delegate::delegate! { to self.pin { fn init_input(&self, pull_down: bool, pull_up: bool, _internal: private::Internal); - fn set_to_input(&mut self, _internal: private::Internal); fn enable_input(&mut self, on: bool, _internal: private::Internal); fn enable_input_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); fn is_input_high(&self, _internal: private::Internal) -> bool; @@ -171,15 +143,6 @@ impl<'d> InputPin for AnyPin<'d> { } } - fn connect_input_to_peripheral(&mut self, signal: InputSignal, _internal: private::Internal) { - self.pin.connect_input_to_peripheral_with_options( - signal, - self.inverted.is_inverted(), - self.inverted.is_inverted(), - private::Internal, - ); - } - fn connect_input_to_peripheral_with_options( &mut self, signal: InputSignal, @@ -187,7 +150,7 @@ impl<'d> InputPin for AnyPin<'d> { force_via_gpio_mux: bool, _internal: private::Internal, ) { - if self.inverted.is_inverted() { + if self.is_inverted { self.pin.connect_input_to_peripheral_with_options( signal, true, @@ -204,85 +167,3 @@ impl<'d> InputPin for AnyPin<'d> { } } } - -/// Generic pin wrapper for pins which can only be Input. -pub struct AnyInputOnlyPin<'d> { - pin: ErasedPin, - inverted: Inverted, - _phantom: PhantomData<&'d ()>, -} - -impl<'d> AnyInputOnlyPin<'d> { - /// Create wrapper for the given pin. - #[inline] - pub fn new( - pin: impl crate::peripheral::Peripheral

+ 'd, - ) -> Self { - crate::into_ref!(pin); - let pin = pin.erased_pin(private::Internal); - - Self { - pin, - inverted: Inverted::NonInverted, - _phantom: PhantomData, - } - } -} - -impl<'d> crate::peripheral::Peripheral for AnyInputOnlyPin<'d> { - type P = Self; - - unsafe fn clone_unchecked(&mut self) -> Self::P { - Self { - pin: unsafe { self.pin.clone_unchecked() }, - inverted: self.inverted, - _phantom: PhantomData, - } - } -} - -impl<'d> private::Sealed for AnyInputOnlyPin<'d> {} - -impl<'d> Pin for AnyInputOnlyPin<'d> { - delegate::delegate! { - to self.pin { - fn number(&self, _internal: private::Internal) -> u8; - fn sleep_mode(&mut self, on: bool, _internal: private::Internal); - fn set_alternate_function(&mut self, alternate: AlternateFunction, _internal: private::Internal); - fn is_listening(&self, _internal: private::Internal) -> bool; - fn listen_with_options( - &mut self, - event: Event, - int_enable: bool, - nmi_enable: bool, - wake_up_from_light_sleep: bool, - _internal: private::Internal, - ); - fn unlisten(&mut self, _internal: private::Internal); - fn is_interrupt_set(&self, _internal: private::Internal) -> bool; - fn clear_interrupt(&mut self, _internal: private::Internal); - fn wakeup_enable(&mut self, enable: bool, event: WakeEvent, _internal: private::Internal); - } - } -} - -impl<'d> InputPin for AnyInputOnlyPin<'d> { - delegate::delegate! { - to self.pin { - fn init_input(&self, pull_down: bool, pull_up: bool, _internal: private::Internal); - fn set_to_input(&mut self, _internal: private::Internal); - fn enable_input(&mut self, on: bool, _internal: private::Internal); - fn enable_input_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); - fn is_input_high(&self, _internal: private::Internal) -> bool; - fn connect_input_to_peripheral(&mut self, signal: InputSignal, _internal: private::Internal); - fn connect_input_to_peripheral_with_options( - &mut self, - signal: InputSignal, - invert: bool, - force_via_gpio_mux: bool, - _internal: private::Internal, - ); - fn disconnect_input_from_peripheral(&mut self, signal: InputSignal, _internal: private::Internal); - } - } -} diff --git a/esp-hal/src/gpio/dummy_pin.rs b/esp-hal/src/gpio/dummy_pin.rs index 6d919a5d666..0b79017aa4e 100644 --- a/esp-hal/src/gpio/dummy_pin.rs +++ b/esp-hal/src/gpio/dummy_pin.rs @@ -32,6 +32,10 @@ impl Pin for DummyPin { panic!("DummyPin not supported here!"); } + fn degrade_internal(&self, _: private::Internal) -> ErasedPin { + panic!("Can not type erase the DummyPin!"); + } + fn sleep_mode(&mut self, _on: bool, _: private::Internal) {} fn set_alternate_function(&mut self, _alternate: AlternateFunction, _: private::Internal) {} @@ -109,8 +113,6 @@ impl OutputPin for DummyPin { impl InputPin for DummyPin { fn init_input(&self, _pull_down: bool, _pull_up: bool, _: private::Internal) {} - fn set_to_input(&mut self, _: private::Internal) {} - fn enable_input(&mut self, _on: bool, _: private::Internal) {} fn enable_input_in_sleep_mode(&mut self, _on: bool, _: private::Internal) {} @@ -132,3 +134,50 @@ impl InputPin for DummyPin { fn disconnect_input_from_peripheral(&mut self, _signal: InputSignal, _: private::Internal) {} } + +impl embedded_hal_02::digital::v2::OutputPin for DummyPin { + type Error = core::convert::Infallible; + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_output_high(true, private::Internal); + Ok(()) + } + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_output_high(false, private::Internal); + Ok(()) + } +} +impl embedded_hal_02::digital::v2::StatefulOutputPin for DummyPin { + fn is_set_high(&self) -> Result { + Ok(OutputPin::is_set_high(self, private::Internal)) + } + fn is_set_low(&self) -> Result { + Ok(!OutputPin::is_set_high(self, private::Internal)) + } +} + +impl embedded_hal::digital::ErrorType for DummyPin { + type Error = core::convert::Infallible; +} + +impl embedded_hal::digital::OutputPin for DummyPin { + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_output_high(true, private::Internal); + Ok(()) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_output_high(false, private::Internal); + Ok(()) + } +} + +impl embedded_hal::digital::StatefulOutputPin for DummyPin { + fn is_set_high(&mut self) -> Result { + Ok(OutputPin::is_set_high(self, private::Internal)) + } + + fn is_set_low(&mut self) -> Result { + Ok(!OutputPin::is_set_high(self, private::Internal)) + } +} diff --git a/esp-hal/src/gpio/etm.rs b/esp-hal/src/gpio/etm.rs index 2c99f21d8ed..7cfe6d8b6ab 100644 --- a/esp-hal/src/gpio/etm.rs +++ b/esp-hal/src/gpio/etm.rs @@ -60,24 +60,39 @@ use crate::{ /// All the GPIO ETM channels #[non_exhaustive] -#[allow(missing_docs)] pub struct GpioEtmChannels<'d> { _gpio_sd: PeripheralRef<'d, crate::peripherals::GPIO_SD>, + /// Task channel 0 for triggering GPIO tasks. pub channel0_task: GpioEtmTaskChannel<0>, + /// Event channel 0 for handling GPIO events. pub channel0_event: GpioEtmEventChannel<0>, + /// Task channel 1 for triggering GPIO tasks. pub channel1_task: GpioEtmTaskChannel<1>, + /// Event channel 1 for handling GPIO events. pub channel1_event: GpioEtmEventChannel<1>, + /// Task channel 2 for triggering GPIO tasks. pub channel2_task: GpioEtmTaskChannel<2>, + /// Event channel 2 for handling GPIO events. pub channel2_event: GpioEtmEventChannel<2>, + /// Task channel 3 for triggering GPIO tasks. pub channel3_task: GpioEtmTaskChannel<3>, + /// Event channel 3 for handling GPIO events. pub channel3_event: GpioEtmEventChannel<3>, + /// Task channel 4 for triggering GPIO tasks. pub channel4_task: GpioEtmTaskChannel<4>, + /// Event channel 4 for handling GPIO events. pub channel4_event: GpioEtmEventChannel<4>, + /// Task channel 5 for triggering GPIO tasks. pub channel5_task: GpioEtmTaskChannel<5>, + /// Event channel 5 for handling GPIO events. pub channel5_event: GpioEtmEventChannel<5>, + /// Task channel 6 for triggering GPIO tasks. pub channel6_task: GpioEtmTaskChannel<6>, + /// Event channel 6 for handling GPIO events. pub channel6_event: GpioEtmEventChannel<6>, + /// Task channel 7 for triggering GPIO tasks. pub channel7_task: GpioEtmTaskChannel<7>, + /// Event channel 7 for handling GPIO events. pub channel7_event: GpioEtmEventChannel<7>, } diff --git a/esp-hal/src/gpio/lp_io.rs b/esp-hal/src/gpio/lp_io.rs index 327d6963111..8909c756c4d 100644 --- a/esp-hal/src/gpio/lp_io.rs +++ b/esp-hal/src/gpio/lp_io.rs @@ -181,7 +181,7 @@ fn get_pin_reg(pin: u8) -> &'static crate::peripherals::lp_io::GPIO0 { let lp_io = &*crate::peripherals::LP_IO::PTR; let pin_ptr = (lp_io.gpio0().as_ptr()).add(pin as usize); - &*(pin_ptr as *const esp32c6::generic::Reg) + &*(pin_ptr as *const crate::peripherals::lp_io::GPIO0) } } diff --git a/esp-hal/src/gpio/mod.rs b/esp-hal/src/gpio/mod.rs index 762843bc37e..f4db13a6489 100644 --- a/esp-hal/src/gpio/mod.rs +++ b/esp-hal/src/gpio/mod.rs @@ -1,16 +1,19 @@ //! # General Purpose I/Os (GPIO) //! //! ## Overview +//! //! Each pin can be used as a general-purpose I/O, or be connected to an //! internal peripheral signal. //! //! ## Configuration +//! //! This driver supports various operations on GPIO pins, including setting the //! pin mode, direction, and manipulating the pin state (setting high/low, //! toggling). It provides an interface to interact with GPIO pins on ESP chips, //! allowing developers to control and read the state of the pins. //! //! ## Usage +//! //! This module also implements a number of traits from [embedded-hal] to //! provide a common interface for GPIO pins. //! @@ -18,16 +21,18 @@ //! designed struct from the pac struct `GPIO` and `IO_MUX` using `Io::new`. //! //! ### Pin Types +//! //! - [Input] pins can be used as digital inputs. //! - [Output] and [OutputOpenDrain] pins can be used as digital outputs. //! - [Flex] pin is a pin that can be used as an input and output pin. -//! - [any_pin::AnyPin] pin is type-erased that can be used for peripherals -//! signals. -//! - It supports inverting the pin, so the peripheral signal can be -//! inverted. +//! - [AnyPin] is a type-erased GPIO pin with support for inverted signalling. +//! - [DummyPin] is a useful for cases where peripheral driver requires a pin, +//! but real pin cannot be used. //! //! ## Examples +//! //! ### Set up a GPIO as an Output +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::gpio::{Io, Level, Output}; @@ -37,6 +42,7 @@ //! ``` //! //! ### Blink an LED +//! //! See the [Commonly Used Setup] section of the crate documentation. //! //! ### Inverting a signal using `AnyPin` @@ -46,8 +52,6 @@ //! [Commonly Used Setup]: ../index.html#commonly-used-setup //! [Inverting TX and RX Pins]: ../uart/index.html#inverting-tx-and-rx-pins -#![warn(missing_docs)] - use core::{cell::Cell, marker::PhantomData}; use critical_section::Mutex; @@ -56,12 +60,12 @@ use procmacros::ram; #[cfg(any(adc, dac))] pub(crate) use crate::analog; pub(crate) use crate::gpio; -#[cfg(any(xtensa, esp32c3))] +#[cfg(any(xtensa, esp32c3, esp32c2))] pub(crate) use crate::rtc_pins; pub use crate::soc::gpio::*; use crate::{ interrupt::InterruptHandler, - peripheral::PeripheralRef, + peripheral::{Peripheral, PeripheralRef}, peripherals::{GPIO, IO_MUX}, private, InterruptConfigurable, @@ -69,8 +73,11 @@ use crate::{ #[cfg(touch)] pub(crate) use crate::{touch_common, touch_into}; -pub mod any_pin; -pub mod dummy_pin; +mod any_pin; +mod dummy_pin; + +pub use any_pin::AnyPin; +pub use dummy_pin::DummyPin; #[cfg(soc_etm)] pub mod etm; @@ -79,11 +86,8 @@ pub mod lp_io; #[cfg(all(rtc_io, not(esp32)))] pub mod rtc_io; -/// Convenience type-alias for a no-pin / don't care - pin -pub type NoPinType = Gpio0; - /// Convenience constant for `Option::None` pin -pub const NO_PIN: Option = None; +pub const NO_PIN: Option = None; static USER_INTERRUPT_HANDLER: Mutex>> = Mutex::new(Cell::new(None)); @@ -132,6 +136,17 @@ pub enum Level { High, } +impl core::ops::Not for Level { + type Output = Self; + + fn not(self) -> Self { + match self { + Self::Low => Self::High, + Self::High => Self::Low, + } + } +} + impl From for Level { fn from(val: bool) -> Self { match val { @@ -172,31 +187,46 @@ pub struct RtcOutput; pub struct Analog; /// Drive strength (values are approximates) -#[allow(missing_docs)] pub enum DriveStrength { + /// Drive strength of approximately 5mA. I5mA = 0, + /// Drive strength of approximately 10mA. I10mA = 1, + /// Drive strength of approximately 20mA. I20mA = 2, + /// Drive strength of approximately 40mA. I40mA = 3, } /// Alternate functions +/// +/// GPIO pins can be configured for various functions, such as GPIO +/// or being directly connected to a peripheral's signal like UART, SPI, etc. +/// The `AlternateFunction` enum allows to select one of several functions that +/// a pin can perform, rather than using it as a general-purpose input or +/// output. #[derive(PartialEq)] -#[allow(missing_docs)] pub enum AlternateFunction { + /// Alternate function 0. Function0 = 0, + /// Alternate function 1. Function1 = 1, + /// Alternate function 2. Function2 = 2, + /// Alternate function 3. Function3 = 3, + /// Alternate function 4. Function4 = 4, + /// Alternate function 5. Function5 = 5, } /// RTC function #[derive(PartialEq)] -#[allow(missing_docs)] pub enum RtcFunction { + /// RTC mode. Rtc = 0, + /// Digital mode. Digital = 1, } @@ -217,7 +247,7 @@ pub trait RtcPin: Pin { /// /// The `level` argument needs to be a valid setting for the /// `rtc_cntl.gpio_wakeup.gpio_pinX_int_type`. - #[cfg(any(esp32c3, esp32c6))] + #[cfg(any(esp32c3, esp32c2, esp32c6))] unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8); } @@ -240,6 +270,21 @@ pub trait Pin: private::Sealed { /// GPIO number fn number(&self, _: private::Internal) -> u8; + /// Type-erase (degrade) this pin into an ErasedPin. + /// + /// This converts pin singletons (`GpioPin<0>`, …), which are all different + /// types, into the same type. It is useful for creating arrays of pins, + /// or avoiding generics. + fn degrade(self) -> ErasedPin + where + Self: Sized, + { + self.degrade_internal(private::Internal) + } + + #[doc(hidden)] + fn degrade_internal(&self, _: private::Internal) -> ErasedPin; + /// Enable/disable sleep-mode fn sleep_mode(&mut self, on: bool, _: private::Internal); @@ -252,7 +297,9 @@ pub trait Pin: private::Sealed { } /// Checks if listening for interrupts is enabled for this Pin - fn is_listening(&self, _: private::Internal) -> bool; + fn is_listening(&self, _: private::Internal) -> bool { + is_listening(self.number(private::Internal)) + } /// Listen for interrupts fn listen_with_options( @@ -274,7 +321,9 @@ pub trait Pin: private::Sealed { fn clear_interrupt(&mut self, _: private::Internal); /// Enable this pin as a wake up source - fn wakeup_enable(&mut self, enable: bool, event: WakeEvent, _: private::Internal); + fn wakeup_enable(&mut self, enable: bool, event: WakeEvent, _: private::Internal) { + self.listen_with_options(event.into(), false, false, enable, private::Internal); + } } /// Trait implemented by pins which can be used as inputs @@ -282,9 +331,6 @@ pub trait InputPin: Pin { /// Init as input with the given pull-up/pull-down fn init_input(&self, pull_down: bool, pull_up: bool, _: private::Internal); - /// Set the pin to input mode without internal pull-up / pull-down resistors - fn set_to_input(&mut self, _: private::Internal); - /// Enable input for the pin fn enable_input(&mut self, on: bool, _: private::Internal); @@ -295,7 +341,9 @@ pub trait InputPin: Pin { fn is_input_high(&self, _: private::Internal) -> bool; /// Connect the pin to a peripheral input signal - fn connect_input_to_peripheral(&mut self, signal: InputSignal, _: private::Internal); + fn connect_input_to_peripheral(&mut self, signal: InputSignal, _: private::Internal) { + self.connect_input_to_peripheral_with_options(signal, false, false, private::Internal); + } /// Connect the pin to a peripheral input signal. /// @@ -353,7 +401,16 @@ pub trait OutputPin: Pin { fn internal_pull_down(&mut self, on: bool, _: private::Internal); /// Connect the pin to a peripheral output signal - fn connect_peripheral_to_output(&mut self, signal: OutputSignal, _: private::Internal); + fn connect_peripheral_to_output(&mut self, signal: OutputSignal, _: private::Internal) { + self.connect_peripheral_to_output_with_options( + signal, + false, + false, + false, + false, + private::Internal, + ) + } /// Connect the pin to a peripheral output signal. /// @@ -412,11 +469,6 @@ pub trait TouchPin: Pin { fn set_threshold(&self, threshold: u16, _: private::Internal); } -#[doc(hidden)] -pub trait CreateErasedPin: Pin { - fn erased_pin(&self, _: private::Internal) -> ErasedPin; -} - #[doc(hidden)] pub trait InterruptStatusRegisterAccess { fn pro_cpu_interrupt_status_read() -> u32; @@ -625,56 +677,15 @@ pub fn connect_high_to_peripheral(signal: InputSignal) { } #[doc(hidden)] -pub trait PinType {} - -#[doc(hidden)] -pub trait IsOutputPin: PinType {} - -#[doc(hidden)] -pub trait IsInputPin: PinType {} - -#[doc(hidden)] -pub trait IsAnalogPin: PinType {} - -#[doc(hidden)] -pub trait IsTouchPin: PinType {} - -#[doc(hidden)] -pub struct InputOutputPinType; - -#[doc(hidden)] -pub struct InputOnlyPinType; - -#[doc(hidden)] -pub struct InputOutputAnalogPinType; +pub trait BooleanType {} #[doc(hidden)] -pub struct InputOnlyAnalogPinType; +pub struct True {} +impl BooleanType for True {} #[doc(hidden)] -pub struct InputOutputAnalogTouchPinType; - -impl PinType for InputOutputPinType {} -impl IsOutputPin for InputOutputPinType {} -impl IsInputPin for InputOutputPinType {} - -impl PinType for InputOnlyPinType {} -impl IsInputPin for InputOnlyPinType {} - -impl PinType for InputOutputAnalogPinType {} -impl IsOutputPin for InputOutputAnalogPinType {} -impl IsInputPin for InputOutputAnalogPinType {} -impl IsAnalogPin for InputOutputAnalogPinType {} - -impl PinType for InputOnlyAnalogPinType {} -impl IsInputPin for InputOnlyAnalogPinType {} -impl IsAnalogPin for InputOnlyAnalogPinType {} - -impl PinType for InputOutputAnalogTouchPinType {} -impl IsOutputPin for InputOutputAnalogTouchPinType {} -impl IsInputPin for InputOutputAnalogTouchPinType {} -impl IsAnalogPin for InputOutputAnalogTouchPinType {} -impl IsTouchPin for InputOutputAnalogTouchPinType {} +pub struct False {} +impl BooleanType for False {} /// GPIO pin pub struct GpioPin; @@ -682,84 +693,76 @@ pub struct GpioPin; impl GpioPin where Self: GpioProperties, - ::PinType: IsOutputPin, { - /// Is the input pin high? - #[inline] - pub fn is_high(&self) -> bool { - ::Bank::read_input() & (1 << (GPIONUM % 32)) != 0 + pub(crate) fn new() -> Self { + Self } - /// Is the input pin low? - #[inline] - pub fn is_low(&self) -> bool { - !self.is_high() + /// Create a pin out of thin air. + /// + /// # Safety + /// + /// Ensure that only one instance of a pin exists at one time. + pub unsafe fn steal() -> Self { + Self::new() + } + + fn write_out_en(&self, enable: bool) { + if enable { + ::Bank::write_out_en_set(1 << (GPIONUM % 32)); + } else { + ::Bank::write_out_en_clear(1 << (GPIONUM % 32)); + } } } -impl GpioPin -where - Self: GpioProperties, - ::PinType: IsInputPin, -{ - pub(crate) fn new() -> Self { - Self +/// Workaround to make D+ and D- work on the ESP32-C3 and ESP32-S3, which by +/// default are assigned to the `USB_SERIAL_JTAG` peripheral. +#[cfg(any(esp32c3, esp32s3))] +fn disable_usb_pads(gpionum: u8) { + cfg_if::cfg_if! { + if #[cfg(esp32c3)] { + let pins = [18, 19]; + } else if #[cfg(esp32s3)] { + let pins = [19, 20]; + } + } + + if pins.contains(&gpionum) { + unsafe { &*crate::peripherals::USB_DEVICE::PTR } + .conf0() + .modify(|_, w| { + w.usb_pad_enable() + .clear_bit() + .dm_pullup() + .clear_bit() + .dm_pulldown() + .clear_bit() + .dp_pullup() + .clear_bit() + .dp_pulldown() + .clear_bit() + }); } } impl InputPin for GpioPin where Self: GpioProperties, - ::PinType: IsInputPin, { fn init_input(&self, pull_down: bool, pull_up: bool, _: private::Internal) { let gpio = unsafe { &*GPIO::PTR }; - ::Bank::write_out_en_clear(1 << (GPIONUM % 32)); + self.write_out_en(false); + gpio.func_out_sel_cfg(GPIONUM as usize) .modify(|_, w| unsafe { w.out_sel().bits(OutputSignal::GPIO as OutputSignalType) }); #[cfg(esp32)] crate::soc::gpio::errata36(GPIONUM, Some(pull_up), Some(pull_down)); - // NOTE: Workaround to make GPIO18 and GPIO19 work on the ESP32-C3, which by - // default are assigned to the `USB_SERIAL_JTAG` peripheral. - #[cfg(esp32c3)] - if GPIONUM == 18 || GPIONUM == 19 { - unsafe { &*crate::peripherals::USB_DEVICE::PTR } - .conf0() - .modify(|_, w| { - w.usb_pad_enable() - .clear_bit() - .dm_pullup() - .clear_bit() - .dm_pulldown() - .clear_bit() - .dp_pullup() - .clear_bit() - .dp_pulldown() - .clear_bit() - }); - } - - // Same workaround as above for ESP32-S3 - #[cfg(esp32s3)] - if GPIONUM == 19 || GPIONUM == 20 { - unsafe { &*crate::peripherals::USB_DEVICE::PTR } - .conf0() - .modify(|_, w| { - w.usb_pad_enable() - .clear_bit() - .dm_pullup() - .clear_bit() - .dm_pulldown() - .clear_bit() - .dp_pullup() - .clear_bit() - .dp_pulldown() - .clear_bit() - }); - } + #[cfg(any(esp32c3, esp32s3))] + disable_usb_pads(GPIONUM); get_io_mux_reg(GPIONUM).modify(|_, w| unsafe { w.mcu_sel() @@ -775,10 +778,6 @@ where }); } - fn set_to_input(&mut self, _: private::Internal) { - self.init_input(false, false, private::Internal); - } - fn enable_input(&mut self, on: bool, _: private::Internal) { get_io_mux_reg(GPIONUM).modify(|_, w| w.fun_ie().bit(on)); } @@ -791,10 +790,6 @@ where ::Bank::read_input() & (1 << (GPIONUM % 32)) != 0 } - fn connect_input_to_peripheral(&mut self, signal: InputSignal, _: private::Internal) { - self.connect_input_to_peripheral_with_options(signal, false, false, private::Internal); - } - fn connect_input_to_peripheral_with_options( &mut self, signal: InputSignal, @@ -854,20 +849,6 @@ where } } -impl GpioPin -where - Self: GpioProperties, -{ - /// Create a pin out of thin air. - /// - /// # Safety - /// - /// Ensure that only one instance of a pin exists at one time. - pub unsafe fn steal() -> Self { - Self - } -} - impl Pin for GpioPin where Self: GpioProperties, @@ -876,6 +857,10 @@ where GPIONUM } + fn degrade_internal(&self, _: private::Internal) -> ErasedPin { + self.degrade_pin(private::Internal) + } + fn sleep_mode(&mut self, on: bool, _: private::Internal) { get_io_mux_reg(GPIONUM).modify(|_, w| w.slp_sel().bit(on)); } @@ -901,25 +886,12 @@ where } } - unsafe { - (*GPIO::PTR).pin(GPIONUM as usize).modify(|_, w| { - w.int_ena() - .bits(gpio_intr_enable(int_enable, nmi_enable)) - .int_type() - .bits(event as u8) - .wakeup_enable() - .bit(wake_up_from_light_sleep) - }); - } - } - - fn is_listening(&self, _: private::Internal) -> bool { - let bits = unsafe { &*GPIO::PTR } - .pin(GPIONUM as usize) - .read() - .int_ena() - .bits(); - bits != 0 + set_int_enable( + GPIONUM, + gpio_intr_enable(int_enable, nmi_enable), + event as u8, + wake_up_from_light_sleep, + ) } fn unlisten(&mut self, _: private::Internal) { @@ -937,62 +909,9 @@ where fn clear_interrupt(&mut self, _: private::Internal) { ::Bank::write_interrupt_status_clear(1 << (GPIONUM % 32)); } - - fn wakeup_enable(&mut self, enable: bool, event: WakeEvent, _: private::Internal) { - self.listen_with_options(event.into(), false, false, enable, private::Internal); - } -} - -impl GpioPin -where - Self: GpioProperties, - ::PinType: IsOutputPin, -{ - /// Drives the pin high. - #[inline] - pub fn set_high(&mut self) { - ::Bank::write_output_set(1 << (GPIONUM % 32)); - } - - /// Drives the pin low. - #[inline] - pub fn set_low(&mut self) { - ::Bank::write_output_clear(1 << (GPIONUM % 32)); - } - - /// Drives the pin high or low depending on the provided value. - #[inline] - pub fn set_state(&mut self, state: bool) { - match state { - true => self.set_high(), - false => self.set_low(), - } - } - - /// Is the pin in drive high mode? - #[inline] - pub fn is_set_high(&self) -> bool { - ::Bank::read_output() & (1 << (GPIONUM % 32)) != 0 - } - - /// Is the pin in drive low mode? - #[inline] - pub fn is_set_low(&self) -> bool { - !self.is_set_high() - } - - /// Toggle pin output. - #[inline] - pub fn toggle(&mut self) { - if self.is_set_high() { - self.set_low(); - } else { - self.set_high(); - } - } } -impl crate::peripheral::Peripheral for GpioPin +impl Peripheral for GpioPin where Self: GpioProperties, { @@ -1007,38 +926,24 @@ impl private::Sealed for GpioPin where Self: GpioPro impl GpioPin where - Self: GpioProperties, - ::PinType: IsOutputPin, + Self: GpioProperties, { - fn init_output(&self, alternate: AlternateFunction, open_drain: bool, _: private::Internal) { + fn init_output(&self, alternate: AlternateFunction, open_drain: bool) { let gpio = unsafe { &*GPIO::PTR }; #[cfg(esp32)] crate::soc::gpio::errata36(GPIONUM, Some(false), Some(false)); - ::Bank::write_out_en_set(1 << (GPIONUM % 32)); + self.write_out_en(true); + gpio.pin(GPIONUM as usize) .modify(|_, w| w.pad_driver().bit(open_drain)); gpio.func_out_sel_cfg(GPIONUM as usize) .modify(|_, w| unsafe { w.out_sel().bits(OutputSignal::GPIO as OutputSignalType) }); - // NOTE: Workaround to make GPIO18 and GPIO19 work on the ESP32-C3, which by - // default are assigned to the `USB_SERIAL_JTAG` peripheral. - #[cfg(esp32c3)] - if GPIONUM == 18 || GPIONUM == 19 { - unsafe { &*crate::peripherals::USB_DEVICE::PTR } - .conf0() - .modify(|_, w| w.usb_pad_enable().clear_bit()); - } - - // Same workaround as above for ESP32-S3 - #[cfg(esp32s3)] - if GPIONUM == 19 || GPIONUM == 20 { - unsafe { &*crate::peripherals::USB_DEVICE::PTR } - .conf0() - .modify(|_, w| w.usb_pad_enable().clear_bit()); - } + #[cfg(any(esp32c3, esp32s3))] + disable_usb_pads(GPIONUM); get_io_mux_reg(GPIONUM).modify(|_, w| unsafe { w.mcu_sel() @@ -1059,23 +964,18 @@ where impl OutputPin for GpioPin where - Self: GpioProperties, - ::PinType: IsOutputPin, + Self: GpioProperties, { fn set_to_open_drain_output(&mut self, _: private::Internal) { - self.init_output(GPIO_FUNCTION, true, private::Internal); + self.init_output(GPIO_FUNCTION, true); } fn set_to_push_pull_output(&mut self, _: private::Internal) { - self.init_output(GPIO_FUNCTION, false, private::Internal); + self.init_output(GPIO_FUNCTION, false); } fn enable_output(&mut self, on: bool, _: private::Internal) { - if on { - ::Bank::write_out_en_set(1 << (GPIONUM % 32)); - } else { - ::Bank::write_out_en_clear(1 << (GPIONUM % 32)); - } + self.write_out_en(on); } fn set_output_high(&mut self, high: bool, _: private::Internal) { @@ -1099,9 +999,11 @@ where fn internal_pull_up_in_sleep_mode(&mut self, on: bool, _: private::Internal) { get_io_mux_reg(GPIONUM).modify(|_, w| w.mcu_wpu().bit(on)); } + fn internal_pull_down_in_sleep_mode(&mut self, on: bool, _: private::Internal) { get_io_mux_reg(GPIONUM).modify(|_, w| w.mcu_wpd().bit(on)); } + fn enable_output_in_sleep_mode(&mut self, on: bool, _: private::Internal) { get_io_mux_reg(GPIONUM).modify(|_, w| w.mcu_oe().bit(on)); } @@ -1112,6 +1014,7 @@ where get_io_mux_reg(GPIONUM).modify(|_, w| w.fun_wpu().bit(on)); } + fn internal_pull_down(&mut self, on: bool, _: private::Internal) { #[cfg(esp32)] crate::soc::gpio::errata36(GPIONUM, None, Some(on)); @@ -1119,17 +1022,6 @@ where get_io_mux_reg(GPIONUM).modify(|_, w| w.fun_wpd().bit(on)); } - fn connect_peripheral_to_output(&mut self, signal: OutputSignal, _: private::Internal) { - self.connect_peripheral_to_output_with_options( - signal, - false, - false, - false, - false, - private::Internal, - ) - } - fn connect_peripheral_to_output_with_options( &mut self, signal: OutputSignal, @@ -1199,23 +1091,10 @@ where } } -#[cfg(any(adc, dac))] -impl AnalogPin for GpioPin -where - Self: GpioProperties, - ::PinType: IsAnalogPin, -{ - /// Configures the pin for analog mode. - fn set_analog(&self, _: private::Internal) { - crate::soc::gpio::internal_into_analog(GPIONUM); - } -} - #[cfg(touch)] impl TouchPin for GpioPin where - Self: GpioProperties, - ::PinType: IsTouchPin, + Self: GpioProperties, { fn set_touch(&self, _: private::Internal) { crate::soc::gpio::internal_into_touch(GPIONUM); @@ -1258,12 +1137,7 @@ impl Io { gpio.bind_gpio_interrupt(gpio_interrupt_handler); crate::interrupt::enable(crate::peripherals::Interrupt::GPIO, prio).unwrap(); - let pins = gpio.pins(); - - Io { - _io_mux: io_mux, - pins, - } + Self::new_no_bind_interrupt(gpio, io_mux) } /// Initialize the I/O driver without enabling the GPIO interrupt or @@ -1272,12 +1146,10 @@ impl Io { /// *Note:* You probably don't want to use this, it is intended to be used /// in very specific use cases. Async GPIO functionality will not work /// when instantiating `Io` using this constructor. - pub fn new_no_bind_interrupt(gpio: GPIO, io_mux: IO_MUX) -> Self { - let pins = gpio.pins(); - + pub fn new_no_bind_interrupt(gpio: GPIO, _io_mux: IO_MUX) -> Self { Io { - _io_mux: io_mux, - pins, + _io_mux, + pins: gpio.pins(), } } } @@ -1308,7 +1180,6 @@ extern "C" fn gpio_interrupt_handler() { user_handler.call(); } - #[cfg(feature = "async")] asynch::handle_gpio_interrupt(); } @@ -1317,7 +1188,52 @@ pub trait GpioProperties { type Bank: BankGpioRegisterAccess; type InterruptStatus: InterruptStatusRegisterAccess; type Signals: GpioSignal; - type PinType: PinType; + + type IsOutput: BooleanType; + type IsAnalog: BooleanType; + type IsTouch: BooleanType; + + fn degrade_pin(&self, _: private::Internal) -> ErasedPin; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! if_output_pin { + (InputOnlyAnalog, { $($then:tt)* } else { $($else:tt)* } ) => { $($else)* }; + (InputOutputAnalog, { $($then:tt)* } else { $($else:tt)* } ) => { $($then)* }; + (InputOutputAnalogTouch, { $($then:tt)* } else { $($else:tt)* } ) => { $($then)* }; + (InputOutput, { $($then:tt)* } else { $($else:tt)* } ) => { $($then)* }; +} +pub(crate) use if_output_pin; + +#[doc(hidden)] +#[macro_export] +macro_rules! pin_types { + (InputOnly) => { + type IsOutput = $crate::gpio::False; + type IsAnalog = $crate::gpio::False; + type IsTouch = $crate::gpio::False; + }; + (InputOnlyAnalog) => { + type IsOutput = $crate::gpio::False; + type IsAnalog = $crate::gpio::True; + type IsTouch = $crate::gpio::False; + }; + (InputOutput) => { + type IsOutput = $crate::gpio::True; + type IsAnalog = $crate::gpio::False; + type IsTouch = $crate::gpio::False; + }; + (InputOutputAnalog) => { + type IsOutput = $crate::gpio::True; + type IsAnalog = $crate::gpio::True; + type IsTouch = $crate::gpio::False; + }; + (InputOutputAnalogTouch) => { + type IsOutput = $crate::gpio::True; + type IsAnalog = $crate::gpio::True; + type IsTouch = $crate::gpio::True; + }; } #[doc(hidden)] @@ -1351,7 +1267,11 @@ macro_rules! gpio { type Bank = $crate::gpio::[< Bank $bank GpioRegisterAccess >]; type InterruptStatus = $crate::gpio::[< InterruptStatusRegisterAccessBank $bank >]; type Signals = [< Gpio $gpionum Signals >]; - type PinType = $crate::gpio::[<$type PinType>]; + $crate::pin_types!($type); + + fn degrade_pin(&self, _: $crate::private::Internal) -> ErasedPin { + ErasedPin($crate::gpio::ErasedPinInner::[< Gpio $gpionum >](unsafe { Self::steal() })) + } } #[doc(hidden)] @@ -1383,65 +1303,75 @@ macro_rules! gpio { input_signals } } - - impl $crate::gpio::CreateErasedPin for GpioPin<$gpionum> { - fn erased_pin(&self, _: $crate::private::Internal) -> ErasedPin { - $crate::gpio::ErasedPin::[< Gpio $gpionum >](unsafe { Self::steal() }) - } - } )+ /// Pins available on this chip - #[allow(missing_docs)] pub struct Pins { $( + #[doc = concat!("GPIO pin number ", $gpionum, ".")] pub [< gpio $gpionum >] : GpioPin<$gpionum>, )+ } - $( - #[doc = concat!("Alias for GpioPin")] - pub type [] = GpioPin<$gpionum>; - )+ - - #[doc(hidden)] - pub enum ErasedPin { + pub(crate) enum ErasedPinInner { $( - []([]), + [](GpioPin<$gpionum>), )+ } + /// Type-erased GPIO pin + pub struct ErasedPin(pub(crate) ErasedPinInner); + impl ErasedPin { - pub(crate) unsafe fn clone_unchecked(&mut self) -> Self { - match self { + pub(crate) unsafe fn clone_unchecked(&self) -> Self { + match self.0 { + $(ErasedPinInner::[](_) => { + Self(ErasedPinInner::[< Gpio $gpionum >](unsafe { GpioPin::steal() })) + })+ + } + } + } + + impl $crate::peripheral::Peripheral for ErasedPin { + type P = ErasedPin; + unsafe fn clone_unchecked(&mut self) -> Self { + ErasedPin::clone_unchecked(self) + } + } + + // These macros call the code block on the actually contained GPIO pin. + + #[doc(hidden)] + #[macro_export] + macro_rules! handle_gpio_output { + ($this:expr, $inner:ident, $code:tt) => { + match $this { $( - ErasedPin::[](_) => { - $crate::gpio::ErasedPin::[< Gpio $gpionum >](unsafe { GpioPin::steal() }) - } + ErasedPinInner::[]($inner) => if_output_pin!($type, { + $code + } else {{ + let _ = $inner; + panic!("Unsupported") + }}), )+ } } } - procmacros::make_gpio_enum_dispatch_macro!( - handle_gpio_output - { InputOutputAnalog, InputOutput, } - { - $( - $type,$gpionum - )+ + #[doc(hidden)] + #[macro_export] + macro_rules! handle_gpio_input { + ($this:expr, $inner:ident, $code:tt) => { + match $this { + $( + ErasedPinInner::[]($inner) => $code + )+ + } } - ); + } - procmacros::make_gpio_enum_dispatch_macro!( - handle_gpio_input - { InputOutputAnalog, InputOutput, InputOnlyAnalog } - { - $( - $type,$gpionum - )+ - } - ); + pub(crate) use handle_gpio_output; + pub(crate) use handle_gpio_input; } }; } @@ -1466,10 +1396,7 @@ macro_rules! rtc_pins { let rtcio = unsafe{ &*RTC_IO::ptr() }; - #[cfg(esp32s3)] - unsafe { $crate::peripherals::SENS::steal() }.sar_peri_clk_gate_conf().modify(|_,w| w.iomux_clk_en().set_bit()); - #[cfg(esp32s2)] - unsafe { $crate::peripherals::SENS::steal() }.sar_io_mux_conf().modify(|_,w| w.iomux_clk_gate_en().set_bit()); + $crate::gpio::enable_iomux_clk_gate(); // disable input paste::paste!{ @@ -1523,7 +1450,7 @@ macro_rules! rtc_pins { }; } -#[cfg(esp32c3)] +#[cfg(any(esp32c2, esp32c3))] #[doc(hidden)] #[macro_export] macro_rules! rtc_pins { @@ -1533,10 +1460,19 @@ macro_rules! rtc_pins { impl $crate::gpio::RtcPin for GpioPin<$pin_num> { unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8) { let rtc_cntl = unsafe { &*$crate::peripherals::RTC_CNTL::ptr() }; - paste::paste! { - rtc_cntl.gpio_wakeup().modify(|_, w| w.[< gpio_pin $pin_num _wakeup_enable >]().bit(wakeup)); - rtc_cntl.gpio_wakeup().modify(|_, w| w.[< gpio_pin $pin_num _int_type >]().bits(level)); - } + cfg_if::cfg_if! { + if #[cfg(esp32c2)] { + paste::paste! { + rtc_cntl.cntl_gpio_wakeup().modify(|_, w| w.[< gpio_pin $pin_num _wakeup_enable >]().bit(wakeup)); + rtc_cntl.cntl_gpio_wakeup().modify(|_, w| w.[< gpio_pin $pin_num _int_type >]().bits(level)); + } + } else { + paste::paste! { + rtc_cntl.gpio_wakeup().modify(|_, w| w.[< gpio_pin $pin_num _wakeup_enable >]().bit(wakeup)); + rtc_cntl.gpio_wakeup().modify(|_, w| w.[< gpio_pin $pin_num _int_type >]().bits(level)); + } + } + }; } fn rtcio_pad_hold(&mut self, enable: bool) { @@ -1564,17 +1500,20 @@ macro_rules! rtc_pins { ( $( $pin_num:expr )+ ) => { $( $crate::gpio::rtc_pins!($pin_num); )+ }; } -// Following code enables `into_analog` - #[doc(hidden)] pub fn enable_iomux_clk_gate() { - #[cfg(esp32s2)] - { - use crate::peripherals::SENS; - let sensors = unsafe { &*SENS::ptr() }; - sensors - .sar_io_mux_conf() - .modify(|_, w| w.iomux_clk_gate_en().set_bit()); + cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + let sensors = unsafe { &*crate::peripherals::SENS::ptr() }; + sensors + .sar_io_mux_conf() + .modify(|_, w| w.iomux_clk_gate_en().set_bit()); + } else if #[cfg(esp32s3)] { + let sensors = unsafe { &*crate::peripherals::SENS::ptr() }; + sensors + .sar_peri_clk_gate_conf() + .modify(|_,w| w.iomux_clk_en().set_bit()); + } } } @@ -1590,51 +1529,52 @@ macro_rules! analog { ) )+ ) => { - pub(crate) fn internal_into_analog(pin: u8) { - use $crate::peripherals::RTC_IO; + $( + #[cfg(any(adc, dac))] + impl $crate::gpio::AnalogPin for GpioPin<$pin_num> + where + Self: $crate::gpio::GpioProperties, + { + /// Configures the pin for analog mode. + fn set_analog(&self, _: $crate::private::Internal) { + let rtcio = unsafe{ &*$crate::peripherals::RTC_IO::ptr() }; - let rtcio = unsafe{ &*RTC_IO::ptr() }; - $crate::gpio::enable_iomux_clk_gate(); + #[cfg(esp32s2)] + $crate::gpio::enable_iomux_clk_gate(); - match pin { - $( - $pin_num => { - // We need `paste` (and a [< >] in it) to rewrite the token stream to - // handle indexed pins. - paste::paste! { - // disable input - rtcio.$pin_reg.modify(|_,w| w.$fun_ie().bit([< false >])); + // We need `paste` (and a [< >] in it) to rewrite the token stream to + // handle indexed pins. + paste::paste! { + // disable input + rtcio.$pin_reg.modify(|_,w| w.$fun_ie().bit([< false >])); - // disable output - rtcio.enable_w1tc().write(|w| unsafe { w.enable_w1tc().bits(1 << $rtc_pin) }); + // disable output + rtcio.enable_w1tc().write(|w| unsafe { w.enable_w1tc().bits(1 << $rtc_pin) }); - // disable open drain - rtcio.pin($rtc_pin).modify(|_,w| w.pad_driver().bit(false)); + // disable open drain + rtcio.pin($rtc_pin).modify(|_,w| w.pad_driver().bit(false)); - rtcio.$pin_reg.modify(|_,w| { - w.$fun_ie().clear_bit(); + rtcio.$pin_reg.modify(|_,w| { + w.$fun_ie().clear_bit(); - // Connect pin to analog / RTC module instead of standard GPIO - w.$mux_sel().set_bit(); + // Connect pin to analog / RTC module instead of standard GPIO + w.$mux_sel().set_bit(); - // Select function "RTC function 1" (GPIO) for analog use - unsafe { w.$fun_sel().bits(0b00) } - }); + // Select function "RTC function 1" (GPIO) for analog use + unsafe { w.$fun_sel().bits(0b00) }; // Disable pull-up and pull-down resistors on the pin, if it has them $( - rtcio.$pin_reg.modify(|_,w| { - w - .$rue().bit(false) - .$rde().bit(false) - }); + w.$rue().bit(false); + w.$rde().bit(false); )? - } + + w + }); } - )+ - _ => unreachable!(), + } } - } + )+ } } @@ -1645,30 +1585,27 @@ macro_rules! analog { ( $($pin_num:literal)+ ) => { - pub(crate) fn internal_into_analog(pin: u8) { - use $crate::peripherals::IO_MUX; - use $crate::peripherals::GPIO; - - let io_mux = unsafe{ &*IO_MUX::PTR }; - let gpio = unsafe{ &*GPIO::PTR }; - - match pin { - $( - $pin_num => { - io_mux.gpio($pin_num).modify(|_,w| unsafe { - w.mcu_sel().bits(1) - .fun_ie().clear_bit() - .fun_wpu().clear_bit() - .fun_wpd().clear_bit() - }); + $( + #[cfg(any(adc, dac))] + impl $crate::gpio::AnalogPin for GpioPin<$pin_num> + where + Self: $crate::gpio::GpioProperties, + { + /// Configures the pin for analog mode. + fn set_analog(&self, _: $crate::private::Internal) { + use $crate::peripherals::{GPIO}; + + get_io_mux_reg($pin_num).modify(|_,w| unsafe { + w.mcu_sel().bits(1) + .fun_ie().clear_bit() + .fun_wpu().clear_bit() + .fun_wpd().clear_bit() + }); - gpio.enable_w1tc().write(|w| unsafe { w.bits(1 << $pin_num) }); - } - )+ - _ => unreachable!() + unsafe{ &*GPIO::PTR }.enable_w1tc().write(|w| unsafe { w.bits(1 << $pin_num) }); + } } - - } + )+ } } @@ -1677,93 +1614,73 @@ macro_rules! analog { #[doc(hidden)] #[macro_export] macro_rules! touch_into { + (@pin_specific $touch_num:expr, true) => { + paste::paste! { + unsafe { &*RTC_IO::ptr() }.[< touch_pad $touch_num >]().write(|w| unsafe { + w + .xpd().set_bit() + // clear input_enable + .fun_ie().clear_bit() + // Connect pin to analog / RTC module instead of standard GPIO + .mux_sel().set_bit() + // Disable pull-up and pull-down resistors on the pin + .rue().clear_bit() + .rde().clear_bit() + .tie_opt().clear_bit() + // Select function "RTC function 1" (GPIO) for analog use + .fun_sel().bits(0b00) + }); + } + }; + + (@pin_specific $touch_num:expr, false) => { + paste::paste! { + unsafe { &*RTC_IO::ptr() }.[< touch_pad $touch_num >]().write(|w| + w + .xpd().set_bit() + .tie_opt().clear_bit() + ); + } + }; + ( - $( ( $touch_num:expr, $pin_num:expr, $rtc_pin:expr, $touch_thres_reg:expr, $touch_thres_field:expr , true ) )+ - --- - $( ( $touch_numx:expr, $pin_numx:expr, $rtc_pinx:expr, $touch_thres_regx:expr , $touch_thres_fieldx:expr , false ) )* + $( ( $touch_num:expr, $pin_num:expr, $rtc_pin:expr, $touch_thres_reg:expr, $touch_thres_field:expr , $needs_extra_setup:tt ))+ ) => { pub(crate) fn internal_into_touch(pin: u8) { - use $crate::peripherals::{GPIO, RTC_IO, SENS}; - - let rtcio = unsafe { &*RTC_IO::ptr() }; - let sens = unsafe { &*SENS::ptr() }; - let gpio = unsafe { &*GPIO::ptr() }; - match pin { $( $pin_num => { - paste::paste! { - // Pad to normal mode (not open-drain) - gpio.pin($rtc_pin).write(|w| w.pad_driver().clear_bit()); + use $crate::peripherals::{GPIO, RTC_IO, SENS}; - // clear output - rtcio.enable_w1tc().write(|w| unsafe { w.enable_w1tc().bits(1 << $rtc_pin) }); - sens - . $touch_thres_reg () - .write(|w| unsafe { - w. $touch_thres_field ().bits( - 0b0 // Default: 0 for esp32 gets overridden later anyway. - ) - }); + let gpio = unsafe { &*GPIO::ptr() }; + let rtcio = unsafe { &*RTC_IO::ptr() }; + let sens = unsafe { &*SENS::ptr() }; - rtcio.[< touch_pad $touch_num >]().write(|w| unsafe { - w - .xpd().set_bit() - // clear input_enable - .fun_ie().clear_bit() - // Connect pin to analog / RTC module instead of standard GPIO - .mux_sel().set_bit() - // Disable pull-up and pull-down resistors on the pin - .rue().clear_bit() - .rde().clear_bit() - .tie_opt().clear_bit() - // Select function "RTC function 1" (GPIO) for analog use - .fun_sel().bits(0b00) - }); - - sens.sar_touch_enable().modify(|r, w| unsafe { - w - // enable the pin - .touch_pad_worken().bits( - r.touch_pad_worken().bits() | ( 1 << [< $touch_num >] ) - ) - }); - } - } - )+ + // Pad to normal mode (not open-drain) + gpio.pin($rtc_pin).write(|w| w.pad_driver().clear_bit()); - $( - $pin_numx => { + // clear output + rtcio.enable_w1tc().write(|w| unsafe { w.enable_w1tc().bits(1 << $rtc_pin) }); paste::paste! { - // Pad to normal mode (not open-drain) - gpio.pin($rtc_pinx).write(|w| w.pad_driver().clear_bit()); - - // clear output - rtcio.enable_w1tc().write(|w| unsafe { w.enable_w1tc().bits(1 << $rtc_pinx) }); - sens - . $touch_thres_regx () + sens . $touch_thres_reg () .write(|w| unsafe { - w. $touch_thres_fieldx ().bits( + w. $touch_thres_field ().bits( 0b0 // Default: 0 for esp32 gets overridden later anyway. ) }); - rtcio.[< touch_pad $touch_numx >]().write(|w| - w - .xpd().set_bit() - .tie_opt().clear_bit() - ); + $crate::touch_into!( @pin_specific $touch_num, $needs_extra_setup ); + // enable the pin sens.sar_touch_enable().modify(|r, w| unsafe { - w - // enable the pin - .touch_pad_worken().bits( - r.touch_pad_worken().bits() | ( 1 << [< $touch_numx >] ) + w.touch_pad_worken().bits( + r.touch_pad_worken().bits() | ( 1 << [< $touch_num >] ) ) }); } } - )* + )+ + _ => unreachable!(), } } @@ -1823,8 +1740,27 @@ macro_rules! touch_common { } /// GPIO output driver. -pub struct Output<'d, P> { - pin: PeripheralRef<'d, P>, +pub struct Output<'d, P = ErasedPin> { + pin: Flex<'d, P>, +} + +impl

private::Sealed for Output<'_, P> {} + +impl<'d, P> Peripheral for Output<'d, P> { + type P = P; + unsafe fn clone_unchecked(&mut self) -> P { + self.pin.clone_unchecked() + } +} + +impl<'d> Output<'d> { + /// Create GPIO output driver for a [GpioPin] with the provided level + #[inline] + pub fn new(pin: impl Peripheral

+ 'd, initial_output: Level) -> Self { + let pin = Flex::new(pin); + + Self::new_inner(pin, initial_output) + } } impl<'d, P> Output<'d, P> @@ -1833,11 +1769,17 @@ where { /// Create GPIO output driver for a [GpioPin] with the provided level #[inline] - pub fn new(pin: impl crate::peripheral::Peripheral

+ 'd, initial_output: Level) -> Self { - crate::into_ref!(pin); + pub fn new_typed(pin: impl Peripheral

+ 'd, initial_output: Level) -> Self { + let pin = Flex::new_typed(pin); + + Self::new_inner(pin, initial_output) + } - pin.set_output_high(initial_output.into(), private::Internal); - pin.set_to_push_pull_output(private::Internal); + fn new_inner(mut pin: Flex<'d, P>, initial_output: Level) -> Self { + pin.pin + .set_output_high(initial_output.into(), private::Internal); + + pin.set_as_output(); Self { pin } } @@ -1845,55 +1787,74 @@ where /// Set the output as high. #[inline] pub fn set_high(&mut self) { - self.pin.set_output_high(true, private::Internal); + self.set_level(Level::High) } /// Set the output as low. #[inline] pub fn set_low(&mut self) { - self.pin.set_output_high(false, private::Internal); + self.set_level(Level::Low) } /// Set the output level. #[inline] pub fn set_level(&mut self, level: Level) { - self.pin.set_output_high(level.into(), private::Internal); + self.pin.set_level(level) } /// Is the output pin set as high? #[inline] pub fn is_set_high(&self) -> bool { - self.pin.is_set_high(private::Internal) + self.get_output_level() == Level::High } /// Is the output pin set as low? #[inline] pub fn is_set_low(&self) -> bool { - !self.pin.is_set_high(private::Internal) + self.get_output_level() == Level::Low } /// What level output is set to #[inline] pub fn get_output_level(&self) -> Level { - self.pin.is_set_high(private::Internal).into() + self.pin.get_output_level() } /// Toggle pin output #[inline] pub fn toggle(&mut self) { - let level = !self.pin.is_set_high(private::Internal); - self.pin.set_output_high(level, private::Internal); + self.pin.toggle(); } /// Configure the [DriveStrength] of the pin pub fn set_drive_strength(&mut self, strength: DriveStrength) { - self.pin.set_drive_strength(strength, private::Internal); + self.pin.set_drive_strength(strength); } } /// GPIO input driver. -pub struct Input<'d, P> { - pin: PeripheralRef<'d, P>, +pub struct Input<'d, P = ErasedPin> { + pin: Flex<'d, P>, +} + +impl

private::Sealed for Input<'_, P> {} + +impl<'d, P> Peripheral for Input<'d, P> { + type P = P; + unsafe fn clone_unchecked(&mut self) -> P { + self.pin.clone_unchecked() + } +} + +impl<'d> Input<'d> { + /// Create GPIO input driver for a [Pin] with the provided [Pull] + /// configuration. + #[inline] + pub fn new(pin: impl Peripheral

+ 'd, pull: Pull) -> Self { + let pin = Flex::new(pin); + + Self::new_inner(pin, pull) + } } impl<'d, P> Input<'d, P> @@ -1903,51 +1864,57 @@ where /// Create GPIO input driver for a [Pin] with the provided [Pull] /// configuration. #[inline] - pub fn new(pin: impl crate::peripheral::Peripheral

+ 'd, pull: Pull) -> Self { - crate::into_ref!(pin); - pin.init_input(pull == Pull::Down, pull == Pull::Up, private::Internal); + pub fn new_typed(pin: impl Peripheral

+ 'd, pull: Pull) -> Self { + let pin = Flex::new_typed(pin); + + Self::new_inner(pin, pull) + } + + fn new_inner(mut pin: Flex<'d, P>, pull: Pull) -> Self { + pin.set_as_input(pull); + Self { pin } } /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { - self.pin.is_input_high(private::Internal) + self.get_level() == Level::High } /// Get whether the pin input level is low. #[inline] pub fn is_low(&self) -> bool { - !self.is_high() + self.get_level() == Level::Low } /// Get the current pin input level. #[inline] pub fn get_level(&self) -> Level { - self.is_high().into() + self.pin.get_level() } /// Listen for interrupts #[inline] pub fn listen(&mut self, event: Event) { - self.pin.listen(event, private::Internal); + self.pin.listen(event); } /// Stop listening for interrupts pub fn unlisten(&mut self) { - self.pin.unlisten(private::Internal); + self.pin.unlisten(); } /// Clear the interrupt status bit for this Pin #[inline] pub fn clear_interrupt(&mut self) { - self.pin.clear_interrupt(private::Internal); + self.pin.clear_interrupt(); } /// Checks if the interrupt status bit for this Pin is set #[inline] pub fn is_interrupt_set(&self) -> bool { - self.pin.is_interrupt_set(private::Internal) + self.pin.is_interrupt_set() } /// Enable as a wake-up source. @@ -1955,342 +1922,203 @@ where /// This will unlisten for interrupts #[inline] pub fn wakeup_enable(&mut self, enable: bool, event: WakeEvent) { - self.pin.wakeup_enable(enable, event, private::Internal); + self.pin.wakeup_enable(enable, event); } } /// GPIO open-drain output driver. -pub struct OutputOpenDrain<'d, P> { - pin: PeripheralRef<'d, P>, +pub struct OutputOpenDrain<'d, P = ErasedPin> { + pin: Flex<'d, P>, } -impl<'d, P> OutputOpenDrain<'d, P> -where - P: InputPin + OutputPin, -{ +impl

private::Sealed for OutputOpenDrain<'_, P> {} + +impl<'d, P> Peripheral for OutputOpenDrain<'d, P> { + type P = P; + unsafe fn clone_unchecked(&mut self) -> P { + self.pin.clone_unchecked() + } +} + +impl<'d> OutputOpenDrain<'d> { /// Create GPIO open-drain output driver for a [Pin] with the provided /// initial output-level and [Pull] configuration. #[inline] - pub fn new( - pin: impl crate::peripheral::Peripheral

+ 'd, + pub fn new( + pin: impl Peripheral

+ 'd, initial_output: Level, pull: Pull, ) -> Self { - crate::into_ref!(pin); - pin.set_output_high(initial_output.into(), private::Internal); - pin.set_to_open_drain_output(private::Internal); - pin.internal_pull_down(pull == Pull::Down, private::Internal); - pin.internal_pull_up(pull == Pull::Up, private::Internal); - - Self { pin } - } - - /// Get whether the pin input level is high. - #[inline] - pub fn is_high(&self) -> bool { - self.pin.is_input_high(private::Internal) - } - - /// Get whether the pin input level is low. - #[inline] - pub fn is_low(&self) -> bool { - !self.is_high() - } - - /// Get the current pin input level. - #[inline] - pub fn get_level(&self) -> Level { - self.is_high().into() - } - - /// Listen for interrupts - #[inline] - pub fn listen(&mut self, event: Event) { - self.pin.listen(event, private::Internal); - } - - /// Clear the interrupt status bit for this Pin - #[inline] - pub fn clear_interrupt(&mut self) { - self.pin.clear_interrupt(private::Internal); - } - - /// Set the output as high. - #[inline] - pub fn set_high(&mut self) { - self.pin.set_output_high(true, private::Internal); - } - - /// Set the output as low. - #[inline] - pub fn set_low(&mut self) { - self.pin.set_output_high(false, private::Internal); - } - - /// Set the output level. - #[inline] - pub fn set_level(&mut self, level: Level) { - self.pin.set_output_high(level.into(), private::Internal); - } - - /// Is the output pin set as high? - #[inline] - pub fn is_set_high(&self) -> bool { - self.pin.is_set_high(private::Internal) - } + let pin = Flex::new(pin); - /// Is the output pin set as low? - #[inline] - pub fn is_set_low(&self) -> bool { - !self.pin.is_set_high(private::Internal) - } - - /// What level output is set to - #[inline] - pub fn get_output_level(&self) -> Level { - self.pin.is_set_high(private::Internal).into() - } - - /// Toggle pin output - #[inline] - pub fn toggle(&mut self) { - let level = !self.pin.is_set_high(private::Internal); - self.pin.set_output_high(level, private::Internal); - } - - /// Configure the [DriveStrength] of the pin - pub fn set_drive_strength(&mut self, strength: DriveStrength) { - self.pin.set_drive_strength(strength, private::Internal); + Self::new_inner(pin, initial_output, pull) } } -/// GPIO flexible pin driver. -pub struct Flex<'d, P> { - pin: PeripheralRef<'d, P>, -} - -impl<'d, P> Flex<'d, P> +impl<'d, P> OutputOpenDrain<'d, P> where P: InputPin + OutputPin, { - /// Create GPIO flexible pin driver for a [Pin]. - /// No mode change happens. + /// Create GPIO open-drain output driver for a [Pin] with the provided + /// initial output-level and [Pull] configuration. #[inline] - pub fn new(pin: impl crate::peripheral::Peripheral

+ 'd) -> Self { - crate::into_ref!(pin); - Self { pin } - } + pub fn new_typed(pin: impl Peripheral

+ 'd, initial_output: Level, pull: Pull) -> Self { + let pin = Flex::new_typed(pin); - /// Set the GPIO to open-drain mode. - pub fn set_as_open_drain(&mut self, pull: Pull) { - self.pin.set_to_open_drain_output(private::Internal); - self.pin - .internal_pull_down(pull == Pull::Down, private::Internal); - self.pin - .internal_pull_up(pull == Pull::Up, private::Internal); + Self::new_inner(pin, initial_output, pull) } - /// Set the GPIO to input mode. - pub fn set_as_input(&mut self, pull: Pull) { - self.pin - .init_input(pull == Pull::Down, pull == Pull::Up, private::Internal); - } + fn new_inner(mut pin: Flex<'d, P>, initial_output: Level, pull: Pull) -> Self { + pin.pin + .set_output_high(initial_output.into(), private::Internal); - /// Set the GPIO to output mode. - pub fn set_as_output(&mut self) { - self.pin.set_to_push_pull_output(private::Internal); + pin.set_as_open_drain(pull); + + Self { pin } } /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { - self.pin.is_input_high(private::Internal) + self.get_level() == Level::High } /// Get whether the pin input level is low. #[inline] pub fn is_low(&self) -> bool { - !self.is_high() + self.get_level() == Level::Low } /// Get the current pin input level. #[inline] pub fn get_level(&self) -> Level { - self.is_high().into() + self.pin.get_level() } /// Listen for interrupts #[inline] pub fn listen(&mut self, event: Event) { - self.pin.listen(event, private::Internal); + self.pin.listen(event); } /// Clear the interrupt status bit for this Pin #[inline] pub fn clear_interrupt(&mut self) { - self.pin.clear_interrupt(private::Internal); + self.pin.clear_interrupt(); } /// Set the output as high. #[inline] pub fn set_high(&mut self) { - self.pin.set_output_high(true, private::Internal); + self.set_level(Level::High); } /// Set the output as low. #[inline] pub fn set_low(&mut self) { - self.pin.set_output_high(false, private::Internal); + self.set_level(Level::Low); } /// Set the output level. #[inline] pub fn set_level(&mut self, level: Level) { - self.pin.set_output_high(level.into(), private::Internal); + self.pin.set_level(level); } /// Is the output pin set as high? #[inline] pub fn is_set_high(&self) -> bool { - self.pin.is_set_high(private::Internal) + self.get_output_level() == Level::High } /// Is the output pin set as low? #[inline] pub fn is_set_low(&self) -> bool { - !self.pin.is_set_high(private::Internal) + self.get_output_level() == Level::Low } /// What level output is set to #[inline] pub fn get_output_level(&self) -> Level { - self.pin.is_set_high(private::Internal).into() + self.pin.get_output_level() } /// Toggle pin output #[inline] pub fn toggle(&mut self) { - let level = !self.pin.is_set_high(private::Internal); - self.pin.set_output_high(level, private::Internal); + self.pin.toggle() } /// Configure the [DriveStrength] of the pin pub fn set_drive_strength(&mut self, strength: DriveStrength) { - self.pin.set_drive_strength(strength, private::Internal); + self.pin.set_drive_strength(strength); } } -/// Generic GPIO output driver. -pub struct AnyOutput<'d> { - pin: ErasedPin, - _phantom: PhantomData<&'d ()>, +/// Flexible pin driver. +pub struct Flex<'d, P = ErasedPin> { + pin: PeripheralRef<'d, P>, } -impl<'d> AnyOutput<'d> { - /// Create GPIO output driver for a [GpioPin] with the provided level - #[inline] - pub fn new( - pin: impl crate::peripheral::Peripheral

+ 'd, - initial_output: Level, - ) -> Self { - crate::into_ref!(pin); - - pin.set_output_high(initial_output.into(), private::Internal); - pin.set_to_push_pull_output(private::Internal); - - let pin = pin.erased_pin(private::Internal); - - Self { - pin, - _phantom: PhantomData, - } - } - - /// Set the output as high. - #[inline] - pub fn set_high(&mut self) { - self.pin.set_output_high(true, private::Internal); - } - - /// Set the output as low. - #[inline] - pub fn set_low(&mut self) { - self.pin.set_output_high(false, private::Internal); - } - - /// Set the output level. - #[inline] - pub fn set_level(&mut self, level: Level) { - self.pin.set_output_high(level.into(), private::Internal); - } - - /// Is the output pin set as high? - #[inline] - pub fn is_set_high(&self) -> bool { - self.pin.is_set_high(private::Internal) - } - - /// Is the output pin set as low? - #[inline] - pub fn is_set_low(&self) -> bool { - !self.pin.is_set_high(private::Internal) - } +impl

private::Sealed for Flex<'_, P> {} - /// What level output is set to - #[inline] - pub fn get_output_level(&self) -> Level { - self.pin.is_set_high(private::Internal).into() +impl<'d, P> Peripheral for Flex<'d, P> { + type P = P; + unsafe fn clone_unchecked(&mut self) -> P { + core::ptr::read(&*self.pin as *const _) } +} - /// Toggle pin output +impl<'d> Flex<'d> { + /// Create flexible pin driver for a [Pin]. + /// No mode change happens. #[inline] - pub fn toggle(&mut self) { - let pin = &mut self.pin; - pin.set_output_high(!pin.is_set_high(private::Internal), private::Internal); + pub fn new(pin: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(pin); + let pin = pin.degrade_internal(private::Internal); + Self::new_typed(pin) } } -/// Generic GPIO input driver. -pub struct AnyInput<'d> { - pin: ErasedPin, - _phantom: PhantomData<&'d ()>, -} - -impl<'d> AnyInput<'d> { - /// Create GPIO input driver for a [Pin] with the provided [Pull] - /// configuration. +impl<'d, P> Flex<'d, P> +where + P: Pin, +{ + /// Create flexible pin driver for a [Pin]. + /// No mode change happens. #[inline] - pub fn new( - pin: impl crate::peripheral::Peripheral

+ 'd, - pull: Pull, - ) -> Self { + pub fn new_typed(pin: impl Peripheral

+ 'd) -> Self { crate::into_ref!(pin); - pin.init_input(pull == Pull::Down, pull == Pull::Up, private::Internal); - - let pin = pin.erased_pin(private::Internal); + Self { pin } + } +} - Self { - pin, - _phantom: PhantomData, - } +impl<'d, P> Flex<'d, P> +where + P: InputPin, +{ + /// Set the GPIO to input mode. + pub fn set_as_input(&mut self, pull: Pull) { + self.pin + .init_input(pull == Pull::Down, pull == Pull::Up, private::Internal); } /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { - self.pin.is_input_high(private::Internal) + self.get_level() == Level::High } /// Get whether the pin input level is low. #[inline] pub fn is_low(&self) -> bool { - !self.is_high() + self.get_level() == Level::Low } /// Get the current pin input level. #[inline] pub fn get_level(&self) -> Level { - self.is_high().into() + self.pin.is_input_high(private::Internal).into() } /// Listen for interrupts @@ -2299,82 +2127,51 @@ impl<'d> AnyInput<'d> { self.pin.listen(event, private::Internal); } + /// Stop listening for interrupts + pub fn unlisten(&mut self) { + self.pin.unlisten(private::Internal); + } + /// Clear the interrupt status bit for this Pin #[inline] pub fn clear_interrupt(&mut self) { self.pin.clear_interrupt(private::Internal); } -} - -/// Generic GPIO open-drain output driver. -pub struct AnyOutputOpenDrain<'d> { - pin: ErasedPin, - _phantom: PhantomData<&'d ()>, -} - -impl<'d> AnyOutputOpenDrain<'d> { - /// Create GPIO open-drain output driver for a [Pin] with the provided - /// initial output-level and [Pull] configuration. - #[inline] - pub fn new( - pin: impl crate::peripheral::Peripheral

+ 'd, - initial_output: Level, - pull: Pull, - ) -> Self { - crate::into_ref!(pin); - pin.set_output_high(initial_output.into(), private::Internal); - pin.internal_pull_down(pull == Pull::Down, private::Internal); - pin.internal_pull_up(pull == Pull::Up, private::Internal); - pin.set_to_open_drain_output(private::Internal); - - let pin = pin.erased_pin(private::Internal); - - Self { - pin, - _phantom: PhantomData, - } - } - - /// Get whether the pin input level is high. - #[inline] - pub fn is_high(&self) -> bool { - self.pin.is_input_high(private::Internal) - } - /// Get whether the pin input level is low. - #[inline] - pub fn is_low(&self) -> bool { - !self.is_high() - } - - /// Get the current pin input level. + /// Checks if the interrupt status bit for this Pin is set #[inline] - pub fn get_level(&self) -> Level { - self.is_high().into() + pub fn is_interrupt_set(&self) -> bool { + self.pin.is_interrupt_set(private::Internal) } - /// Listen for interrupts + /// Enable as a wake-up source. + /// + /// This will unlisten for interrupts #[inline] - pub fn listen(&mut self, event: Event) { - self.pin.listen(event, private::Internal); + pub fn wakeup_enable(&mut self, enable: bool, event: WakeEvent) { + self.pin.wakeup_enable(enable, event, private::Internal); } +} - /// Clear the interrupt status bit for this Pin - #[inline] - pub fn clear_interrupt(&mut self) { - self.pin.clear_interrupt(private::Internal); +impl<'d, P> Flex<'d, P> +where + P: OutputPin, +{ + /// Set the GPIO to output mode. + pub fn set_as_output(&mut self) { + self.pin.set_to_push_pull_output(private::Internal); } /// Set the output as high. #[inline] pub fn set_high(&mut self) { - self.pin.set_output_high(true, private::Internal); + self.set_level(Level::High) } /// Set the output as low. #[inline] pub fn set_low(&mut self) { - self.pin.set_output_high(false, private::Internal); + self.set_level(Level::Low) } /// Set the output level. @@ -2386,13 +2183,13 @@ impl<'d> AnyOutputOpenDrain<'d> { /// Is the output pin set as high? #[inline] pub fn is_set_high(&self) -> bool { - self.pin.is_set_high(private::Internal) + self.get_output_level() == Level::High } /// Is the output pin set as low? #[inline] pub fn is_set_low(&self) -> bool { - !self.pin.is_set_high(private::Internal) + self.get_output_level() == Level::Low } /// What level output is set to @@ -2404,32 +2201,20 @@ impl<'d> AnyOutputOpenDrain<'d> { /// Toggle pin output #[inline] pub fn toggle(&mut self) { - let pin = &mut self.pin; - pin.set_output_high(!pin.is_set_high(private::Internal), private::Internal); + let level = !self.get_output_level(); + self.set_level(level); } -} - -/// Generic GPIO flexible pin driver. -pub struct AnyFlex<'d> { - pin: ErasedPin, - _phantom: PhantomData<&'d ()>, -} -impl<'d> AnyFlex<'d> { - /// Create GPIO flexible pin driver for a [Pin]. - /// No mode change happens. - #[inline] - pub fn new( - pin: impl crate::peripheral::Peripheral

+ 'd, - ) -> Self { - crate::into_ref!(pin); - let pin = pin.erased_pin(private::Internal); - Self { - pin, - _phantom: PhantomData, - } + /// Configure the [DriveStrength] of the pin + pub fn set_drive_strength(&mut self, strength: DriveStrength) { + self.pin.set_drive_strength(strength, private::Internal); } +} +impl<'d, P> Flex<'d, P> +where + P: InputPin + OutputPin, +{ /// Set the GPIO to open-drain mode. pub fn set_as_open_drain(&mut self, pull: Pull) { self.pin.set_to_open_drain_output(private::Internal); @@ -2438,90 +2223,6 @@ impl<'d> AnyFlex<'d> { self.pin .internal_pull_up(pull == Pull::Up, private::Internal); } - - /// Set the GPIO to input mode. - pub fn set_as_input(&mut self, pull: Pull) { - self.pin - .init_input(pull == Pull::Down, pull == Pull::Up, private::Internal); - } - - /// Set the GPIO to output mode. - pub fn set_as_output(&mut self) { - self.pin.set_to_push_pull_output(private::Internal); - } - - /// Get whether the pin input level is high. - #[inline] - pub fn is_high(&self) -> bool { - self.pin.is_input_high(private::Internal) - } - - /// Get whether the pin input level is low. - #[inline] - pub fn is_low(&self) -> bool { - !self.is_high() - } - - /// Get the current pin input level. - #[inline] - pub fn get_level(&self) -> Level { - self.is_high().into() - } - - /// Listen for interrupts - #[inline] - pub fn listen(&mut self, event: Event) { - self.pin.listen(event, private::Internal); - } - - /// Clear the interrupt status bit for this Pin - #[inline] - pub fn clear_interrupt(&mut self) { - self.pin.clear_interrupt(private::Internal); - } - - /// Set the output as high. - #[inline] - pub fn set_high(&mut self) { - self.pin.set_output_high(true, private::Internal); - } - - /// Set the output as low. - #[inline] - pub fn set_low(&mut self) { - self.pin.set_output_high(false, private::Internal); - } - - /// Set the output level. - #[inline] - pub fn set_level(&mut self, level: Level) { - self.pin.set_output_high(level.into(), private::Internal); - } - - /// Is the output pin set as high? - #[inline] - pub fn is_set_high(&self) -> bool { - self.pin.is_set_high(private::Internal) - } - - /// Is the output pin set as low? - #[inline] - pub fn is_set_low(&self) -> bool { - !self.pin.is_set_high(private::Internal) - } - - /// What level output is set to - #[inline] - pub fn get_output_level(&self) -> Level { - self.pin.is_set_high(private::Internal).into() - } - - /// Toggle pin output - #[inline] - pub fn toggle(&mut self) { - let pin = &mut self.pin; - pin.set_output_high(!pin.is_set_high(private::Internal), private::Internal); - } } pub(crate) mod internal { @@ -2531,27 +2232,25 @@ pub(crate) mod internal { impl Pin for ErasedPin { fn number(&self, _: private::Internal) -> u8 { - handle_gpio_input!(self, target, { Pin::number(target, private::Internal) }) + handle_gpio_input!(&self.0, target, { Pin::number(target, private::Internal) }) + } + + fn degrade_internal(&self, _: private::Internal) -> ErasedPin { + unsafe { self.clone_unchecked() } } fn sleep_mode(&mut self, on: bool, _: private::Internal) { - handle_gpio_input!(self, target, { + handle_gpio_input!(&mut self.0, target, { Pin::sleep_mode(target, on, private::Internal) }) } fn set_alternate_function(&mut self, alternate: AlternateFunction, _: private::Internal) { - handle_gpio_input!(self, target, { + handle_gpio_input!(&mut self.0, target, { Pin::set_alternate_function(target, alternate, private::Internal) }) } - fn is_listening(&self, _: private::Internal) -> bool { - handle_gpio_input!(self, target, { - Pin::is_listening(target, private::Internal) - }) - } - fn listen_with_options( &mut self, event: Event, @@ -2560,7 +2259,7 @@ pub(crate) mod internal { wake_up_from_light_sleep: bool, _: private::Internal, ) { - handle_gpio_input!(self, target, { + handle_gpio_input!(&mut self.0, target, { Pin::listen_with_options( target, event, @@ -2573,65 +2272,49 @@ pub(crate) mod internal { } fn unlisten(&mut self, _: private::Internal) { - handle_gpio_input!(self, target, { Pin::unlisten(target, private::Internal) }) + handle_gpio_input!(&mut self.0, target, { + Pin::unlisten(target, private::Internal) + }) } fn is_interrupt_set(&self, _: private::Internal) -> bool { - handle_gpio_input!(self, target, { + handle_gpio_input!(&self.0, target, { Pin::is_interrupt_set(target, private::Internal) }) } fn clear_interrupt(&mut self, _: private::Internal) { - handle_gpio_input!(self, target, { + handle_gpio_input!(&mut self.0, target, { Pin::clear_interrupt(target, private::Internal) }) } - - fn wakeup_enable(&mut self, enable: bool, event: WakeEvent, _: private::Internal) { - handle_gpio_input!(self, target, { - Pin::wakeup_enable(target, enable, event, private::Internal) - }) - } } impl InputPin for ErasedPin { fn init_input(&self, pull_down: bool, pull_up: bool, _: private::Internal) { - handle_gpio_input!(self, target, { + handle_gpio_input!(&self.0, target, { InputPin::init_input(target, pull_down, pull_up, private::Internal) }) } - fn set_to_input(&mut self, _: private::Internal) { - handle_gpio_input!(self, target, { - InputPin::set_to_input(target, private::Internal); - }); - } - fn enable_input(&mut self, on: bool, _: private::Internal) { - handle_gpio_input!(self, target, { + handle_gpio_input!(&mut self.0, target, { InputPin::enable_input(target, on, private::Internal) }); } fn enable_input_in_sleep_mode(&mut self, on: bool, _: private::Internal) { - handle_gpio_input!(self, target, { + handle_gpio_input!(&mut self.0, target, { InputPin::enable_input_in_sleep_mode(target, on, private::Internal) }); } fn is_input_high(&self, _: private::Internal) -> bool { - handle_gpio_input!(self, target, { + handle_gpio_input!(&self.0, target, { InputPin::is_input_high(target, private::Internal) }) } - fn connect_input_to_peripheral(&mut self, signal: InputSignal, _: private::Internal) { - handle_gpio_input!(self, target, { - InputPin::connect_input_to_peripheral(target, signal, private::Internal) - }); - } - fn connect_input_to_peripheral_with_options( &mut self, signal: InputSignal, @@ -2639,7 +2322,7 @@ pub(crate) mod internal { force_via_gpio_mux: bool, _: private::Internal, ) { - handle_gpio_input!(self, target, { + handle_gpio_input!(&mut self.0, target, { InputPin::connect_input_to_peripheral_with_options( target, signal, @@ -2651,7 +2334,7 @@ pub(crate) mod internal { } fn disconnect_input_from_peripheral(&mut self, signal: InputSignal, _: private::Internal) { - handle_gpio_input!(self, target, { + handle_gpio_input!(&mut self.0, target, { InputPin::disconnect_input_from_peripheral(target, signal, private::Internal) }); } @@ -2659,77 +2342,71 @@ pub(crate) mod internal { impl OutputPin for ErasedPin { fn set_to_open_drain_output(&mut self, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::set_to_open_drain_output(target, private::Internal) }); } fn set_to_push_pull_output(&mut self, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::set_to_push_pull_output(target, private::Internal) }); } fn enable_output(&mut self, on: bool, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::enable_output(target, on, private::Internal) }); } fn set_output_high(&mut self, on: bool, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::set_output_high(target, on, private::Internal) }); } fn set_drive_strength(&mut self, strength: DriveStrength, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::set_drive_strength(target, strength, private::Internal) }); } fn enable_open_drain(&mut self, on: bool, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::enable_open_drain(target, on, private::Internal) }); } fn enable_output_in_sleep_mode(&mut self, on: bool, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::enable_output_in_sleep_mode(target, on, private::Internal) }); } fn internal_pull_up_in_sleep_mode(&mut self, on: bool, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::internal_pull_up_in_sleep_mode(target, on, private::Internal) }); } fn internal_pull_down_in_sleep_mode(&mut self, on: bool, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::internal_pull_down_in_sleep_mode(target, on, private::Internal) }); } fn internal_pull_up(&mut self, on: bool, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::internal_pull_up(target, on, private::Internal) }); } fn internal_pull_down(&mut self, on: bool, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::internal_pull_down(target, on, private::Internal) }); } - fn connect_peripheral_to_output(&mut self, signal: OutputSignal, _: private::Internal) { - handle_gpio_output!(self, target, { - OutputPin::connect_peripheral_to_output(target, signal, private::Internal) - }); - } - fn connect_peripheral_to_output_with_options( &mut self, signal: OutputSignal, @@ -2739,7 +2416,7 @@ pub(crate) mod internal { force_via_gpio_mux: bool, _: private::Internal, ) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::connect_peripheral_to_output_with_options( target, signal, @@ -2753,20 +2430,40 @@ pub(crate) mod internal { } fn disconnect_peripheral_from_output(&mut self, _: private::Internal) { - handle_gpio_output!(self, target, { + handle_gpio_output!(&mut self.0, target, { OutputPin::disconnect_peripheral_from_output(target, private::Internal) }); } fn is_set_high(&self, _: private::Internal) -> bool { - handle_gpio_output!(self, target, { + handle_gpio_output!(&self.0, target, { OutputPin::is_set_high(target, private::Internal) }) } } } -#[cfg(feature = "async")] +fn is_listening(pin_num: u8) -> bool { + let bits = unsafe { &*GPIO::PTR } + .pin(pin_num as usize) + .read() + .int_ena() + .bits(); + bits != 0 +} + +fn set_int_enable(gpio_num: u8, int_ena: u8, int_type: u8, wake_up_from_light_sleep: bool) { + let gpio = unsafe { &*crate::peripherals::GPIO::PTR }; + gpio.pin(gpio_num as usize).modify(|_, w| unsafe { + w.int_ena() + .bits(int_ena) + .int_type() + .bits(int_type) + .wakeup_enable() + .bit(wake_up_from_light_sleep) + }); +} + mod asynch { use core::task::{Context, Poll}; @@ -2778,7 +2475,7 @@ mod asynch { const NEW_AW: AtomicWaker = AtomicWaker::new(); static PIN_WAKERS: [AtomicWaker; NUM_PINS] = [NEW_AW; NUM_PINS]; - impl<'d, P> Input<'d, P> + impl<'d, P> Flex<'d, P> where P: InputPin, { @@ -2815,46 +2512,45 @@ mod asynch { } } - impl<'d> AnyInput<'d> { + impl<'d, P> Input<'d, P> + where + P: InputPin, + { /// Wait until the pin is high. If it is already high, return /// immediately. pub async fn wait_for_high(&mut self) { - self.listen(Event::HighLevel); - PinFuture::new(self.pin.number(private::Internal)).await + self.pin.wait_for_high().await } /// Wait until the pin is low. If it is already low, return immediately. pub async fn wait_for_low(&mut self) { - self.listen(Event::LowLevel); - PinFuture::new(self.pin.number(private::Internal)).await + self.pin.wait_for_low().await } /// Wait for the pin to undergo a transition from low to high. pub async fn wait_for_rising_edge(&mut self) { - self.listen(Event::RisingEdge); - PinFuture::new(self.pin.number(private::Internal)).await + self.pin.wait_for_rising_edge().await } /// Wait for the pin to undergo a transition from high to low. pub async fn wait_for_falling_edge(&mut self) { - self.listen(Event::FallingEdge); - PinFuture::new(self.pin.number(private::Internal)).await + self.pin.wait_for_falling_edge().await } /// Wait for the pin to undergo any transition, i.e low to high OR high /// to low. pub async fn wait_for_any_edge(&mut self) { - self.listen(Event::AnyEdge); - PinFuture::new(self.pin.number(private::Internal)).await + self.pin.wait_for_any_edge().await } } - pub struct PinFuture { + #[must_use = "futures do nothing unless you `.await` or poll them"] + struct PinFuture { pin_num: u8, } impl PinFuture { - pub fn new(pin_num: u8) -> Self { + fn new(pin_num: u8) -> Self { Self { pin_num } } } @@ -2875,32 +2571,6 @@ mod asynch { } } - pub(crate) fn is_listening(gpio_num: u8) -> bool { - let bits = unsafe { &*GPIO::PTR } - .pin(gpio_num as usize) - .read() - .int_ena() - .bits(); - bits != 0 - } - - pub(crate) fn set_int_enable( - gpio_num: u8, - int_ena: u8, - int_type: u8, - wake_up_from_light_sleep: bool, - ) { - let gpio = unsafe { &*crate::peripherals::GPIO::PTR }; - gpio.pin(gpio_num as usize).modify(|_, w| unsafe { - w.int_ena() - .bits(int_ena) - .int_type() - .bits(int_type) - .wakeup_enable() - .bit(wake_up_from_light_sleep) - }); - } - #[ram] pub(super) fn handle_gpio_interrupt() { let intrs_bank0 = InterruptStatusRegisterAccessBank0::interrupt_status_read(); @@ -2933,7 +2603,6 @@ mod asynch { } } -#[cfg(feature = "embedded-hal-02")] mod embedded_hal_02_impls { use embedded_hal_02::digital::v2 as digital; @@ -2946,10 +2615,10 @@ mod embedded_hal_02_impls { type Error = core::convert::Infallible; fn is_high(&self) -> Result { - Ok(self.pin.is_input_high(private::Internal)) + Ok(self.pin.is_high()) } fn is_low(&self) -> Result { - Ok(!self.pin.is_input_high(private::Internal)) + Ok(self.pin.is_low()) } } @@ -2960,11 +2629,11 @@ mod embedded_hal_02_impls { type Error = core::convert::Infallible; fn set_high(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(true, private::Internal); + self.pin.is_set_high(); Ok(()) } fn set_low(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(false, private::Internal); + self.pin.is_set_low(); Ok(()) } } @@ -3000,10 +2669,10 @@ mod embedded_hal_02_impls { type Error = core::convert::Infallible; fn is_high(&self) -> Result { - Ok(self.pin.is_input_high(private::Internal)) + Ok(self.pin.is_high()) } fn is_low(&self) -> Result { - Ok(!self.pin.is_input_high(private::Internal)) + Ok(self.pin.is_low()) } } @@ -3014,11 +2683,12 @@ mod embedded_hal_02_impls { type Error = core::convert::Infallible; fn set_high(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(true, private::Internal); + self.set_high(); Ok(()) } + fn set_low(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(false, private::Internal); + self.set_low(); Ok(()) } } @@ -3054,10 +2724,10 @@ mod embedded_hal_02_impls { type Error = core::convert::Infallible; fn is_high(&self) -> Result { - Ok(self.pin.is_input_high(private::Internal)) + Ok(self.is_high()) } fn is_low(&self) -> Result { - Ok(!self.pin.is_input_high(private::Internal)) + Ok(self.is_low()) } } @@ -3100,135 +2770,8 @@ mod embedded_hal_02_impls { Ok(()) } } - - impl<'d> digital::InputPin for AnyInput<'d> { - type Error = core::convert::Infallible; - - fn is_high(&self) -> Result { - Ok(self.pin.is_input_high(private::Internal)) - } - fn is_low(&self) -> Result { - Ok(!self.pin.is_input_high(private::Internal)) - } - } - - impl<'d> digital::OutputPin for AnyOutput<'d> { - type Error = core::convert::Infallible; - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(true, private::Internal); - Ok(()) - } - fn set_low(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(false, private::Internal); - Ok(()) - } - } - - impl<'d> digital::StatefulOutputPin for AnyOutput<'d> { - fn is_set_high(&self) -> Result { - Ok(self.is_set_high()) - } - fn is_set_low(&self) -> Result { - Ok(self.is_set_low()) - } - } - - impl<'d> digital::ToggleableOutputPin for AnyOutput<'d> { - type Error = core::convert::Infallible; - - fn toggle(&mut self) -> Result<(), Self::Error> { - self.toggle(); - Ok(()) - } - } - - impl<'d> digital::InputPin for AnyOutputOpenDrain<'d> { - type Error = core::convert::Infallible; - - fn is_high(&self) -> Result { - Ok(self.pin.is_input_high(private::Internal)) - } - fn is_low(&self) -> Result { - Ok(!self.pin.is_input_high(private::Internal)) - } - } - - impl<'d> digital::OutputPin for AnyOutputOpenDrain<'d> { - type Error = core::convert::Infallible; - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(true, private::Internal); - Ok(()) - } - fn set_low(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(false, private::Internal); - Ok(()) - } - } - - impl<'d> digital::StatefulOutputPin for AnyOutputOpenDrain<'d> { - fn is_set_high(&self) -> Result { - Ok(self.is_set_high()) - } - fn is_set_low(&self) -> Result { - Ok(self.is_set_low()) - } - } - - impl<'d> digital::ToggleableOutputPin for AnyOutputOpenDrain<'d> { - type Error = core::convert::Infallible; - - fn toggle(&mut self) -> Result<(), Self::Error> { - self.toggle(); - Ok(()) - } - } - - impl<'d> digital::InputPin for AnyFlex<'d> { - type Error = core::convert::Infallible; - - fn is_high(&self) -> Result { - Ok(self.pin.is_input_high(private::Internal)) - } - fn is_low(&self) -> Result { - Ok(!self.pin.is_input_high(private::Internal)) - } - } - - impl<'d> digital::OutputPin for AnyFlex<'d> { - type Error = core::convert::Infallible; - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(true, private::Internal); - Ok(()) - } - fn set_low(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(false, private::Internal); - Ok(()) - } - } - - impl<'d> digital::StatefulOutputPin for AnyFlex<'d> { - fn is_set_high(&self) -> Result { - Ok(self.is_set_high()) - } - fn is_set_low(&self) -> Result { - Ok(self.is_set_low()) - } - } - - impl<'d> digital::ToggleableOutputPin for AnyFlex<'d> { - type Error = core::convert::Infallible; - - fn toggle(&mut self) -> Result<(), Self::Error> { - self.toggle(); - Ok(()) - } - } } -#[cfg(feature = "embedded-hal")] mod embedded_hal_impls { use embedded_hal::digital; @@ -3246,11 +2789,11 @@ mod embedded_hal_impls { P: InputPin, { fn is_high(&mut self) -> Result { - Ok(Input::is_high(self)) + Ok(Self::is_high(self)) } fn is_low(&mut self) -> Result { - Ok(Input::is_low(self)) + Ok(Self::is_low(self)) } } @@ -3266,12 +2809,12 @@ mod embedded_hal_impls { P: OutputPin, { fn set_low(&mut self) -> Result<(), Self::Error> { - self.set_low(); + Self::set_low(self); Ok(()) } fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_high(); + Self::set_high(self); Ok(()) } } @@ -3294,11 +2837,11 @@ mod embedded_hal_impls { P: InputPin + OutputPin, { fn is_high(&mut self) -> Result { - Ok(OutputOpenDrain::is_high(self)) + Ok(Self::is_high(self)) } fn is_low(&mut self) -> Result { - Ok(OutputOpenDrain::is_low(self)) + Ok(Self::is_low(self)) } } @@ -3314,12 +2857,12 @@ mod embedded_hal_impls { P: InputPin + OutputPin, { fn set_low(&mut self) -> Result<(), Self::Error> { - self.set_low(); + Self::set_low(self); Ok(()) } fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_high(); + Self::set_high(self); Ok(()) } } @@ -3339,42 +2882,39 @@ mod embedded_hal_impls { impl<'d, P> digital::InputPin for Flex<'d, P> where - P: InputPin + OutputPin, + P: InputPin, { fn is_high(&mut self) -> Result { - Ok(Flex::is_high(self)) + Ok(Self::is_high(self)) } fn is_low(&mut self) -> Result { - Ok(Flex::is_low(self)) + Ok(Self::is_low(self)) } } - impl<'d, P> digital::ErrorType for Flex<'d, P> - where - P: InputPin + OutputPin, - { + impl<'d, P> digital::ErrorType for Flex<'d, P> { type Error = core::convert::Infallible; } impl<'d, P> digital::OutputPin for Flex<'d, P> where - P: InputPin + OutputPin, + P: OutputPin, { fn set_low(&mut self) -> Result<(), Self::Error> { - self.set_low(); + Self::set_low(self); Ok(()) } fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_high(); + Self::set_high(self); Ok(()) } } impl<'d, P> digital::StatefulOutputPin for Flex<'d, P> where - P: InputPin + OutputPin, + P: OutputPin, { fn is_set_high(&mut self) -> Result { Ok(Self::is_set_high(self)) @@ -3384,160 +2924,69 @@ mod embedded_hal_impls { Ok(Self::is_set_low(self)) } } - - impl<'d> digital::ErrorType for AnyInput<'d> { - type Error = core::convert::Infallible; - } - - impl<'d> digital::InputPin for AnyInput<'d> { - fn is_high(&mut self) -> Result { - Ok(AnyInput::is_high(self)) - } - - fn is_low(&mut self) -> Result { - Ok(AnyInput::is_low(self)) - } - } - - impl<'d> digital::ErrorType for AnyOutput<'d> { - type Error = core::convert::Infallible; - } - - impl<'d> digital::OutputPin for AnyOutput<'d> { - fn set_low(&mut self) -> Result<(), Self::Error> { - self.set_low(); - Ok(()) - } - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_high(); - Ok(()) - } - } - - impl<'d> digital::StatefulOutputPin for AnyOutput<'d> { - fn is_set_high(&mut self) -> Result { - Ok(Self::is_set_high(self)) - } - - fn is_set_low(&mut self) -> Result { - Ok(Self::is_set_low(self)) - } - } - - impl<'d> digital::ErrorType for AnyOutputOpenDrain<'d> { - type Error = core::convert::Infallible; - } - - impl<'d> digital::OutputPin for AnyOutputOpenDrain<'d> { - fn set_low(&mut self) -> Result<(), Self::Error> { - self.set_low(); - Ok(()) - } - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_high(); - Ok(()) - } - } - - impl<'d> digital::StatefulOutputPin for AnyOutputOpenDrain<'d> { - fn is_set_high(&mut self) -> Result { - Ok(Self::is_set_high(self)) - } - - fn is_set_low(&mut self) -> Result { - Ok(Self::is_set_low(self)) - } - } - - impl<'d> digital::ErrorType for AnyFlex<'d> { - type Error = core::convert::Infallible; - } - - impl<'d> digital::OutputPin for AnyFlex<'d> { - fn set_low(&mut self) -> Result<(), Self::Error> { - self.set_low(); - Ok(()) - } - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_high(); - Ok(()) - } - } - - impl<'d> digital::StatefulOutputPin for AnyFlex<'d> { - fn is_set_high(&mut self) -> Result { - Ok(Self::is_set_high(self)) - } - - fn is_set_low(&mut self) -> Result { - Ok(Self::is_set_low(self)) - } - } } -#[cfg(feature = "embedded-hal")] -#[cfg(feature = "async")] mod embedded_hal_async_impls { use embedded_hal_async::digital::Wait; use super::*; - impl<'d, P> Wait for Input<'d, P> + impl<'d, P> Wait for Flex<'d, P> where P: InputPin, { async fn wait_for_high(&mut self) -> Result<(), Self::Error> { - self.wait_for_high().await; + Self::wait_for_high(self).await; Ok(()) } async fn wait_for_low(&mut self) -> Result<(), Self::Error> { - self.wait_for_low().await; + Self::wait_for_low(self).await; Ok(()) } async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_rising_edge().await; + Self::wait_for_rising_edge(self).await; Ok(()) } async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_falling_edge().await; + Self::wait_for_falling_edge(self).await; Ok(()) } async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_any_edge().await; + Self::wait_for_any_edge(self).await; Ok(()) } } - impl<'d> Wait for AnyInput<'d> { + impl<'d, P> Wait for Input<'d, P> + where + P: InputPin, + { async fn wait_for_high(&mut self) -> Result<(), Self::Error> { - self.wait_for_high().await; + Self::wait_for_high(self).await; Ok(()) } async fn wait_for_low(&mut self) -> Result<(), Self::Error> { - self.wait_for_low().await; + Self::wait_for_low(self).await; Ok(()) } async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_rising_edge().await; + Self::wait_for_rising_edge(self).await; Ok(()) } async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_falling_edge().await; + Self::wait_for_falling_edge(self).await; Ok(()) } async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_any_edge().await; + Self::wait_for_any_edge(self).await; Ok(()) } } diff --git a/esp-hal/src/gpio/rtc_io.rs b/esp-hal/src/gpio/rtc_io.rs index 83c97dbbe46..db3ed328829 100644 --- a/esp-hal/src/gpio/rtc_io.rs +++ b/esp-hal/src/gpio/rtc_io.rs @@ -1,10 +1,12 @@ //! RTC IO //! //! # Overview +//! //! The hardware provides a couple of GPIO pins with low power (LP) //! capabilities and analog functions. //! //! ## Configuration +//! //! These pins can be controlled by either IO MUX or RTC IO. //! //! If controlled by RTC IO, these pins will bypass IO MUX and GPIO @@ -14,12 +16,18 @@ //! the peripherals in RTC system during chip Deep-sleep, and wake up the //! chip from Deep-sleep. //! -//! # Example -//! ## Configure a ULP Pin as Output -//! ```rust, ignore +//! ## Example +//! +//! ### Configure a ULP Pin as Output +//! +//! ```rust, no_run +#![doc = crate::before_snippet!()] +//! # use esp_hal::gpio::rtc_io::LowPowerOutput; +//! # use esp_hal::gpio::Io; //! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! // configure GPIO 1 as ULP output pin -//! let lp_pin = LowPowerOutput::new(io.pins.gpio1); +//! let lp_pin = LowPowerOutput::<'static, 1>::new(io.pins.gpio1); +//! # } //! ``` use core::marker::PhantomData; @@ -169,24 +177,13 @@ impl<'d, const PIN: u8> LowPowerOutputOpenDrain<'d, PIN> { } } -#[cfg(esp32s3)] -#[inline(always)] -fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD0 { - unsafe { - let rtc_io = &*crate::peripherals::RTC_IO::PTR; - let pin_ptr = (rtc_io.touch_pad0().as_ptr()).add(pin as usize); - - &*(pin_ptr as *const esp32s3::generic::Reg) - } -} - -#[cfg(esp32s2)] +#[cfg(any(esp32s2, esp32s3))] #[inline(always)] fn get_pin_reg(pin: u8) -> &'static crate::peripherals::rtc_io::TOUCH_PAD { unsafe { let rtc_io = &*crate::peripherals::RTC_IO::PTR; let pin_ptr = (rtc_io.touch_pad(0).as_ptr()).add(pin as usize); - &*(pin_ptr as *const esp32s2::generic::Reg) + &*(pin_ptr as *const crate::peripherals::rtc_io::TOUCH_PAD) } } diff --git a/esp-hal/src/hmac.rs b/esp-hal/src/hmac.rs index f2b37a83048..e7709972b50 100644 --- a/esp-hal/src/hmac.rs +++ b/esp-hal/src/hmac.rs @@ -43,6 +43,9 @@ use crate::{ system::{Peripheral as PeripheralEnable, PeripheralClockControl}, }; +/// Provides an interface for interacting with the HMAC hardware peripheral. +/// It allows users to compute HMACs for cryptographic purposes, ensuring data +/// integrity and authenticity. pub struct Hmac<'d> { hmac: PeripheralRef<'d, HMAC>, alignment_helper: AlignmentHelper, @@ -77,12 +80,19 @@ pub enum HmacPurpose { #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Represents the key identifiers for the HMAC peripheral. pub enum KeyId { + /// Key 0. Key0 = 0, + /// Key 1. Key1 = 1, + /// Key 2. Key2 = 2, + /// Key 3. Key3 = 3, + /// Key 4. Key4 = 4, + /// Key 5. Key5 = 5, } @@ -93,10 +103,11 @@ enum NextCommand { } impl<'d> Hmac<'d> { + /// Creates a new instance of the HMAC peripheral. pub fn new(hmac: impl Peripheral

+ 'd) -> Self { crate::into_ref!(hmac); - PeripheralClockControl::enable(PeripheralEnable::Sha); + PeripheralClockControl::reset(PeripheralEnable::Hmac); PeripheralClockControl::enable(PeripheralEnable::Hmac); Self { @@ -107,10 +118,6 @@ impl<'d> Hmac<'d> { } } - pub fn free(self) -> PeripheralRef<'d, HMAC> { - self.hmac - } - /// Step 1. Enable HMAC module. /// /// Before these steps, the user shall set the peripheral clocks bits for @@ -157,6 +164,7 @@ impl<'d> Hmac<'d> { Ok(remaining) } + /// Finalizes the HMAC computation and retrieves the resulting hash output. pub fn finalize(&mut self, output: &mut [u8]) -> nb::Result<(), Infallible> { if self.is_busy() { return Err(nb::Error::WouldBlock); diff --git a/esp-hal/src/i2c.rs b/esp-hal/src/i2c.rs index 643df5541e1..69bc04ab730 100644 --- a/esp-hal/src/i2c.rs +++ b/esp-hal/src/i2c.rs @@ -5,63 +5,45 @@ //! same bus. I2C uses two bidirectional open-drain lines: serial data line //! (SDA) and serial clock line (SCL), pulled up by resistors. //! -//! Espressif devices sometimes have more than one I2C controller (also called -//! port), responsible for handling communication on the I2C bus. A single I2C -//! controller can be a master or a slave. +//! Espressif devices sometimes have more than one I2C controller, responsible +//! for handling communication on the I2C bus. A single I2C controller can be +//! a master or a slave. //! //! Typically, an I2C slave device has a 7-bit address or 10-bit address. -//! Espressif devices supports both I2C Standard-mode (Sm) and Fast-mode -//! (Fm) which can go up to 100KHz and 400KHz respectively. +//! Devices supports both I2C Standard-mode (Sm) and Fast-mode (Fm) which can +//! go up to 100KHz and 400KHz respectively. //! //! ## Configuration //! //! Each I2C controller is individually configurable, and the usual setting //! such as frequency, timeout, and SDA/SCL pins can easily be configured. //! -//! ```rust, no_run -#![doc = crate::before_snippet!()] -//! # use esp_hal::i2c::I2C; -//! # use esp_hal::gpio::Io; -//! # use core::option::Option::None; -//! # use crate::esp_hal::prelude::_fugit_RateExtU32; -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! // Create a new peripheral object with the described wiring -//! // and standard I2C clock speed -//! let mut i2c = I2C::new( -//! peripherals.I2C0, -//! io.pins.gpio1, -//! io.pins.gpio2, -//! 100.kHz(), -//! &clocks, -//! ); -//! # } -//! ``` -//! //! ## Usage //! //! The I2C driver implements a number of third-party traits, with the //! intention of making the HAL inter-compatible with various device drivers //! from the community. This includes the [embedded-hal] for both 0.2.x and -//! 1.x.x versions. +//! 1.0.x versions. //! //! ## Examples +//! //! ### Read Data from a BMP180 Sensor +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::i2c::I2C; //! # use esp_hal::gpio::Io; -//! # use core::option::Option::None; -//! # use crate::esp_hal::prelude::_fugit_RateExtU32; //! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); +//! //! // Create a new peripheral object with the described wiring -//! // and standard I2C clock speed +//! // and standard I2C clock speed. //! let mut i2c = I2C::new( //! peripherals.I2C0, //! io.pins.gpio1, //! io.pins.gpio2, //! 100.kHz(), -//! &clocks, //! ); +//! //! loop { //! let mut data = [0u8; 22]; //! i2c.write_read(0x77, &[0xaa], &mut data).ok(); @@ -98,26 +80,45 @@ const MAX_ITERATIONS: u32 = 1_000_000; #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { + /// The transmission exceeded the FIFO size. ExceedingFifo, + /// The acknowledgment check failed. AckCheckFailed, + /// A timeout occurred during transmission. TimeOut, + /// The arbitration for the bus was lost. ArbitrationLost, + /// The execution of the I2C command was incomplete. ExecIncomplete, + /// The number of commands issued exceeded the limit. CommandNrExceeded, + /// Zero length read or write operation. + InvalidZeroLength, } -#[cfg(any(feature = "embedded-hal", feature = "async"))] #[derive(PartialEq)] -// This enum is used to keep track of the last operation that was performed -// in an embedded-hal(-async) I2C::transaction. It used to determine whether -// a START condition should be issued at the start of the current operation. -enum LastOpWas { +// This enum is used to keep track of the last/next operation that was/will be +// performed in an embedded-hal(-async) I2C::transaction. It is used to +// determine whether a START condition should be issued at the start of the +// current operation and whether a read needs an ack or a nack for the final +// byte. +enum Op { Write, Read, None, } -#[cfg(feature = "embedded-hal")] +impl From>> for Op { + fn from(op: Option<&&mut embedded_hal::i2c::Operation<'_>>) -> Self { + use embedded_hal::i2c::Operation; + match op { + Some(Operation::Write(_)) => Op::Write, + Some(Operation::Read(_)) => Op::Read, + None => Op::None, + } + } +} + impl embedded_hal::i2c::Error for Error { fn kind(&self) -> embedded_hal::i2c::ErrorKind { use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource}; @@ -182,55 +183,6 @@ impl From for u32 { } } -cfg_if::cfg_if! { - if #[cfg(any(esp32, esp32s2))] { - const OPCODE_RSTART: u32 = 0; - const OPCODE_WRITE: u32 = 1; - const OPCODE_READ: u32 = 2; - const OPCODE_STOP: u32 = 3; - const OPCODE_END: u32 = 4; - } else if #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] { - const OPCODE_RSTART: u32 = 6; - const OPCODE_WRITE: u32 = 1; - const OPCODE_READ: u32 = 3; - const OPCODE_STOP: u32 = 2; - const OPCODE_END: u32 = 4; - } -} -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(PartialEq)] -enum Opcode { - RStart, - Write, - Read, - Stop, - End, -} - -impl From for u32 { - fn from(opcode: Opcode) -> u32 { - match opcode { - Opcode::RStart => OPCODE_RSTART, - Opcode::Write => OPCODE_WRITE, - Opcode::Read => OPCODE_READ, - Opcode::Stop => OPCODE_STOP, - Opcode::End => OPCODE_END, - } - } -} -impl From for Opcode { - fn from(opcode: u32) -> Self { - match opcode { - OPCODE_RSTART => Opcode::RStart, - OPCODE_WRITE => Opcode::Write, - OPCODE_READ => Opcode::Read, - OPCODE_STOP => Opcode::Stop, - OPCODE_END => Opcode::End, - _ => unreachable!(), - } - } -} - /// I2C peripheral container (I2C) pub struct I2C<'d, T, DM: crate::Mode> { peripheral: PeripheralRef<'d, T>, @@ -263,7 +215,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::blocking::i2c::Read for I2C<'_, T, crate::Blocking> where T: Instance, @@ -275,7 +226,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::blocking::i2c::Write for I2C<'_, T, crate::Blocking> where T: Instance, @@ -287,7 +237,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::blocking::i2c::WriteRead for I2C<'_, T, crate::Blocking> where T: Instance, @@ -304,12 +253,10 @@ where } } -#[cfg(feature = "embedded-hal")] impl embedded_hal::i2c::ErrorType for I2C<'_, T, DM> { type Error = Error; } -#[cfg(feature = "embedded-hal")] impl embedded_hal::i2c::I2c for I2C<'_, T, DM> where T: Instance, @@ -320,13 +267,20 @@ where operations: &mut [embedded_hal::i2c::Operation<'_>], ) -> Result<(), Self::Error> { use embedded_hal::i2c::Operation; - let mut last_op = LastOpWas::None; - let mut op_iter = operations.iter_mut().peekable(); + let mut last_op = Op::None; + // filter out 0 length read operations + let mut op_iter = operations + .iter_mut() + .filter(|op| match op { + Operation::Write(_) => true, + Operation::Read(buffer) => !buffer.is_empty(), + }) + .peekable(); while let Some(op) = op_iter.next() { + let next_op: Op = op_iter.peek().into(); // Clear all I2C interrupts self.peripheral.clear_all_interrupts(); - // TODO somehow know that we can combine a write and a read into one transaction let cmd_iterator = &mut self.peripheral.register_block().comd_iter(); match op { Operation::Write(bytes) => { @@ -336,24 +290,26 @@ where self.peripheral.write_operation( address, bytes, - last_op != LastOpWas::Write, - op_iter.peek().is_none(), + last_op != Op::Write, + next_op == Op::None, cmd_iterator, )?; - last_op = LastOpWas::Write; + last_op = Op::Write; } Operation::Read(buffer) => { // execute a read operation: // - issue START/RSTART if op is different from previous // - issue STOP if op is the last one + // - will_continue is true if there is another read operation next self.peripheral.read_operation( address, buffer, - last_op != LastOpWas::Read, - op_iter.peek().is_none(), + last_op != Op::Read, + next_op == Op::None, + next_op == Op::Read, cmd_iterator, )?; - last_op = LastOpWas::Read; + last_op = Op::Read; } } } @@ -370,7 +326,6 @@ where sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, frequency: HertzU32, - clocks: &Clocks<'d>, timeout: Option, ) -> Self { crate::into_ref!(i2c, sda, scl); @@ -422,7 +377,7 @@ where crate::private::Internal, ); - i2c.peripheral.setup(frequency, clocks, timeout); + i2c.peripheral.setup(frequency, timeout); i2c } @@ -446,9 +401,8 @@ where sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, frequency: HertzU32, - clocks: &Clocks<'d>, ) -> Self { - Self::new_with_timeout(i2c, sda, scl, frequency, clocks, None) + Self::new_with_timeout(i2c, sda, scl, frequency, None) } /// Create a new I2C instance with a custom timeout value. @@ -459,10 +413,9 @@ where sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, frequency: HertzU32, - clocks: &Clocks<'d>, timeout: Option, ) -> Self { - Self::new_internal(i2c, sda, scl, frequency, clocks, timeout) + Self::new_internal(i2c, sda, scl, frequency, timeout) } } @@ -477,7 +430,6 @@ where } } -#[cfg(feature = "async")] impl<'d, T> I2C<'d, T, crate::Async> where T: Instance, @@ -490,9 +442,8 @@ where sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, frequency: HertzU32, - clocks: &Clocks<'d>, ) -> Self { - Self::new_with_timeout_async(i2c, sda, scl, frequency, clocks, None) + Self::new_with_timeout_async(i2c, sda, scl, frequency, None) } /// Create a new I2C instance with a custom timeout value. @@ -503,10 +454,9 @@ where sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, frequency: HertzU32, - clocks: &Clocks<'d>, timeout: Option, ) -> Self { - let mut this = Self::new_internal(i2c, sda, scl, frequency, clocks, timeout); + let mut this = Self::new_internal(i2c, sda, scl, frequency, timeout); let handler = match T::I2C_NUMBER { 0 => asynch::i2c0_handler, @@ -524,7 +474,6 @@ where } } -#[cfg(feature = "async")] mod asynch { #[cfg(not(esp32))] use core::{ @@ -559,6 +508,7 @@ mod asynch { } #[cfg(not(esp32))] + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct I2cFuture<'a, T> where T: Instance, @@ -800,6 +750,14 @@ mod asynch { Ok(()) } + /// Executes an async I2C write operation. + /// - `addr` is the address of the slave device. + /// - `bytes` is the data two be sent. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `stop` indicates whether the operation should end with a STOP + /// condition. + /// - `cmd_iterator` is an iterator over the command registers. async fn write_operation<'a, I>( &self, address: u8, @@ -811,13 +769,20 @@ mod asynch { where I: Iterator, { + // Short circuit for zero length writes without start or end as that would be an + // invalid operation write lengths in the TRM (at least for ESP32-S3) are 1-255 + if bytes.is_empty() && !start && !stop { + return Ok(()); + } + // Reset FIFO and command list self.peripheral.reset_fifo(); self.peripheral.reset_command_list(); if start { add_cmd(cmd_iterator, Command::Start)?; } - self.peripheral.setup_write(address, bytes, cmd_iterator)?; + self.peripheral + .setup_write(address, bytes, start, cmd_iterator)?; add_cmd( cmd_iterator, if stop { Command::Stop } else { Command::End }, @@ -831,24 +796,42 @@ mod asynch { Ok(()) } + /// Executes an async I2C read operation. + /// - `addr` is the address of the slave device. + /// - `buffer` is the buffer to store the read data. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `stop` indicates whether the operation should end with a STOP + /// condition. + /// - `will_continue` indicates whether there is another read operation + /// following this one and we should not nack the last byte. + /// - `cmd_iterator` is an iterator over the command registers. async fn read_operation<'a, I>( &self, address: u8, buffer: &mut [u8], start: bool, stop: bool, + will_continue: bool, cmd_iterator: &mut I, ) -> Result<(), Error> where I: Iterator, { + // Short circuit for zero length reads as that would be an invalid operation + // read lengths in the TRM (at least for ESP32-S3) are 1-255 + if buffer.is_empty() { + return Ok(()); + } + // Reset FIFO and command list self.peripheral.reset_fifo(); self.peripheral.reset_command_list(); if start { add_cmd(cmd_iterator, Command::Start)?; } - self.peripheral.setup_read(address, buffer, cmd_iterator)?; + self.peripheral + .setup_read(address, buffer, start, will_continue, cmd_iterator)?; add_cmd( cmd_iterator, if stop { Command::Stop } else { Command::End }, @@ -886,6 +869,7 @@ mod asynch { buffer, true, true, + false, &mut self.peripheral.register_block().comd_iter(), ) .await?; @@ -911,16 +895,19 @@ mod asynch { addr, bytes, true, - false, + buffer.is_empty(), // if the read buffer is empty, then issue a stop &mut self.peripheral.register_block().comd_iter(), ) .await?; self.peripheral.clear_all_interrupts(); + // this will be a no-op if the buffer is empty, in that case we issued the stop + // with the write self.read_operation( addr, buffer, true, true, + false, &mut self.peripheral.register_block().comd_iter(), ) .await?; @@ -952,7 +939,6 @@ mod asynch { } } - #[cfg(feature = "embedded-hal")] impl<'d, T> embedded_hal_async::i2c::I2c for I2C<'d, T, crate::Async> where T: Instance, @@ -962,9 +948,17 @@ mod asynch { address: u8, operations: &mut [Operation<'_>], ) -> Result<(), Self::Error> { - let mut last_op = LastOpWas::None; - let mut op_iter = operations.iter_mut().peekable(); + let mut last_op = Op::None; + // filter out 0 length read operations + let mut op_iter = operations + .iter_mut() + .filter(|op| match op { + Operation::Write(_) => true, + Operation::Read(buffer) => !buffer.is_empty(), + }) + .peekable(); while let Some(op) = op_iter.next() { + let next_op: Op = op_iter.peek().into(); // Clear all I2C interrupts self.peripheral.clear_all_interrupts(); @@ -974,12 +968,12 @@ mod asynch { self.write_operation( address, bytes, - last_op != LastOpWas::Write, - op_iter.peek().is_none(), + last_op != Op::Write, + next_op == Op::None, cmd_iterator, ) .await?; - last_op = LastOpWas::Write; + last_op = Op::Write; } Operation::Read(buffer) => { // execute a read operation: @@ -988,12 +982,13 @@ mod asynch { self.read_operation( address, buffer, - last_op != LastOpWas::Read, - op_iter.peek().is_none(), + last_op != Op::Read, + next_op == Op::None, + next_op == Op::Read, cmd_iterator, ) .await?; - last_op = LastOpWas::Read; + last_op = Op::Read; } } } @@ -1057,20 +1052,30 @@ mod asynch { /// I2C Peripheral Instance pub trait Instance: crate::private::Sealed { + /// The identifier number for this I2C instance. const I2C_NUMBER: usize; + /// Returns the interrupt associated with this I2C peripheral. fn interrupt() -> crate::peripherals::Interrupt; + /// Returns the SCL output signal for this I2C peripheral. fn scl_output_signal(&self) -> OutputSignal; + /// Returns the SCL input signal for this I2C peripheral. fn scl_input_signal(&self) -> InputSignal; + /// Returns the SDA output signal for this I2C peripheral. fn sda_output_signal(&self) -> OutputSignal; + /// Returns the SDA input signal for this I2C peripheral. fn sda_input_signal(&self) -> InputSignal; + /// Returns a reference to the register block of the I2C peripheral. fn register_block(&self) -> &RegisterBlock; + /// Returns the I2C peripheral's index number. fn i2c_number(&self) -> usize; - fn setup(&mut self, frequency: HertzU32, clocks: &Clocks<'_>, timeout: Option) { + /// Configures the I2C peripheral with the specified frequency, clocks, and + /// optional timeout. + fn setup(&mut self, frequency: HertzU32, timeout: Option) { self.register_block().ctr().modify(|_, w| unsafe { // Clear register w.bits(0) @@ -1102,12 +1107,16 @@ pub trait Instance: crate::private::Sealed { self.set_filter(Some(7), Some(7)); // Configure frequency - #[cfg(esp32)] - self.set_frequency(clocks.i2c_clock.convert(), frequency, timeout); - #[cfg(esp32s2)] - self.set_frequency(clocks.apb_clock.convert(), frequency, timeout); - #[cfg(not(any(esp32, esp32s2)))] - self.set_frequency(clocks.xtal_clock.convert(), frequency, timeout); + let clocks = Clocks::get(); + cfg_if::cfg_if! { + if #[cfg(esp32)] { + self.set_frequency(clocks.i2c_clock.convert(), frequency, timeout); + } else if #[cfg(esp32s2)] { + self.set_frequency(clocks.apb_clock.convert(), frequency, timeout); + } else { + self.set_frequency(clocks.xtal_clock.convert(), frequency, timeout); + } + } self.update_config(); @@ -1386,6 +1395,7 @@ pub trait Instance: crate::private::Sealed { } #[allow(clippy::too_many_arguments, unused)] + /// Configures the clock and timing parameters for the I2C peripheral. fn configure_clock( &mut self, sclk_div: u32, @@ -1492,89 +1502,134 @@ pub trait Instance: crate::private::Sealed { } } - fn setup_write<'a, I>(&self, addr: u8, bytes: &[u8], cmd_iterator: &mut I) -> Result<(), Error> + /// Configures the I2C peripheral for a write operation. + /// - `addr` is the address of the slave device. + /// - `bytes` is the data two be sent. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `cmd_iterator` is an iterator over the command registers. + fn setup_write<'a, I>( + &self, + addr: u8, + bytes: &[u8], + start: bool, + cmd_iterator: &mut I, + ) -> Result<(), Error> where I: Iterator, { - if bytes.len() > 254 { + // if start is true we can only send 254 additional bytes with the address as + // the first + let max_len = if start { 254usize } else { 255usize }; + if bytes.len() > max_len { // we could support more by adding multiple write operations return Err(Error::ExceedingFifo); } - // WRITE command - add_cmd( - cmd_iterator, - Command::Write { - ack_exp: Ack::Ack, - ack_check_en: true, - length: 1 + bytes.len() as u8, - }, - )?; + let write_len = if start { bytes.len() + 1 } else { bytes.len() }; + // don't issue write if there is no data to write + if write_len > 0 { + // WRITE command + add_cmd( + cmd_iterator, + Command::Write { + ack_exp: Ack::Ack, + ack_check_en: true, + length: write_len as u8, + }, + )?; + } self.update_config(); - // Load address and R/W bit into FIFO - write_fifo( - self.register_block(), - addr << 1 | OperationType::Write as u8, - ); - + if start { + // Load address and R/W bit into FIFO + write_fifo( + self.register_block(), + addr << 1 | OperationType::Write as u8, + ); + } Ok(()) } + /// Configures the I2C peripheral for a read operation. + /// - `addr` is the address of the slave device. + /// - `buffer` is the buffer to store the read data. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `will_continue` indicates whether there is another read operation + /// following this one and we should not nack the last byte. + /// - `cmd_iterator` is an iterator over the command registers. fn setup_read<'a, I>( &self, addr: u8, buffer: &mut [u8], + start: bool, + will_continue: bool, cmd_iterator: &mut I, ) -> Result<(), Error> where I: Iterator, { - if buffer.len() > 254 { + if buffer.is_empty() { + return Err(Error::InvalidZeroLength); + } + let (max_len, initial_len) = if will_continue { + (255usize, buffer.len()) + } else { + (254usize, buffer.len() - 1) + }; + if buffer.len() > max_len { // we could support more by adding multiple read operations return Err(Error::ExceedingFifo); } - // WRITE command - add_cmd( - cmd_iterator, - Command::Write { - ack_exp: Ack::Ack, - ack_check_en: true, - length: 1, - }, - )?; + if start { + // WRITE command + add_cmd( + cmd_iterator, + Command::Write { + ack_exp: Ack::Ack, + ack_check_en: true, + length: 1, + }, + )?; + } - if buffer.len() > 1 { - // READ command (N - 1) + if initial_len > 0 { + // READ command add_cmd( cmd_iterator, Command::Read { ack_value: Ack::Ack, - length: buffer.len() as u8 - 1, + length: initial_len as u8, }, )?; } - // READ w/o ACK - add_cmd( - cmd_iterator, - Command::Read { - ack_value: Ack::Nack, - length: 1, - }, - )?; + if !will_continue { + // this is the last read so we need to nack the last byte + // READ w/o ACK + add_cmd( + cmd_iterator, + Command::Read { + ack_value: Ack::Nack, + length: 1, + }, + )?; + } self.update_config(); - // Load address and R/W bit into FIFO - write_fifo(self.register_block(), addr << 1 | OperationType::Read as u8); - + if start { + // Load address and R/W bit into FIFO + write_fifo(self.register_block(), addr << 1 | OperationType::Read as u8); + } Ok(()) } #[cfg(not(any(esp32, esp32s2)))] + /// Reads all bytes from the RX FIFO. fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { // Read bytes from FIFO // FIXME: Handle case where less data has been provided by the slave than @@ -1596,6 +1651,7 @@ pub trait Instance: crate::private::Sealed { } #[cfg(any(esp32, esp32s2))] + /// Reads all bytes from the RX FIFO. fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the // FIFO apparently it would be possible by using non-fifo mode @@ -1620,12 +1676,14 @@ pub trait Instance: crate::private::Sealed { Ok(()) } + /// Clears all pending interrupts for the I2C peripheral. fn clear_all_interrupts(&self) { self.register_block() .int_clr() .write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) }); } + /// Waits for the completion of an I2C transaction. fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { let mut tout = MAX_ITERATIONS; loop { @@ -1651,6 +1709,7 @@ pub trait Instance: crate::private::Sealed { Ok(()) } + /// Checks whether all I2C commands have completed execution. fn check_all_commands_done(&self) -> Result<(), Error> { // NOTE: on esp32 executing the end command generates the end_detect interrupt // but does not seem to clear the done bit! So we don't check the done @@ -1658,16 +1717,21 @@ pub trait Instance: crate::private::Sealed { for cmd_reg in self.register_block().comd_iter() { let cmd = cmd_reg.read(); - if cmd.bits() != 0x0 - && cmd.opcode().bits() != (OPCODE_END as u8) - && !cmd.command_done().bit_is_set() - { + if cmd.bits() != 0x0 && !cmd.opcode().is_end() && !cmd.command_done().bit_is_set() { return Err(Error::ExecIncomplete); } } Ok(()) } + + /// Checks for I2C transmission errors and handles them. + /// + /// This function inspects specific I2C-related interrupts to detect errors + /// during communication, such as timeouts, failed acknowledgments, or + /// arbitration loss. If an error is detected, the function handles it + /// by resetting the I2C peripheral to clear the error condition and then + /// returns an appropriate error. fn check_errors(&self) -> Result<(), Error> { let interrupts = self.register_block().int_raw().read(); @@ -1708,6 +1772,15 @@ pub trait Instance: crate::private::Sealed { Ok(()) } + /// Updates the configuration of the I2C peripheral. + /// + /// This function ensures that the configuration values, such as clock + /// settings, SDA/SCL filtering, timeouts, and other operational + /// parameters, which are configured in other functions, are properly + /// propagated to the I2C hardware. This step is necessary to synchronize + /// the software-configured settings with the peripheral's internal + /// registers, ensuring that the hardware behaves according to the + /// current configuration. fn update_config(&self) { // Ensure that the configuration of the peripheral is correctly propagated // (only necessary for C2, C3, C6, H2 and S3 variant) @@ -1717,6 +1790,7 @@ pub trait Instance: crate::private::Sealed { .modify(|_, w| w.conf_upgate().set_bit()); } + /// Starts an I2C transmission. fn start_transmission(&self) { // Start transmission self.register_block() @@ -1725,6 +1799,7 @@ pub trait Instance: crate::private::Sealed { } #[cfg(not(any(esp32, esp32s2)))] + /// Fills the TX FIFO with data from the provided slice. fn fill_tx_fifo(&self, bytes: &[u8]) -> usize { let mut index = 0; while index < bytes.len() @@ -1754,6 +1829,8 @@ pub trait Instance: crate::private::Sealed { } #[cfg(not(any(esp32, esp32s2)))] + /// Writes remaining data from byte slice to the TX FIFO from the specified + /// index. fn write_remaining_tx_fifo(&self, start_index: usize, bytes: &[u8]) -> Result<(), Error> { let mut index = start_index; loop { @@ -1793,6 +1870,7 @@ pub trait Instance: crate::private::Sealed { } #[cfg(any(esp32, esp32s2))] + /// Fills the TX FIFO with data from the provided slice. fn fill_tx_fifo(&self, bytes: &[u8]) -> usize { // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the // FIFO apparently it would be possible by using non-fifo mode @@ -1810,6 +1888,8 @@ pub trait Instance: crate::private::Sealed { } #[cfg(any(esp32, esp32s2))] + /// Writes remaining data from byte slice to the TX FIFO from the specified + /// index. fn write_remaining_tx_fifo(&self, start_index: usize, bytes: &[u8]) -> Result<(), Error> { // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the // FIFO apparently it would be possible by using non-fifo mode @@ -1888,6 +1968,14 @@ pub trait Instance: crate::private::Sealed { .write(|w| w.rxfifo_full().clear_bit_by_one()); } + /// Executes an I2C write operation. + /// - `addr` is the address of the slave device. + /// - `bytes` is the data two be sent. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `stop` indicates whether the operation should end with a STOP + /// condition. + /// - `cmd_iterator` is an iterator over the command registers. fn write_operation<'a, I>( &self, address: u8, @@ -1899,6 +1987,12 @@ pub trait Instance: crate::private::Sealed { where I: Iterator, { + // Short circuit for zero length writes without start or end as that would be an + // invalid operation write lengths in the TRM (at least for ESP32-S3) are 1-255 + if bytes.is_empty() && !start && !stop { + return Ok(()); + } + // Reset FIFO and command list self.reset_fifo(); self.reset_command_list(); @@ -1906,7 +2000,7 @@ pub trait Instance: crate::private::Sealed { if start { add_cmd(cmd_iterator, Command::Start)?; } - self.setup_write(address, bytes, cmd_iterator)?; + self.setup_write(address, bytes, start, cmd_iterator)?; add_cmd( cmd_iterator, if stop { Command::Stop } else { Command::End }, @@ -1920,17 +2014,34 @@ pub trait Instance: crate::private::Sealed { Ok(()) } + /// Executes an I2C read operation. + /// - `addr` is the address of the slave device. + /// - `buffer` is the buffer to store the read data. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `stop` indicates whether the operation should end with a STOP + /// condition. + /// - `will_continue` indicates whether there is another read operation + /// following this one and we should not nack the last byte. + /// - `cmd_iterator` is an iterator over the command registers. fn read_operation<'a, I>( &self, address: u8, buffer: &mut [u8], start: bool, stop: bool, + will_continue: bool, cmd_iterator: &mut I, ) -> Result<(), Error> where I: Iterator, { + // Short circuit for zero length reads as that would be an invalid operation + // read lengths in the TRM (at least for ESP32-S3) are 1-255 + if buffer.is_empty() { + return Ok(()); + } + // Reset FIFO and command list self.reset_fifo(); self.reset_command_list(); @@ -1938,7 +2049,9 @@ pub trait Instance: crate::private::Sealed { if start { add_cmd(cmd_iterator, Command::Start)?; } - self.setup_read(address, buffer, cmd_iterator)?; + + self.setup_read(address, buffer, start, will_continue, cmd_iterator)?; + add_cmd( cmd_iterator, if stop { Command::Stop } else { Command::End }, @@ -1975,6 +2088,7 @@ pub trait Instance: crate::private::Sealed { buffer, true, true, + false, &mut self.register_block().comd_iter(), )?; Ok(()) @@ -1999,21 +2113,25 @@ pub trait Instance: crate::private::Sealed { addr, bytes, true, - false, + buffer.is_empty(), // if the read buffer is empty, then issue a stop &mut self.register_block().comd_iter(), )?; self.clear_all_interrupts(); + // this will be a no-op if the buffer is empty, in that case we issued the stop + // with the write self.read_operation( addr, buffer, true, true, + false, &mut self.register_block().comd_iter(), )?; Ok(()) } } +/// Adds a command to the I2C command sequence. fn add_cmd<'a, I>(cmd_iterator: &mut I, command: Command) -> Result<(), Error> where I: Iterator, @@ -2189,12 +2307,19 @@ pub mod lp_i2c { #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { + /// The transmission exceeded the FIFO size. ExceedingFifo, + /// The acknowledgment check failed. AckCheckFailed, + /// A timeout occurred during transmission. TimeOut, + /// The arbitration for the bus was lost. ArbitrationLost, + /// The execution of the I2C command was incomplete. ExecIncomplete, + /// The number of commands issued exceeded the limit. CommandNrExceeded, + /// The response received from the I2C device was invalid. InvalidResponse, } @@ -2211,15 +2336,6 @@ pub mod lp_i2c { Nack, } - #[allow(unused)] - enum Opcode { - RStart = 6, - Write = 1, - Read = 3, - Stop = 2, - End = 4, - } - #[derive(PartialEq)] #[allow(unused)] enum Command { @@ -2246,122 +2362,6 @@ pub mod lp_i2c { }, } - impl From for u16 { - fn from(c: Command) -> u16 { - let opcode = match c { - Command::Start => Opcode::RStart, - Command::Stop => Opcode::Stop, - Command::End => Opcode::End, - Command::Write { .. } => Opcode::Write, - Command::Read { .. } => Opcode::Read, - }; - - let length = match c { - Command::Start | Command::Stop | Command::End => 0, - Command::Write { length: l, .. } | Command::Read { length: l, .. } => l, - }; - - let ack_exp = match c { - Command::Start | Command::Stop | Command::End | Command::Read { .. } => Ack::Nack, - Command::Write { ack_exp: exp, .. } => exp, - }; - - let ack_check_en = match c { - Command::Start | Command::Stop | Command::End | Command::Read { .. } => false, - Command::Write { - ack_check_en: en, .. - } => en, - }; - - let ack_value = match c { - Command::Start | Command::Stop | Command::End | Command::Write { .. } => Ack::Nack, - Command::Read { ack_value: ack, .. } => ack, - }; - - let mut cmd: u16 = length.into(); - - if ack_check_en { - cmd |= 1 << 8; - } else { - cmd &= !(1 << 8); - } - - if ack_exp == Ack::Nack { - cmd |= 1 << 9; - } else { - cmd &= !(1 << 9); - } - - if ack_value == Ack::Nack { - cmd |= 1 << 10; - } else { - cmd &= !(1 << 10); - } - - cmd |= (opcode as u16) << 11; - - cmd - } - } - - impl From for u32 { - fn from(c: Command) -> u32 { - let opcode = match c { - Command::Start => Opcode::RStart, - Command::Stop => Opcode::Stop, - Command::End => Opcode::End, - Command::Write { .. } => Opcode::Write, - Command::Read { .. } => Opcode::Read, - }; - - let length = match c { - Command::Start | Command::Stop | Command::End => 0, - Command::Write { length: l, .. } | Command::Read { length: l, .. } => l, - }; - - let ack_exp = match c { - Command::Start | Command::Stop | Command::End | Command::Read { .. } => Ack::Nack, - Command::Write { ack_exp: exp, .. } => exp, - }; - - let ack_check_en = match c { - Command::Start | Command::Stop | Command::End | Command::Read { .. } => false, - Command::Write { - ack_check_en: en, .. - } => en, - }; - - let ack_value = match c { - Command::Start | Command::Stop | Command::End | Command::Write { .. } => Ack::Nack, - Command::Read { ack_value: ack, .. } => ack, - }; - - let mut cmd: u32 = length.into(); - - if ack_check_en { - cmd |= 1 << 8; - } else { - cmd &= !(1 << 8); - } - - if ack_exp == Ack::Nack { - cmd |= 1 << 9; - } else { - cmd &= !(1 << 9); - } - - if ack_value == Ack::Nack { - cmd |= 1 << 10; - } else { - cmd &= !(1 << 10); - } - - cmd |= (opcode as u32) << 11; - - cmd - } - } - // https://github.com/espressif/esp-idf/blob/master/components/ulp/lp_core/lp_core_i2c.c#L122 // TX/RX RAM size is 16*8 bit // TX RX FIFO has 16 bit depth @@ -2372,11 +2372,13 @@ pub mod lp_i2c { // Configure LP_EXT_I2C_CK_EN high to enable the clock source of I2C_SCLK. // Adjust the timing registers accordingly when the clock frequency changes. + /// Represents a Low-Power I2C peripheral. pub struct LpI2c { i2c: LP_I2C0, } impl LpI2c { + /// Creates a new instance of the `LpI2c` peripheral. pub fn new( i2c: LP_I2C0, _sda: LowPowerOutputOpenDrain<'_, 6>, @@ -2619,6 +2621,7 @@ pub mod lp_i2c { self.i2c.ctr().modify(|_, w| w.conf_upgate().set_bit()); } + /// Resets the transmit and receive FIFO buffers. fn reset_fifo(&self) { self.i2c .fifo_conf() diff --git a/esp-hal/src/i2s.rs b/esp-hal/src/i2s.rs index 4a53db16452..3767a1431d5 100644 --- a/esp-hal/src/i2s.rs +++ b/esp-hal/src/i2s.rs @@ -1,14 +1,15 @@ //! # Inter-IC Sound (I2S) //! //! ## Overview +//! //! I2S (Inter-IC Sound) is a synchronous serial communication protocol usually //! used for transmitting audio data between two digital audio devices. //! Espressif devices may contain more than one I2S peripheral(s). These //! peripherals can be configured to input and output sample data via the I2S //! driver. //! -//! //! ## Configuration +//! //! I2S supports different data formats, including varying data and channel //! widths, different standards, such as the Philips standard and configurable //! pin mappings for I2S clock (BCLK), word select (WS), and data input/output @@ -18,29 +19,28 @@ //! supports various configurations, such as different data formats, standards //! (e.g., Philips) and pin configurations. It relies on other peripheral //! modules, such as -//! - `GPIO` -//! - `DMA` -//! - `system` (to configure and enable the I2S peripheral) +//! - `GPIO` +//! - `DMA` +//! - `system` (to configure and enable the I2S peripheral) +//! +//! ## Example //! -//! ## Examples //! ### Initialization +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::i2s::I2s; //! # use esp_hal::i2s::Standard; //! # use esp_hal::i2s::DataFormat; +//! # use esp_hal::i2s::I2sReadDma; //! # use esp_hal::gpio::Io; //! # use esp_hal::dma_buffers; //! # use esp_hal::dma::{Dma, DmaPriority}; -//! # use crate::esp_hal::prelude::_fugit_RateExtU32; -//! # use crate::esp_hal::peripherals::Peripherals; -//! # use crate::esp_hal::i2s::I2sReadDma; -//! # use core::ptr::addr_of_mut; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! let dma = Dma::new(peripherals.DMA); #![cfg_attr(any(esp32, esp32s2), doc = "let dma_channel = dma.i2s0channel;")] #![cfg_attr(not(any(esp32, esp32s2)), doc = "let dma_channel = dma.channel0;")] -//! let (_, tx_descriptors, mut rx_buffer, rx_descriptors) = +//! let (mut rx_buffer, rx_descriptors, _, tx_descriptors) = //! dma_buffers!(0, 4 * 4092); //! //! let i2s = I2s::new( @@ -52,9 +52,8 @@ //! false, //! DmaPriority::Priority0, //! ), -//! tx_descriptors, //! rx_descriptors, -//! &clocks, +//! tx_descriptors, //! ); #![cfg_attr(not(esp32), doc = "let i2s = i2s.with_mclk(io.pins.gpio0);")] //! let mut i2s_rx = i2s.i2s_rx @@ -88,7 +87,6 @@ use private::*; #[cfg(any(esp32, esp32s3))] use crate::dma::I2s1Peripheral; use crate::{ - clock::Clocks, dma::{ dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx}, Channel, @@ -119,13 +117,18 @@ use crate::{ }; #[derive(EnumSetType)] +/// Represents the various interrupt types for the I2S peripheral. pub enum I2sInterrupt { - TxHung, + /// Receive buffer hung, indicating a stall in data reception. RxHung, + /// Transmit buffer hung, indicating a stall in data transmission. + TxHung, #[cfg(not(any(esp32, esp32s2)))] - TxDone, - #[cfg(not(any(esp32, esp32s2)))] + /// Reception of data is complete. RxDone, + #[cfg(not(any(esp32, esp32s2)))] + /// Transmission of data is complete. + TxDone, } #[cfg(any(esp32, esp32s2, esp32s3))] @@ -148,8 +151,11 @@ impl AcceptedWord for i32 {} #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { + /// An unspecified or unknown error occurred during an I2S operation. Unknown, + /// A DMA-related error occurred during I2S operations. DmaError(DmaError), + /// An illegal or invalid argument was passed to an I2S function or method. IllegalArgument, } @@ -163,6 +169,7 @@ impl From for Error { #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Standard { + /// The Philips I2S standard. Philips, // Tdm, // Pdm, @@ -173,12 +180,19 @@ pub enum Standard { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg(not(any(esp32, esp32s2)))] pub enum DataFormat { + /// 32-bit data width and 32-bit channel width. Data32Channel32, + /// 32-bit data width and 24-bit channel width. Data32Channel24, + /// 32-bit data width and 16-bit channel width. Data32Channel16, + /// 32-bit data width and 8-bit channel width. Data32Channel8, + /// 16-bit data width and 16-bit channel width. Data16Channel16, + /// 16-bit data width and 8-bit channel width. Data16Channel8, + /// 8-bit data width and 8-bit channel width. Data8Channel8, } @@ -187,12 +201,15 @@ pub enum DataFormat { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg(any(esp32, esp32s2))] pub enum DataFormat { + /// 32-bit data width and 32-bit channel width. Data32Channel32, + /// 16-bit data width and 16-bit channel width. Data16Channel16, } #[cfg(not(any(esp32, esp32s2)))] impl DataFormat { + /// Returns the number of data bits for the selected data format. pub fn data_bits(&self) -> u8 { match self { DataFormat::Data32Channel32 => 32, @@ -205,6 +222,7 @@ impl DataFormat { } } + /// Returns the number of channel bits for the selected data format. pub fn channel_bits(&self) -> u8 { match self { DataFormat::Data32Channel32 => 32, @@ -220,6 +238,7 @@ impl DataFormat { #[cfg(any(esp32, esp32s2))] impl DataFormat { + /// Returns the number of data bits for the selected data format. pub fn data_bits(&self) -> u8 { match self { DataFormat::Data32Channel32 => 32, @@ -227,6 +246,7 @@ impl DataFormat { } } + /// Returns the number of channel bits for the selected data format. pub fn channel_bits(&self) -> u8 { match self { DataFormat::Data32Channel32 => 32, @@ -237,6 +257,7 @@ impl DataFormat { /// Blocking I2s Write pub trait I2sWrite { + /// Writes a slice of data to the I2S peripheral. fn write(&mut self, words: &[W]) -> Result<(), Error>; } @@ -251,7 +272,7 @@ where /// Write I2S. /// Returns [DmaTransferTx] which represents the in-progress DMA /// transfer - fn write_dma<'t>(&'t mut self, words: &'t TXBUF) -> Result, Error> + fn write_dma<'t>(&'t mut self, words: &'t TXBUF) -> Result, Error> where TXBUF: ReadBuffer; @@ -260,13 +281,15 @@ where fn write_dma_circular<'t>( &'t mut self, words: &'t TXBUF, - ) -> Result, Error> + ) -> Result, Error> where TXBUF: ReadBuffer; } /// Blocking I2S Read pub trait I2sRead { + /// Reads a slice of data from the I2S peripheral and stores it in the + /// provided buffer. fn read(&mut self, words: &mut [W]) -> Result<(), Error>; } @@ -281,7 +304,7 @@ where /// Read I2S. /// Returns [DmaTransferRx] which represents the in-progress DMA /// transfer - fn read_dma<'t>(&'t mut self, words: &'t mut RXBUF) -> Result, Error> + fn read_dma<'t>(&'t mut self, words: &'t mut RXBUF) -> Result, Error> where RXBUF: WriteBuffer; @@ -291,7 +314,7 @@ where fn read_dma_circular<'t>( &'t mut self, words: &'t mut RXBUF, - ) -> Result, Error> + ) -> Result, Error> where RXBUF: WriteBuffer; } @@ -303,8 +326,10 @@ where CH: DmaChannel, DmaMode: Mode, { - pub i2s_tx: TxCreator<'d, I, CH, DmaMode>, + /// Handles the reception (RX) side of the I2S peripheral. pub i2s_rx: RxCreator<'d, I, CH, DmaMode>, + /// Handles the transmission (TX) side of the I2S peripheral. + pub i2s_tx: TxCreator<'d, I, CH, DmaMode>, phantom: PhantomData, } @@ -321,39 +346,34 @@ where data_format: DataFormat, sample_rate: impl Into, mut channel: Channel<'d, CH, DmaMode>, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], - clocks: &Clocks<'d>, + tx_descriptors: &'static mut [DmaDescriptor], ) -> Self { // on ESP32-C3 / ESP32-S3 and later RX and TX are independent and // could be configured totally independently but for now handle all // the targets the same and force same configuration for both, TX and RX channel.tx.init_channel(); + PeripheralClockControl::reset(I::get_peripheral()); PeripheralClockControl::enable(I::get_peripheral()); - I::set_clock(calculate_clock( - sample_rate, - 2, - data_format.channel_bits(), - clocks, - )); + I::set_clock(calculate_clock(sample_rate, 2, data_format.channel_bits())); I::configure(&standard, &data_format); I::set_master(); I::update(); Self { - i2s_tx: TxCreator { - register_access: PhantomData, - tx_channel: channel.tx, - descriptors: tx_descriptors, - phantom: PhantomData, - }, i2s_rx: RxCreator { register_access: PhantomData, rx_channel: channel.rx, descriptors: rx_descriptors, phantom: PhantomData, }, + i2s_tx: TxCreator { + register_access: PhantomData, + tx_channel: channel.tx, + descriptors: tx_descriptors, + phantom: PhantomData, + }, phantom: PhantomData, } } @@ -427,9 +447,8 @@ where data_format: DataFormat, sample_rate: impl Into, channel: Channel<'d, CH, DmaMode>, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], - clocks: &Clocks<'d>, + tx_descriptors: &'static mut [DmaDescriptor], ) -> Self where I: I2s0Instance, @@ -442,9 +461,8 @@ where data_format, sample_rate, channel, - tx_descriptors, rx_descriptors, - clocks, + tx_descriptors, ) } @@ -458,9 +476,8 @@ where data_format: DataFormat, sample_rate: impl Into, channel: Channel<'d, CH, DmaMode>, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], - clocks: &Clocks<'d>, + tx_descriptors: &'static mut [DmaDescriptor], ) -> Self where I: I2s1Instance, @@ -472,12 +489,12 @@ where data_format, sample_rate, channel, - tx_descriptors, rx_descriptors, - clocks, + tx_descriptors, ) } + /// Configures the I2S peripheral to use a master clock (MCLK) output pin. pub fn with_mclk(self, pin: impl Peripheral

+ 'd) -> Self { into_ref!(pin); pin.set_to_push_pull_output(crate::private::Internal); @@ -515,7 +532,7 @@ where CH: DmaChannel, DmaMode: Mode, { - fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { self.wait_tx_dma_done().ok(); } @@ -646,7 +663,7 @@ where CH: DmaChannel, DmaMode: Mode, { - fn write_dma<'t>(&'t mut self, words: &'t TXBUF) -> Result, Error> + fn write_dma<'t>(&'t mut self, words: &'t TXBUF) -> Result, Error> where TXBUF: ReadBuffer, { @@ -657,7 +674,7 @@ where fn write_dma_circular<'t>( &'t mut self, words: &'t TXBUF, - ) -> Result, Error> + ) -> Result, Error> where TXBUF: ReadBuffer, { @@ -696,7 +713,7 @@ where CH: DmaChannel, DmaMode: Mode, { - fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { T::wait_for_rx_done(); } @@ -827,7 +844,7 @@ where DmaMode: Mode, Self: DmaSupportRx + Sized, { - fn read_dma<'t>(&'t mut self, words: &'t mut RXBUF) -> Result, Error> + fn read_dma<'t>(&'t mut self, words: &'t mut RXBUF) -> Result, Error> where RXBUF: WriteBuffer, { @@ -838,7 +855,7 @@ where fn read_dma_circular<'t>( &'t mut self, words: &'t mut RXBUF, - ) -> Result, Error> + ) -> Result, Error> where RXBUF: WriteBuffer, { @@ -847,6 +864,7 @@ where } } +/// Provides an abstraction for accessing the I2S peripheral registers. pub trait RegisterAccess: RegisterAccessPrivate {} mod private { @@ -871,7 +889,6 @@ mod private { #[cfg(any(esp32, esp32s3))] use crate::peripherals::{i2s1::RegisterBlock, I2S1}; use crate::{ - clock::Clocks, dma::{ChannelRx, ChannelTx, DmaChannel, DmaDescriptor, DmaPeripheral}, gpio::{InputPin, InputSignal, OutputPin, OutputSignal}, interrupt::InterruptHandler, @@ -982,7 +999,7 @@ mod private { P: InputPin, { into_ref!(pin); - pin.set_to_input(crate::private::Internal); + pin.init_input(false, false, crate::private::Internal); pin.connect_input_to_peripheral(T::din_signal(), crate::private::Internal); self } @@ -1021,12 +1038,12 @@ mod private { for interrupt in interrupts { match interrupt { - I2sInterrupt::TxHung => { - reg_block.int_ena().modify(|_, w| w.tx_hung().set_bit()) - } I2sInterrupt::RxHung => { reg_block.int_ena().modify(|_, w| w.rx_hung().set_bit()) } + I2sInterrupt::TxHung => { + reg_block.int_ena().modify(|_, w| w.tx_hung().set_bit()) + } } } } @@ -1036,12 +1053,12 @@ mod private { for interrupt in interrupts { match interrupt { - I2sInterrupt::TxHung => { - reg_block.int_ena().modify(|_, w| w.tx_hung().clear_bit()) - } I2sInterrupt::RxHung => { reg_block.int_ena().modify(|_, w| w.rx_hung().clear_bit()) } + I2sInterrupt::TxHung => { + reg_block.int_ena().modify(|_, w| w.tx_hung().clear_bit()) + } } } } @@ -1051,12 +1068,12 @@ mod private { let reg_block = Self::register_block(); let ints = reg_block.int_st().read(); - if ints.tx_hung().bit() { - res.insert(I2sInterrupt::TxHung); - } if ints.rx_hung().bit() { res.insert(I2sInterrupt::RxHung); } + if ints.tx_hung().bit() { + res.insert(I2sInterrupt::TxHung); + } res } @@ -1066,12 +1083,12 @@ mod private { for interrupt in interrupts { match interrupt { - I2sInterrupt::TxHung => reg_block - .int_clr() - .write(|w| w.tx_hung().clear_bit_by_one()), I2sInterrupt::RxHung => reg_block .int_clr() .write(|w| w.rx_hung().clear_bit_by_one()), + I2sInterrupt::TxHung => reg_block + .int_clr() + .write(|w| w.tx_hung().clear_bit_by_one()), } } } @@ -1285,18 +1302,18 @@ mod private { for interrupt in interrupts { match interrupt { - I2sInterrupt::TxHung => { - reg_block.int_ena().modify(|_, w| w.tx_hung().set_bit()) - } I2sInterrupt::RxHung => { reg_block.int_ena().modify(|_, w| w.rx_hung().set_bit()) } - I2sInterrupt::TxDone => { - reg_block.int_ena().modify(|_, w| w.tx_done().set_bit()) + I2sInterrupt::TxHung => { + reg_block.int_ena().modify(|_, w| w.tx_hung().set_bit()) } I2sInterrupt::RxDone => { reg_block.int_ena().modify(|_, w| w.rx_done().set_bit()) } + I2sInterrupt::TxDone => { + reg_block.int_ena().modify(|_, w| w.tx_done().set_bit()) + } } } } @@ -1306,18 +1323,18 @@ mod private { for interrupt in interrupts { match interrupt { - I2sInterrupt::TxHung => { - reg_block.int_ena().modify(|_, w| w.tx_hung().clear_bit()) - } I2sInterrupt::RxHung => { reg_block.int_ena().modify(|_, w| w.rx_hung().clear_bit()) } - I2sInterrupt::TxDone => { - reg_block.int_ena().modify(|_, w| w.tx_done().clear_bit()) + I2sInterrupt::TxHung => { + reg_block.int_ena().modify(|_, w| w.tx_hung().clear_bit()) } I2sInterrupt::RxDone => { reg_block.int_ena().modify(|_, w| w.rx_done().clear_bit()) } + I2sInterrupt::TxDone => { + reg_block.int_ena().modify(|_, w| w.tx_done().clear_bit()) + } } } } @@ -1327,18 +1344,18 @@ mod private { let reg_block = Self::register_block(); let ints = reg_block.int_st().read(); - if ints.tx_hung().bit() { - res.insert(I2sInterrupt::TxHung); - } if ints.rx_hung().bit() { res.insert(I2sInterrupt::RxHung); } - if ints.tx_done().bit() { - res.insert(I2sInterrupt::TxDone); + if ints.tx_hung().bit() { + res.insert(I2sInterrupt::TxHung); } if ints.rx_done().bit() { res.insert(I2sInterrupt::RxDone); } + if ints.tx_done().bit() { + res.insert(I2sInterrupt::TxDone); + } res } @@ -1348,18 +1365,18 @@ mod private { for interrupt in interrupts { match interrupt { - I2sInterrupt::TxHung => reg_block - .int_clr() - .write(|w| w.tx_hung().clear_bit_by_one()), I2sInterrupt::RxHung => reg_block .int_clr() .write(|w| w.rx_hung().clear_bit_by_one()), - I2sInterrupt::TxDone => reg_block + I2sInterrupt::TxHung => reg_block .int_clr() - .write(|w| w.tx_done().clear_bit_by_one()), + .write(|w| w.tx_hung().clear_bit_by_one()), I2sInterrupt::RxDone => reg_block .int_clr() .write(|w| w.rx_done().clear_bit_by_one()), + I2sInterrupt::TxDone => reg_block + .int_clr() + .write(|w| w.tx_done().clear_bit_by_one()), } } } @@ -2084,7 +2101,6 @@ mod private { sample_rate: impl Into, channels: u8, data_bits: u8, - _clocks: &Clocks<'_>, ) -> I2sClockDividers { // this loosely corresponds to `i2s_std_calculate_clock` and // `i2s_ll_tx_set_mclk` in esp-idf @@ -2150,7 +2166,7 @@ mod private { } } -#[cfg(feature = "async")] +/// Async functionality pub mod asynch { use super::{Error, I2sRx, I2sTx, RegisterAccess}; use crate::{ @@ -2248,8 +2264,6 @@ pub mod asynch { } /// An in-progress async circular DMA write transfer. - #[non_exhaustive] - pub struct I2sWriteDmaTransferAsync<'d, T, CH, BUFFER> where T: RegisterAccess, @@ -2310,7 +2324,7 @@ pub mod asynch { /// One-shot read I2S. async fn read_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error>; - /// Continuously read frm I2S. Returns [I2sReadDmaTransferAsync] + /// Continuously read from I2S. Returns [I2sReadDmaTransferAsync] fn read_dma_circular_async( self, words: RXBUF, @@ -2404,8 +2418,6 @@ pub mod asynch { } /// An in-progress async circular DMA read transfer. - #[non_exhaustive] - pub struct I2sReadDmaTransferAsync<'d, T, CH, BUFFER> where T: RegisterAccess, diff --git a/esp-hal/src/interrupt/mod.rs b/esp-hal/src/interrupt/mod.rs index 1977d7d7070..b71b9757f93 100644 --- a/esp-hal/src/interrupt/mod.rs +++ b/esp-hal/src/interrupt/mod.rs @@ -24,47 +24,41 @@ //! We reserve a number of CPU interrupts, which cannot be used; see //! [`RESERVED_INTERRUPTS`]. //! -//! ## Examples -//! ### Using the Peripheral Driver to Register an Interrupt Handler +//! ## Example +//! +//! ### Using the peripheral driver to register an interrupt handler +//! //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use core::cell::RefCell; +//! let mut sw_int = +//! SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); +//! critical_section::with(|cs| { +//! sw_int +//! .software_interrupt0 +//! .set_interrupt_handler(swint0_handler); +//! SWINT0 +//! .borrow_ref_mut(cs) +//! .replace(sw_int.software_interrupt0); +//! }); //! +//! critical_section::with(|cs| { +//! SWINT0.borrow_ref(cs).as_ref().unwrap().raise(); +//! }); +//! # +//! # loop {} +//! # } +//! +//! # use core::cell::RefCell; +//! # //! # use critical_section::Mutex; -//! # use esp_hal::{ -//! # prelude::*, -//! # system::{SoftwareInterrupt, SystemControl}, -//! # }; +//! # use esp_hal::interrupt::software::{SoftwareInterrupt, SoftwareInterruptControl}; //! # use esp_hal::interrupt::Priority; //! # use esp_hal::interrupt::InterruptHandler; -//! +//! # //! static SWINT0: Mutex>>> = -//! Mutex::new(RefCell::new(None)); -//! -//! let mut sw_int = system.software_interrupt_control; -//! critical_section::with(|cs| { -//! sw_int -//! .software_interrupt0 -//! .set_interrupt_handler(swint0_handler); -//! SWINT0 -//! .borrow_ref_mut(cs) -//! .replace(sw_int.software_interrupt0); -//! }); -//! -//! critical_section::with(|cs| { -//! SWINT0.borrow_ref(cs).as_ref().unwrap().raise(); -//! }); +//! Mutex::new(RefCell::new(None)); //! -//! loop {} -//! } -//! -//! # use procmacros::handler; -//! # use esp_hal::interrupt; -//! # use critical_section::Mutex; -//! # use core::cell::RefCell; -//! # use esp_hal::system::SoftwareInterrupt; -//! # static SWINT0: Mutex>>> = Mutex::new(RefCell::new(None)); -//! #[handler(priority = esp_hal::interrupt::Priority::Priority1)] +//! #[handler(priority = Priority::Priority1)] //! fn swint0_handler() { //! // esp_println::println!("SW interrupt0"); //! critical_section::with(|cs| { @@ -73,8 +67,6 @@ //! } //! ``` -#![warn(missing_docs)] - use core::ops::BitAnd; #[cfg(riscv)] @@ -87,6 +79,8 @@ mod riscv; #[cfg(xtensa)] mod xtensa; +pub mod software; + /// An interrupt handler #[cfg_attr( multi_core, @@ -227,10 +221,6 @@ impl Iterator for InterruptStatusIterator { type Item = u8; fn next(&mut self) -> Option { - if self.idx == usize::MAX { - return None; - } - for i in self.idx..STATUS_WORDS { if self.status.status[i] != 0 { let bit = self.status.status[i].trailing_zeros(); diff --git a/esp-hal/src/interrupt/riscv.rs b/esp-hal/src/interrupt/riscv.rs index 6b9f8da7722..f21a44bc07a 100644 --- a/esp-hal/src/interrupt/riscv.rs +++ b/esp-hal/src/interrupt/riscv.rs @@ -15,10 +15,8 @@ pub use esp_riscv_rt::TrapFrame; use riscv::register::{mcause, mtvec}; -#[cfg(not(any(plic, clic)))] +#[cfg(not(plic))] pub use self::classic::*; -#[cfg(clic)] -pub use self::clic::*; #[cfg(plic)] pub use self::plic::*; pub use self::vectored::*; @@ -53,38 +51,68 @@ pub enum InterruptKind { #[repr(u32)] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] pub enum CpuInterrupt { + /// Interrupt number 1. Interrupt1 = 1, + /// Interrupt number 2. Interrupt2, + /// Interrupt number 3. Interrupt3, + /// Interrupt number 4. Interrupt4, + /// Interrupt number 5. Interrupt5, + /// Interrupt number 6. Interrupt6, + /// Interrupt number 7. Interrupt7, + /// Interrupt number 8. Interrupt8, + /// Interrupt number 9. Interrupt9, + /// Interrupt number 10. Interrupt10, + /// Interrupt number 11. Interrupt11, + /// Interrupt number 12. Interrupt12, + /// Interrupt number 13. Interrupt13, + /// Interrupt number 14. Interrupt14, + /// Interrupt number 15. Interrupt15, + /// Interrupt number 16. Interrupt16, + /// Interrupt number 17. Interrupt17, + /// Interrupt number 18. Interrupt18, + /// Interrupt number 19. Interrupt19, + /// Interrupt number 20. Interrupt20, + /// Interrupt number 21. Interrupt21, + /// Interrupt number 22. Interrupt22, + /// Interrupt number 23. Interrupt23, + /// Interrupt number 24. Interrupt24, + /// Interrupt number 25. Interrupt25, + /// Interrupt number 26. Interrupt26, + /// Interrupt number 27. Interrupt27, + /// Interrupt number 28. Interrupt28, + /// Interrupt number 29. Interrupt29, + /// Interrupt number 30. Interrupt30, + /// Interrupt number 31. Interrupt31, } @@ -92,44 +120,45 @@ pub enum CpuInterrupt { #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] -#[allow(missing_docs)] pub enum Priority { + /// No priority. None = 0, + /// Priority level 1. Priority1, + /// Priority level 2. Priority2, + /// Priority level 3. Priority3, + /// Priority level 4. Priority4, + /// Priority level 5. Priority5, + /// Priority level 6. Priority6, + /// Priority level 7. Priority7, - #[cfg(not(clic))] + /// Priority level 8. Priority8, - #[cfg(not(clic))] + /// Priority level 9. Priority9, - #[cfg(not(clic))] + /// Priority level 10. Priority10, - #[cfg(not(clic))] + /// Priority level 11. Priority11, - #[cfg(not(clic))] + /// Priority level 12. Priority12, - #[cfg(not(clic))] + /// Priority level 13. Priority13, - #[cfg(not(clic))] + /// Priority level 14. Priority14, - #[cfg(not(clic))] + /// Priority level 15. Priority15, } impl Priority { /// Maximum interrupt priority pub const fn max() -> Priority { - cfg_if::cfg_if! { - if #[cfg(not(clic))] { - Priority::Priority15 - } else { - Priority::Priority7 - } - } + Priority::Priority15 } /// Minimum interrupt priority @@ -515,7 +544,7 @@ mod vectored { interrupt_handler!(19); } -#[cfg(not(any(plic, clic)))] +#[cfg(not(plic))] mod classic { use super::{CpuInterrupt, InterruptKind, Priority}; use crate::Cpu; @@ -572,12 +601,8 @@ mod classic { /// priority of interrupts 1 - 15. pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) { let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR; - let cpu_interrupt_number = which as isize; - let intr_prio_base = intr.cpu_int_pri(0).as_ptr(); - - intr_prio_base - .offset(cpu_interrupt_number) - .write_volatile(priority as u32); + intr.cpu_int_pri(which as usize) + .write(|w| w.map().bits(priority as u8)); } /// Clear a CPU interrupt @@ -601,12 +626,9 @@ mod classic { #[inline] pub(super) unsafe extern "C" fn get_priority(cpu_interrupt: CpuInterrupt) -> Priority { let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR; - let intr_prio_base = intr.cpu_int_pri(0).as_ptr(); - - let prio = intr_prio_base - .offset(cpu_interrupt as isize) - .read_volatile(); - core::mem::transmute::(prio as u8) + core::mem::transmute::( + intr.cpu_int_pri(cpu_interrupt as usize).read().map().bits(), + ) } #[no_mangle] #[link_section = ".trap"] @@ -659,22 +681,19 @@ mod plic { 1, 2, 0, 0, 3, 4, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ]; - const DR_REG_PLIC_MX_BASE: u32 = 0x20001000; - const PLIC_MXINT_ENABLE_REG: u32 = DR_REG_PLIC_MX_BASE; - const PLIC_MXINT_TYPE_REG: u32 = DR_REG_PLIC_MX_BASE + 0x4; - const PLIC_MXINT_CLEAR_REG: u32 = DR_REG_PLIC_MX_BASE + 0x8; - const PLIC_MXINT0_PRI_REG: u32 = DR_REG_PLIC_MX_BASE + 0x10; - const PLIC_MXINT_THRESH_REG: u32 = DR_REG_PLIC_MX_BASE + 0x90; /// Enable a CPU interrupt /// /// # Safety /// /// Make sure there is an interrupt handler registered. pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) { - let cpu_interrupt_number = which as isize; - let mxint_enable = PLIC_MXINT_ENABLE_REG as *mut u32; unsafe { - mxint_enable.write_volatile(mxint_enable.read_volatile() | 1 << cpu_interrupt_number); + let plic = &*crate::peripherals::PLIC_MX::PTR; + plic.mxint_enable().modify(|r, w| { + let old = r.cpu_mxint_enable().bits(); + let new = old | 1 << (which as isize); + w.cpu_mxint_enable().bits(new) + }); } } @@ -684,17 +703,17 @@ mod plic { /// bits. pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) { unsafe { - let intr = PLIC_MXINT_TYPE_REG as *mut u32; - let cpu_interrupt_number = which as isize; - + let plic = &*crate::peripherals::PLIC_MX::PTR; let interrupt_type = match kind { InterruptKind::Level => 0, InterruptKind::Edge => 1, }; - intr.write_volatile( - intr.read_volatile() & !(1 << cpu_interrupt_number) - | (interrupt_type << cpu_interrupt_number), - ); + + plic.mxint_type().modify(|r, w| { + let old = r.cpu_mxint_type().bits(); + let new = old & !(1 << (which as isize)) | (interrupt_type << (which as isize)); + w.cpu_mxint_type().bits(new) + }); } } @@ -705,21 +724,23 @@ mod plic { /// Great care must be taken when using this function; avoid changing the /// priority of interrupts 1 - 15. pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) { - let plic_mxint_pri_ptr = PLIC_MXINT0_PRI_REG as *mut u32; - - let cpu_interrupt_number = which as isize; - plic_mxint_pri_ptr - .offset(cpu_interrupt_number) - .write_volatile(priority as u32); + unsafe { + let plic = &*crate::peripherals::PLIC_MX::PTR; + plic.mxint_pri(which as usize) + .modify(|_, w| w.cpu_mxint_pri().bits(priority as u8)); + } } /// Clear a CPU interrupt #[inline] pub fn clear(_core: Cpu, which: CpuInterrupt) { unsafe { - let cpu_interrupt_number = which as isize; - let intr = PLIC_MXINT_CLEAR_REG as *mut u32; - intr.write_volatile(1 << cpu_interrupt_number); + let plic = &*crate::peripherals::PLIC_MX::PTR; + plic.mxint_clear().modify(|r, w| { + let old = r.cpu_mxint_clear().bits(); + let new = old | (1 << (which as isize)); + w.cpu_mxint_clear().bits(new) + }); } } @@ -735,162 +756,40 @@ mod plic { /// Get interrupt priority - called by assembly code #[inline] pub(super) unsafe extern "C" fn get_priority(cpu_interrupt: CpuInterrupt) -> Priority { - let plic_mxint_pri_ptr = PLIC_MXINT0_PRI_REG as *mut u32; - - let cpu_interrupt_number = cpu_interrupt as isize; - let prio = plic_mxint_pri_ptr - .offset(cpu_interrupt_number) - .read_volatile(); - core::mem::transmute::(prio as u8) + let plic = &*crate::peripherals::PLIC_MX::PTR; + let prio = plic + .mxint_pri(cpu_interrupt as usize) + .read() + .cpu_mxint_pri() + .bits(); + core::mem::transmute::(prio) } #[no_mangle] #[link_section = ".trap"] pub(super) unsafe extern "C" fn _handle_priority() -> u32 { use super::mcause; - let plic_mxint_pri_ptr = PLIC_MXINT0_PRI_REG as *mut u32; - let interrupt_id: isize = unwrap!(mcause::read().code().try_into()); // MSB is whether its exception or interrupt. - let interrupt_priority = plic_mxint_pri_ptr.offset(interrupt_id).read_volatile(); + let plic = &*crate::peripherals::PLIC_MX::PTR; - let thresh_reg = PLIC_MXINT_THRESH_REG as *mut u32; - let prev_interrupt_priority = thresh_reg.read_volatile() & 0x000000FF; - // this is a u8 according to esp-idf, so mask everything else. + let interrupt_id: usize = mcause::read().code(); // MSB is whether its exception or interrupt. + let interrupt_priority = plic.mxint_pri(interrupt_id).read().cpu_mxint_pri().bits(); + + let prev_interrupt_priority = plic.mxint_thresh().read().cpu_mxint_thresh().bits(); if interrupt_priority < 15 { // leave interrupts disabled if interrupt is of max priority. - thresh_reg.write_volatile(interrupt_priority + 1); + plic.mxint_thresh() + .write(|w| w.cpu_mxint_thresh().bits(interrupt_priority + 1)); unsafe { riscv::interrupt::enable(); } } - prev_interrupt_priority + prev_interrupt_priority as u32 } #[no_mangle] #[link_section = ".trap"] pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) { riscv::interrupt::disable(); - let thresh_reg = PLIC_MXINT_THRESH_REG as *mut u32; - thresh_reg.write_volatile(stored_prio); - } -} - -#[cfg(clic)] -mod clic { - use super::{CpuInterrupt, InterruptKind, Priority}; - use crate::Cpu; - - pub(super) const DISABLED_CPU_INTERRUPT: u32 = 0; - - pub(super) const EXTERNAL_INTERRUPT_OFFSET: u32 = 16; - - pub(super) const PRIORITY_TO_INTERRUPT: &[usize] = - &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - - pub(super) const INTERRUPT_TO_PRIORITY: &[usize] = - &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - - // The memory map for interrupt registers is on a per-core basis, - // base points to the current core interrupt register, - // whereas base + DUALCORE_CLIC_CTRL_OFF points to the other - // core registers, regardless of the core we are currently running on. - - const DR_REG_CLIC_CTRL_BASE: u32 = 0x20801000; - const DUALCORE_CLIC_CTRL_OFF: u32 = 0x10000; - - const CLIC_EXT_INTR_NUM_OFFSET: usize = 16; - - bitfield::bitfield! { - #[derive(Clone, Copy, Default)] - pub struct InterruptControl(u32); - - bool,pending, set_pending: 0; - bool,enabled, set_enabled: 8; - bool,vectored, set_vectored: 16; - u8,trigger, set_trigger: 18, 17; - u8,mode, _: 23, 22; - u8,priority, set_priority: 31, 29; - } - - /// Get pointer to interrupt control register for the given core and CPU - /// interrupt number - fn intr_cntrl(core: Cpu, cpu_interrupt_number: usize) -> *mut u32 { - let offset = if core == crate::get_core() { - 0 - } else { - DUALCORE_CLIC_CTRL_OFF - }; - unsafe { - ((DR_REG_CLIC_CTRL_BASE + offset) as *mut u32) - .add(CLIC_EXT_INTR_NUM_OFFSET + cpu_interrupt_number) - } - } - - /// Enable a CPU interrupt on current core - /// - /// # Safety - /// - /// Make sure there is an interrupt handler registered. - pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) { - let cpu_interrupt_number = which as usize; - let intr_cntrl = intr_cntrl(crate::get_core(), cpu_interrupt_number); - unsafe { - let mut val = InterruptControl(intr_cntrl.read_volatile()); - val.set_enabled(true); - intr_cntrl.write_volatile(val.0); - } - } - - /// Set the interrupt kind (i.e. level or edge) of an CPU interrupt - /// - /// The vectored interrupt handler will take care of clearing edge interrupt - /// bits. - pub fn set_kind(core: Cpu, which: CpuInterrupt, kind: InterruptKind) { - let cpu_interrupt_number = which as usize; - unsafe { - let intr_cntrl = intr_cntrl(core, cpu_interrupt_number); - let mut val = InterruptControl(intr_cntrl.read_volatile()); - val.set_trigger(match kind { - InterruptKind::Level => 0b00, - InterruptKind::Edge => 0b10, - }); - intr_cntrl.write_volatile(val.0); - } - } - - /// Set the priority level of an CPU interrupt - /// - /// # Safety - /// - /// Great care must be taken when using this function; avoid changing the - /// priority of interrupts 1 - 15. - pub unsafe fn set_priority(core: Cpu, which: CpuInterrupt, priority: Priority) { - let cpu_interrupt_number = which as usize; - let intr_cntrl = intr_cntrl(core, cpu_interrupt_number); - unsafe { - let mut val = InterruptControl(intr_cntrl.read_volatile()); - val.set_priority(priority as u8); - intr_cntrl.write_volatile(val.0); - } - } - - /// Clear a CPU interrupt - #[inline] - pub fn clear(core: Cpu, which: CpuInterrupt) { - let cpu_interrupt_number = which as usize; - unsafe { - let intr_cntrl = intr_cntrl(core, cpu_interrupt_number); - let mut val = InterruptControl(intr_cntrl.read_volatile()); - val.set_pending(false); - intr_cntrl.write_volatile(val.0); - } - } - - /// Get interrupt priority - #[inline] - pub(super) fn get_priority_by_core(core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority { - let cpu_interrupt_number = cpu_interrupt as usize; - unsafe { - let intr_cntrl = intr_cntrl(core, cpu_interrupt_number); - let val = InterruptControl(intr_cntrl.read_volatile()); - core::mem::transmute::(val.priority()) - } + let plic = &*crate::peripherals::PLIC_MX::PTR; + plic.mxint_thresh() + .write(|w| w.cpu_mxint_thresh().bits(stored_prio as u8)); } } diff --git a/esp-hal/src/interrupt/software.rs b/esp-hal/src/interrupt/software.rs new file mode 100644 index 00000000000..67aab7dcb1d --- /dev/null +++ b/esp-hal/src/interrupt/software.rs @@ -0,0 +1,192 @@ +//! # Software Interrupts +//! +//! The [`SoftwareInterruptControl`] struct gives access to the available +//! software interrupts. +//! +//! The [`SoftwareInterrupt`] struct allows raising or resetting software +//! interrupts using the [`raise()`][SoftwareInterrupt::raise] and +//! [`reset()`][SoftwareInterrupt::reset] methods. +//! +//! ## Examples +//! +//! ```rust, no_run +#![doc = crate::before_snippet!()] +//! let sw_ints = +//! SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); +//! +//! // Take the interrupt you want to use. +//! let mut int0 = sw_ints.software_interrupt0; +//! +//! // Set up the interrupt handler. Do this in a critical section so the global +//! // contains the interrupt object before the interrupt is triggered. +//! critical_section::with(|cs| { +//! int0.set_interrupt_handler(interrupt_handler); +//! SWINT0.borrow_ref_mut(cs).replace(int0); +//! }); +//! # } +//! +//! # use core::cell::RefCell; +//! # use critical_section::Mutex; +//! # use esp_hal::interrupt::software::{SoftwareInterrupt, SoftwareInterruptControl}; +//! // ... somewhere outside of your main function +//! +//! // Define a shared handle to the software interrupt. +//! static SWINT0: Mutex>>> = +//! Mutex::new(RefCell::new(None)); +//! +//! #[handler] +//! fn interrupt_handler() { +//! // esp_println::println!("SW interrupt0 handled"); +//! +//! // Clear the interrupt request. +//! critical_section::with(|cs| { +//! SWINT0.borrow_ref(cs).as_ref().unwrap().reset(); +//! }); +//! } +//! ``` + +use crate::{interrupt::InterruptHandler, InterruptConfigurable}; + +/// A software interrupt can be triggered by software. +#[non_exhaustive] +pub struct SoftwareInterrupt; + +impl SoftwareInterrupt { + /// Sets the interrupt handler for this software-interrupt + pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) { + let interrupt = match NUM { + 0 => crate::peripherals::Interrupt::FROM_CPU_INTR0, + 1 => crate::peripherals::Interrupt::FROM_CPU_INTR1, + 2 => crate::peripherals::Interrupt::FROM_CPU_INTR2, + 3 => crate::peripherals::Interrupt::FROM_CPU_INTR3, + _ => unreachable!(), + }; + + unsafe { + crate::interrupt::bind_interrupt(interrupt, handler.handler()); + crate::interrupt::enable(interrupt, handler.priority()).unwrap(); + } + } + + /// Trigger this software-interrupt + pub fn raise(&self) { + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2))] { + let system = unsafe { &*crate::peripherals::INTPRI::PTR }; + } else { + let system = unsafe { &*crate::peripherals::SYSTEM::PTR }; + } + } + + match NUM { + 0 => system + .cpu_intr_from_cpu_0() + .write(|w| w.cpu_intr_from_cpu_0().set_bit()), + 1 => system + .cpu_intr_from_cpu_1() + .write(|w| w.cpu_intr_from_cpu_1().set_bit()), + 2 => system + .cpu_intr_from_cpu_2() + .write(|w| w.cpu_intr_from_cpu_2().set_bit()), + 3 => system + .cpu_intr_from_cpu_3() + .write(|w| w.cpu_intr_from_cpu_3().set_bit()), + _ => unreachable!(), + } + } + + /// Resets this software-interrupt + pub fn reset(&self) { + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2))] { + let system = unsafe { &*crate::peripherals::INTPRI::PTR }; + } else { + let system = unsafe { &*crate::peripherals::SYSTEM::PTR }; + } + } + + match NUM { + 0 => system + .cpu_intr_from_cpu_0() + .write(|w| w.cpu_intr_from_cpu_0().clear_bit()), + 1 => system + .cpu_intr_from_cpu_1() + .write(|w| w.cpu_intr_from_cpu_1().clear_bit()), + 2 => system + .cpu_intr_from_cpu_2() + .write(|w| w.cpu_intr_from_cpu_2().clear_bit()), + 3 => system + .cpu_intr_from_cpu_3() + .write(|w| w.cpu_intr_from_cpu_3().clear_bit()), + _ => unreachable!(), + } + } + + /// Unsafely create an instance of this peripheral out of thin air. + /// + /// # Safety + /// + /// You must ensure that you're only using one instance of this type at a + /// time. + #[inline] + pub unsafe fn steal() -> Self { + Self + } +} + +impl crate::peripheral::Peripheral for SoftwareInterrupt { + type P = SoftwareInterrupt; + + #[inline] + unsafe fn clone_unchecked(&mut self) -> Self::P { + Self::steal() + } +} + +impl crate::private::Sealed for SoftwareInterrupt {} + +impl InterruptConfigurable for SoftwareInterrupt { + fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { + SoftwareInterrupt::set_interrupt_handler(self, handler); + } +} + +/// This gives access to the available software interrupts. +/// +/// This struct contains several instances of software interrupts that can be +/// used for signaling between different parts of a program or system. Each +/// interrupt is identified by an index (0 to 3). +#[cfg_attr( + multi_core, + doc = r#" + +Please note: Software interrupt 3 is reserved +for inter-processor communication when using +`esp-hal-embassy`."# +)] +#[non_exhaustive] +pub struct SoftwareInterruptControl { + /// Software interrupt 0. + pub software_interrupt0: SoftwareInterrupt<0>, + /// Software interrupt 1. + pub software_interrupt1: SoftwareInterrupt<1>, + /// Software interrupt 2. + pub software_interrupt2: SoftwareInterrupt<2>, + #[cfg(not(all(feature = "__esp_hal_embassy", multi_core)))] + /// Software interrupt 3. Only available when not using `esp-hal-embassy`, + /// or on single-core systems. + pub software_interrupt3: SoftwareInterrupt<3>, +} + +impl SoftwareInterruptControl { + /// Create a new instance of the software interrupt control. + pub fn new(_peripheral: crate::peripherals::SW_INTERRUPT) -> Self { + SoftwareInterruptControl { + software_interrupt0: SoftwareInterrupt {}, + software_interrupt1: SoftwareInterrupt {}, + software_interrupt2: SoftwareInterrupt {}, + #[cfg(not(all(feature = "__esp_hal_embassy", multi_core)))] + software_interrupt3: SoftwareInterrupt {}, + } + } +} diff --git a/esp-hal/src/interrupt/xtensa.rs b/esp-hal/src/interrupt/xtensa.rs index 23382b03a63..798837d52bc 100644 --- a/esp-hal/src/interrupt/xtensa.rs +++ b/esp-hal/src/interrupt/xtensa.rs @@ -1,6 +1,6 @@ //! Interrupt handling -use xtensa_lx::interrupt::{self, InterruptNumber}; +use xtensa_lx::interrupt; use xtensa_lx_rt::exception::Context; pub use self::vectored::*; @@ -27,39 +27,70 @@ pub enum Error { #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u32)] -#[allow(missing_docs)] pub enum CpuInterrupt { + /// Level-triggered interrupt with priority 1. Interrupt0LevelPriority1 = 0, + /// Level-triggered interrupt with priority 1. Interrupt1LevelPriority1, + /// Level-triggered interrupt with priority 1. Interrupt2LevelPriority1, + /// Level-triggered interrupt with priority 1. Interrupt3LevelPriority1, + /// Level-triggered interrupt with priority 1. Interrupt4LevelPriority1, + /// Level-triggered interrupt with priority 1. Interrupt5LevelPriority1, + /// Timer 0 interrupt with priority 1. Interrupt6Timer0Priority1, + /// Software-triggered interrupt with priority 1. Interrupt7SoftwarePriority1, + /// Level-triggered interrupt with priority 1. Interrupt8LevelPriority1, + /// Level-triggered interrupt with priority 1. Interrupt9LevelPriority1, + /// Edge-triggered interrupt with priority 1. Interrupt10EdgePriority1, + /// Profiling-related interrupt with priority 3. Interrupt11ProfilingPriority3, + /// Level-triggered interrupt with priority 1. Interrupt12LevelPriority1, + /// Level-triggered interrupt with priority 1. Interrupt13LevelPriority1, + /// Non-maskable interrupt (NMI) with priority 7. Interrupt14NmiPriority7, + /// Timer 1 interrupt with priority 3. Interrupt15Timer1Priority3, + /// Timer 2 interrupt with priority 5. Interrupt16Timer2Priority5, + /// Level-triggered interrupt with priority 1. Interrupt17LevelPriority1, + /// Level-triggered interrupt with priority 1. Interrupt18LevelPriority1, + /// Level-triggered interrupt with priority 2. Interrupt19LevelPriority2, + /// Level-triggered interrupt with priority 2. Interrupt20LevelPriority2, + /// Level-triggered interrupt with priority 2. Interrupt21LevelPriority2, + /// Edge-triggered interrupt with priority 3. Interrupt22EdgePriority3, + /// Level-triggered interrupt with priority 3. Interrupt23LevelPriority3, + /// Level-triggered interrupt with priority 4. Interrupt24LevelPriority4, + /// Level-triggered interrupt with priority 4. Interrupt25LevelPriority4, + /// Level-triggered interrupt with priority 5. Interrupt26LevelPriority5, + /// Level-triggered interrupt with priority 3. Interrupt27LevelPriority3, + /// Edge-triggered interrupt with priority 4. Interrupt28EdgePriority4, + /// Software-triggered interrupt with priority 3. Interrupt29SoftwarePriority3, + /// Edge-triggered interrupt with priority 4. Interrupt30EdgePriority4, + /// Edge-triggered interrupt with priority 5. Interrupt31EdgePriority5, } @@ -267,11 +298,14 @@ mod vectored { #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] - #[allow(missing_docs)] pub enum Priority { + /// No priority. None = 0, + /// Priority level 1. Priority1, + /// Priority level 2. Priority2, + /// Priority level 3. Priority3, } @@ -502,7 +536,7 @@ mod vectored { fn EspDefaultHandler(level: u32, interrupt: Interrupt); } - let handler = peripherals::__INTERRUPTS[interrupt.number() as usize]._handler; + let handler = peripherals::__INTERRUPTS[interrupt as usize]._handler; if core::ptr::eq( handler as *const _, EspDefaultHandler as *const unsafe extern "C" fn(), @@ -520,9 +554,9 @@ mod vectored { mod chip_specific { use super::*; pub const INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from( - 0b0000_0000_0000_0000_0000_0000_0000_0011, - 0b1111_1100_0000_0000_0000_0000_0000_0000, 0b0000_0000_0000_0000_0000_0000_0000_0000, + 0b1111_1100_0000_0000_0000_0000_0000_0000, + 0b0000_0000_0000_0000_0000_0000_0000_0011, ); #[inline] pub fn interrupt_is_edge(interrupt: Interrupt) -> bool { @@ -541,14 +575,13 @@ mod vectored { } } - #[allow(clippy::unusual_byte_groupings)] #[cfg(esp32s2)] mod chip_specific { use super::*; pub const INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from( - 0b0000_0000_0000_0000_0000_0011_1011_1111, - 0b1100_0000_0000_0000_0000_0000_0000_0000, 0b0000_0000_0000_0000_0000_0000_0000_0000, + 0b1100_0000_0000_0000_0000_0000_0000_0000, + 0b0000_0000_0000_0000_0000_0011_1011_1111, ); #[inline] pub fn interrupt_is_edge(interrupt: Interrupt) -> bool { diff --git a/esp-hal/src/lcd_cam/cam.rs b/esp-hal/src/lcd_cam/cam.rs index 111016d9110..41e30c1b6ba 100644 --- a/esp-hal/src/lcd_cam/cam.rs +++ b/esp-hal/src/lcd_cam/cam.rs @@ -26,7 +26,7 @@ //! # let dma = Dma::new(peripherals.DMA); //! # let channel = dma.channel0; //! -//! # let (tx_buffer, tx_descriptors, _, rx_descriptors) = dma_buffers!(32678, 0); +//! # let (_, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32678, 0); //! //! # let channel = channel.configure( //! # false, @@ -55,7 +55,6 @@ //! rx_descriptors, //! data_pins, //! 20u32.MHz(), -//! &clocks //! ) //! // Remove this for slave mode. //! .with_master_clock(mclk_pin) @@ -102,20 +101,31 @@ pub enum EofMode { #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum VsyncFilterThreshold { + /// Requires 1 valid VSYNC pulse to trigger synchronization. One, + /// Requires 2 valid VSYNC pulse to trigger synchronization. Two, + /// Requires 3 valid VSYNC pulse to trigger synchronization. Three, + /// Requires 4 valid VSYNC pulse to trigger synchronization. Four, + /// Requires 5 valid VSYNC pulse to trigger synchronization. Five, + /// Requires 6 valid VSYNC pulse to trigger synchronization. Six, + /// Requires 7 valid VSYNC pulse to trigger synchronization. Seven, + /// Requires 8 valid VSYNC pulse to trigger synchronization. Eight, } +/// Represents the camera interface. pub struct Cam<'d> { + /// The LCD_CAM peripheral reference for managing the camera functionality. pub(crate) lcd_cam: PeripheralRef<'d, LCD_CAM>, } +/// Represents the camera interface with DMA support. pub struct Camera<'d, CH: DmaChannel> { lcd_cam: PeripheralRef<'d, LCD_CAM>, rx_channel: ChannelRx<'d, CH>, @@ -128,16 +138,17 @@ impl<'d, CH: DmaChannel> Camera<'d, CH> where CH::P: LcdCamPeripheral, { + /// Creates a new `Camera` instance with DMA support. pub fn new( cam: Cam<'d>, mut channel: ChannelRx<'d, CH>, descriptors: &'static mut [DmaDescriptor], _pins: P, frequency: HertzU32, - clocks: &Clocks<'d>, ) -> Self { let lcd_cam = cam.lcd_cam; + let clocks = Clocks::get(); let (i, divider) = calculate_clkm( frequency.to_Hz() as _, &[ @@ -207,7 +218,7 @@ where } impl<'d, CH: DmaChannel> DmaSupport for Camera<'d, CH> { - fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { loop { // Wait for IN_SUC_EOF (i.e. VSYNC) if self.rx_channel.is_done() { @@ -244,6 +255,7 @@ impl<'d, CH: DmaChannel> DmaSupportRx for Camera<'d, CH> { } impl<'d, CH: DmaChannel> Camera<'d, CH> { + /// Configures the byte order for the camera data. pub fn set_byte_order(&mut self, byte_order: ByteOrder) -> &mut Self { self.lcd_cam .cam_ctrl() @@ -251,6 +263,7 @@ impl<'d, CH: DmaChannel> Camera<'d, CH> { self } + /// Configures the bit order for the camera data. pub fn set_bit_order(&mut self, bit_order: BitOrder) -> &mut Self { self.lcd_cam .cam_ctrl() @@ -258,6 +271,7 @@ impl<'d, CH: DmaChannel> Camera<'d, CH> { self } + /// Configures the VSYNC filter threshold. pub fn set_vsync_filter(&mut self, threshold: Option) -> &mut Self { if let Some(threshold) = threshold { let value = match threshold { @@ -285,6 +299,7 @@ impl<'d, CH: DmaChannel> Camera<'d, CH> { self } + /// Configures the master clock (MCLK) pin for the camera interface. pub fn with_master_clock(self, mclk: impl Peripheral

+ 'd) -> Self { crate::into_ref!(mclk); mclk.set_to_push_pull_output(crate::private::Internal); @@ -292,15 +307,18 @@ impl<'d, CH: DmaChannel> Camera<'d, CH> { self } + /// Configures the pixel clock (PCLK) pin for the camera interface. pub fn with_pixel_clock(self, pclk: impl Peripheral

+ 'd) -> Self { crate::into_ref!(pclk); - pclk.set_to_input(crate::private::Internal); + pclk.init_input(false, false, crate::private::Internal); pclk.connect_input_to_peripheral(InputSignal::CAM_PCLK, crate::private::Internal); self } + /// Configures the control pins for the camera interface (VSYNC and + /// HENABLE). pub fn with_ctrl_pins( self, vsync: impl Peripheral

+ 'd, @@ -309,9 +327,9 @@ impl<'d, CH: DmaChannel> Camera<'d, CH> { crate::into_ref!(vsync); crate::into_ref!(h_enable); - vsync.set_to_input(crate::private::Internal); + vsync.init_input(false, false, crate::private::Internal); vsync.connect_input_to_peripheral(InputSignal::CAM_V_SYNC, crate::private::Internal); - h_enable.set_to_input(crate::private::Internal); + h_enable.init_input(false, false, crate::private::Internal); h_enable.connect_input_to_peripheral(InputSignal::CAM_H_ENABLE, crate::private::Internal); self.lcd_cam @@ -321,6 +339,8 @@ impl<'d, CH: DmaChannel> Camera<'d, CH> { self } + /// Configures the control pins for the camera interface (VSYNC, HSYNC, and + /// HENABLE) with DE (data enable). pub fn with_ctrl_pins_and_de( self, vsync: impl Peripheral

+ 'd, @@ -331,11 +351,11 @@ impl<'d, CH: DmaChannel> Camera<'d, CH> { crate::into_ref!(hsync); crate::into_ref!(h_enable); - vsync.set_to_input(crate::private::Internal); + vsync.init_input(false, false, crate::private::Internal); vsync.connect_input_to_peripheral(InputSignal::CAM_V_SYNC, crate::private::Internal); - hsync.set_to_input(crate::private::Internal); + hsync.init_input(false, false, crate::private::Internal); hsync.connect_input_to_peripheral(InputSignal::CAM_H_SYNC, crate::private::Internal); - h_enable.set_to_input(crate::private::Internal); + h_enable.init_input(false, false, crate::private::Internal); h_enable.connect_input_to_peripheral(InputSignal::CAM_H_ENABLE, crate::private::Internal); self.lcd_cam @@ -388,6 +408,7 @@ impl<'d, CH: DmaChannel> Camera<'d, CH> { self.rx_channel.start_transfer() } + /// Starts a DMA transfer to receive data from the camera peripheral. pub fn read_dma<'t, RXBUF: WriteBuffer>( &'t mut self, buf: &'t mut RXBUF, @@ -400,6 +421,8 @@ impl<'d, CH: DmaChannel> Camera<'d, CH> { Ok(DmaTransferRx::new(self)) } + /// Starts a circular DMA transfer to receive data from the camera + /// peripheral. pub fn read_dma_circular<'t, RXBUF: WriteBuffer>( &'t mut self, buf: &'t mut RXBUF, @@ -413,12 +436,16 @@ impl<'d, CH: DmaChannel> Camera<'d, CH> { } } +/// Represents an 8-bit wide camera data bus. +/// Is used to configure the camera interface to receive 8-bit data. pub struct RxEightBits { _pins: (), } impl RxEightBits { #[allow(clippy::too_many_arguments)] + /// Creates a new instance of `RxEightBits`, configuring the specified pins + /// as the 8-bit data bus. pub fn new<'d, P0, P1, P2, P3, P4, P5, P6, P7>( pin_0: impl Peripheral

+ 'd, pin_1: impl Peripheral

+ 'd, @@ -448,21 +475,21 @@ impl RxEightBits { crate::into_ref!(pin_6); crate::into_ref!(pin_7); - pin_0.set_to_input(crate::private::Internal); + pin_0.init_input(false, false, crate::private::Internal); pin_0.connect_input_to_peripheral(InputSignal::CAM_DATA_0, crate::private::Internal); - pin_1.set_to_input(crate::private::Internal); + pin_1.init_input(false, false, crate::private::Internal); pin_1.connect_input_to_peripheral(InputSignal::CAM_DATA_1, crate::private::Internal); - pin_2.set_to_input(crate::private::Internal); + pin_2.init_input(false, false, crate::private::Internal); pin_2.connect_input_to_peripheral(InputSignal::CAM_DATA_2, crate::private::Internal); - pin_3.set_to_input(crate::private::Internal); + pin_3.init_input(false, false, crate::private::Internal); pin_3.connect_input_to_peripheral(InputSignal::CAM_DATA_3, crate::private::Internal); - pin_4.set_to_input(crate::private::Internal); + pin_4.init_input(false, false, crate::private::Internal); pin_4.connect_input_to_peripheral(InputSignal::CAM_DATA_4, crate::private::Internal); - pin_5.set_to_input(crate::private::Internal); + pin_5.init_input(false, false, crate::private::Internal); pin_5.connect_input_to_peripheral(InputSignal::CAM_DATA_5, crate::private::Internal); - pin_6.set_to_input(crate::private::Internal); + pin_6.init_input(false, false, crate::private::Internal); pin_6.connect_input_to_peripheral(InputSignal::CAM_DATA_6, crate::private::Internal); - pin_7.set_to_input(crate::private::Internal); + pin_7.init_input(false, false, crate::private::Internal); pin_7.connect_input_to_peripheral(InputSignal::CAM_DATA_7, crate::private::Internal); Self { _pins: () } @@ -473,12 +500,16 @@ impl RxPins for RxEightBits { const BUS_WIDTH: usize = 1; } +/// Represents a 16-bit wide camera data bus. +/// Is used to configure the camera interface to receive 16-bit data. pub struct RxSixteenBits { _pins: (), } impl RxSixteenBits { #[allow(clippy::too_many_arguments)] + /// Creates a new instance of `RxSixteenBits`, configuring the specified + /// pins as the 16-bit data bus. pub fn new<'d, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15>( pin_0: impl Peripheral

+ 'd, pin_1: impl Peripheral

+ 'd, @@ -532,37 +563,37 @@ impl RxSixteenBits { crate::into_ref!(pin_14); crate::into_ref!(pin_15); - pin_0.set_to_input(crate::private::Internal); + pin_0.init_input(false, false, crate::private::Internal); pin_0.connect_input_to_peripheral(InputSignal::CAM_DATA_0, crate::private::Internal); - pin_1.set_to_input(crate::private::Internal); + pin_1.init_input(false, false, crate::private::Internal); pin_1.connect_input_to_peripheral(InputSignal::CAM_DATA_1, crate::private::Internal); - pin_2.set_to_input(crate::private::Internal); + pin_2.init_input(false, false, crate::private::Internal); pin_2.connect_input_to_peripheral(InputSignal::CAM_DATA_2, crate::private::Internal); - pin_3.set_to_input(crate::private::Internal); + pin_3.init_input(false, false, crate::private::Internal); pin_3.connect_input_to_peripheral(InputSignal::CAM_DATA_3, crate::private::Internal); - pin_4.set_to_input(crate::private::Internal); + pin_4.init_input(false, false, crate::private::Internal); pin_4.connect_input_to_peripheral(InputSignal::CAM_DATA_4, crate::private::Internal); - pin_5.set_to_input(crate::private::Internal); + pin_5.init_input(false, false, crate::private::Internal); pin_5.connect_input_to_peripheral(InputSignal::CAM_DATA_5, crate::private::Internal); - pin_6.set_to_input(crate::private::Internal); + pin_6.init_input(false, false, crate::private::Internal); pin_6.connect_input_to_peripheral(InputSignal::CAM_DATA_6, crate::private::Internal); - pin_7.set_to_input(crate::private::Internal); + pin_7.init_input(false, false, crate::private::Internal); pin_7.connect_input_to_peripheral(InputSignal::CAM_DATA_7, crate::private::Internal); - pin_8.set_to_input(crate::private::Internal); + pin_8.init_input(false, false, crate::private::Internal); pin_8.connect_input_to_peripheral(InputSignal::CAM_DATA_8, crate::private::Internal); - pin_9.set_to_input(crate::private::Internal); + pin_9.init_input(false, false, crate::private::Internal); pin_9.connect_input_to_peripheral(InputSignal::CAM_DATA_9, crate::private::Internal); - pin_10.set_to_input(crate::private::Internal); + pin_10.init_input(false, false, crate::private::Internal); pin_10.connect_input_to_peripheral(InputSignal::CAM_DATA_10, crate::private::Internal); - pin_11.set_to_input(crate::private::Internal); + pin_11.init_input(false, false, crate::private::Internal); pin_11.connect_input_to_peripheral(InputSignal::CAM_DATA_11, crate::private::Internal); - pin_12.set_to_input(crate::private::Internal); + pin_12.init_input(false, false, crate::private::Internal); pin_12.connect_input_to_peripheral(InputSignal::CAM_DATA_12, crate::private::Internal); - pin_13.set_to_input(crate::private::Internal); + pin_13.init_input(false, false, crate::private::Internal); pin_13.connect_input_to_peripheral(InputSignal::CAM_DATA_13, crate::private::Internal); - pin_14.set_to_input(crate::private::Internal); + pin_14.init_input(false, false, crate::private::Internal); pin_14.connect_input_to_peripheral(InputSignal::CAM_DATA_14, crate::private::Internal); - pin_15.set_to_input(crate::private::Internal); + pin_15.init_input(false, false, crate::private::Internal); pin_15.connect_input_to_peripheral(InputSignal::CAM_DATA_15, crate::private::Internal); Self { _pins: () } diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index 288d5148410..8539bd7a3e5 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -1,14 +1,17 @@ //! # LCD - I8080/MOTO6800 Mode. //! //! ## Overview +//! //! The LCD_CAM peripheral I8080 driver provides support for the I8080 -//! format/timing. The driver mandates DMA for DMA (Direct Memory Access) for +//! format/timing. The driver mandates DMA (Direct Memory Access) for //! efficient data transfer. //! -//! ## Examples +//! ## Example +//! //! ### MIPI-DSI Display -//! Following code show how to send a command to a MIPI-DSI display over I8080 -//! protocol. +//! +//! The following example shows how to send a command to a MIPI-DSI display over +//! the I8080 protocol. //! //! ```rust, no_run #![doc = crate::before_snippet!()] @@ -16,14 +19,12 @@ //! # use esp_hal::lcd_cam::{LcdCam, lcd::i8080::{Config, I8080, TxEightBits}}; //! # use esp_hal::dma_buffers; //! # use esp_hal::dma::{Dma, DmaPriority}; -//! # use fugit::RateExtU32; -//! //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! //! # let dma = Dma::new(peripherals.DMA); //! # let channel = dma.channel0; //! -//! # let (tx_buffer, tx_descriptors, _, rx_descriptors) = dma_buffers!(32678, 0); +//! # let ( _, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32678, 0); //! //! # let channel = channel.configure( //! # false, @@ -49,7 +50,6 @@ //! tx_pins, //! 20.MHz(), //! Config::default(), -//! &clocks, //! ) //! .with_ctrl_pins(io.pins.gpio0, io.pins.gpio47); //! @@ -61,8 +61,6 @@ use core::{fmt::Formatter, marker::PhantomData, mem::size_of}; use fugit::HertzU32; -#[cfg(feature = "async")] -use crate::lcd_cam::asynch::LcdDoneFuture; use crate::{ clock::Clocks, dma::{ @@ -80,6 +78,7 @@ use crate::{ }, gpio::{OutputPin, OutputSignal}, lcd_cam::{ + asynch::LcdDoneFuture, lcd::{i8080::private::TxPins, ClockMode, DelayMode, Phase, Polarity}, private::calculate_clkm, BitOrder, @@ -91,6 +90,7 @@ use crate::{ Mode, }; +/// Represents the I8080 LCD interface. pub struct I8080<'d, CH: DmaChannel, P, DM: Mode> { lcd_cam: PeripheralRef<'d, LCD_CAM>, tx_channel: ChannelTx<'d, CH>, @@ -104,6 +104,7 @@ where CH::P: LcdCamPeripheral, P::Word: Into, { + /// Creates a new instance of the I8080 LCD interface. pub fn new( lcd: Lcd<'d, DM>, mut channel: ChannelTx<'d, CH>, @@ -111,16 +112,15 @@ where mut pins: P, frequency: HertzU32, config: Config, - clocks: &Clocks<'d>, ) -> Self { let is_2byte_mode = size_of::() == 2; let lcd_cam = lcd.lcd_cam; + let clocks = Clocks::get(); // Due to https://www.espressif.com/sites/default/files/documentation/esp32-s3_errata_en.pdf // the LCD_PCLK divider must be at least 2. To make up for this the user // provided frequency is doubled to match. - let (i, divider) = calculate_clkm( (frequency.to_Hz() * 2) as _, &[ @@ -259,7 +259,7 @@ where } impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> DmaSupport for I8080<'d, CH, P, DM> { - fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { let lcd_user = self.lcd_cam.lcd_user(); // Wait until LCD_START is cleared by hardware. while lcd_user.read().lcd_start().bit_is_set() {} @@ -287,6 +287,7 @@ impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> I8080<'d, CH, P, DM> where P::Word: Into, { + /// Configures the byte order for data transmission. pub fn set_byte_order(&mut self, byte_order: ByteOrder) -> &mut Self { let is_inverted = byte_order != ByteOrder::default(); self.lcd_cam.lcd_user().modify(|_, w| { @@ -299,6 +300,7 @@ where self } + /// Configures the bit order for data transmission. pub fn set_bit_order(&mut self, bit_order: BitOrder) -> &mut Self { self.lcd_cam .lcd_user() @@ -306,6 +308,7 @@ where self } + /// Associates a CS pin with the I8080 interface. pub fn with_cs(self, cs: impl Peripheral

+ 'd) -> Self { crate::into_ref!(cs); cs.set_to_push_pull_output(crate::private::Internal); @@ -314,6 +317,7 @@ where self } + /// Configures the control pins for the I8080 interface. pub fn with_ctrl_pins( self, dc: impl Peripheral

+ 'd, @@ -331,6 +335,7 @@ where self } + /// Sends a command and data to the LCD using the I8080 interface. pub fn send( &mut self, cmd: impl Into>, @@ -350,6 +355,7 @@ where Ok(()) } + /// Sends a command and data to the LCD using DMA. pub fn send_dma<'t, TXBUF>( &'t mut self, cmd: impl Into>, @@ -369,11 +375,11 @@ where } } -#[cfg(feature = "async")] impl<'d, CH: DmaChannel, P: TxPins> I8080<'d, CH, P, crate::Async> where P::Word: Into, { + /// Asynchronously sends a command and data to the LCD using DMA. pub async fn send_dma_async<'t, TXBUF>( &'t mut self, cmd: impl Into>, @@ -482,21 +488,14 @@ impl<'d, CH: DmaChannel, P, DM: Mode> I8080<'d, CH, P, DM> { .lcd_user() .modify(|_, w| w.lcd_dout().clear_bit()); } else { - // Set transfer length. - self.lcd_cam.lcd_user().modify(|_, w| unsafe { - if len <= 8192 { - // Data length in fixed mode. (13 bits) - w.lcd_always_out_en() - .clear_bit() - .lcd_dout_cyclelen() - .bits((len - 1) as _) - } else { - // Enable continuous output. - w.lcd_always_out_en().set_bit() - } - .lcd_dout() - .set_bit() - }); + // Use continous mode for DMA. FROM the S3 TRM: + // > In a continuous output, LCD module keeps sending data till: + // > i. LCD_CAM_LCD_START is cleared; + // > ii. or LCD_CAM_LCD_RESET is set; + // > iii. or all the data in GDMA is sent out. + self.lcd_cam + .lcd_user() + .modify(|_, w| w.lcd_always_out_en().set_bit().lcd_dout().set_bit()); unsafe { self.tx_chain.fill_for_tx(false, ptr, len)?; @@ -517,7 +516,9 @@ impl<'d, CH: DmaChannel, P, DM: Mode> core::fmt::Debug for I8080<'d, CH, P, DM> #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Configuration settings for the I8080 interface. pub struct Config { + /// Specifies the clock mode, including polarity and phase settings. pub clock_mode: ClockMode, /// Setup cycles expected, must be at least 1. (6 bits) @@ -563,8 +564,11 @@ impl Default for Config { #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Command { + /// Suppresses the command phase. No command is sent. None, + /// Sends a single-word command. One(T), + /// Sends a two-word command. Two(T, T), } @@ -574,6 +578,8 @@ impl From for Command { } } +/// Represents a group of 8 output pins configured for 8-bit parallel data +/// transmission. pub struct TxEightBits<'d, P0, P1, P2, P3, P4, P5, P6, P7> { pin_0: PeripheralRef<'d, P0>, pin_1: PeripheralRef<'d, P1>, @@ -597,6 +603,7 @@ where P7: OutputPin, { #[allow(clippy::too_many_arguments)] + /// Creates a new `TxEightBits` instance with the provided output pins. pub fn new( pin_0: impl Peripheral

+ 'd, pin_1: impl Peripheral

+ 'd, @@ -670,6 +677,8 @@ where } } +/// Represents a group of 16 output pins configured for 16-bit parallel data +/// transmission. pub struct TxSixteenBits<'d, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15> { pin_0: PeripheralRef<'d, P0>, pin_1: PeripheralRef<'d, P1>, @@ -710,6 +719,7 @@ where P15: OutputPin, { #[allow(clippy::too_many_arguments)] + /// Creates a new `TxSixteenBits` instance with the provided output pins. pub fn new( pin_0: impl Peripheral

+ 'd, pin_1: impl Peripheral

+ 'd, diff --git a/esp-hal/src/lcd_cam/lcd/mod.rs b/esp-hal/src/lcd_cam/lcd/mod.rs index a1e5dc85153..cf7e8036b02 100644 --- a/esp-hal/src/lcd_cam/lcd/mod.rs +++ b/esp-hal/src/lcd_cam/lcd/mod.rs @@ -14,36 +14,53 @@ use crate::{peripheral::PeripheralRef, peripherals::LCD_CAM}; pub mod i8080; +/// Represents an LCD interface. pub struct Lcd<'d, DM: crate::Mode> { + /// The `LCD_CAM` peripheral reference for managing the LCD functionality. pub(crate) lcd_cam: PeripheralRef<'d, LCD_CAM>, + + /// A marker for the mode of operation (blocking or asynchronous). pub(crate) _mode: core::marker::PhantomData, } #[derive(Debug, Clone, Copy, PartialEq, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Represents the clock mode configuration for the LCD interface. pub struct ClockMode { + /// The polarity of the clock signal (idle high or low). pub polarity: Polarity, + + /// The phase of the clock signal (shift on the rising or falling edge). pub phase: Phase, } #[derive(Debug, Clone, Copy, PartialEq, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Represents the polarity of the clock signal for the LCD interface. pub enum Polarity { + /// The clock signal is low when idle. #[default] IdleLow, + + /// The clock signal is high when idle. IdleHigh, } #[derive(Debug, Clone, Copy, PartialEq, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Represents the phase of the clock signal for the LCD interface. pub enum Phase { + /// Data is shifted on the low (falling) edge of the clock signal. #[default] ShiftLow, + + /// Data is shifted on the high (rising) edge of the clock signal. ShiftHigh, } #[derive(Debug, Clone, Copy, PartialEq, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Represents the delay mode for the LCD signal output. pub enum DelayMode { /// Output without delay. #[default] diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index ec1b2b3b334..f14f021b3e0 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -19,15 +19,20 @@ use crate::{ InterruptConfigurable, }; +/// Represents a combined LCD and Camera interface. pub struct LcdCam<'d, DM: crate::Mode> { + /// The LCD interface. pub lcd: Lcd<'d, DM>, + /// The Camera interface. pub cam: Cam<'d>, } impl<'d> LcdCam<'d, crate::Blocking> { + /// Creates a new `LcdCam` instance. pub fn new(lcd_cam: impl Peripheral

+ 'd) -> Self { crate::into_ref!(lcd_cam); + PeripheralClockControl::reset(system::Peripheral::LcdCam); PeripheralClockControl::enable(system::Peripheral::LcdCam); Self { @@ -58,8 +63,8 @@ impl<'d> InterruptConfigurable for LcdCam<'d, crate::Blocking> { } } -#[cfg(feature = "async")] impl<'d> LcdCam<'d, crate::Async> { + /// Creates a new `LcdCam` instance for asynchronous operation. pub fn new_async(lcd_cam: impl Peripheral

+ 'd) -> Self { crate::into_ref!(lcd_cam); @@ -112,7 +117,6 @@ pub enum ByteOrder { } #[doc(hidden)] -#[cfg(feature = "async")] pub mod asynch { use core::task::Poll; @@ -123,6 +127,7 @@ pub mod asynch { static TX_WAKER: AtomicWaker = AtomicWaker::new(); + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct LcdDoneFuture {} impl LcdDoneFuture { @@ -166,13 +171,11 @@ pub mod asynch { } mod private { - #[cfg(feature = "async")] pub(crate) struct Instance; // NOTE: the LCD_CAM interrupt registers are shared between LCD and Camera and // this is only implemented for the LCD side, when the Camera is implemented a // CriticalSection will be needed to protect these shared registers. - #[cfg(feature = "async")] impl Instance { pub(crate) fn listen_lcd_done() { let lcd_cam = unsafe { crate::peripherals::LCD_CAM::steal() }; diff --git a/esp-hal/src/ledc/channel.rs b/esp-hal/src/ledc/channel.rs index 21701ac82ba..a838fccc36d 100644 --- a/esp-hal/src/ledc/channel.rs +++ b/esp-hal/src/ledc/channel.rs @@ -48,15 +48,23 @@ pub enum Error { #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Number { + /// Channel 0 Channel0 = 0, + /// Channel 1 Channel1 = 1, + /// Channel 2 Channel2 = 2, + /// Channel 3 Channel3 = 3, + /// Channel 4 Channel4 = 4, + /// Channel 5 Channel5 = 5, #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] + /// Channel 6 Channel6 = 6, #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] + /// Channel 7 Channel7 = 7, } @@ -66,16 +74,22 @@ pub mod config { #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] + /// Pin configuration for the LEDC channel. pub enum PinConfig { + /// Push-pull pin configuration. PushPull, + /// Open-drain pin configuration. OpenDrain, } /// Channel configuration #[derive(Copy, Clone)] pub struct Config<'a, S: TimerSpeed> { + /// A reference to the timer associated with this channel. pub timer: &'a dyn TimerIFace, + /// The duty cycle percentage (0-100). pub duty_pct: u8, + /// The pin configuration (PushPull or OpenDrain). pub pin_config: PinConfig, } } @@ -108,6 +122,8 @@ pub trait ChannelHW { /// Configure Channel HW except for the duty which is set via /// [`Self::set_duty_hw`]. fn configure_hw(&mut self) -> Result<(), Error>; + /// Configure the hardware for the channel with a specific pin + /// configuration. fn configure_hw_with_pin_config(&mut self, cfg: config::PinConfig) -> Result<(), Error>; /// Set channel duty HW @@ -278,7 +294,6 @@ where } } -#[cfg(feature = "embedded-hal")] mod ehal1 { use embedded_hal::pwm::{self, ErrorKind, ErrorType, SetDutyCycle}; diff --git a/esp-hal/src/ledc/mod.rs b/esp-hal/src/ledc/mod.rs index 58a1650eb8f..dc862b11045 100644 --- a/esp-hal/src/ledc/mod.rs +++ b/esp-hal/src/ledc/mod.rs @@ -1,6 +1,7 @@ //! # LED Controller (LEDC) //! //! ## Overview +//! //! The LEDC peripheral is primarily designed to control the intensity of LEDs, //! although it can also be used to generate PWM signals for other purposes. It //! has multiple channels which can generate independent waveforms that can be @@ -15,9 +16,12 @@ //! supported chips. //! //! ## Examples +//! //! ### Low Speed Channel -//! The following will configure the Low Speed Channel0 to 24kHz output with -//! 10% duty using the ABPClock +//! +//! The following example will configure the Low Speed Channel0 to 24kHz output +//! with 10% duty using the ABPClock. +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::ledc::Ledc; @@ -26,14 +30,10 @@ //! # use esp_hal::ledc::LowSpeed; //! # use esp_hal::ledc::channel; //! # use esp_hal::gpio::Io; -//! # use crate::esp_hal::prelude::_esp_hal_ledc_timer_TimerIFace; -//! # use crate::esp_hal::prelude::_fugit_RateExtU32; -//! # use crate::esp_hal::prelude::_esp_hal_ledc_channel_ChannelIFace; -//! //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! # let led = io.pins.gpio0; //! -//! let mut ledc = Ledc::new(peripherals.LEDC, &clocks); +//! let mut ledc = Ledc::new(peripherals.LEDC); //! ledc.set_global_slow_clock(LSGlobalClkSource::APBClk); //! //! let mut lstimer0 = ledc.get_timer::(timer::Number::Timer0); @@ -65,7 +65,6 @@ use self::{ timer::{Timer, TimerSpeed}, }; use crate::{ - clock::Clocks, gpio::OutputPin, peripheral::{Peripheral, PeripheralRef}, system::{Peripheral as PeripheralEnable, PeripheralClockControl}, @@ -77,6 +76,7 @@ pub mod timer; /// Global slow clock source #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum LSGlobalClkSource { + /// APB clock. APBClk, } @@ -84,7 +84,6 @@ pub enum LSGlobalClkSource { pub struct Ledc<'d> { _instance: PeripheralRef<'d, crate::peripherals::LEDC>, ledc: &'d crate::peripherals::ledc::RegisterBlock, - clock_control_config: &'d Clocks<'d>, } #[cfg(esp32)] @@ -94,7 +93,9 @@ pub struct HighSpeed {} /// Used to specify LowSpeed Timer/Channel pub struct LowSpeed {} +/// Trait representing the speed mode of a clock or peripheral. pub trait Speed { + /// Boolean constant indicating whether the speed is high-speed. const IS_HS: bool; } @@ -109,19 +110,14 @@ impl Speed for LowSpeed { impl<'d> Ledc<'d> { /// Return a new LEDC - pub fn new( - _instance: impl Peripheral

+ 'd, - clock_control_config: &'d Clocks<'d>, - ) -> Self { + pub fn new(_instance: impl Peripheral

+ 'd) -> Self { crate::into_ref!(_instance); + + PeripheralClockControl::reset(PeripheralEnable::Ledc); PeripheralClockControl::enable(PeripheralEnable::Ledc); let ledc = unsafe { &*crate::peripherals::LEDC::ptr() }; - Ledc { - _instance, - ledc, - clock_control_config, - } + Ledc { _instance, ledc } } /// Set global slow clock source @@ -165,7 +161,7 @@ impl<'d> Ledc<'d> { /// Return a new timer pub fn get_timer(&self, number: timer::Number) -> Timer<'d, S> { - Timer::new(self.ledc, self.clock_control_config, number) + Timer::new(self.ledc, number) } /// Return a new channel diff --git a/esp-hal/src/ledc/timer.rs b/esp-hal/src/ledc/timer.rs index 5c81489e8c7..ef3d7b7b630 100644 --- a/esp-hal/src/ledc/timer.rs +++ b/esp-hal/src/ledc/timer.rs @@ -33,6 +33,7 @@ pub enum Error { #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum HSClockSource { + /// APB clock. APBClk, // TODO RefTick, } @@ -41,6 +42,7 @@ pub enum HSClockSource { #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum LSClockSource { + /// APB clock. APBClk, // TODO SLOWClk } @@ -49,9 +51,13 @@ pub enum LSClockSource { #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Number { + /// Timer 0. Timer0 = 0, + /// Timer 1. Timer1 = 1, + /// Timer 2. Timer2 = 2, + /// Timer 3. Timer3 = 3, } @@ -63,56 +69,82 @@ pub mod config { #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Duty { + /// 1-bit resolution for duty cycle adjustment. Duty1Bit = 1, + /// 2-bit resolution for duty cycle adjustment. Duty2Bit, + /// 3-bit resolution for duty cycle adjustment. Duty3Bit, + /// 4-bit resolution for duty cycle adjustment. Duty4Bit, + /// 5-bit resolution for duty cycle adjustment. Duty5Bit, + /// 6-bit resolution for duty cycle adjustment. Duty6Bit, + /// 7-bit resolution for duty cycle adjustment. Duty7Bit, + /// 8-bit resolution for duty cycle adjustment. Duty8Bit, + /// 9-bit resolution for duty cycle adjustment. Duty9Bit, + /// 10-bit resolution for duty cycle adjustment. Duty10Bit, + /// 11-bit resolution for duty cycle adjustment. Duty11Bit, + /// 12-bit resolution for duty cycle adjustment. Duty12Bit, + /// 13-bit resolution for duty cycle adjustment. Duty13Bit, + /// 14-bit resolution for duty cycle adjustment. Duty14Bit, #[cfg(esp32)] + /// 15-bit resolution for duty cycle adjustment. Duty15Bit, #[cfg(esp32)] + /// 16-bit resolution for duty cycle adjustment. Duty16Bit, #[cfg(esp32)] + /// 17-bit resolution for duty cycle adjustment. Duty17Bit, #[cfg(esp32)] + /// 18-bit resolution for duty cycle adjustment. Duty18Bit, #[cfg(esp32)] + /// 19-bit resolution for duty cycle adjustment. Duty19Bit, #[cfg(esp32)] + /// 20-bit resolution for duty cycle adjustment. Duty20Bit, } /// Timer configuration #[derive(Copy, Clone)] pub struct Config { + /// The duty cycle resolution. pub duty: Duty, + /// The clock source for the timer. pub clock_source: CS, + /// The frequency of the PWM signal in Hertz. pub frequency: HertzU32, } } /// Trait defining the type of timer source pub trait TimerSpeed: Speed { + /// The type of clock source used by the timer in this speed mode. type ClockSourceType; } /// Timer source type for LowSpeed timers impl TimerSpeed for LowSpeed { + /// The clock source type for low-speed timers. type ClockSourceType = LSClockSource; } #[cfg(esp32)] /// Timer source type for HighSpeed timers impl TimerSpeed for HighSpeed { + /// The clock source type for high-speed timers. type ClockSourceType = HSClockSource; } @@ -152,7 +184,6 @@ pub trait TimerHW { /// Timer struct pub struct Timer<'a, S: TimerSpeed> { ledc: &'a crate::peripherals::ledc::RegisterBlock, - clock_control_config: &'a Clocks<'a>, number: Number, duty: Option, frequency: u32, @@ -224,15 +255,10 @@ where } impl<'a, S: TimerSpeed> Timer<'a, S> { - /// Create a new intance of a timer - pub fn new( - ledc: &'a ledc::RegisterBlock, - clock_control_config: &'a Clocks<'a>, - number: Number, - ) -> Self { + /// Create a new instance of a timer + pub fn new(ledc: &'a ledc::RegisterBlock, number: Number) -> Self { Timer { ledc, - clock_control_config, number, duty: None, frequency: 0u32, @@ -246,9 +272,12 @@ impl<'a, S: TimerSpeed> Timer<'a, S> { /// Timer HW implementation for LowSpeed timers impl<'a> TimerHW for Timer<'a, LowSpeed> { /// Get the current source timer frequency from the HW - fn get_freq_hw(&self) -> Option { - self.clock_source.map(|cs| match cs { - LSClockSource::APBClk => self.clock_control_config.apb_clock, + fn get_freq_hw(&self) -> Option { + self.clock_source.map(|source| match source { + LSClockSource::APBClk => { + let clocks = Clocks::get(); + clocks.apb_clock + } }) } @@ -290,10 +319,13 @@ impl<'a> TimerHW for Timer<'a, LowSpeed> { /// Update the timer in HW fn update_hw(&self) { - #[cfg(esp32)] - let tmr = self.ledc.lstimer(self.number as usize); - #[cfg(not(esp32))] - let tmr = self.ledc.timer(self.number as usize); + cfg_if::cfg_if! { + if #[cfg(esp32)] { + let tmr = self.ledc.lstimer(self.number as usize); + } else { + let tmr = self.ledc.timer(self.number as usize); + } + } tmr.conf().modify(|_, w| w.para_up().set_bit()); } @@ -304,8 +336,11 @@ impl<'a> TimerHW for Timer<'a, LowSpeed> { impl<'a> TimerHW for Timer<'a, HighSpeed> { /// Get the current source timer frequency from the HW fn get_freq_hw(&self) -> Option { - self.clock_source.map(|cs| match cs { - HSClockSource::APBClk => self.clock_control_config.apb_clock, + self.clock_source.map(|source| match source { + HSClockSource::APBClk => { + let clocks = Clocks::get(); + clocks.apb_clock + } }) } diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index 7b2dc0c98eb..45879e1b749 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -17,7 +17,7 @@ #![cfg_attr(esp32c3, doc = "**ESP32-C3**")] #![cfg_attr(esp32c6, doc = "**ESP32-C6**")] #![cfg_attr(esp32h2, doc = "**ESP32-H2**")] -//! please ensure you are reading the correct [documentation] for your target +//! . Please ensure you are reading the correct [documentation] for your target //! device. //! //! ## Choosing a Device @@ -60,32 +60,26 @@ //! #![no_std] //! #![no_main] //! -//! // A panic - handler e.g. `use esp_backtrace as _;` -//! +//! // You'll need a panic handler e.g. `use esp_backtrace as _;` +//! # #[panic_handler] +//! # fn panic(_ : &core::panic::PanicInfo) -> ! { +//! # loop {} +//! # } //! use esp_hal::{ -//! clock::ClockControl, //! delay::Delay, //! gpio::{Io, Level, Output}, -//! peripherals::Peripherals, //! prelude::*, -//! system::SystemControl, //! }; -//! # #[panic_handler] -//! # fn panic(_ : &core::panic::PanicInfo) -> ! { -//! # loop {} -//! # } //! //! #[entry] //! fn main() -> ! { -//! let peripherals = Peripherals::take(); -//! let system = SystemControl::new(peripherals.SYSTEM); -//! let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); +//! let peripherals = esp_hal::init(esp_hal::Config::default()); //! //! // Set GPIO0 as an output, and set its state high initially. //! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! let mut led = Output::new(io.pins.gpio0, Level::High); //! -//! let delay = Delay::new(&clocks); +//! let delay = Delay::new(); //! //! loop { //! led.toggle(); @@ -95,14 +89,11 @@ //! ``` //! //! The steps here are: -//! - Take all the peripherals from the PAC to pass them to the HAL drivers -//! later -//! - Create [system::SystemControl] -//! - Configure the system clocks - in this case use the boot defaults -//! - Create [gpio::Io] which provides access to the GPIO pins -//! - Create an [gpio::Output] pin driver which lets us control the logical +//! - Call [`init`] with the desired [`CpuClock`] configuration +//! - Create [`gpio::Io`] which provides access to the GPIO pins +//! - Create an [`gpio::Output`] pin driver which lets us control the logical //! level of an output pin -//! - Create a [delay::Delay] driver +//! - Create a [`delay::Delay`] driver //! - In a loop, toggle the output pin's logical level with a delay of 1000 ms //! //! ## `PeripheralRef` Pattern @@ -111,7 +102,7 @@ //! This means you can pass the pin/peripheral or a mutable reference to the //! pin/peripheral. //! -//! The later can be used to regain access to the pin when the driver gets +//! The latter can be used to regain access to the pin when the driver gets //! dropped. Then it's possible to reuse the pin/peripheral for a different //! purpose. //! @@ -139,10 +130,9 @@ //! ## Feature Flags #![doc = document_features::document_features!(feature_label = r#"{feature}"#)] #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")] -#![allow(asm_sub_register)] -#![cfg_attr(feature = "async", allow(stable_features, async_fn_in_trait))] +#![allow(asm_sub_register, async_fn_in_trait, stable_features)] #![cfg_attr(xtensa, feature(asm_experimental_arch))] -#![deny(rust_2018_idioms)] +#![deny(missing_docs, rust_2018_idioms)] #![no_std] // MUST be the first module @@ -241,6 +231,8 @@ pub mod uart; #[cfg(usb_device)] pub mod usb_serial_jtag; +pub mod debugger; + /// State of the CPU saved when entering exception or interrupt pub mod trapframe { #[cfg(riscv)] @@ -570,54 +562,91 @@ mod critical_section_impl { } } -/// FlashSafeDma -/// -/// The embedded-hal traits make no guarantees about -/// where the buffers are placed. The DMA implementation in Espressif chips has -/// a limitation in that it can only access the RAM address space, meaning data -/// to be transmitted from the flash address space must be copied into RAM -/// first. -/// -/// This wrapper struct should be used when a peripheral using the DMA engine -/// needs to transmit data from flash (ROM) via the embedded-hal traits. This is -/// often a `const` variable. -/// -/// Example usage using [`spi::master::dma::SpiDma`] -/// ```rust, ignore -/// const ARRAY_IN_FLASH = [0xAA; 128]; -/// -/// let spi = SpiDma::new(/* */); -/// -/// spi.write(&ARRAY_IN_FLASH[..]).unwrap(); // error when transmission starts -/// -/// let spi = FlashSafeDma::new(spi); -/// -/// spi.write(&ARRAY_IN_FLASH[..]).unwrap(); // success -/// ``` -pub struct FlashSafeDma { - inner: T, - #[allow(unused)] - buffer: [u8; SIZE], +// The state of a re-entrant lock +pub(crate) struct LockState { + #[cfg(multi_core)] + core: portable_atomic::AtomicUsize, } -impl FlashSafeDma { - pub fn new(inner: T) -> Self { +impl LockState { + #[cfg(multi_core)] + const UNLOCKED: usize = usize::MAX; + + pub const fn new() -> Self { Self { - inner, - buffer: [0u8; SIZE], + #[cfg(multi_core)] + core: portable_atomic::AtomicUsize::new(Self::UNLOCKED), } } +} - pub fn inner_mut(&mut self) -> &mut T { - &mut self.inner - } +// This is preferred over critical-section as this allows you to have multiple +// locks active at the same time rather than using the global mutex that is +// critical-section. +#[allow(unused_variables)] +pub(crate) fn lock(state: &LockState, f: impl FnOnce() -> T) -> T { + // In regards to disabling interrupts, we only need to disable + // the interrupts that may be calling this function. + + #[cfg(not(multi_core))] + { + // Disabling interrupts is enough on single core chips to ensure mutual + // exclusion. - pub fn inner(&self) -> &T { - &self.inner + #[cfg(riscv)] + return riscv::interrupt::free(f); + #[cfg(xtensa)] + return xtensa_lx::interrupt::free(|_| f()); } - pub fn free(self) -> T { - self.inner + #[cfg(multi_core)] + { + use portable_atomic::Ordering; + + let current_core = get_core() as usize; + + let mut f = f; + + loop { + let func = || { + // Use Acquire ordering in success to ensure `f()` "happens after" the lock is + // taken. Use Relaxed ordering in failure as there's no + // synchronisation happening. + if let Err(locked_core) = state.core.compare_exchange( + LockState::UNLOCKED, + current_core, + Ordering::Acquire, + Ordering::Relaxed, + ) { + assert_ne!( + locked_core, current_core, + "esp_hal::lock is not re-entrant!" + ); + + Err(f) + } else { + let result = f(); + + // Use Release ordering here to ensure `f()` "happens before" this lock is + // released. + state.core.store(LockState::UNLOCKED, Ordering::Release); + + Ok(result) + } + }; + + #[cfg(riscv)] + let result = riscv::interrupt::free(func); + #[cfg(xtensa)] + let result = xtensa_lx::interrupt::free(|_| func()); + + match result { + Ok(result) => break result, + Err(the_function) => f = the_function, + } + + // Consider using core::hint::spin_loop(); Might need SW_INT. + } } } @@ -670,20 +699,61 @@ unsafe extern "C" fn stack_chk_fail() { #[doc(hidden)] /// Helper macro for checking doctest code snippets -#[cfg(not(host_os = "windows"))] #[macro_export] macro_rules! before_snippet { () => { - core::include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/doc-helper/before")) + r#" +# #![no_std] +# use esp_hal::prelude::*; +# use procmacros::handler; +# use esp_hal::interrupt; +# #[panic_handler] +# fn panic(_ : &core::panic::PanicInfo) -> ! { +# loop {} +# } +# fn main() { +# let mut peripherals = esp_hal::init(esp_hal::Config::default()); +"# }; } -#[doc(hidden)] -/// Helper macro for checking doctest code snippets -#[cfg(host_os = "windows")] -#[macro_export] -macro_rules! before_snippet { - () => { - core::include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "\\doc-helper\\before")) - }; +use crate::{ + clock::{Clocks, CpuClock}, + peripherals::Peripherals, +}; + +/// System configuration. +#[non_exhaustive] +#[derive(Default)] +pub struct Config { + /// The CPU clock configuration. + pub cpu_clock: CpuClock, +} + +/// Initialize the system. +/// +/// This function sets up the CPU clock and returns the peripherals and clocks. +pub fn init(config: Config) -> Peripherals { + let mut peripherals = Peripherals::take(); + + // RTC domain must be enabled before we try to disable + let mut rtc = crate::rtc_cntl::Rtc::new(&mut peripherals.LPWR); + #[cfg(not(any(esp32, esp32s2)))] + rtc.swd.disable(); + rtc.rwdt.disable(); + + unsafe { + crate::timer::timg::Wdt::::set_wdt_enabled(false); + #[cfg(timg1)] + crate::timer::timg::Wdt::::set_wdt_enabled(false); + } + + Clocks::init(config.cpu_clock); + + #[cfg(xtensa)] + crate::interrupt::setup_interrupts(); + #[cfg(esp32)] + crate::time::time_init(); + + peripherals } diff --git a/esp-hal/src/mcpwm/mod.rs b/esp-hal/src/mcpwm/mod.rs index 7ebfedf48ee..a4907be376f 100644 --- a/esp-hal/src/mcpwm/mod.rs +++ b/esp-hal/src/mcpwm/mod.rs @@ -1,6 +1,7 @@ //! # Motor Control Pulse Width Modulator (MCPWM) //! //! ## Overview +//! //! The MCPWM peripheral is a versatile PWM generator, which contains various //! submodules to make it a key element in power electronic applications like //! motor control, digital power, and so on. Typically, the MCPWM peripheral can @@ -13,6 +14,7 @@ //! - Generate Space Vector PWM (SVPWM) signals for Field Oriented Control (FOC) //! //! ## Configuration +//! //! * PWM Timers 0, 1 and 2 //! * Every PWM timer has a dedicated 8-bit clock prescaler. //! * The 16-bit counter in the PWM timer can work in count-up mode, @@ -40,14 +42,16 @@ #![cfg_attr(esp32h2, doc = "Clock source is XTAL")] #![doc = ""] //! ## Examples +//! //! ### Output a 20 kHz signal -//! Uses timer0 and operator0 of the MCPWM0 peripheral to output a 50% duty -//! signal at 20 kHz. The signal will be output to the pin assigned to `pin`. +//! +//! This example uses timer0 and operator0 of the MCPWM0 peripheral to output a +//! 50% duty signal at 20 kHz. The signal will be output to the pin assigned to +//! `pin`. +//! //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::{mcpwm, prelude::*}; -//! # use esp_hal::mcpwm::operator::{DeadTimeCfg, PWMStream}; -//! # use mcpwm::{operator::PwmPinConfig, timer::PwmWorkingMode, McPwm, PeripheralClockConfig}; +//! # use esp_hal::mcpwm::{operator::{DeadTimeCfg, PWMStream, PwmPinConfig}, timer::PwmWorkingMode, McPwm, PeripheralClockConfig}; //! # use esp_hal::gpio::Io; //! //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -56,11 +60,11 @@ //! // initialize peripheral #![cfg_attr( esp32h2, - doc = "let clock_cfg = PeripheralClockConfig::with_frequency(&clocks, 40.MHz()).unwrap();" + doc = "let clock_cfg = PeripheralClockConfig::with_frequency(40.MHz()).unwrap();" )] #![cfg_attr( not(esp32h2), - doc = "let clock_cfg = PeripheralClockConfig::with_frequency(&clocks, 32.MHz()).unwrap();" + doc = "let clock_cfg = PeripheralClockConfig::with_frequency(32.MHz()).unwrap();" )] //! let mut mcpwm = McPwm::new(peripherals.MCPWM0, clock_cfg); //! @@ -83,9 +87,7 @@ //! # } //! ``` -#![deny(missing_docs)] - -use core::{marker::PhantomData, ops::Deref}; +use core::ops::Deref; use fugit::HertzU32; use operator::Operator; @@ -128,10 +130,11 @@ impl<'d, PWM: PwmPeripheral> McPwm<'d, PWM> { // clocks.crypto_pwm_clock normally is 160 MHz pub fn new( peripheral: impl Peripheral

+ 'd, - peripheral_clock: PeripheralClockConfig<'d>, + peripheral_clock: PeripheralClockConfig, ) -> Self { crate::into_ref!(peripheral); + PWM::reset(); PWM::enable(); #[cfg(not(esp32c6))] @@ -187,13 +190,12 @@ impl<'d, PWM: PwmPeripheral> McPwm<'d, PWM> { /// Clock configuration of the MCPWM peripheral #[derive(Copy, Clone)] -pub struct PeripheralClockConfig<'a> { +pub struct PeripheralClockConfig { frequency: HertzU32, prescaler: u8, - phantom: PhantomData<&'a Clocks<'a>>, } -impl<'a> PeripheralClockConfig<'a> { +impl PeripheralClockConfig { /// Get a clock configuration with the given prescaler. /// /// With standard system clock configurations the input clock to the MCPWM @@ -201,20 +203,23 @@ impl<'a> PeripheralClockConfig<'a> { /// /// The peripheral clock frequency is calculated as: /// `peripheral_clock = input_clock / (prescaler + 1)` - pub fn with_prescaler(clocks: &'a Clocks<'a>, prescaler: u8) -> Self { - #[cfg(esp32)] - let source_clock = clocks.pwm_clock; - #[cfg(esp32c6)] - let source_clock = clocks.crypto_clock; - #[cfg(esp32s3)] - let source_clock = clocks.crypto_pwm_clock; - #[cfg(esp32h2)] - let source_clock = clocks.xtal_clock; + pub fn with_prescaler(prescaler: u8) -> Self { + let clocks = Clocks::get(); + cfg_if::cfg_if! { + if #[cfg(esp32)] { + let source_clock = clocks.pwm_clock; + } else if #[cfg(esp32c6)] { + let source_clock = clocks.crypto_clock; + } else if #[cfg(esp32s3)] { + let source_clock = clocks.crypto_pwm_clock; + } else if #[cfg(esp32h2)] { + let source_clock = clocks.xtal_clock; + } + } Self { frequency: source_clock / (prescaler as u32 + 1), prescaler, - phantom: PhantomData, } } @@ -232,18 +237,19 @@ impl<'a> PeripheralClockConfig<'a> { /// Only divisors of the input clock (`160 Mhz / 1`, `160 Mhz / 2`, ..., /// `160 Mhz / 256`) are representable exactly. Other target frequencies /// will be rounded up to the next divisor. - pub fn with_frequency( - clocks: &'a Clocks<'a>, - target_freq: HertzU32, - ) -> Result { - #[cfg(esp32)] - let source_clock = clocks.pwm_clock; - #[cfg(esp32c6)] - let source_clock = clocks.crypto_clock; - #[cfg(esp32s3)] - let source_clock = clocks.crypto_pwm_clock; - #[cfg(esp32h2)] - let source_clock = clocks.xtal_clock; + pub fn with_frequency(target_freq: HertzU32) -> Result { + let clocks = Clocks::get(); + cfg_if::cfg_if! { + if #[cfg(esp32)] { + let source_clock = clocks.pwm_clock; + } else if #[cfg(esp32c6)] { + let source_clock = clocks.crypto_clock; + } else if #[cfg(esp32s3)] { + let source_clock = clocks.crypto_pwm_clock; + } else if #[cfg(esp32h2)] { + let source_clock = clocks.xtal_clock; + } + } if target_freq.raw() == 0 || target_freq > source_clock { return Err(FrequencyError); @@ -254,7 +260,7 @@ impl<'a> PeripheralClockConfig<'a> { return Err(FrequencyError); } - Ok(Self::with_prescaler(clocks, prescaler as u8)) + Ok(Self::with_prescaler(prescaler as u8)) } /// Get the peripheral clock frequency. @@ -279,7 +285,7 @@ impl<'a> PeripheralClockConfig<'a> { period: u16, mode: timer::PwmWorkingMode, prescaler: u8, - ) -> timer::TimerClockConfig<'a> { + ) -> timer::TimerClockConfig { timer::TimerClockConfig::with_prescaler(self, period, mode, prescaler) } @@ -297,7 +303,7 @@ impl<'a> PeripheralClockConfig<'a> { period: u16, mode: timer::PwmWorkingMode, target_freq: HertzU32, - ) -> Result, FrequencyError> { + ) -> Result { timer::TimerClockConfig::with_frequency(self, period, mode, target_freq) } } @@ -312,6 +318,8 @@ pub struct FrequencyError; pub trait PwmPeripheral: Deref + crate::private::Sealed { /// Enable peripheral fn enable(); + /// Reset peripheral + fn reset(); /// Get a pointer to the peripheral RegisterBlock fn block() -> *const RegisterBlock; /// Get operator GPIO mux output signal @@ -324,6 +332,10 @@ impl PwmPeripheral for crate::peripherals::MCPWM0 { PeripheralClockControl::enable(PeripheralEnable::Mcpwm0) } + fn reset() { + PeripheralClockControl::reset(PeripheralEnable::Mcpwm0) + } + fn block() -> *const RegisterBlock { Self::PTR } @@ -347,6 +359,10 @@ impl PwmPeripheral for crate::peripherals::MCPWM1 { PeripheralClockControl::enable(PeripheralEnable::Mcpwm1) } + fn reset() { + PeripheralClockControl::reset(PeripheralEnable::Mcpwm1) + } + fn block() -> *const RegisterBlock { Self::PTR } diff --git a/esp-hal/src/mcpwm/operator.rs b/esp-hal/src/mcpwm/operator.rs index 272b13b8e4e..8f4a1536310 100644 --- a/esp-hal/src/mcpwm/operator.rs +++ b/esp-hal/src/mcpwm/operator.rs @@ -414,7 +414,6 @@ impl<'d, Pin: OutputPin, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> } } -#[cfg(feature = "embedded-hal-02")] impl<'d, Pin: OutputPin, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal_02::PwmPin for PwmPin<'d, Pin, PWM, OP, IS_A> { @@ -449,7 +448,6 @@ impl<'d, Pin: OutputPin, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> emb } /// Implement no error type for the PwmPin because the method are infallible -#[cfg(feature = "embedded-hal")] impl<'d, Pin: OutputPin, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal::pwm::ErrorType for PwmPin<'d, Pin, PWM, OP, IS_A> { @@ -457,7 +455,6 @@ impl<'d, Pin: OutputPin, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> } /// Implement the trait SetDutyCycle for PwmPin -#[cfg(feature = "embedded-hal")] impl<'d, Pin: OutputPin, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal::pwm::SetDutyCycle for PwmPin<'d, Pin, PWM, OP, IS_A> { @@ -479,8 +476,9 @@ impl<'d, Pin: OutputPin, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> /// configured deadtime. /// /// # H-Bridge example +/// /// ```rust, no_run -#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/doc-helper/before"))] +#[doc = crate::before_snippet!()] /// # use esp_hal::{mcpwm, prelude::*}; /// # use esp_hal::mcpwm::{McPwm, PeripheralClockConfig}; /// # use esp_hal::mcpwm::operator::{DeadTimeCfg, PwmPinConfig, PWMStream}; @@ -493,11 +491,11 @@ impl<'d, Pin: OutputPin, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> /// true); #[cfg_attr( esp32h2, - doc = "let clock_cfg = PeripheralClockConfig::with_frequency(&clocks, 40.MHz()).unwrap();" + doc = "let clock_cfg = PeripheralClockConfig::with_frequency(40.MHz()).unwrap();" )] #[cfg_attr( not(esp32h2), - doc = "let clock_cfg = PeripheralClockConfig::with_frequency(&clocks, 32.MHz()).unwrap();" + doc = "let clock_cfg = PeripheralClockConfig::with_frequency(32.MHz()).unwrap();" )] /// let mut mcpwm = McPwm::new(peripherals.MCPWM0, clock_cfg); /// diff --git a/esp-hal/src/mcpwm/timer.rs b/esp-hal/src/mcpwm/timer.rs index 340ebef33bd..5afe5a37426 100644 --- a/esp-hal/src/mcpwm/timer.rs +++ b/esp-hal/src/mcpwm/timer.rs @@ -8,10 +8,7 @@ use core::marker::PhantomData; use fugit::HertzU32; -use crate::{ - clock::Clocks, - mcpwm::{FrequencyError, PeripheralClockConfig, PwmPeripheral}, -}; +use crate::mcpwm::{FrequencyError, PeripheralClockConfig, PwmPeripheral}; /// A MCPWM timer /// @@ -47,7 +44,7 @@ impl Timer { /// /// The hardware supports writing these settings in sync with certain timer /// events but this HAL does not expose these for now. - pub fn start(&mut self, timer_config: TimerClockConfig<'_>) { + pub fn start(&mut self, timer_config: TimerClockConfig) { // write prescaler and period with immediate update method self.cfg0().write(|w| unsafe { w.prescale().bits(timer_config.prescaler); @@ -115,18 +112,17 @@ impl Timer { /// Use [`PeripheralClockConfig::timer_clock_with_prescaler`](super::PeripheralClockConfig::timer_clock_with_prescaler) or /// [`PeripheralClockConfig::timer_clock_with_frequency`](super::PeripheralClockConfig::timer_clock_with_frequency) to it. #[derive(Copy, Clone)] -pub struct TimerClockConfig<'a> { +pub struct TimerClockConfig { frequency: HertzU32, period: u16, period_updating_method: PeriodUpdatingMethod, prescaler: u8, mode: PwmWorkingMode, - phantom: PhantomData<&'a Clocks<'a>>, } -impl<'a> TimerClockConfig<'a> { +impl TimerClockConfig { pub(super) fn with_prescaler( - clock: &PeripheralClockConfig<'a>, + clock: &PeripheralClockConfig, period: u16, mode: PwmWorkingMode, prescaler: u8, @@ -144,12 +140,11 @@ impl<'a> TimerClockConfig<'a> { period, period_updating_method: PeriodUpdatingMethod::Immediately, mode, - phantom: PhantomData, } } pub(super) fn with_frequency( - clock: &PeripheralClockConfig<'a>, + clock: &PeripheralClockConfig, period: u16, mode: PwmWorkingMode, target_freq: HertzU32, @@ -178,7 +173,6 @@ impl<'a> TimerClockConfig<'a> { period, period_updating_method: PeriodUpdatingMethod::Immediately, mode, - phantom: PhantomData, }) } diff --git a/esp-hal/src/otg_fs.rs b/esp-hal/src/otg_fs.rs index 75c4c0c0b23..22a4c9b0fe5 100644 --- a/esp-hal/src/otg_fs.rs +++ b/esp-hal/src/otg_fs.rs @@ -47,16 +47,20 @@ use crate::{ }; #[doc(hidden)] +/// Trait representing the USB D+ (data plus) pin. pub trait UsbDp: crate::private::Sealed {} #[doc(hidden)] +/// Trait representing the USB D- (data minus) pin. pub trait UsbDm: crate::private::Sealed {} +/// USB peripheral. pub struct Usb<'d> { _usb0: PeripheralRef<'d, peripherals::USB0>, } impl<'d> Usb<'d> { + /// Creates a new `Usb` instance. pub fn new( usb0: impl Peripheral

+ 'd, _usb_dp: impl Peripheral

+ 'd, @@ -66,6 +70,7 @@ impl<'d> Usb<'d> { P: UsbDp + Send + Sync, M: UsbDm + Send + Sync, { + PeripheralClockControl::reset(PeripheralEnable::Usb); PeripheralClockControl::enable(PeripheralEnable::Usb); Self { @@ -126,8 +131,7 @@ unsafe impl<'d> UsbPeripheral for Usb<'d> { 80_000_000 } } - -#[cfg(feature = "async")] +/// Async functionality pub mod asynch { use embassy_usb_driver::{ EndpointAddress, diff --git a/esp-hal/src/parl_io.rs b/esp-hal/src/parl_io.rs index af1bd40da57..4105acb19bb 100644 --- a/esp-hal/src/parl_io.rs +++ b/esp-hal/src/parl_io.rs @@ -23,8 +23,6 @@ //! //! [Parallel IO TX]: https://github.com/esp-rs/esp-hal/blob/main/examples/src/bin/parl_io_tx.rs -#![warn(missing_docs)] - use core::marker::PhantomData; use enumset::{EnumSet, EnumSetType}; @@ -33,7 +31,6 @@ use peripheral::PeripheralRef; use private::*; use crate::{ - clock::Clocks, dma::{ dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx}, Channel, @@ -335,7 +332,7 @@ where pcr.parl_clk_tx_conf() .modify(|_, w| unsafe { w.parl_clk_tx_sel().bits(3).parl_clk_tx_div_num().bits(0) }); // PAD_CLK_TX, no divider - self.pin.set_to_input(crate::private::Internal); + self.pin.init_input(false, false, crate::private::Internal); self.pin.connect_input_to_peripheral( crate::gpio::InputSignal::PARL_TX_CLK, crate::private::Internal, @@ -370,7 +367,7 @@ where pcr.parl_clk_rx_conf() .modify(|_, w| unsafe { w.parl_clk_rx_sel().bits(3).parl_clk_rx_div_num().bits(0) }); // PAD_CLK_TX, no divider - self.pin.set_to_input(crate::private::Internal); + self.pin.init_input(false, false, crate::private::Internal); self.pin.connect_input_to_peripheral( crate::gpio::InputSignal::PARL_RX_CLK, crate::private::Internal, @@ -637,7 +634,8 @@ where { fn configure(&mut self) -> Result<(), Error> { self.rx_pins.configure()?; - self.valid_pin.set_to_input(crate::private::Internal); + self.valid_pin + .init_input(false, false, crate::private::Internal); self.valid_pin .connect_input_to_peripheral(Instance::rx_valid_pin_signal(), crate::private::Internal); Instance::set_rx_sw_en(false); @@ -741,7 +739,7 @@ macro_rules! rx_pins { { fn configure(&mut self) -> Result<(), Error> { $( - self.[< pin_ $pin:lower >].set_to_input($crate::private::Internal); + self.[< pin_ $pin:lower >].init_input(false, false, $crate::private::Internal); self.[< pin_ $pin:lower >].connect_input_to_peripheral(crate::gpio::InputSignal::$signal, $crate::private::Internal); )+ @@ -1098,14 +1096,17 @@ fn internal_clear_interrupts(interrupts: EnumSet) { /// Parallel IO in full duplex mode /// /// Full duplex mode might limit the maximum possible bit width. -#[allow(missing_docs)] pub struct ParlIoFullDuplex<'d, CH, DM> where CH: DmaChannel, CH::P: ParlIoPeripheral, DM: Mode, { + /// The transmitter (TX) channel responsible for handling DMA transfers in + /// the parallel I/O full-duplex operation. pub tx: TxCreatorFullDuplex<'d, CH, DM>, + /// The receiver (RX) channel responsible for handling DMA transfers in the + /// parallel I/O full-duplex operation. pub rx: RxCreatorFullDuplex<'d, CH, DM>, } @@ -1122,7 +1123,6 @@ where tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], frequency: HertzU32, - _clocks: &Clocks<'d>, ) -> Result { internal_init(&mut dma_channel, frequency)?; @@ -1193,13 +1193,14 @@ where } /// Parallel IO in half duplex / TX only mode -#[allow(missing_docs)] pub struct ParlIoTxOnly<'d, CH, DM> where CH: DmaChannel, CH::P: ParlIoPeripheral, DM: Mode, { + /// The transmitter (TX) channel responsible for handling DMA transfers in + /// the parallel I/O operation. pub tx: TxCreator<'d, CH, DM>, } @@ -1215,7 +1216,6 @@ where mut dma_channel: Channel<'d, CH, DM>, descriptors: &'static mut [DmaDescriptor], frequency: HertzU32, - _clocks: &Clocks<'d>, ) -> Result { internal_init(&mut dma_channel, frequency)?; @@ -1281,13 +1281,14 @@ where } /// Parallel IO in half duplex / RX only mode -#[allow(missing_docs)] pub struct ParlIoRxOnly<'d, CH, DM> where CH: DmaChannel, CH::P: ParlIoPeripheral, DM: Mode, { + /// The receiver (RX) channel responsible for handling DMA transfers in the + /// parallel I/O operation. pub rx: RxCreator<'d, CH, DM>, } @@ -1303,7 +1304,6 @@ where mut dma_channel: Channel<'d, CH, DM>, descriptors: &'static mut [DmaDescriptor], frequency: HertzU32, - _clocks: &Clocks<'d>, ) -> Result { internal_init(&mut dma_channel, frequency)?; @@ -1381,6 +1381,7 @@ where return Err(Error::UnreachableClockRate); } + PeripheralClockControl::reset(crate::system::Peripheral::ParlIo); PeripheralClockControl::enable(crate::system::Peripheral::ParlIo); let pcr = unsafe { &*crate::peripherals::PCR::PTR }; @@ -1430,7 +1431,7 @@ where pub fn write_dma<'t, TXBUF>( &'t mut self, words: &'t TXBUF, - ) -> Result, Error> + ) -> Result, Error> where TXBUF: ReadBuffer, { @@ -1481,7 +1482,7 @@ where CH::P: ParlIoPeripheral, DM: Mode, { - fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { while !Instance::is_tx_eof() {} Instance::set_tx_start(false); @@ -1526,7 +1527,7 @@ where pub fn read_dma<'t, RXBUF>( &'t mut self, words: &'t mut RXBUF, - ) -> Result, Error> + ) -> Result, Error> where RXBUF: WriteBuffer, { @@ -1576,7 +1577,7 @@ where CH::P: ParlIoPeripheral, DM: Mode, { - fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { loop { if self.rx_channel.is_done() || self.rx_channel.has_eof_error() @@ -1656,7 +1657,6 @@ where } #[doc(hidden)] -#[cfg(feature = "async")] pub mod asynch { use core::task::Poll; @@ -1665,12 +1665,13 @@ pub mod asynch { use super::{private::Instance, Error, ParlIoRx, ParlIoTx, MAX_DMA_SIZE}; use crate::{ - dma::{asynch::DmaRxFuture, DmaChannel, ParlIoPeripheral}, + dma::{asynch::DmaRxFuture, DmaChannel, ParlIoPeripheral, ReadBuffer, WriteBuffer}, peripherals::Interrupt, }; static TX_WAKER: AtomicWaker = AtomicWaker::new(); + #[must_use = "futures do nothing unless you `.await` or poll them"] struct TxDoneFuture {} impl TxDoneFuture { @@ -1727,8 +1728,11 @@ pub mod asynch { /// Perform a DMA write. /// /// The maximum amount of data to be sent is 32736 bytes. - pub async fn write_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error> { - let (ptr, len) = (words.as_ptr(), words.len()); + pub async fn write_dma_async<'t, TXBUF>(&mut self, words: &'t TXBUF) -> Result<(), Error> + where + TXBUF: ReadBuffer, + { + let (ptr, len) = unsafe { words.read_buffer() }; if len > MAX_DMA_SIZE { return Err(Error::MaxDmaTransferSizeExceeded); @@ -1750,8 +1754,14 @@ pub mod asynch { /// Perform a DMA write. /// /// The maximum amount of data to be sent is 32736 bytes. - pub async fn read_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error> { - let (ptr, len) = (words.as_mut_ptr(), words.len()); + pub async fn read_dma_async<'t, RXBUF>( + &'t mut self, + words: &'t mut RXBUF, + ) -> Result<(), Error> + where + RXBUF: WriteBuffer, + { + let (ptr, len) = unsafe { words.write_buffer() }; if !Instance::is_suc_eof_generated_externally() && len > MAX_DMA_SIZE { return Err(Error::MaxDmaTransferSizeExceeded); @@ -2031,7 +2041,6 @@ mod private { reg_block.rx_cfg0().read().rx_eof_gen_sel().bit_is_set() } - #[cfg(feature = "async")] pub fn listen_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; @@ -2039,7 +2048,6 @@ mod private { reg_block.int_ena().modify(|_, w| w.tx_eof().set_bit()); } - #[cfg(feature = "async")] pub fn unlisten_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; @@ -2047,7 +2055,6 @@ mod private { reg_block.int_ena().modify(|_, w| w.tx_eof().clear_bit()); } - #[cfg(feature = "async")] pub fn is_listening_tx_done() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; @@ -2055,7 +2062,6 @@ mod private { reg_block.int_ena().read().tx_eof().bit() } - #[cfg(feature = "async")] pub fn is_tx_done_set() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; @@ -2063,7 +2069,6 @@ mod private { reg_block.int_raw().read().tx_eof().bit() } - #[cfg(feature = "async")] pub fn clear_is_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; @@ -2298,7 +2303,6 @@ mod private { .bit_is_set() } - #[cfg(feature = "async")] pub fn listen_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; @@ -2306,7 +2310,6 @@ mod private { reg_block.int_ena().modify(|_, w| w.tx_eof().set_bit()); } - #[cfg(feature = "async")] pub fn unlisten_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; @@ -2314,7 +2317,6 @@ mod private { reg_block.int_ena().modify(|_, w| w.tx_eof().clear_bit()); } - #[cfg(feature = "async")] pub fn is_listening_tx_done() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; @@ -2322,7 +2324,6 @@ mod private { reg_block.int_ena().read().tx_eof().bit() } - #[cfg(feature = "async")] pub fn is_tx_done_set() -> bool { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; @@ -2330,7 +2331,6 @@ mod private { reg_block.int_raw().read().tx_eof().bit() } - #[cfg(feature = "async")] pub fn clear_is_tx_done() { let reg_block: crate::peripherals::PARL_IO = unsafe { crate::peripherals::PARL_IO::steal() }; diff --git a/esp-hal/src/pcnt/channel.rs b/esp-hal/src/pcnt/channel.rs index 1e6ac296f70..d0a2782486e 100644 --- a/esp-hal/src/pcnt/channel.rs +++ b/esp-hal/src/pcnt/channel.rs @@ -39,6 +39,8 @@ pub struct PcntSource { } impl PcntSource { + /// Creates a `PcntSource` from an input pin with the specified + /// configuration. pub fn from_pin<'a, P: InputPin>( pin: impl Peripheral

+ 'a, pin_config: PcntInputConfig, @@ -56,12 +58,16 @@ impl PcntSource { inverted: false, } } + + /// Creates a `PcntSource` that is always high. pub fn always_high() -> Self { Self { source: ONE_INPUT, inverted: false, } } + + /// Creates a `PcntSource` that is always low. pub fn always_low() -> Self { Self { source: ZERO_INPUT, @@ -69,6 +75,7 @@ impl PcntSource { } } + /// Inverts the `PcntSource` signal. pub fn invert(self) -> Self { Self { source: self.source, @@ -77,6 +84,7 @@ impl PcntSource { } } +/// Represents a channel within a pulse counter unit. pub struct Channel<'d, const UNIT: usize, const NUM: usize> { _phantom: PhantomData<&'d ()>, // Individual channels are not Send, since they share registers. diff --git a/esp-hal/src/pcnt/mod.rs b/esp-hal/src/pcnt/mod.rs index 8bd28fe33d7..4c4817c46cf 100644 --- a/esp-hal/src/pcnt/mod.rs +++ b/esp-hal/src/pcnt/mod.rs @@ -32,6 +32,7 @@ use crate::{ pub mod channel; pub mod unit; +/// Pulse Counter (PCNT) peripheral driver. pub struct Pcnt<'d> { _instance: PeripheralRef<'d, peripherals::PCNT>, @@ -61,6 +62,7 @@ impl<'d> Pcnt<'d> { /// Return a new PCNT pub fn new(_instance: impl Peripheral

+ 'd) -> Self { crate::into_ref!(_instance); + // Enable the PCNT peripherals clock in the system peripheral PeripheralClockControl::reset(crate::system::Peripheral::Pcnt); PeripheralClockControl::enable(crate::system::Peripheral::Pcnt); diff --git a/esp-hal/src/pcnt/unit.rs b/esp-hal/src/pcnt/unit.rs index 01e9c706eaa..1f0006f64e6 100644 --- a/esp-hal/src/pcnt/unit.rs +++ b/esp-hal/src/pcnt/unit.rs @@ -57,21 +57,30 @@ impl From for ZeroMode { } } -// Events +/// Events that can occur in a pulse counter unit. #[derive(Copy, Clone, Debug, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Events { + /// Set when the pulse counter reaches the low limit. pub low_limit: bool, + /// Set when the pulse counter reaches the high limit. pub high_limit: bool, + /// Set when the pulse counter crosses threshold 0. pub threshold0: bool, + /// Set when the pulse counter crosses threshold 1. pub threshold1: bool, + /// Set when the pulse counter reaches zero. pub zero: bool, } +/// Represents a pulse counter unit. #[non_exhaustive] pub struct Unit<'d, const NUM: usize> { + /// The counter for PCNT unit. pub counter: Counter<'d, NUM>, + /// The first channel in PCNT unit. pub channel0: Channel<'d, NUM, 0>, + /// The second channel in PCNT unit. pub channel1: Channel<'d, NUM, 1>, } @@ -301,6 +310,7 @@ impl<'d, const NUM: usize> Drop for Unit<'d, NUM> { // The entire Unit is Send but the individual channels are not. unsafe impl<'d, const NUM: usize> Send for Unit<'d, NUM> {} +/// Represents the counter within a pulse counter unit. #[derive(Clone)] pub struct Counter<'d, const NUM: usize> { _phantom: PhantomData<&'d ()>, diff --git a/esp-hal/src/peripheral.rs b/esp-hal/src/peripheral.rs index da05b07c551..3f56ab9a3fc 100644 --- a/esp-hal/src/peripheral.rs +++ b/esp-hal/src/peripheral.rs @@ -1,12 +1,14 @@ //! # Exclusive peripheral access //! //! ## Overview +//! //! The Peripheral module provides an exclusive access mechanism to peripherals //! on ESP chips. It includes the `PeripheralRef` struct, which represents an //! exclusive reference to a peripheral. It offers memory efficiency benefits //! for zero-sized types. //! //! ## Configuration +//! //! The `PeripheralRef` struct is used to access and interact with peripherals. //! It implements the `Deref` and `DerefMut` traits, allowing you to dereference //! it to access the underlying peripheral. It also provides methods for cloning @@ -46,6 +48,7 @@ pub struct PeripheralRef<'a, T> { } impl<'a, T> PeripheralRef<'a, T> { + /// Create a new exclusive reference to a peripheral #[inline] pub fn new(inner: T) -> Self { Self { @@ -191,11 +194,11 @@ pub trait Peripheral: Sized + crate::private::Sealed { } } -impl Peripheral for &mut T +impl Peripheral for &mut T where - T: Peripheral

, + T: Peripheral

, { - type P = T; + type P = P; unsafe fn clone_unchecked(&mut self) -> Self::P { T::clone_unchecked(self) @@ -271,10 +274,12 @@ mod peripheral_macros { $crate::impl_dma_eligible!(MEM2MEM15,Mem2Mem15); } + /// The `Peripherals` struct provides access to all of the hardware peripherals on the chip. #[allow(non_snake_case)] pub struct Peripherals { $( $(#[$cfg])? + /// Each field represents a hardware peripheral. pub $name: peripherals::$name, )* } @@ -282,8 +287,7 @@ mod peripheral_macros { impl Peripherals { /// Returns all the peripherals *once* #[inline] - pub fn take() -> Self { - + pub(crate) fn take() -> Self { #[no_mangle] static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false; @@ -324,6 +328,7 @@ mod peripheral_macros { impl peripherals::$name { $( paste::paste!{ + /// Binds an interrupt handler to the corresponding interrupt for this peripheral. pub fn [](&mut self, handler: unsafe extern "C" fn() -> ()) { unsafe { $crate::interrupt::bind_interrupt($crate::peripherals::Interrupt::$interrupt, handler); } } @@ -349,11 +354,16 @@ mod peripheral_macros { #[doc(hidden)] #[macro_export] + /// Macro to create a peripheral structure. macro_rules! create_peripheral { ($(#[$cfg:meta])? $name:ident <= virtual) => { $(#[$cfg])? #[derive(Debug)] #[allow(non_camel_case_types)] + /// Represents a virtual peripheral with no associated hardware. + /// + /// This struct is generated by the `create_peripheral!` macro when the peripheral + /// is defined as virtual. pub struct $name { _inner: () } $(#[$cfg])? @@ -384,6 +394,10 @@ mod peripheral_macros { $(#[$cfg])? #[derive(Debug)] #[allow(non_camel_case_types)] + /// Represents a concrete hardware peripheral. + /// + /// This struct is generated by the `create_peripheral!` macro when the peripheral + /// is tied to an actual hardware device. pub struct $name { _inner: () } $(#[$cfg])? diff --git a/esp-hal/src/prelude.rs b/esp-hal/src/prelude.rs index 32c9a1cd8a5..11ad6b9ae56 100644 --- a/esp-hal/src/prelude.rs +++ b/esp-hal/src/prelude.rs @@ -38,4 +38,4 @@ pub use crate::timer::timg::{ pub use crate::timer::Timer as _esp_hal_timer_Timer; #[cfg(any(uart0, uart1, uart2))] pub use crate::uart::Instance as _esp_hal_uart_Instance; -pub use crate::{entry, macros::*, InterruptConfigurable}; +pub use crate::{clock::CpuClock, entry, macros::*, InterruptConfigurable}; diff --git a/esp-hal/src/reg_access.rs b/esp-hal/src/reg_access.rs index 7ad9f1b0fc6..54d39c88d73 100644 --- a/esp-hal/src/reg_access.rs +++ b/esp-hal/src/reg_access.rs @@ -28,6 +28,7 @@ impl EndianessConverter for NativeEndianess { } /// Use BE for ESP32, NE otherwise +#[derive(Debug, Clone)] pub(crate) struct SocDependentEndianess; #[cfg(not(esp32))] @@ -61,7 +62,7 @@ impl EndianessConverter for SocDependentEndianess { // It assumes incoming `dst` are aligned to desired layout (in future // ptr.is_aligned can be used). It also assumes that writes are done in FIFO // order. -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct AlignmentHelper { buf: [u8; U32_ALIGN_SIZE], buf_fill: usize, diff --git a/esp-hal/src/reset.rs b/esp-hal/src/reset.rs index a1b18f62fce..5b3486769d6 100644 --- a/esp-hal/src/reset.rs +++ b/esp-hal/src/reset.rs @@ -17,6 +17,7 @@ use crate::rtc_cntl::SocResetReason; +/// Source of the wakeup event #[derive(Debug, Copy, Clone)] pub enum SleepSource { /// In case of deep sleep, reset was not caused by exit from deep sleep diff --git a/esp-hal/src/rmt.rs b/esp-hal/src/rmt.rs index 1d1d288f0ef..7ea901626c9 100644 --- a/esp-hal/src/rmt.rs +++ b/esp-hal/src/rmt.rs @@ -18,24 +18,25 @@ //! the input signal //! //! ### Channels +//! //! There are #![cfg_attr( esp32, - doc = "8 channels, each of them can be either receiver or transmitter" + doc = "8 channels, each of them can be either receiver or transmitter." )] #![cfg_attr( esp32s2, - doc = "4 channels, each of them can be either receiver or transmitter" + doc = "4 channels, each of them can be either receiver or transmitter." )] #![cfg_attr( esp32s3, - doc = "8 channels, `Channel<0>`-`Channel<3>` hardcoded for transmitting signals and `Channel<4>`-`Channel<7>` hardcoded for receiving signals" + doc = "8 channels, `Channel<0>`-`Channel<3>` hardcoded for transmitting signals and `Channel<4>`-`Channel<7>` hardcoded for receiving signals." )] #![cfg_attr( any(esp32c3, esp32c6, esp32h2), doc = "4 channels, `Channel<0>` and `Channel<1>` hardcoded for transmitting signals and `Channel<2>` and `Channel<3>` hardcoded for receiving signals." )] -#![doc = " "] +#![doc = ""] //! For more information, please refer to the #![doc = concat!("[ESP-IDF documentation](https://docs.espressif.com/projects/esp-idf/en/latest/", crate::soc::chip!(), "/api-reference/peripherals/rmt.html)")] //! ## Configuration @@ -44,21 +45,21 @@ //! channels are indicated by n which is used as a placeholder for the channel //! number, and by m for RX channels. //! -//! ## Examples +//! ## Example +//! //! ### Initialization +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::peripherals::Peripherals; //! # use esp_hal::rmt::TxChannelConfig; //! # use esp_hal::rmt::Rmt; //! # use esp_hal::gpio::Io; -//! # use esp_hal::clock::ClockControl; //! # use crate::esp_hal::rmt::TxChannelCreator; -//! # use crate::esp_hal::prelude::_fugit_RateExtU32; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); #![cfg_attr(esp32h2, doc = "let freq = 32.MHz();")] #![cfg_attr(not(esp32h2), doc = "let freq = 80.MHz();")] -//! let rmt = Rmt::new(peripherals.RMT, freq, &clocks).unwrap(); +//! let rmt = Rmt::new(peripherals.RMT, freq).unwrap(); //! let mut channel = rmt //! .channel0 //! .configure( @@ -76,17 +77,14 @@ //! .unwrap(); //! # } //! ``` -//! (on ESP32 and ESP32-S2 you cannot specify a base frequency other than 80 -//! MHz) - -#![warn(missing_docs)] +//! +//! > Note: on ESP32 and ESP32-S2 you cannot specify a base frequency other than 80 MHz use core::marker::PhantomData; use fugit::HertzU32; use crate::{ - clock::Clocks, gpio::{InputPin, OutputPin}, interrupt::InterruptHandler, peripheral::Peripheral, @@ -208,7 +206,6 @@ pub struct RxChannelConfig { pub use impl_for_chip::{ChannelCreator, Rmt}; -#[cfg(feature = "async")] use self::asynch::{RxChannelAsync, TxChannelAsync}; impl<'d, M> Rmt<'d, M> @@ -218,7 +215,6 @@ where pub(crate) fn new_internal( peripheral: impl Peripheral

+ 'd, frequency: HertzU32, - _clocks: &Clocks<'d>, ) -> Result { let me = Rmt::create(peripheral); @@ -227,13 +223,16 @@ where return Err(Error::UnreachableTargetFrequency); } + PeripheralClockControl::reset(crate::system::Peripheral::Rmt); PeripheralClockControl::enable(crate::system::Peripheral::Rmt); - #[cfg(not(any(esp32, esp32s2)))] - me.configure_clock(frequency, _clocks)?; - - #[cfg(any(esp32, esp32s2))] - self::chip_specific::configure_clock(); + cfg_if::cfg_if! { + if #[cfg(any(esp32, esp32s2))] { + self::chip_specific::configure_clock(); + } else { + me.configure_clock(frequency)?; + } + } Ok(me) } @@ -247,7 +246,7 @@ where } #[cfg(not(any(esp32, esp32s2)))] - fn configure_clock(&self, frequency: HertzU32, _clocks: &Clocks<'d>) -> Result<(), Error> { + fn configure_clock(&self, frequency: HertzU32) -> Result<(), Error> { let src_clock = crate::soc::constants::RMT_CLOCK_SRC_FREQ; if frequency > src_clock { @@ -271,9 +270,8 @@ impl<'d> Rmt<'d, crate::Blocking> { pub fn new( peripheral: impl Peripheral

+ 'd, frequency: HertzU32, - _clocks: &Clocks<'d>, ) -> Result { - Self::new_internal(peripheral, frequency, _clocks) + Self::new_internal(peripheral, frequency) } } @@ -285,15 +283,13 @@ impl<'d> InterruptConfigurable for Rmt<'d, crate::Blocking> { } } -#[cfg(feature = "async")] impl<'d> Rmt<'d, crate::Async> { /// Create a new RMT instance pub fn new_async( peripheral: impl Peripheral

+ 'd, frequency: HertzU32, - _clocks: &Clocks<'d>, ) -> Result { - let mut this = Self::new_internal(peripheral, frequency, _clocks)?; + let mut this = Self::new_internal(peripheral, frequency)?; this.internal_set_interrupt_handler(asynch::async_interrupt_handler); Ok(this) } @@ -331,7 +327,6 @@ where } /// Creates a TX channel in async mode -#[cfg(feature = "async")] pub trait TxChannelCreatorAsync<'d, T, P> where P: OutputPin, @@ -392,7 +387,7 @@ where } crate::into_ref!(pin); - pin.set_to_input(crate::private::Internal); + pin.init_input(false, false, crate::private::Internal); pin.connect_input_to_peripheral(T::input_signal(), crate::private::Internal); T::set_divider(config.clk_divider); T::set_carrier( @@ -409,7 +404,6 @@ where } /// Creates a RX channel in async mode -#[cfg(feature = "async")] pub trait RxChannelCreatorAsync<'d, T, P> where P: InputPin, @@ -439,7 +433,7 @@ where } crate::into_ref!(pin); - pin.set_to_input(crate::private::Internal); + pin.init_input(false, false, crate::private::Internal); pin.connect_input_to_peripheral(T::input_signal(), crate::private::Internal); T::set_divider(config.clk_divider); T::set_carrier( @@ -598,7 +592,6 @@ macro_rules! impl_tx_channel_creator { impl $crate::rmt::TxChannel for $crate::rmt::Channel<$crate::Blocking, $channel> {} - #[cfg(feature = "async")] impl<'d, P> $crate::rmt::TxChannelCreatorAsync<'d, $crate::rmt::Channel<$crate::Async, $channel>, P> for ChannelCreator<$crate::Async, $channel> where @@ -606,7 +599,6 @@ macro_rules! impl_tx_channel_creator { { } - #[cfg(feature = "async")] impl $crate::rmt::asynch::TxChannelAsync for $crate::rmt::Channel<$crate::Async, $channel> {} }; } @@ -622,7 +614,6 @@ macro_rules! impl_rx_channel_creator { impl $crate::rmt::RxChannel for $crate::rmt::Channel<$crate::Blocking, $channel> {} - #[cfg(feature = "async")] impl<'d, P> $crate::rmt::RxChannelCreatorAsync<'d, $crate::rmt::Channel<$crate::Async, $channel>, P> for ChannelCreator<$crate::Async, $channel> where @@ -630,7 +621,6 @@ macro_rules! impl_rx_channel_creator { { } - #[cfg(feature = "async")] impl $crate::rmt::asynch::RxChannelAsync for $crate::rmt::Channel<$crate::Async, $channel> {} }; } @@ -643,15 +633,18 @@ mod impl_for_chip { use crate::peripheral::{Peripheral, PeripheralRef}; /// RMT Instance - #[allow(missing_docs)] pub struct Rmt<'d, M> where M: crate::Mode, { _peripheral: PeripheralRef<'d, crate::peripherals::RMT>, + /// RMT Channel 0. pub channel0: ChannelCreator, + /// RMT Channel 1. pub channel1: ChannelCreator, + /// RMT Channel 2. pub channel2: ChannelCreator, + /// RMT Channel 3. pub channel3: ChannelCreator, phantom: PhantomData, } @@ -711,19 +704,26 @@ mod impl_for_chip { use crate::peripheral::{Peripheral, PeripheralRef}; /// RMT Instance - #[allow(missing_docs)] pub struct Rmt<'d, M> where M: crate::Mode, { _peripheral: PeripheralRef<'d, crate::peripherals::RMT>, + /// RMT Channel 0. pub channel0: ChannelCreator, + /// RMT Channel 1. pub channel1: ChannelCreator, + /// RMT Channel 2. pub channel2: ChannelCreator, + /// RMT Channel 3. pub channel3: ChannelCreator, + /// RMT Channel 4. pub channel4: ChannelCreator, + /// RMT Channel 5. pub channel5: ChannelCreator, + /// RMT Channel 6. pub channel6: ChannelCreator, + /// RMT Channel 7. pub channel7: ChannelCreator, phantom: PhantomData, } @@ -819,15 +819,18 @@ mod impl_for_chip { use crate::peripheral::{Peripheral, PeripheralRef}; /// RMT Instance - #[allow(missing_docs)] pub struct Rmt<'d, M> where M: crate::Mode, { _peripheral: PeripheralRef<'d, crate::peripherals::RMT>, + /// RMT Channel 0. pub channel0: ChannelCreator, + /// RMT Channel 1. pub channel1: ChannelCreator, + /// RMT Channel 2. pub channel2: ChannelCreator, + /// RMT Channel 3. pub channel3: ChannelCreator, phantom: PhantomData, } @@ -895,19 +898,26 @@ mod impl_for_chip { use crate::peripheral::{Peripheral, PeripheralRef}; /// RMT Instance - #[allow(missing_docs)] pub struct Rmt<'d, M> where M: crate::Mode, { _peripheral: PeripheralRef<'d, crate::peripherals::RMT>, + /// RMT Channel 0. pub channel0: ChannelCreator, + /// RMT Channel 1. pub channel1: ChannelCreator, + /// RMT Channel 2. pub channel2: ChannelCreator, + /// RMT Channel 3. pub channel3: ChannelCreator, + /// RMT Channel 4. pub channel4: ChannelCreator, + /// RMT Channel 5. pub channel5: ChannelCreator, + /// RMT Channel 6. pub channel6: ChannelCreator, + /// RMT Channel 7. pub channel7: ChannelCreator, phantom: PhantomData, } @@ -1109,7 +1119,6 @@ pub trait RxChannel: private::RxChannelInternal { } /// Async functionality -#[cfg(feature = "async")] pub mod asynch { use core::{ pin::Pin, @@ -1130,6 +1139,7 @@ pub mod asynch { const INIT: AtomicWaker = AtomicWaker::new(); static WAKER: [AtomicWaker; NUM_CHANNELS] = [INIT; NUM_CHANNELS]; + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct RmtTxFuture where T: TxChannelAsync, @@ -1193,6 +1203,7 @@ pub mod asynch { } } + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct RmtRxFuture where T: RxChannelAsync, diff --git a/esp-hal/src/rng.rs b/esp-hal/src/rng.rs index 96e594c0656..8e469bd43d5 100644 --- a/esp-hal/src/rng.rs +++ b/esp-hal/src/rng.rs @@ -93,7 +93,6 @@ impl Rng { } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::blocking::rng::Read for Rng { type Error = core::convert::Infallible; @@ -144,7 +143,6 @@ impl rand_core::RngCore for Rng { /// # use esp_hal::analog::adc::{AdcConfig, Attenuation, Adc}; /// # use esp_hal::gpio::Io; /// -/// let mut peripherals = Peripherals::take(); /// let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); /// let mut buf = [0u8; 16]; /// @@ -152,7 +150,7 @@ impl rand_core::RngCore for Rng { /// let mut trng = Trng::new(peripherals.RNG, &mut peripherals.ADC1); /// trng.read(&mut buf); /// let mut true_rand = trng.random(); -#[cfg_attr(not(esp32c6), doc = "let mut rng = trng.downgrade();")] +/// let mut rng = trng.downgrade(); /// // ADC is available now #[cfg_attr(esp32, doc = "let analog_pin = io.pins.gpio32;")] #[cfg_attr(not(esp32), doc = "let analog_pin = io.pins.gpio3;")] @@ -161,8 +159,8 @@ impl rand_core::RngCore for Rng { /// Attenuation::Attenuation11dB); let mut adc1 = /// Adc::::new(peripherals.ADC1, adc1_config); let pin_value: u16 = /// nb::block!(adc1.read_oneshot(&mut adc1_pin)).unwrap(); -#[cfg_attr(not(esp32c6), doc = "rng.read(&mut buf);")] -#[cfg_attr(not(esp32c6), doc = "true_rand = rng.random();")] +/// rng.read(&mut buf); +/// true_rand = rng.random(); /// let pin_value: u16 = nb::block!(adc1.read_oneshot(&mut adc1_pin)).unwrap(); /// # } /// ``` @@ -208,22 +206,17 @@ impl<'d> Trng<'d> { /// Downgrades the `Trng` instance to a `Rng` instance and releases the /// ADC1. - /// For esp32c6 - blocked on - #[cfg(not(esp32c6))] pub fn downgrade(self) -> Rng { self.rng } } -/// For esp32c6 - blocked on -#[cfg(not(esp32c6))] impl<'d> Drop for Trng<'d> { fn drop(&mut self) { crate::soc::trng::revert_trng(); } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::blocking::rng::Read for Trng<'_> { type Error = core::convert::Infallible; /// Fills the provided buffer with random bytes. diff --git a/esp-hal/src/rom/md5.rs b/esp-hal/src/rom/md5.rs index 1ef90c86de7..985334104d0 100644 --- a/esp-hal/src/rom/md5.rs +++ b/esp-hal/src/rom/md5.rs @@ -25,7 +25,9 @@ //! than it would be if you included an MD5 implementation in your project. //! //! ## Examples -//! ## Compute a Full Digest From a Single Buffer +//! +//! ### Compute a Full Digest From a Single Buffer +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::rom::md5; @@ -34,13 +36,14 @@ //! # use core::writeln; //! # use core::fmt::Write; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut uart0 = Uart::new(peripherals.UART0, &clocks, io.pins.gpio1, io.pins.gpio2).unwrap(); +//! # let mut uart0 = Uart::new(peripherals.UART0, io.pins.gpio1, io.pins.gpio2).unwrap(); //! # let data = "Dummy"; //! let d: md5::Digest = md5::compute(&data); //! writeln!(uart0, "{}", d); //! # } //! ``` -//! ## Compute a Digest Over Multiple Buffers +//! +//! ### Compute a Digest Over Multiple Buffers //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::rom::md5; @@ -49,9 +52,10 @@ //! # use core::writeln; //! # use core::fmt::Write; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut uart0 = Uart::new(peripherals.UART0, &clocks, io.pins.gpio1, io.pins.gpio2).unwrap(); +//! # let mut uart0 = Uart::new(peripherals.UART0, io.pins.gpio1, io.pins.gpio2).unwrap(); //! # let data0 = "Dummy"; //! # let data1 = "Dummy"; +//! # //! let mut ctx = md5::Context::new(); //! ctx.consume(&data0); //! ctx.consume(&data1); diff --git a/esp-hal/src/rsa/esp32.rs b/esp-hal/src/rsa/esp32.rs index 22fad29f1a3..c5b9938d824 100644 --- a/esp-hal/src/rsa/esp32.rs +++ b/esp-hal/src/rsa/esp32.rs @@ -1,8 +1,4 @@ -use core::{ - convert::Infallible, - marker::PhantomData, - ptr::{copy_nonoverlapping, write_bytes}, -}; +use core::convert::Infallible; use crate::rsa::{ implement_op, @@ -25,43 +21,46 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { Ok(()) } + /// Writes the multi-mode configuration to the RSA hardware. pub(super) fn write_multi_mode(&mut self, mode: u32) { self.rsa.mult_mode().write(|w| unsafe { w.bits(mode) }); } + /// Writes the modular exponentiation mode configuration to the RSA + /// hardware. pub(super) fn write_modexp_mode(&mut self, mode: u32) { self.rsa.modexp_mode().write(|w| unsafe { w.bits(mode) }); } - pub(super) fn write_modexp_start(&mut self) { + /// Starts the modular exponentiation operation. + pub(super) fn write_modexp_start(&self) { self.rsa .modexp_start() .write(|w| w.modexp_start().set_bit()); } - pub(super) fn write_multi_start(&mut self) { + /// Starts the multiplication operation. + pub(super) fn write_multi_start(&self) { self.rsa.mult_start().write(|w| w.mult_start().set_bit()); } - pub(super) fn clear_interrupt(&mut self) { - self.rsa.interrupt().write(|w| w.interrupt().set_bit()); + /// Starts the modular multiplication operation. + pub(super) fn write_modmulti_start(&self) { + self.write_multi_start(); } - pub(super) fn is_idle(&mut self) -> bool { - self.rsa.interrupt().read().bits() == 1 - } - - unsafe fn write_multi_operand_a(&mut self, operand_a: &[u32; N]) { - copy_nonoverlapping(operand_a.as_ptr(), self.rsa.x_mem(0).as_ptr(), N); - write_bytes(self.rsa.x_mem(0).as_ptr().add(N), 0, N); + /// Clears the RSA interrupt flag. + pub(super) fn clear_interrupt(&mut self) { + self.rsa.interrupt().write(|w| w.interrupt().set_bit()); } - unsafe fn write_multi_operand_b(&mut self, operand_b: &[u32; N]) { - write_bytes(self.rsa.z_mem(0).as_ptr(), 0, N); - copy_nonoverlapping(operand_b.as_ptr(), self.rsa.z_mem(0).as_ptr().add(N), N); + /// Checks if the RSA peripheral is idle. + pub(super) fn is_idle(&self) -> bool { + self.rsa.interrupt().read().interrupt().bit_is_set() } } +/// Module defining marker types for various RSA operand sizes. pub mod operand_sizes { //! Marker types for the operand sizes use paste::paste; @@ -84,58 +83,18 @@ impl<'a, 'd, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularMultiplicati where T: RsaMode, { - /// Creates an Instance of `RsaMultiplication`. - /// `m_prime` could be calculated using `-(modular multiplicative inverse of - /// modulus) mod 2^32`, for more information check 24.3.2 in the - /// - pub fn new(rsa: &'a mut Rsa<'d, DM>, modulus: &T::InputType, m_prime: u32) -> Self { - Self::set_mode(rsa); - unsafe { - rsa.write_modulus(modulus); - } - rsa.write_mprime(m_prime); - - Self { - rsa, - phantom: PhantomData, - } - } - - fn set_mode(rsa: &mut Rsa<'d, DM>) { + pub(super) fn write_mode(rsa: &mut Rsa<'d, DM>) { rsa.write_multi_mode((N / 16 - 1) as u32) } - /// Starts the first step of modular multiplication operation. `r` could be - /// calculated using `2 ^ ( bitlength * 2 ) mod modulus`, - /// for more information check 24.3.2 in the - /// - pub fn start_step1(&mut self, operand_a: &T::InputType, r: &T::InputType) { - unsafe { - self.rsa.write_operand_a(operand_a); - self.rsa.write_r(r); - } - self.set_start(); - } - - /// Starts the second step of modular multiplication operation. - /// This is a non blocking function that returns without an error if - /// operation is completed successfully. `start_step1` must be called - /// before calling this function. - pub fn start_step2(&mut self, operand_b: &T::InputType) { - loop { - if self.rsa.is_idle() { - self.rsa.clear_interrupt(); - unsafe { - self.rsa.write_operand_a(operand_b); - } - self.set_start(); - break; - } - } - } - - fn set_start(&mut self) { + /// Starts the modular multiplication operation. + /// + /// For more information refer to 24.3.2 of . + pub(super) fn set_up_modular_multiplication(&mut self, operand_b: &T::InputType) { self.rsa.write_multi_start(); + self.rsa.wait_for_idle(); + + self.rsa.write_operand_a(operand_b); } } @@ -143,64 +102,22 @@ impl<'a, 'd, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularExponentiati where T: RsaMode, { - /// Creates an Instance of `RsaModularExponentiation`. - /// `m_prime` could be calculated using `-(modular multiplicative inverse of - /// modulus) mod 2^32`, for more information check 24.3.2 in the - /// - pub fn new( - rsa: &'a mut Rsa<'d, DM>, - exponent: &T::InputType, - modulus: &T::InputType, - m_prime: u32, - ) -> Self { - Self::set_mode(rsa); - unsafe { - rsa.write_operand_b(exponent); - rsa.write_modulus(modulus); - } - rsa.write_mprime(m_prime); - Self { - rsa, - phantom: PhantomData, - } - } - - pub(super) fn set_mode(rsa: &mut Rsa<'d, DM>) { + /// Sets the modular exponentiation mode for the RSA hardware. + pub(super) fn write_mode(rsa: &mut Rsa<'d, DM>) { rsa.write_modexp_mode((N / 16 - 1) as u32) } - - pub(super) fn set_start(&mut self) { - self.rsa.write_modexp_start(); - } } impl<'a, 'd, T: RsaMode + Multi, DM: crate::Mode, const N: usize> RsaMultiplication<'a, 'd, T, DM> where T: RsaMode, { - /// Creates an Instance of `RsaMultiplication`. - pub fn new(rsa: &'a mut Rsa<'d, DM>) -> Self { - Self::set_mode(rsa); - Self { - rsa, - phantom: PhantomData, - } - } - - /// Starts the multiplication operation. - pub fn start_multiplication(&mut self, operand_a: &T::InputType, operand_b: &T::InputType) { - unsafe { - self.rsa.write_multi_operand_a(operand_a); - self.rsa.write_multi_operand_b(operand_b); - } - self.set_start(); - } - - pub(super) fn set_mode(rsa: &mut Rsa<'d, DM>) { + /// Sets the multiplication mode for the RSA hardware. + pub(super) fn write_mode(rsa: &mut Rsa<'d, DM>) { rsa.write_multi_mode(((N * 2) / 16 + 7) as u32) } - pub(super) fn set_start(&mut self) { - self.rsa.write_multi_start(); + pub(super) fn set_up_multiplication(&mut self, operand_b: &T::InputType) { + self.rsa.write_multi_operand_b(operand_b); } } diff --git a/esp-hal/src/rsa/esp32cX.rs b/esp-hal/src/rsa/esp32cX.rs index e89edadaac6..ea6d79d44c2 100644 --- a/esp-hal/src/rsa/esp32cX.rs +++ b/esp-hal/src/rsa/esp32cX.rs @@ -1,4 +1,4 @@ -use core::{convert::Infallible, marker::PhantomData, ptr::copy_nonoverlapping}; +use core::convert::Infallible; use crate::rsa::{ implement_op, @@ -21,8 +21,10 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { Ok(()) } - /// Enables/disables rsa interrupt, when enabled rsa perpheral would - /// generate an interrupt when a operation is finished. + /// Enables/disables rsa interrupt. + /// + /// When enabled rsa peripheral would generate an interrupt when a operation + /// is finished. pub fn enable_disable_interrupt(&mut self, enable: bool) { match enable { true => self.rsa.int_ena().write(|w| w.int_ena().set_bit()), @@ -34,10 +36,15 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { self.rsa.mode().write(|w| unsafe { w.bits(mode) }); } - /// Enables/disables search acceleration, when enabled it would increases - /// the performance of modular exponentiation by discarding the - /// exponent's bits before the most significant set bit. Note: this might - /// affect the security, for more info refer 18.3.4 of + /// Enables/disables search acceleration. + /// + /// When enabled it would increases the performance of modular + /// exponentiation by discarding the exponent's bits before the most + /// significant set bit. + /// + /// Note: this might decrease security. + /// + /// For more information refer to 18.3.4 of pub fn enable_disable_search_acceleration(&mut self, enable: bool) { match enable { true => self @@ -51,21 +58,28 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { } } + /// Checks if the search functionality is enabled in the RSA hardware. pub(super) fn is_search_enabled(&mut self) -> bool { self.rsa.search_enable().read().search_enable().bit_is_set() } + /// Sets the search position in the RSA hardware. pub(super) fn write_search_position(&mut self, search_position: u32) { self.rsa .search_pos() .write(|w| unsafe { w.bits(search_position) }); } - /// Enables/disables constant time acceleration, when enabled it would - /// increases the performance of modular exponentiation by simplifying - /// the calculation concerning the 0 bits of the exponent i.e. lesser the - /// hamming weight, greater the performance. Note : this might affect - /// the security, for more info refer 18.3.4 of + /// Enables/disables constant time acceleration. + /// + /// When enabled it would increases the performance of modular + /// exponentiation by simplifying the calculation concerning the 0 bits + /// of the exponent. I.e. lesser the hamming weight, greater the + /// performance. + /// + /// Note: this might decrease security. + /// + /// For more information refer to 18.3.4 of . pub fn enable_disable_constant_time_acceleration(&mut self, enable: bool) { match enable { true => self @@ -79,37 +93,39 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { } } - pub(super) fn write_modexp_start(&mut self) { + /// Starts the modular exponentiation operation. + pub(super) fn write_modexp_start(&self) { self.rsa .set_start_modexp() .write(|w| w.set_start_modexp().set_bit()); } - pub(super) fn write_multi_start(&mut self) { + /// Starts the multiplication operation. + pub(super) fn write_multi_start(&self) { self.rsa .set_start_mult() .write(|w| w.set_start_mult().set_bit()); } - fn write_modmulti_start(&mut self) { + /// Starts the modular multiplication operation. + pub(super) fn write_modmulti_start(&self) { self.rsa .set_start_modmult() .write(|w| w.set_start_modmult().set_bit()); } + /// Clears the RSA interrupt flag. pub(super) fn clear_interrupt(&mut self) { self.rsa.int_clr().write(|w| w.clear_interrupt().set_bit()); } - pub(super) fn is_idle(&mut self) -> bool { + /// Checks if the RSA peripheral is idle. + pub(super) fn is_idle(&self) -> bool { self.rsa.query_idle().read().query_idle().bit_is_set() } - - unsafe fn write_multi_operand_b(&mut self, operand_b: &[u32; N]) { - copy_nonoverlapping(operand_b.as_ptr(), self.rsa.z_mem(0).as_ptr().add(N), N); - } } +/// Module defining marker types for various RSA operand sizes. pub mod operand_sizes { //! Marker types for the operand sizes use paste::paste; @@ -220,32 +236,7 @@ impl<'a, 'd, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularExponentiati where T: RsaMode, { - /// Creates an Instance of `RsaModularExponentiation`. - /// `m_prime` could be calculated using `-(modular multiplicative inverse of - /// modulus) mod 2^32`, for more information check 19.3.1 in the - /// - pub fn new( - rsa: &'a mut Rsa<'d, DM>, - exponent: &T::InputType, - modulus: &T::InputType, - m_prime: u32, - ) -> Self { - Self::set_mode(rsa); - unsafe { - rsa.write_operand_b(exponent); - rsa.write_modulus(modulus); - } - rsa.write_mprime(m_prime); - if rsa.is_search_enabled() { - rsa.write_search_position(Self::find_search_pos(exponent)); - } - Self { - rsa, - phantom: PhantomData, - } - } - - fn find_search_pos(exponent: &T::InputType) -> u32 { + pub(super) fn find_search_pos(exponent: &T::InputType) -> u32 { for (i, byte) in exponent.iter().rev().enumerate() { if *byte == 0 { continue; @@ -255,59 +246,22 @@ where 0 } - pub(super) fn set_mode(rsa: &mut Rsa<'d, DM>) { + /// Sets the modular exponentiation mode for the RSA hardware. + pub(super) fn write_mode(rsa: &mut Rsa<'d, DM>) { rsa.write_mode((N - 1) as u32) } - - pub(super) fn set_start(&mut self) { - self.rsa.write_modexp_start(); - } } impl<'a, 'd, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularMultiplication<'a, 'd, T, DM> where T: RsaMode, { - fn write_mode(rsa: &mut Rsa<'d, DM>) { + pub(super) fn write_mode(rsa: &mut Rsa<'d, DM>) { rsa.write_mode((N - 1) as u32) } - /// Creates an Instance of `RsaModularMultiplication`. - /// `m_prime` could be calculated using `-(modular multiplicative inverse of - /// modulus) mod 2^32`, for more information check 19.3.1 in the - /// - pub fn new( - rsa: &'a mut Rsa<'d, DM>, - operand_a: &T::InputType, - operand_b: &T::InputType, - modulus: &T::InputType, - m_prime: u32, - ) -> Self { - Self::write_mode(rsa); - rsa.write_mprime(m_prime); - unsafe { - rsa.write_modulus(modulus); - rsa.write_operand_a(operand_a); - rsa.write_operand_b(operand_b); - } - Self { - rsa, - phantom: PhantomData, - } - } - - /// Starts the modular multiplication operation. `r` could be calculated - /// using `2 ^ ( bitlength * 2 ) mod modulus`, for more information - /// check 19.3.1 in the - pub fn start_modular_multiplication(&mut self, r: &T::InputType) { - unsafe { - self.rsa.write_r(r); - } - self.set_start(); - } - - fn set_start(&mut self) { - self.rsa.write_modmulti_start(); + pub(super) fn set_up_modular_multiplication(&mut self, operand_b: &T::InputType) { + self.rsa.write_operand_b(operand_b); } } @@ -315,31 +269,12 @@ impl<'a, 'd, T: RsaMode + Multi, DM: crate::Mode, const N: usize> RsaMultiplicat where T: RsaMode, { - /// Creates an Instance of `RsaMultiplication`. - pub fn new(rsa: &'a mut Rsa<'d, DM>, operand_a: &T::InputType) -> Self { - Self::set_mode(rsa); - unsafe { - rsa.write_operand_a(operand_a); - } - Self { - rsa, - phantom: PhantomData, - } + pub(super) fn set_up_multiplication(&mut self, operand_b: &T::InputType) { + self.rsa.write_multi_operand_b(operand_b); } - /// Starts the multiplication operation. - pub fn start_multiplication(&mut self, operand_b: &T::InputType) { - unsafe { - self.rsa.write_multi_operand_b(operand_b); - } - self.set_start(); - } - - pub(super) fn set_mode(rsa: &mut Rsa<'d, DM>) { + /// Sets the multiplication mode for the RSA hardware. + pub(super) fn write_mode(rsa: &mut Rsa<'d, DM>) { rsa.write_mode((N * 2 - 1) as u32) } - - pub(super) fn set_start(&mut self) { - self.rsa.write_multi_start(); - } } diff --git a/esp-hal/src/rsa/esp32sX.rs b/esp-hal/src/rsa/esp32sX.rs index 31120033570..956ec3fe4e8 100644 --- a/esp-hal/src/rsa/esp32sX.rs +++ b/esp-hal/src/rsa/esp32sX.rs @@ -1,4 +1,4 @@ -use core::{convert::Infallible, marker::PhantomData, ptr::copy_nonoverlapping}; +use core::convert::Infallible; use crate::rsa::{ implement_op, @@ -11,9 +11,10 @@ use crate::rsa::{ }; impl<'d, DM: crate::Mode> Rsa<'d, DM> { - /// After the RSA Accelerator is released from reset, the memory blocks + /// After the RSA accelerator is released from reset, the memory blocks /// needs to be initialized, only after that peripheral should be used. - /// This function would return without an error if the memory is initialized + /// This function would return without an error if the memory is + /// initialized. pub fn ready(&mut self) -> nb::Result<(), Infallible> { if self.rsa.clean().read().clean().bit_is_clear() { return Err(nb::Error::WouldBlock); @@ -21,8 +22,10 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { Ok(()) } - /// Enables/disables rsa interrupt, when enabled rsa peripheral would - /// generate an interrupt when a operation is finished. + /// Enables/disables rsa interrupt. + /// + /// When enabled rsa peripheral would generate an interrupt when a operation + /// is finished. pub fn enable_disable_interrupt(&mut self, enable: bool) { match enable { true => self @@ -40,10 +43,15 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { self.rsa.mode().write(|w| unsafe { w.bits(mode) }); } - /// Enables/disables search acceleration, when enabled it would increases - /// the performance of modular exponentiation by discarding the - /// exponent's bits before the most significant set bit. Note: this might - /// affect the security, for more info refer 18.3.4 of + /// Enables/disables search acceleration. + /// + /// When enabled it would increases the performance of modular + /// exponentiation by discarding the exponent's bits before the most + /// significant set bit. + /// + /// Note: this might decrease security. + /// + /// For more information refer to 20.3.4 of . pub fn enable_disable_search_acceleration(&mut self, enable: bool) { match enable { true => self @@ -57,21 +65,28 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { } } + /// Checks if the search functionality is enabled in the RSA hardware. pub(super) fn is_search_enabled(&mut self) -> bool { self.rsa.search_enable().read().search_enable().bit_is_set() } + /// Sets the search position in the RSA hardware. pub(super) fn write_search_position(&mut self, search_position: u32) { self.rsa .search_pos() .write(|w| unsafe { w.bits(search_position) }); } - /// Enables/disables constant time acceleration, when enabled it would - /// increases the performance of modular exponentiation by simplifying - /// the calculation concerning the 0 bits of the exponent i.e. lesser the - /// hamming weight, greater the performance. Note : this might affect - /// the security, for more info refer 18.3.4 of + /// Enables/disables constant time acceleration. + /// + /// When enabled it would increases the performance of modular + /// exponentiation by simplifying the calculation concerning the 0 bits + /// of the exponent. I.e. lesser the hamming weight, greater the + /// performance. + /// + /// Note: this might decrease security. + /// + /// For more information refer to 20.3.4 of . pub fn enable_disable_constant_time_acceleration(&mut self, enable: bool) { match enable { true => self @@ -85,35 +100,36 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { } } - pub(super) fn write_modexp_start(&mut self) { + /// Starts the modular exponentiation operation. + pub(super) fn write_modexp_start(&self) { self.rsa .modexp_start() .write(|w| w.modexp_start().set_bit()); } - pub(super) fn write_multi_start(&mut self) { + /// Starts the multiplication operation. + pub(super) fn write_multi_start(&self) { self.rsa.mult_start().write(|w| w.mult_start().set_bit()); } - fn write_modmulti_start(&mut self) { + /// Starts the modular multiplication operation. + pub(super) fn write_modmulti_start(&self) { self.rsa .modmult_start() .write(|w| w.modmult_start().set_bit()); } + /// Clears the RSA interrupt flag. pub(super) fn clear_interrupt(&mut self) { self.rsa .clear_interrupt() .write(|w| w.clear_interrupt().set_bit()); } - pub(super) fn is_idle(&mut self) -> bool { + /// Checks if the RSA peripheral is idle. + pub(super) fn is_idle(&self) -> bool { self.rsa.idle().read().idle().bit_is_set() } - - unsafe fn write_multi_operand_b(&mut self, operand_b: &[u32; N]) { - copy_nonoverlapping(operand_b.as_ptr(), self.rsa.z_mem(0).as_ptr().add(N), N); - } } pub mod operand_sizes { @@ -261,32 +277,7 @@ impl<'a, 'd, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularExponentiati where T: RsaMode, { - /// Creates an Instance of `RsaModularExponentiation`. - /// `m_prime` could be calculated using `-(modular multiplicative inverse of - /// modulus) mod 2^32`, for more information check 19.3.1 in the - /// - pub fn new( - rsa: &'a mut Rsa<'d, DM>, - exponent: &T::InputType, - modulus: &T::InputType, - m_prime: u32, - ) -> Self { - Self::set_mode(rsa); - unsafe { - rsa.write_operand_b(exponent); - rsa.write_modulus(modulus); - } - rsa.write_mprime(m_prime); - if rsa.is_search_enabled() { - rsa.write_search_position(Self::find_search_pos(exponent)); - } - Self { - rsa, - phantom: PhantomData, - } - } - - fn find_search_pos(exponent: &T::InputType) -> u32 { + pub(super) fn find_search_pos(exponent: &T::InputType) -> u32 { for (i, byte) in exponent.iter().rev().enumerate() { if *byte == 0 { continue; @@ -296,59 +287,22 @@ where 0 } - pub(super) fn set_mode(rsa: &mut Rsa<'d, DM>) { + /// Sets the modular exponentiation mode for the RSA hardware. + pub(super) fn write_mode(rsa: &mut Rsa<'d, DM>) { rsa.write_mode((N - 1) as u32) } - - pub(super) fn set_start(&mut self) { - self.rsa.write_modexp_start(); - } } impl<'a, 'd, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularMultiplication<'a, 'd, T, DM> where T: RsaMode, { - /// Creates an Instance of `RsaModularMultiplication`. - /// `m_prime` could be calculated using `-(modular multiplicative inverse of - /// modulus) mod 2^32`, for more information check 19.3.1 in the - /// - pub fn new( - rsa: &'a mut Rsa<'d, DM>, - operand_a: &T::InputType, - operand_b: &T::InputType, - modulus: &T::InputType, - m_prime: u32, - ) -> Self { - Self::write_mode(rsa); - rsa.write_mprime(m_prime); - unsafe { - rsa.write_modulus(modulus); - rsa.write_operand_a(operand_a); - rsa.write_operand_b(operand_b); - } - Self { - rsa, - phantom: PhantomData, - } - } - - fn write_mode(rsa: &mut Rsa<'d, DM>) { + pub(super) fn write_mode(rsa: &mut Rsa<'d, DM>) { rsa.write_mode((N - 1) as u32) } - /// Starts the modular multiplication operation. `r` could be calculated - /// using `2 ^ ( bitlength * 2 ) mod modulus`, for more information - /// check 19.3.1 in the - pub fn start_modular_multiplication(&mut self, r: &T::InputType) { - unsafe { - self.rsa.write_r(r); - } - self.set_start(); - } - - fn set_start(&mut self) { - self.rsa.write_modmulti_start(); + pub(super) fn set_up_modular_multiplication(&mut self, operand_b: &T::InputType) { + self.rsa.write_operand_b(operand_b); } } @@ -356,31 +310,12 @@ impl<'a, 'd, T: RsaMode + Multi, DM: crate::Mode, const N: usize> RsaMultiplicat where T: RsaMode, { - /// Creates an Instance of `RsaMultiplication`. - pub fn new(rsa: &'a mut Rsa<'d, DM>, operand_a: &T::InputType) -> Self { - Self::set_mode(rsa); - unsafe { - rsa.write_operand_a(operand_a); - } - Self { - rsa, - phantom: PhantomData, - } - } - - /// Starts the multiplication operation. - pub fn start_multiplication(&mut self, operand_b: &T::InputType) { - unsafe { - self.rsa.write_multi_operand_b(operand_b); - } - self.set_start(); - } - - pub(super) fn set_mode(rsa: &mut Rsa<'d, DM>) { + /// Sets the multiplication mode for the RSA hardware. + pub(super) fn write_mode(rsa: &mut Rsa<'d, DM>) { rsa.write_mode((N * 2 - 1) as u32) } - pub(super) fn set_start(&mut self) { - self.rsa.write_multi_start(); + pub(super) fn set_up_multiplication(&mut self, operand_b: &T::InputType) { + self.rsa.write_multi_operand_b(operand_b); } } diff --git a/esp-hal/src/rsa/mod.rs b/esp-hal/src/rsa/mod.rs index 8a0afd88364..fdae81078d2 100644 --- a/esp-hal/src/rsa/mod.rs +++ b/esp-hal/src/rsa/mod.rs @@ -1,38 +1,25 @@ -//! # Rivest–Shamir–Adleman (RSA) Accelerator. +//! # RSA (Rivest–Shamir–Adleman) accelerator. //! //! ## Overview -//! The RSA Accelerator provides hardware support for high precision computation +//! +//! The RSA accelerator provides hardware support for high precision computation //! used in various RSA asymmetric cipher algorithms by significantly reducing //! their software complexity. Compared with RSA algorithms implemented solely //! in software, this hardware accelerator can speed up RSA algorithms //! significantly. //! //! ## Configuration -//! The RSA Accelerator also supports operands of different lengths, which -//! provides more flexibility during the computation. -//! -//! ## Usage -//! Implementation details; -//! * The driver uses low-level peripheral access to read and write data -//! from/to the `RSA` peripheral. -//! * The driver contains `unsafe` code blocks as it directly manipulates -//! memory addresses for data transfer. -//! * The driver supports different sizes of operands based on the generic -//! types provided during instantiation. -//! * The [nb] crate is used to handle non-blocking operations. -//! * The driver provides a set of high-level abstractions to simplify `RSA` -//! cryptographic operations on `ESP` chips, allowing developers to -//! leverage the `RSA accelerator` for improved performance. //! -//! This peripheral supports `async` on every available chip except of `esp32` -//! (to be solved). +//! The RSA accelerator also supports operands of different lengths, which +//! provides more flexibility during the computation. //! //! ## Examples +//! //! ### Modular Exponentiation, Modular Multiplication, and Multiplication -//! Visit the [RSA] test for an example of using the peripheral. +//! Visit the [RSA test suite] for an example of using the peripheral. //! //! [nb]: https://docs.rs/nb/1.1.0/nb/ -//! [the repository with corresponding example]: https://github.com/esp-rs/esp-hal/blob/main/hil-test/tests/rsa.rs +//! [RSA test suite]: https://github.com/esp-rs/esp-hal/blob/main/hil-test/tests/rsa.rs use core::{marker::PhantomData, ptr::copy_nonoverlapping}; @@ -60,16 +47,6 @@ pub struct Rsa<'d, DM: crate::Mode> { phantom: PhantomData, } -impl<'d, DM: crate::Mode> Rsa<'d, DM> { - fn internal_set_interrupt_handler(&mut self, handler: InterruptHandler) { - unsafe { - crate::interrupt::bind_interrupt(crate::peripherals::Interrupt::RSA, handler.handler()); - crate::interrupt::enable(crate::peripherals::Interrupt::RSA, handler.priority()) - .unwrap(); - } - } -} - impl<'d> Rsa<'d, crate::Blocking> { /// Create a new instance in [crate::Blocking] mode. /// @@ -87,7 +64,6 @@ impl<'d> InterruptConfigurable for Rsa<'d, crate::Blocking> { } } -#[cfg(feature = "async")] impl<'d> Rsa<'d, crate::Async> { /// Create a new instance in [crate::Blocking] mode. pub fn new_async(rsa: impl Peripheral

+ 'd) -> Self { @@ -101,6 +77,7 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { fn new_internal(rsa: impl Peripheral

+ 'd) -> Self { crate::into_ref!(rsa); + PeripheralClockControl::reset(PeripheralEnable::Rsa); PeripheralClockControl::enable(PeripheralEnable::Rsa); Self { @@ -109,67 +86,110 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { } } - unsafe fn write_operand_b(&mut self, operand_b: &[u32; N]) { - copy_nonoverlapping(operand_b.as_ptr(), self.rsa.y_mem(0).as_ptr(), N); + fn write_operand_b(&mut self, operand_b: &[u32; N]) { + unsafe { + copy_nonoverlapping(operand_b.as_ptr(), self.rsa.y_mem(0).as_ptr(), N); + } } - unsafe fn write_modulus(&mut self, modulus: &[u32; N]) { - copy_nonoverlapping(modulus.as_ptr(), self.rsa.m_mem(0).as_ptr(), N); + fn write_modulus(&mut self, modulus: &[u32; N]) { + unsafe { + copy_nonoverlapping(modulus.as_ptr(), self.rsa.m_mem(0).as_ptr(), N); + } } fn write_mprime(&mut self, m_prime: u32) { self.rsa.m_prime().write(|w| unsafe { w.bits(m_prime) }); } - unsafe fn write_operand_a(&mut self, operand_a: &[u32; N]) { - copy_nonoverlapping(operand_a.as_ptr(), self.rsa.x_mem(0).as_ptr(), N); + fn write_operand_a(&mut self, operand_a: &[u32; N]) { + unsafe { + copy_nonoverlapping(operand_a.as_ptr(), self.rsa.x_mem(0).as_ptr(), N); + } + } + + fn write_multi_operand_b(&mut self, operand_b: &[u32; N]) { + unsafe { + copy_nonoverlapping(operand_b.as_ptr(), self.rsa.z_mem(0).as_ptr().add(N), N); + } + } + + fn write_r(&mut self, r: &[u32; N]) { + unsafe { + copy_nonoverlapping(r.as_ptr(), self.rsa.z_mem(0).as_ptr(), N); + } + } + + fn read_out(&self, outbuf: &mut [u32; N]) { + unsafe { + copy_nonoverlapping( + self.rsa.z_mem(0).as_ptr() as *const u32, + outbuf.as_ptr() as *mut u32, + N, + ); + } + } + + fn internal_set_interrupt_handler(&mut self, handler: InterruptHandler) { + unsafe { + crate::interrupt::bind_interrupt(crate::peripherals::Interrupt::RSA, handler.handler()); + crate::interrupt::enable(crate::peripherals::Interrupt::RSA, handler.priority()) + .unwrap(); + } } - unsafe fn write_r(&mut self, r: &[u32; N]) { - copy_nonoverlapping(r.as_ptr(), self.rsa.z_mem(0).as_ptr(), N); + fn wait_for_idle(&mut self) { + while !self.is_idle() {} + self.clear_interrupt(); } - unsafe fn read_out(&mut self, outbuf: &mut [u32; N]) { - copy_nonoverlapping( - self.rsa.z_mem(0).as_ptr() as *const u32, - outbuf.as_ptr() as *mut u32, - N, - ); + fn read_results(&mut self, outbuf: &mut [u32; N]) { + self.wait_for_idle(); + self.read_out(outbuf); } } +/// Defines the input size of an RSA operation. pub trait RsaMode: crate::private::Sealed { + /// The input data type used for the operation. type InputType; } + +/// Defines the output type of RSA multiplications. pub trait Multi: RsaMode { + /// The type of the output produced by the operation. type OutputType; } macro_rules! implement_op { (($x:literal, multi)) => { - paste! {pub struct [];} - paste! { - impl Multi for [] { - type OutputType = [u32; $x*2 / 32]; - }} - paste! { - impl crate::private::Sealed for [] {} - } - paste! { - impl RsaMode for [] { - type InputType = [u32; $x / 32]; - }} + paste! { + #[doc = concat!($x, "-bit RSA operation.")] + pub struct []; + + impl Multi for [] { + type OutputType = [u32; $x * 2 / 32]; + } + + impl crate::private::Sealed for [] {} + + impl RsaMode for [] { + type InputType = [u32; $x / 32]; + } + } }; (($x:literal)) => { - paste! {pub struct [];} paste! { + /// Represents an RSA operation for the given bit size. + pub struct []; + impl crate::private::Sealed for [] {} + + impl RsaMode for [] { + type InputType = [u32; $x / 32]; + } } - paste!{ - impl RsaMode for [] { - type InputType = [u32; $x / 32]; - }} }; ($x:tt, $($y:tt),+) => { @@ -193,31 +213,56 @@ impl<'a, 'd, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularExponentiati where T: RsaMode, { - /// starts the modular exponentiation operation. `r` could be calculated - /// using `2 ^ ( bitlength * 2 ) mod modulus`, for more information - /// check 24.3.2 in the - pub fn start_exponentiation(&mut self, base: &T::InputType, r: &T::InputType) { - unsafe { - self.rsa.write_operand_a(base); - self.rsa.write_r(r); + /// Creates an instance of `RsaModularExponentiation`. + /// + /// `m_prime` could be calculated using `-(modular multiplicative inverse of + /// modulus) mod 2^32`. + /// + /// For more information refer to 24.3.2 of . + pub fn new( + rsa: &'a mut Rsa<'d, DM>, + exponent: &T::InputType, + modulus: &T::InputType, + m_prime: u32, + ) -> Self { + Self::write_mode(rsa); + rsa.write_operand_b(exponent); + rsa.write_modulus(modulus); + rsa.write_mprime(m_prime); + + #[cfg(not(esp32))] + if rsa.is_search_enabled() { + rsa.write_search_position(Self::find_search_pos(exponent)); } - self.set_start(); + + Self { + rsa, + phantom: PhantomData, + } + } + + fn set_up_exponentiation(&mut self, base: &T::InputType, r: &T::InputType) { + self.rsa.write_operand_a(base); + self.rsa.write_r(r); + } + + /// Starts the modular exponentiation operation. + /// + /// `r` can be calculated using `2 ^ ( bitlength * 2 ) mod modulus`. + /// + /// For more information refer to 24.3.2 of . + pub fn start_exponentiation(&mut self, base: &T::InputType, r: &T::InputType) { + self.set_up_exponentiation(base, r); + self.rsa.write_modexp_start(); } - /// reads the result to the given buffer. + /// Reads the result to the given buffer. + /// /// This is a non blocking function that returns without an error if /// operation is completed successfully. `start_exponentiation` must be /// called before calling this function. pub fn read_results(&mut self, outbuf: &mut T::InputType) { - loop { - if self.rsa.is_idle() { - unsafe { - self.rsa.read_out(outbuf); - } - self.rsa.clear_interrupt(); - break; - } - } + self.rsa.read_results(outbuf); } } @@ -234,19 +279,45 @@ impl<'a, 'd, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularMultiplicati where T: RsaMode, { + /// Creates an instance of `RsaModularMultiplication`. + /// + /// - `r` can be calculated using `2 ^ ( bitlength * 2 ) mod modulus`. + /// - `m_prime` can be calculated using `-(modular multiplicative inverse of + /// modulus) mod 2^32`. + /// + /// For more information refer to 20.3.1 of . + pub fn new( + rsa: &'a mut Rsa<'d, DM>, + operand_a: &T::InputType, + modulus: &T::InputType, + r: &T::InputType, + m_prime: u32, + ) -> Self { + Self::write_mode(rsa); + rsa.write_mprime(m_prime); + rsa.write_modulus(modulus); + rsa.write_operand_a(operand_a); + rsa.write_r(r); + + Self { + rsa, + phantom: PhantomData, + } + } + + /// Starts the modular multiplication operation. + /// + /// For more information refer to 19.3.1 of . + pub fn start_modular_multiplication(&mut self, operand_b: &T::InputType) { + self.set_up_modular_multiplication(operand_b); + self.rsa.write_modmulti_start(); + } + /// Reads the result to the given buffer. /// This is a non blocking function that returns without an error if /// operation is completed successfully. pub fn read_results(&mut self, outbuf: &mut T::InputType) { - loop { - if self.rsa.is_idle() { - unsafe { - self.rsa.read_out(outbuf); - } - self.rsa.clear_interrupt(); - break; - } - } + self.rsa.read_results(outbuf); } } @@ -263,6 +334,23 @@ impl<'a, 'd, T: RsaMode + Multi, DM: crate::Mode, const N: usize> RsaMultiplicat where T: RsaMode, { + /// Creates an instance of `RsaMultiplication`. + pub fn new(rsa: &'a mut Rsa<'d, DM>, operand_a: &T::InputType) -> Self { + Self::write_mode(rsa); + rsa.write_operand_a(operand_a); + + Self { + rsa, + phantom: PhantomData, + } + } + + /// Starts the multiplication operation. + pub fn start_multiplication(&mut self, operand_b: &T::InputType) { + self.set_up_multiplication(operand_b); + self.rsa.write_multi_start(); + } + /// Reads the result to the given buffer. /// This is a non blocking function that returns without an error if /// operation is completed successfully. `start_multiplication` must be @@ -271,73 +359,76 @@ where where T: Multi, { - loop { - if self.rsa.is_idle() { - unsafe { - self.rsa.read_out(outbuf); - } - self.rsa.clear_interrupt(); - break; - } - } + self.rsa.read_results(outbuf); } } -#[cfg(feature = "async")] +/// Async functionality pub(crate) mod asynch { use core::task::Poll; use embassy_sync::waitqueue::AtomicWaker; + use portable_atomic::{AtomicBool, Ordering}; use procmacros::handler; - use crate::rsa::{ - Multi, - RsaMode, - RsaModularExponentiation, - RsaModularMultiplication, - RsaMultiplication, + use crate::{ + rsa::{ + Multi, + Rsa, + RsaMode, + RsaModularExponentiation, + RsaModularMultiplication, + RsaMultiplication, + }, + Async, }; static WAKER: AtomicWaker = AtomicWaker::new(); - pub(crate) struct RsaFuture<'d> { - instance: &'d crate::peripherals::RSA, - } + static SIGNALED: AtomicBool = AtomicBool::new(false); - impl<'d> RsaFuture<'d> { - pub async fn new(instance: &'d crate::peripherals::RSA) -> Self { - #[cfg(not(any(esp32, esp32s2, esp32s3)))] - instance.int_ena().modify(|_, w| w.int_ena().set_bit()); + /// `Future` that waits for the RSA operation to complete. + #[must_use = "futures do nothing unless you `.await` or poll them"] + struct RsaFuture<'a, 'd> { + #[cfg_attr(esp32, allow(dead_code))] + instance: &'a Rsa<'d, Async>, + } - #[cfg(any(esp32s2, esp32s3))] - instance - .interrupt_ena() - .modify(|_, w| w.interrupt_ena().set_bit()); + impl<'a, 'd> RsaFuture<'a, 'd> { + fn new(instance: &'a Rsa<'d, Async>) -> Self { + SIGNALED.store(false, Ordering::Relaxed); - #[cfg(esp32)] - instance.interrupt().modify(|_, w| w.interrupt().set_bit()); + cfg_if::cfg_if! { + if #[cfg(esp32)] { + } else if #[cfg(any(esp32s2, esp32s3))] { + instance.rsa.interrupt_ena().write(|w| w.interrupt_ena().set_bit()); + } else { + instance.rsa.int_ena().write(|w| w.int_ena().set_bit()); + } + } Self { instance } } - fn event_bit_is_clear(&self) -> bool { - #[cfg(not(any(esp32, esp32s2, esp32s3)))] - return self.instance.int_ena().read().int_ena().bit_is_clear(); - - #[cfg(any(esp32s2, esp32s3))] - return self - .instance - .interrupt_ena() - .read() - .interrupt_ena() - .bit_is_clear(); + fn is_done(&self) -> bool { + SIGNALED.load(Ordering::Acquire) + } + } - #[cfg(esp32)] - return self.instance.interrupt().read().interrupt().bit_is_clear(); + impl Drop for RsaFuture<'_, '_> { + fn drop(&mut self) { + cfg_if::cfg_if! { + if #[cfg(esp32)] { + } else if #[cfg(any(esp32s2, esp32s3))] { + self.instance.rsa.interrupt_ena().write(|w| w.interrupt_ena().clear_bit()); + } else { + self.instance.rsa.int_ena().write(|w| w.int_ena().clear_bit()); + } + } } } - impl<'d> core::future::Future for RsaFuture<'d> { + impl core::future::Future for RsaFuture<'_, '_> { type Output = (); fn poll( @@ -345,7 +436,7 @@ pub(crate) mod asynch { cx: &mut core::task::Context<'_>, ) -> core::task::Poll { WAKER.register(cx.waker()); - if self.event_bit_is_clear() { + if self.is_done() { Poll::Ready(()) } else { Poll::Pending @@ -353,57 +444,59 @@ pub(crate) mod asynch { } } - impl<'a, 'd, T: RsaMode, const N: usize> RsaModularExponentiation<'a, 'd, T, crate::Async> + impl<'a, 'd, T: RsaMode, const N: usize> RsaModularExponentiation<'a, 'd, T, Async> where T: RsaMode, { + /// Asynchronously performs an RSA modular exponentiation operation. pub async fn exponentiation( &mut self, base: &T::InputType, r: &T::InputType, outbuf: &mut T::InputType, ) { - self.start_exponentiation(base, r); - RsaFuture::new(&self.rsa.rsa).await; - self.read_results(outbuf); + self.set_up_exponentiation(base, r); + let fut = RsaFuture::new(self.rsa); + self.rsa.write_modexp_start(); + fut.await; + self.rsa.read_out(outbuf); } } - impl<'a, 'd, T: RsaMode, const N: usize> RsaModularMultiplication<'a, 'd, T, crate::Async> + impl<'a, 'd, T: RsaMode, const N: usize> RsaModularMultiplication<'a, 'd, T, Async> where T: RsaMode, { - #[cfg(not(esp32))] - pub async fn modular_multiplication( - &mut self, - r: &T::InputType, - outbuf: &mut T::InputType, - ) { - self.start_modular_multiplication(r); - RsaFuture::new(&self.rsa.rsa).await; - self.read_results(outbuf); - } - - #[cfg(esp32)] + /// Asynchronously performs an RSA modular multiplication operation. pub async fn modular_multiplication( &mut self, - operand_a: &T::InputType, operand_b: &T::InputType, - r: &T::InputType, outbuf: &mut T::InputType, ) { - self.start_step1(operand_a, r); - self.start_step2(operand_b); - RsaFuture::new(&self.rsa.rsa).await; - self.read_results(outbuf); + cfg_if::cfg_if! { + if #[cfg(esp32)] { + let fut = RsaFuture::new(self.rsa); + self.rsa.write_multi_start(); + fut.await; + + self.rsa.write_operand_a(operand_b); + } else { + self.set_up_modular_multiplication(operand_b); + } + } + + let fut = RsaFuture::new(self.rsa); + self.rsa.write_modmulti_start(); + fut.await; + self.rsa.read_out(outbuf); } } - impl<'a, 'd, T: RsaMode + Multi, const N: usize> RsaMultiplication<'a, 'd, T, crate::Async> + impl<'a, 'd, T: RsaMode + Multi, const N: usize> RsaMultiplication<'a, 'd, T, Async> where T: RsaMode, { - #[cfg(not(esp32))] + /// Asynchronously performs an RSA multiplication operation. pub async fn multiplication<'b, const O: usize>( &mut self, operand_b: &T::InputType, @@ -411,42 +504,28 @@ pub(crate) mod asynch { ) where T: Multi, { - self.start_multiplication(operand_b); - RsaFuture::new(&self.rsa.rsa).await; - self.read_results(outbuf); - } - - #[cfg(esp32)] - pub async fn multiplication<'b, const O: usize>( - &mut self, - operand_a: &T::InputType, - operand_b: &T::InputType, - outbuf: &mut T::OutputType, - ) where - T: Multi, - { - self.start_multiplication(operand_a, operand_b); - RsaFuture::new(&self.rsa.rsa).await; - self.read_results(outbuf); + self.set_up_multiplication(operand_b); + let fut = RsaFuture::new(self.rsa); + self.rsa.write_multi_start(); + fut.await; + self.rsa.read_out(outbuf); } } #[handler] + /// Interrupt handler for RSA. pub(super) fn rsa_interrupt_handler() { - #[cfg(not(any(esp32, esp32s2, esp32s3)))] - unsafe { &*crate::peripherals::RSA::ptr() } - .int_ena() - .modify(|_, w| w.int_ena().clear_bit()); - - #[cfg(esp32)] - unsafe { &*crate::peripherals::RSA::ptr() } - .interrupt() - .modify(|_, w| w.interrupt().clear_bit()); - - #[cfg(any(esp32s2, esp32s3))] - unsafe { &*crate::peripherals::RSA::ptr() } - .interrupt_ena() - .modify(|_, w| w.interrupt_ena().clear_bit()); + let rsa = unsafe { &*crate::peripherals::RSA::ptr() }; + SIGNALED.store(true, Ordering::Release); + cfg_if::cfg_if! { + if #[cfg(esp32)] { + rsa.interrupt().write(|w| w.interrupt().set_bit()); + } else if #[cfg(any(esp32s2, esp32s3))] { + rsa.clear_interrupt().write(|w| w.clear_interrupt().set_bit()); + } else { + rsa.int_clr().write(|w| w.clear_interrupt().set_bit()); + } + } WAKER.wake(); } diff --git a/esp-hal/src/rtc_cntl/mod.rs b/esp-hal/src/rtc_cntl/mod.rs index 22a99efac97..a44a330375d 100644 --- a/esp-hal/src/rtc_cntl/mod.rs +++ b/esp-hal/src/rtc_cntl/mod.rs @@ -1,21 +1,23 @@ -//! # Real-Time Clock Control and Low-power Management (RTC_CNTL) +//! # Real-Time Control and Low-power Management (RTC_CNTL) //! //! ## Overview -//! The RTC_CNTL peripheral is responsible for managing the real-time clock and -//! low-power modes on the chip. +//! +//! The RTC_CNTL peripheral is responsible for managing the low-power modes on +//! the chip. //! //! ## Configuration -//! It also includes the necessary configurations and constants for clock +//! +//! It also includes the necessary configurations and constants for clock //! sources and low-power management. The driver provides the following features //! and functionalities: +//! //! * Clock Configuration //! * Calibration //! * Low-Power Management -//! * Real-Time Clock //! * Handling Watchdog Timers //! -//! ## Examples -//! ### Print Time in Milliseconds From the RTC Timer +//! ## Example +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use core::cell::RefCell; @@ -27,31 +29,26 @@ //! # use crate::esp_hal::prelude::_fugit_ExtU64; //! # use crate::esp_hal::InterruptConfigurable; //! static RWDT: Mutex>> = Mutex::new(RefCell::new(None)); -//! let mut delay = Delay::new(&clocks); +//! let mut delay = Delay::new(); //! //! let mut rtc = Rtc::new(peripherals.LPWR); +//! //! rtc.set_interrupt_handler(interrupt_handler); //! rtc.rwdt.set_timeout(2000.millis()); //! rtc.rwdt.listen(); //! //! critical_section::with(|cs| RWDT.borrow_ref_mut(cs).replace(rtc.rwdt)); -//! -//! -//! loop {} //! # } //! //! // Where the `LP_WDT` interrupt handler is defined as: -//! // Handle the corresponding interrupt //! # use core::cell::RefCell; //! //! # use critical_section::Mutex; -//! # use esp_hal::prelude::handler; -//! # use esp_hal::interrupt::InterruptHandler; -//! # use esp_hal::interrupt; -//! # use esp_hal::interrupt::Priority; -//! # use crate::esp_hal::prelude::_fugit_ExtU64; //! # use esp_hal::rtc_cntl::Rwdt; +//! //! static RWDT: Mutex>> = Mutex::new(RefCell::new(None)); +//! +//! // Handle the corresponding interrupt //! #[handler] //! fn interrupt_handler() { //! critical_section::with(|cs| { @@ -82,7 +79,7 @@ use crate::efuse::Efuse; use crate::peripherals::{LPWR, TIMG0}; #[cfg(any(esp32c6, esp32h2))] use crate::peripherals::{LP_TIMER, LP_WDT}; -#[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] +#[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] use crate::rtc_cntl::sleep::{RtcSleepConfig, WakeSource, WakeTriggers}; use crate::{ clock::Clock, @@ -94,7 +91,7 @@ use crate::{ InterruptConfigurable, }; // only include sleep where its been implemented -#[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] +#[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] pub mod sleep; #[cfg_attr(esp32, path = "rtc/esp32.rs")] @@ -184,8 +181,10 @@ pub(crate) enum RtcCalSel { /// Low-power Management pub struct Rtc<'d> { _inner: PeripheralRef<'d, crate::peripherals::LPWR>, + /// Reset Watchdog Timer. pub rwdt: Rwdt, #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] + /// Super Watchdog pub swd: Swd, } @@ -204,7 +203,7 @@ impl<'d> Rtc<'d> { swd: Swd::new(), }; - #[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] + #[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] RtcSleepConfig::base_settings(&this); this @@ -265,7 +264,7 @@ impl<'d> Rtc<'d> { } /// Enter deep sleep and wake with the provided `wake_sources`. - #[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] + #[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] pub fn sleep_deep(&mut self, wake_sources: &[&dyn WakeSource]) -> ! { let config = RtcSleepConfig::deep(); self.sleep(&config, wake_sources); @@ -273,7 +272,7 @@ impl<'d> Rtc<'d> { } /// Enter light sleep and wake with the provided `wake_sources`. - #[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] + #[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] pub fn sleep_light(&mut self, wake_sources: &[&dyn WakeSource]) { let config = RtcSleepConfig::default(); self.sleep(&config, wake_sources); @@ -281,7 +280,7 @@ impl<'d> Rtc<'d> { /// Enter sleep with the provided `config` and wake with the provided /// `wake_sources`. - #[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] + #[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] pub fn sleep(&mut self, config: &RtcSleepConfig, wake_sources: &[&dyn WakeSource]) { let mut config = *config; let mut wakeup_triggers = WakeTriggers::default(); @@ -855,14 +854,12 @@ impl Rwdt { } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::watchdog::WatchdogDisable for Rwdt { fn disable(&mut self) { self.disable(); } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::watchdog::WatchdogEnable for Rwdt { type Time = MicrosDurationU64; @@ -874,7 +871,6 @@ impl embedded_hal_02::watchdog::WatchdogEnable for Rwdt { } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::watchdog::Watchdog for Rwdt { fn feed(&mut self) { self.feed(); @@ -936,10 +932,7 @@ impl Default for Swd { } } -#[cfg(all( - any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3), - feature = "embedded-hal-02" -))] +#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] impl embedded_hal_02::watchdog::WatchdogDisable for Swd { fn disable(&mut self) { self.disable(); diff --git a/esp-hal/src/rtc_cntl/rtc/esp32.rs b/esp-hal/src/rtc_cntl/rtc/esp32.rs index 59fe4a85980..77a900dbaee 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32.rs @@ -34,6 +34,7 @@ pub(crate) fn configure_clock() { // Chip Reset: Reset the whole chip, including the analog part #[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)] +/// SOC Reset Reason. pub enum SocResetReason { /// Power on reset ChipPowerOn = 0x01, diff --git a/esp-hal/src/rtc_cntl/rtc/esp32c2.rs b/esp-hal/src/rtc_cntl/rtc/esp32c2.rs index ab9c7efc870..75eba0d45c2 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32c2.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32c2.rs @@ -201,6 +201,7 @@ fn rtc_sleep_pu() { // Chip Reset: Reset the whole chip, including the analog part #[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)] +/// SOC Reset Reason. pub enum SocResetReason { /// Power on reset ChipPowerOn = 0x01, diff --git a/esp-hal/src/rtc_cntl/rtc/esp32c3.rs b/esp-hal/src/rtc_cntl/rtc/esp32c3.rs index 0c795e9bab4..8fc81c9e279 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32c3.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32c3.rs @@ -259,6 +259,7 @@ fn rtc_sleep_pu() { // System Reset: Reset the whole digital system, including RTC sub-system // Chip Reset: Reset the whole chip, including the analog part +/// SOC Reset Reason. #[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)] pub enum SocResetReason { /// Power on reset diff --git a/esp-hal/src/rtc_cntl/rtc/esp32c6.rs b/esp-hal/src/rtc_cntl/rtc/esp32c6.rs index 2f888ed299b..456701a936c 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32c6.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32c6.rs @@ -1305,6 +1305,7 @@ fn modem_clk_domain_active_state_icg_map_preinit() { // System Reset: Reset the whole digital system, including RTC sub-system // Chip Reset: Reset the whole chip, including the analog part +/// SOC Reset Reason. #[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)] pub enum SocResetReason { /// Power on reset diff --git a/esp-hal/src/rtc_cntl/rtc/esp32h2.rs b/esp-hal/src/rtc_cntl/rtc/esp32h2.rs index 22f6989f2a8..67bf9c39387 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32h2.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32h2.rs @@ -149,6 +149,7 @@ pub(crate) fn configure_clock() { // System Reset: Reset the whole digital system, including RTC sub-system // Chip Reset: Reset the whole chip, including the analog part +/// SOC Reset Reason. #[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)] pub enum SocResetReason { /// Power on reset diff --git a/esp-hal/src/rtc_cntl/rtc/esp32s2.rs b/esp-hal/src/rtc_cntl/rtc/esp32s2.rs index e27462d8376..51250e35e05 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32s2.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32s2.rs @@ -39,6 +39,7 @@ pub(crate) fn configure_clock() { // System Reset: Reset the whole digital system, including RTC sub-system // Chip Reset: Reset the whole chip, including the analog part +/// SOC Reset Reason. #[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)] pub enum SocResetReason { /// Power on reset diff --git a/esp-hal/src/rtc_cntl/rtc/esp32s3.rs b/esp-hal/src/rtc_cntl/rtc/esp32s3.rs index 53364bafc09..35c12f8bde3 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32s3.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32s3.rs @@ -39,6 +39,7 @@ pub(crate) fn configure_clock() { // System Reset: Reset the whole digital system, including RTC sub-system // Chip Reset: Reset the whole chip, including the analog part +/// SOC Reset Reason. #[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)] pub enum SocResetReason { /// Power on reset diff --git a/esp-hal/src/rtc_cntl/sleep/esp32.rs b/esp-hal/src/rtc_cntl/sleep/esp32.rs index cfc70e67e2c..77435035172 100644 --- a/esp-hal/src/rtc_cntl/sleep/esp32.rs +++ b/esp-hal/src/rtc_cntl/sleep/esp32.rs @@ -7,38 +7,66 @@ use crate::{ // Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP, // RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values. // Valid if RTC_CNTL_DBG_ATTEN is 0. +/// RTC digital bias setting corresponding to 0.90V. pub const RTC_CNTL_DBIAS_0V90: u8 = 0; +/// RTC digital bias setting corresponding to 0.95V. pub const RTC_CNTL_DBIAS_0V95: u8 = 1; +/// RTC digital bias setting corresponding to 1.00V. pub const RTC_CNTL_DBIAS_1V00: u8 = 2; +/// RTC digital bias setting corresponding to 1.05V. pub const RTC_CNTL_DBIAS_1V05: u8 = 3; +/// RTC digital bias setting corresponding to 1.10V. pub const RTC_CNTL_DBIAS_1V10: u8 = 4; +/// RTC digital bias setting corresponding to 1.15V. pub const RTC_CNTL_DBIAS_1V15: u8 = 5; +/// RTC digital bias setting corresponding to 1.20V. pub const RTC_CNTL_DBIAS_1V20: u8 = 6; +/// RTC digital bias setting corresponding to 1.25V. pub const RTC_CNTL_DBIAS_1V25: u8 = 7; + // Various delays to be programmed into power control state machines +/// Time (in microseconds) for waiting the XTL buffer to stabilize during sleep. pub const RTC_CNTL_XTL_BUF_WAIT_SLP_US: u32 = 1000; +/// Cycles to wait for PLL buffer stabilization. pub const RTC_CNTL_PLL_BUF_WAIT_SLP_CYCLES: u8 = 1; +/// Cycles to wait for the 8MHz clock to stabilize. pub const RTC_CNTL_CK8M_WAIT_SLP_CYCLES: u8 = 4; +/// Delay in cycles for wakeup signal to be applied. pub const RTC_CNTL_WAKEUP_DELAY_CYCLES: u8 = 7; +/// Power-up cycles for other blocks. pub const RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES: u8 = 1; +/// Wait cycles for other blocks. pub const RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES: u16 = 1; +/// Minimum sleep value (in cycles). pub const RTC_CNTL_MIN_SLP_VAL_MIN: u8 = 128; +/// Default debug attenuation value. pub const RTC_CNTL_DBG_ATTEN_DEFAULT: u8 = 3; +/// Power-up cycles for RTC memory. pub const RTC_MEM_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES; +/// Wait cycles for RTC memory. pub const RTC_MEM_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES; +/// Power-up cycles for ROM and RAM. pub const ROM_RAM_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES; +/// Wait cycles for ROM and RAM. pub const ROM_RAM_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES; +/// Power-up cycles for Wi-Fi. pub const WIFI_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES; +/// Wait cycles for Wi-Fi. pub const WIFI_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES; +/// Power-up cycles for RTC components. pub const RTC_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES; +/// Wait cycles for RTC components. pub const RTC_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES; +/// Power-up cycles for the digital wrap components. pub const DG_WRAP_POWERUP_CYCLES: u8 = RTC_CNTL_OTHER_BLOCKS_POWERUP_CYCLES; +/// Wait cycles for the digital wrap components. pub const DG_WRAP_WAIT_CYCLES: u16 = RTC_CNTL_OTHER_BLOCKS_WAIT_CYCLES; +/// Default wait cycles for the 8MHz clock. pub const RTC_CNTL_CK8M_WAIT_DEFAULT: u8 = 20; +/// Default wait cycles to enable the 8MHz clock. pub const RTC_CK8M_ENABLE_WAIT_DEFAULT: u8 = 5; - impl WakeSource for TimerWakeupSource { fn apply( &self, @@ -162,6 +190,7 @@ impl Drop for Ext1WakeupSource<'_, '_> { bitfield::bitfield! { #[derive(Clone, Copy)] + /// Configuration for the RTC sleep behavior. pub struct RtcSleepConfig(u32); impl Debug; /// force normal voltage in sleep mode (digital domain memory) @@ -221,6 +250,7 @@ impl Default for RtcSleepConfig { } impl RtcSleepConfig { + /// Configures the RTC for deep sleep mode. pub fn deep() -> Self { let mut cfg = Self::default(); cfg.set_deep_slp(true); diff --git a/esp-hal/src/rtc_cntl/sleep/esp32c2.rs b/esp-hal/src/rtc_cntl/sleep/esp32c2.rs new file mode 100644 index 00000000000..c0fe69a5094 --- /dev/null +++ b/esp-hal/src/rtc_cntl/sleep/esp32c2.rs @@ -0,0 +1,834 @@ +use super::{TimerWakeupSource, WakeSource, WakeTriggers, WakeupLevel}; +use crate::{ + gpio::{RtcFunction, RtcPinWithResistors}, + regi2c_write_mask, + rtc_cntl::{sleep::RtcioWakeupSource, Clock, Rtc, RtcClock}, +}; + +const I2C_DIG_REG: u32 = 0x6D; +const I2C_DIG_REG_HOSTID: u32 = 0; + +const I2C_DIG_REG_EXT_RTC_DREG: u32 = 4; +const I2C_DIG_REG_EXT_RTC_DREG_MSB: u32 = 4; +const I2C_DIG_REG_EXT_RTC_DREG_LSB: u32 = 0; + +const I2C_DIG_REG_EXT_RTC_DREG_SLEEP: u32 = 5; +const I2C_DIG_REG_EXT_RTC_DREG_SLEEP_MSB: u32 = 4; +const I2C_DIG_REG_EXT_RTC_DREG_SLEEP_LSB: u32 = 0; + +const I2C_DIG_REG_EXT_DIG_DREG: u32 = 6; +const I2C_DIG_REG_EXT_DIG_DREG_MSB: u32 = 4; +const I2C_DIG_REG_EXT_DIG_DREG_LSB: u32 = 0; + +const I2C_DIG_REG_EXT_DIG_DREG_SLEEP: u32 = 7; +const I2C_DIG_REG_EXT_DIG_DREG_SLEEP_MSB: u32 = 4; +const I2C_DIG_REG_EXT_DIG_DREG_SLEEP_LSB: u32 = 0; + +const I2C_DIG_REG_XPD_RTC_REG: u32 = 13; +const I2C_DIG_REG_XPD_RTC_REG_MSB: u32 = 2; +const I2C_DIG_REG_XPD_RTC_REG_LSB: u32 = 2; + +const I2C_DIG_REG_XPD_DIG_REG: u32 = 13; +const I2C_DIG_REG_XPD_DIG_REG_MSB: u32 = 3; +const I2C_DIG_REG_XPD_DIG_REG_LSB: u32 = 3; + +const I2C_ULP_IR_FORCE_XPD_CK: u8 = 0; +const I2C_ULP_IR_FORCE_XPD_CK_MSB: u8 = 2; +const I2C_ULP_IR_FORCE_XPD_CK_LSB: u8 = 2; + +const I2C_ULP: u8 = 0x61; +const I2C_ULP_HOSTID: u8 = 0; + +// Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP, +// RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values. +// Valid if RTC_CNTL_DBG_ATTEN is 0. +/// Digital bias voltage level of 0.90V. +pub const RTC_CNTL_DBIAS_0V90: u32 = 13; +/// Digital bias voltage level of 0.95V. +pub const RTC_CNTL_DBIAS_0V95: u32 = 16; +/// Digital bias voltage level of 1.00V. +pub const RTC_CNTL_DBIAS_1V00: u32 = 18; +/// Digital bias voltage level of 1.05V. +pub const RTC_CNTL_DBIAS_1V05: u32 = 20; +/// Digital bias voltage level of 1.10V. +pub const RTC_CNTL_DBIAS_1V10: u32 = 23; +/// Digital bias voltage level of 1.15V. +pub const RTC_CNTL_DBIAS_1V15: u32 = 25; +/// Digital bias voltage level of 1.20V. +pub const RTC_CNTL_DBIAS_1V20: u32 = 28; +/// Digital bias voltage level of 1.25V. +pub const RTC_CNTL_DBIAS_1V25: u32 = 30; +/// Digital bias voltage level of approximately 1.34V. +pub const RTC_CNTL_DBIAS_1V30: u32 = 31; + +/// Default attenuation setting during light sleep, with a voltage drop. +pub const RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_DEFAULT: u8 = 5; +/// No attenuation (no voltage drop) during light sleep. +pub const RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_NODROP: u8 = 0; +/// Default attenuation setting during deep sleep, with maximum voltage drop. +pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT: u8 = 15; +/// No attenuation (no voltage drop) during deep sleep. +pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_NODROP: u8 = 0; + +/// Default bias setting during sleep mode. +pub const RTC_CNTL_BIASSLP_SLEEP_DEFAULT: u8 = 1; +/// Keeps the bias for ultra-low power sleep mode always on. +pub const RTC_CNTL_BIASSLP_SLEEP_ON: u8 = 0; + +/// Default power-down current setting during sleep mode. +pub const RTC_CNTL_PD_CUR_SLEEP_DEFAULT: u8 = 1; +/// Keeps the power-down current setting for sleep mode always on. +pub const RTC_CNTL_PD_CUR_SLEEP_ON: u8 = 0; + +/// Default driver bias setting for the digital domain during sleep mode. +pub const RTC_CNTL_DG_VDD_DRV_B_SLP_DEFAULT: u8 = 254; + +/// Default debug attenuation setting for the monitor mode. +pub const RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT: u8 = 0; +/// Default bias setting for sleep mode in the monitor mode. +pub const RTC_CNTL_BIASSLP_MONITOR_DEFAULT: bool = false; +/// Default power-down current setting for the monitor mode. +pub const RTC_CNTL_PD_CUR_MONITOR_DEFAULT: bool = false; + +/// Default number of cycles to wait for the PLL buffer to stabilize. +pub const RTC_CNTL_PLL_BUF_WAIT_DEFAULT: u8 = 20; +/// Default number of cycles to wait for the XTL buffer to stabilize. +pub const RTC_CNTL_XTL_BUF_WAIT_DEFAULT: u8 = 100; +/// Default number of cycles to wait for the internal 8MHz clock to stabilize. +pub const RTC_CNTL_CK8M_WAIT_DEFAULT: u8 = 20; +/// Default number of cycles required to enable the internal 8MHz clock. +pub const RTC_CK8M_ENABLE_WAIT_DEFAULT: u8 = 5; + +/// Minimum number of cycles for sleep duration. +pub const RTC_CNTL_MIN_SLP_VAL_MIN: u8 = 2; + +/// Power-up cycles for other hardware blocks. +pub const OTHER_BLOCKS_POWERUP: u8 = 1; +/// Wait cycles for other hardware blocks to stabilize. +pub const OTHER_BLOCKS_WAIT: u16 = 1; + +/// Disables GPIO interrupt. +pub const GPIO_INTR_DISABLE: u8 = 0; +/// Sets GPIO interrupt to trigger on a low level signal. +pub const GPIO_INTR_LOW_LEVEL: u8 = 4; +/// Sets GPIO interrupt to trigger on a high level signal. +pub const GPIO_INTR_HIGH_LEVEL: u8 = 5; + +/// Specifies the function configuration for GPIO pins. +pub const PIN_FUNC_GPIO: u8 = 1; +/// Index for signaling GPIO output. +pub const SIG_GPIO_OUT_IDX: u32 = 128; +/// Maximum number of GPIO pins supported. +pub const GPIO_NUM_MAX: usize = 22; + +impl WakeSource for TimerWakeupSource { + fn apply( + &self, + rtc: &Rtc<'_>, + triggers: &mut WakeTriggers, + _sleep_config: &mut RtcSleepConfig, + ) { + triggers.set_timer(true); + let rtc_cntl = unsafe { &*esp32c2::RTC_CNTL::ptr() }; + let clock_freq = RtcClock::get_slow_freq(); + // TODO: maybe add sleep time adjustlemnt like idf + // TODO: maybe add check to prevent overflow? + let clock_hz = clock_freq.frequency().to_Hz() as u64; + let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64; + // "alarm" time in slow rtc ticks + let now = rtc.get_time_raw(); + let time_in_ticks = now + ticks; + unsafe { + rtc_cntl + .slp_timer0() + .write(|w| w.slp_val_lo().bits((time_in_ticks & 0xffffffff) as u32)); + + rtc_cntl + .int_clr() + .write(|w| w.main_timer().clear_bit_by_one()); + + rtc_cntl.slp_timer1().write(|w| { + w.slp_val_hi() + .bits(((time_in_ticks >> 32) & 0xffff) as u16) + .main_timer_alarm_en() + .set_bit() + }); + } + } +} + +impl<'a, 'b> RtcioWakeupSource<'a, 'b> { + fn apply_pin(&self, pin: &mut dyn RtcPinWithResistors, level: WakeupLevel) { + // The pullup/pulldown part is like in gpio_deep_sleep_wakeup_prepare + let level = match level { + WakeupLevel::High => { + pin.rtcio_pullup(false); + pin.rtcio_pulldown(true); + GPIO_INTR_HIGH_LEVEL + } + WakeupLevel::Low => { + pin.rtcio_pullup(true); + pin.rtcio_pulldown(false); + GPIO_INTR_LOW_LEVEL + } + }; + pin.rtcio_pad_hold(true); + + // apply_wakeup does the same as idf's esp_deep_sleep_enable_gpio_wakeup + unsafe { + pin.apply_wakeup(true, level); + } + } +} + +fn isolate_digital_gpio() { + // like esp_sleep_isolate_digital_gpio + let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() }; + let io_mux = unsafe { &*crate::peripherals::IO_MUX::ptr() }; + let gpio = unsafe { &*crate::peripherals::GPIO::ptr() }; + + let dig_iso = &rtc_cntl.dig_iso().read(); + let deep_sleep_hold_is_en = + !dig_iso.dg_pad_force_unhold().bit() && dig_iso.dg_pad_autohold_en().bit(); + if !deep_sleep_hold_is_en { + return; + } + + // TODO: assert that the task stack is not in external ram + + for pin_num in 0..GPIO_NUM_MAX { + let pin_hold = rtc_cntl.dig_pad_hold().read().bits() & (1 << pin_num) != 0; + if !pin_hold { + // input disable, like gpio_ll_input_disable + io_mux.gpio(pin_num).modify(|_, w| w.fun_ie().clear_bit()); + // output disable, like gpio_ll_output_disable + unsafe { + gpio.func_out_sel_cfg(pin_num) + .modify(|_, w| w.bits(SIG_GPIO_OUT_IDX)); + } + + // disable pull-up and pull-down + io_mux.gpio(pin_num).modify(|_, w| w.fun_wpu().clear_bit()); + io_mux.gpio(pin_num).modify(|_, w| w.fun_wpd().clear_bit()); + + // make pad work as gpio (otherwise, deep_sleep bottom current will rise) + io_mux + .gpio(pin_num) + .modify(|_, w| unsafe { w.mcu_sel().bits(RtcFunction::Digital as u8) }); + } + } +} + +impl WakeSource for RtcioWakeupSource<'_, '_> { + fn apply( + &self, + _rtc: &Rtc<'_>, + triggers: &mut WakeTriggers, + sleep_config: &mut RtcSleepConfig, + ) { + let mut pins = self.pins.borrow_mut(); + + if pins.is_empty() { + return; + } + + triggers.set_gpio(true); + + // If deep sleep is enabled, esp_start_sleep calls + // gpio_deep_sleep_wakeup_prepare which sets these pullup and + // pulldown values. But later in esp_start_sleep it calls + // esp_sleep_isolate_digital_gpio, which disables the pullup and pulldown (but + // only if it isn't held). + // But it looks like gpio_deep_sleep_wakeup_prepare enables hold for all pins + // in the wakeup mask. + // + // So: all pins in the wake mask should get this treatment here, and all pins + // not in the wake mask should get + // - pullup and pulldowns disabled + // - input and output disabled, and + // - their func should get set to GPIO. + // But this last block of things gets skipped if hold is disabled globally (see + // gpio_ll_deep_sleep_hold_is_en) + + let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::PTR }; + + rtc_cntl + .cntl_gpio_wakeup() + .modify(|_, w| w.gpio_pin_clk_gate().set_bit()); + + rtc_cntl + .ext_wakeup_conf() + .modify(|_, w| w.gpio_wakeup_filter().set_bit()); + + if sleep_config.deep_slp() { + for (pin, level) in pins.iter_mut() { + self.apply_pin(*pin, *level); + } + + isolate_digital_gpio(); + } + + // like rtc_cntl_ll_gpio_clear_wakeup_status, as called from + // gpio_deep_sleep_wakeup_prepare + rtc_cntl + .cntl_gpio_wakeup() + .modify(|_, w| w.gpio_wakeup_status_clr().set_bit()); + rtc_cntl + .cntl_gpio_wakeup() + .modify(|_, w| w.gpio_wakeup_status_clr().clear_bit()); + } +} + +// impl Drop for RtcioWakeupSource<'_, '_> { +// fn drop(&mut self) { +// should we have saved the pin configuration first? +// set pin back to IO_MUX (input_enable and func have no effect when pin is sent +// to IO_MUX) +// let mut pins = self.pins.borrow_mut(); +// for (pin, _level) in pins.iter_mut() { +// pin.rtc_set_config(true, false, RtcFunction::Rtc); +// } +// } +// } + +bitfield::bitfield! { + #[derive(Clone, Copy)] + /// RTC Configuration. + pub struct RtcConfig(u32); + impl Debug; + /// Number of rtc_fast_clk cycles to wait for 8M clock to be ready + pub u8, ck8m_wait, set_ck8m_wait: 7, 0; + /// Number of rtc_fast_clk cycles to wait for XTAL clock to be ready + pub u8, xtal_wait, set_xtal_wait: 15, 8; + /// Number of rtc_fast_clk cycles to wait for PLL clock to be ready + pub u8, pll_wait, set_pll_wait: 23, 16; + /// Perform clock control related initialization. + pub clkctl_init, set_clkctl_init: 24; + /// Perform power control related initialization. + pub pwrctl_init, set_pwrctl_init: 25; + /// Force power down RTC_DBOOST + pub rtc_dboost_fpd, set_rtc_dboost_fpd: 26; + /// Keep the XTAL oscillator powered up in sleep. + pub xtal_fpu, set_xtal_fpu: 27; + /// Keep the BBPLL oscillator powered up in sleep. + pub bbpll_fpu, set_bbpll_fpu: 28; + /// Enable clock gating when the CPU is in wait-for-interrupt state. + pub cpu_waiti_clk_gate, set_cpu_waiti_clk_gate: 29; + /// Calibrate Ocode to make bandgap voltage more precise. + pub cali_ocode, set_cali_ocode: 30; +} + +impl Default for RtcConfig { + fn default() -> Self { + let mut cfg = Self(Default::default()); + cfg.set_ck8m_wait(RTC_CNTL_CK8M_WAIT_DEFAULT); + cfg.set_xtal_wait(RTC_CNTL_XTL_BUF_WAIT_DEFAULT); + cfg.set_pll_wait(RTC_CNTL_PLL_BUF_WAIT_DEFAULT); + cfg.set_clkctl_init(true); + cfg.set_pwrctl_init(true); + cfg.set_rtc_dboost_fpd(true); + cfg.set_cpu_waiti_clk_gate(true); + cfg + } +} + +bitfield::bitfield! { + #[derive(Clone, Copy)] + /// Configuration for RTC initialization. + pub struct RtcInitConfig(u128); + impl Debug; + /// Number of cycles required to power up WiFi + pub u8, wifi_powerup_cycles, set_wifi_powerup_cycles: 6, 0; + /// Number of wait cycles for WiFi to stabilize + pub u16, wifi_wait_cycles, set_wifi_wait_cycles: 15, 7; + /// Number of cycles required to power up Bluetooth + pub u8, bt_powerup_cycles, set_bt_powerup_cycles: 22, 16; + /// Number of wait cycles for Bluetooth to stabilize + pub u16, bt_wait_cycles, set_bt_wait_cycles: 31, 23; + /// Number of cycles required to power up the top CPU + pub u8, cpu_top_powerup_cycles, set_cpu_top_powerup_cycles: 38, 32; + /// Number of wait cycles for the top CPU to stabilize + pub u16, cpu_top_wait_cycles, set_cpu_top_wait_cycles: 47, 39; + /// Number of cycles required to power up the digital wrapper + pub u8, dg_wrap_powerup_cycles, set_dg_wrap_powerup_cycles: 54, 48; + /// Number of wait cycles for the digital wrapper to stabilize + pub u16, dg_wrap_wait_cycles, set_dg_wrap_wait_cycles: 63, 55; + /// Number of cycles required to power up the digital peripherals + pub u8, dg_peri_powerup_cycles, set_dg_peri_powerup_cycles: 70, 64; + /// Number of wait cycles for the digital peripherals to stabilize + pub u16, dg_peri_wait_cycles, set_dg_peri_wait_cycles: 79, 71; +} + +impl Default for RtcInitConfig { + fn default() -> Self { + let mut cfg = Self(Default::default()); + cfg.set_wifi_powerup_cycles(OTHER_BLOCKS_POWERUP); + cfg.set_bt_powerup_cycles(OTHER_BLOCKS_POWERUP); + cfg.set_cpu_top_powerup_cycles(OTHER_BLOCKS_POWERUP); + cfg.set_dg_wrap_powerup_cycles(OTHER_BLOCKS_POWERUP); + cfg.set_dg_peri_powerup_cycles(OTHER_BLOCKS_POWERUP); + cfg.set_wifi_wait_cycles(OTHER_BLOCKS_WAIT); + cfg.set_bt_wait_cycles(OTHER_BLOCKS_WAIT); + cfg.set_cpu_top_wait_cycles(OTHER_BLOCKS_WAIT); + cfg.set_dg_wrap_wait_cycles(OTHER_BLOCKS_WAIT); + cfg.set_dg_peri_wait_cycles(OTHER_BLOCKS_WAIT); + cfg + } +} + +bitfield::bitfield! { + #[derive(Clone, Copy)] + /// Configuration for RTC sleep mode. + pub struct RtcSleepConfig(u64); + impl Debug; + /// force normal voltage in sleep mode (digital domain memory) + pub lslp_mem_inf_fpu, set_lslp_mem_inf_fpu: 0; + /// keep low voltage in sleep mode (even if ULP/touch is used) + pub rtc_mem_inf_follow_cpu, set_rtc_mem_inf_follow_cpu: 1; + /// power down RTC fast memory + pub rtc_fastmem_pd_en, set_rtc_fastmem_pd_en: 2; + /// power down RTC slow memory + pub rtc_slowmem_pd_en, set_rtc_slowmem_pd_en: 3; + /// power down RTC peripherals + pub rtc_peri_pd_en, set_rtc_peri_pd_en: 4; + /// power down WiFi + pub wifi_pd_en, set_wifi_pd_en: 5; + /// power down BT + pub bt_pd_en, set_bt_pd_en: 6; + /// power down CPU, but not restart when lightsleep. + pub cpu_pd_en, set_cpu_pd_en: 7; + /// Power down Internal 8M oscillator + pub int_8m_pd_en, set_int_8m_pd_en: 8; + /// power down digital peripherals + pub dig_peri_pd_en, set_dig_peri_pd_en: 9; + /// power down digital domain + pub deep_slp, set_deep_slp: 10; + /// enable WDT flashboot mode + pub wdt_flashboot_mod_en, set_wdt_flashboot_mod_en: 11; + /// set bias for digital domain, in sleep mode + pub u32, dig_dbias_slp, set_dig_dbias_slp: 16, 12; + /// set bias for RTC domain, in sleep mode + pub u32, rtc_dbias_slp, set_rtc_dbias_slp: 21, 17; + /// voltage parameter, in sleep mode + pub u8, dbg_atten_slp, set_dbg_atten_slp: 25, 22; + /// circuit control parameter, in monitor mode + pub bias_sleep_monitor, set_bias_sleep_monitor: 26; + /// circuit control parameter, in sleep mode + pub bias_sleep_slp, set_bias_sleep_slp: 27; + /// circuit control parameter, in monitor mode + pub pd_cur_slp, set_pd_cur_slp: 28; + /// power down VDDSDIO regulator + pub vddsdio_pd_en, set_vddsdio_pd_en: 29; + /// keep main XTAL powered up in sleep + pub xtal_fpu, set_xtal_fpu: 30; + /// keep rtc regulator powered up in sleep + pub rtc_regulator_fpu, set_rtc_regulator_fpu: 31; + /// enable deep sleep reject + pub deep_slp_reject, set_deep_slp_reject: 32; + /// enable light sleep reject + pub light_slp_reject, set_light_slp_reject: 33; +} + +impl Default for RtcSleepConfig { + fn default() -> Self { + let mut cfg = Self(Default::default()); + cfg.set_deep_slp_reject(true); + cfg.set_light_slp_reject(true); + cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10); + cfg.set_dig_dbias_slp(RTC_CNTL_DBIAS_1V10); + cfg + } +} + +const DR_REG_NRX_BASE: u32 = 0x6001CC00; +const DR_REG_FE_BASE: u32 = 0x60006000; +const DR_REG_FE2_BASE: u32 = 0x60005000; + +const NRXPD_CTRL: u32 = DR_REG_NRX_BASE + 0x00d4; +const FE_GEN_CTRL: u32 = DR_REG_FE_BASE + 0x0090; +const FE2_TX_INTERP_CTRL: u32 = DR_REG_FE2_BASE + 0x00f0; + +const SYSCON_SRAM_POWER_UP: u8 = 0x0000000F; +const SYSCON_ROM_POWER_UP: u8 = 0x00000003; + +const NRX_RX_ROT_FORCE_PU: u32 = 1 << 5; +const NRX_VIT_FORCE_PU: u32 = 1 << 3; +const NRX_DEMAP_FORCE_PU: u32 = 1 << 1; + +const FE_IQ_EST_FORCE_PU: u32 = 1 << 5; +const FE2_TX_INF_FORCE_PU: u32 = 1 << 10; + +fn modify_register(reg: u32, mask: u32, value: u32) { + let reg = reg as *mut u32; + + unsafe { reg.write_volatile((reg.read_volatile() & !mask) | value) }; +} + +fn register_modify_bits(reg: u32, bits: u32, set: bool) { + if set { + modify_register(reg, bits, bits); + } else { + modify_register(reg, bits, 0); + } +} + +fn rtc_sleep_pu(val: bool) { + let rtc_cntl = unsafe { &*esp32c2::RTC_CNTL::ptr() }; + let syscon = unsafe { &*esp32c2::APB_CTRL::ptr() }; + let bb = unsafe { &*esp32c2::BB::ptr() }; + + rtc_cntl + .dig_pwc() + .modify(|_, w| w.lslp_mem_force_pu().bit(val)); + + syscon.front_end_mem_pd().modify(|_r, w| { + w.dc_mem_force_pu() + .bit(val) + .pbus_mem_force_pu() + .bit(val) + .agc_mem_force_pu() + .bit(val) + }); + + bb.bbpd_ctrl() + .modify(|_r, w| w.fft_force_pu().bit(val).dc_est_force_pu().bit(val)); + + register_modify_bits( + NRXPD_CTRL, + NRX_RX_ROT_FORCE_PU | NRX_VIT_FORCE_PU | NRX_DEMAP_FORCE_PU, + val, + ); + + register_modify_bits(FE_GEN_CTRL, FE_IQ_EST_FORCE_PU, val); + + register_modify_bits(FE2_TX_INTERP_CTRL, FE2_TX_INF_FORCE_PU, val); + + syscon.mem_power_up().modify(|_r, w| unsafe { + w.sram_power_up() + .bits(if val { SYSCON_SRAM_POWER_UP } else { 0 }) + .rom_power_up() + .bits(if val { SYSCON_ROM_POWER_UP } else { 0 }) + }); +} + +impl RtcSleepConfig { + /// Configures the RTC for deep sleep mode. + pub fn deep() -> Self { + // Set up for ultra-low power sleep. Wakeup sources may modify these settings. + let mut cfg = Self::default(); + + cfg.set_lslp_mem_inf_fpu(false); + cfg.set_rtc_mem_inf_follow_cpu(true); // ? + cfg.set_rtc_fastmem_pd_en(true); + cfg.set_rtc_slowmem_pd_en(true); + cfg.set_rtc_peri_pd_en(true); + cfg.set_wifi_pd_en(true); + cfg.set_bt_pd_en(true); + cfg.set_cpu_pd_en(true); + cfg.set_int_8m_pd_en(true); + + cfg.set_dig_peri_pd_en(true); + cfg.set_dig_dbias_slp(0); // because of dig_peri_pd_en + + cfg.set_deep_slp(true); + cfg.set_wdt_flashboot_mod_en(false); + cfg.set_vddsdio_pd_en(true); + cfg.set_xtal_fpu(false); + cfg.set_deep_slp_reject(true); + cfg.set_light_slp_reject(true); + cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10); + + // because of dig_peri_pd_en + cfg.set_rtc_regulator_fpu(false); + cfg.set_dbg_atten_slp(RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT); + + // because of xtal_fpu + cfg.set_bias_sleep_monitor(true); + cfg.set_bias_sleep_slp(true); + cfg.set_pd_cur_slp(true); + + cfg + } + + pub(crate) fn base_settings(_rtc: &Rtc<'_>) { + let cfg = RtcConfig::default(); + + // settings derived from esp_clk_init -> rtc_init + unsafe { + let rtc_cntl = &*esp32c2::RTC_CNTL::ptr(); + let extmem = &*esp32c2::EXTMEM::ptr(); + let system = &*esp32c2::SYSTEM::ptr(); + + regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0); + + regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0); + + rtc_cntl.timer1().modify(|_, w| { + w.pll_buf_wait() + .bits(cfg.pll_wait()) + .ck8m_wait() + .bits(cfg.ck8m_wait()) + }); + + // Moved from rtc sleep to rtc init to save sleep function running time + // set shortest possible sleep time limit + + rtc_cntl + .timer5() + .modify(|_, w| w.min_slp_val().bits(RTC_CNTL_MIN_SLP_VAL_MIN)); + + // TODO: something about cali_ocode + + // Reset RTC bias to default value (needed if waking up from deep sleep) + regi2c_write_mask!( + I2C_DIG_REG, + I2C_DIG_REG_EXT_RTC_DREG_SLEEP, + RTC_CNTL_DBIAS_1V10 + ); + + // LDO dbias initialization + // TODO: this modifies g_rtc_dbias_pvt_non_240m and g_dig_dbias_pvt_non_240m. + // We're using a high enough default but we should read from the efuse. + // set_rtc_dig_dbias(); + + regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_EXT_RTC_DREG, RTC_CNTL_DBIAS_1V25); + regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_EXT_DIG_DREG, RTC_CNTL_DBIAS_1V25); + + if cfg.clkctl_init() { + // clear CMMU clock force on + + extmem + .cache_mmu_power_ctrl() + .modify(|_, w| w.cache_mmu_mem_force_on().clear_bit()); + // clear tag clock force on + + extmem + .icache_tag_power_ctrl() + .modify(|_, w| w.icache_tag_mem_force_on().clear_bit()); + // clear register clock force on + // clear register clock force on + (*esp32c2::SPI0::ptr()) + .clock_gate() + .modify(|_, w| w.clk_en().clear_bit()); + (*esp32c2::SPI1::ptr()) + .clock_gate() + .modify(|_, w| w.clk_en().clear_bit()); + } + + if cfg.pwrctl_init() { + rtc_cntl + .clk_conf() + .modify(|_, w| w.ck8m_force_pu().clear_bit()); + + rtc_cntl + .options0() + .modify(|_, w| w.xtl_force_pu().bit(cfg.xtal_fpu() || cfg.bbpll_fpu())); + + // cancel bbpll force pu if setting no force power up + + rtc_cntl.options0().modify(|_, w| { + w.bbpll_force_pu() + .bit(cfg.bbpll_fpu()) + .bbpll_i2c_force_pu() + .bit(cfg.bbpll_fpu()) + .bb_i2c_force_pu() + .bit(cfg.bbpll_fpu()) + }); + + rtc_cntl + .rtc_cntl() + .modify(|_, w| w.regulator_force_pu().clear_bit()); + + // If this mask is enabled, all soc memories cannot enter power down mode + // We should control soc memory power down mode from RTC, so we will not touch + // this register any more + + system + .mem_pd_mask() + .modify(|_, w| w.lslp_mem_pd_mask().clear_bit()); + + // If this pd_cfg is set to 1, all memory won't enter low power mode during + // light sleep If this pd_cfg is set to 0, all memory will enter low + // power mode during light sleep + rtc_sleep_pu(false); + + rtc_cntl + .dig_pwc() + .modify(|_, w| w.dg_wrap_force_pu().clear_bit()); + + rtc_cntl + .dig_iso() + .modify(|_, w| w.dg_wrap_force_noiso().clear_bit()); + + // if SYSTEM_CPU_WAIT_MODE_FORCE_ON == 0 , the cpu clk will be closed when cpu + // enter WAITI mode + + system + .cpu_per_conf() + .modify(|_, w| w.cpu_wait_mode_force_on().bit(!cfg.cpu_waiti_clk_gate())); + + // cancel digital PADS force no iso + + rtc_cntl.dig_iso().modify(|_, w| { + w.dg_pad_force_unhold() + .clear_bit() + .dg_pad_force_noiso() + .clear_bit() + }); + } + + rtc_cntl.int_ena().write(|w| w.bits(0)); + rtc_cntl.int_clr().write(|w| w.bits(u32::MAX)); + + regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 1); + } + } + + pub(crate) fn apply(&self) { + // like esp-idf rtc_sleep_init() + let rtc_cntl = unsafe { &*esp32c2::RTC_CNTL::ptr() }; + + if self.lslp_mem_inf_fpu() { + rtc_sleep_pu(true); + } + + unsafe { + assert!(!self.pd_cur_slp() || self.bias_sleep_slp()); + + regi2c_write_mask!( + I2C_DIG_REG, + I2C_DIG_REG_EXT_RTC_DREG_SLEEP, + self.rtc_dbias_slp() + ); + + regi2c_write_mask!( + I2C_DIG_REG, + I2C_DIG_REG_EXT_DIG_DREG_SLEEP, + self.dig_dbias_slp() + ); + + rtc_cntl.bias_conf().modify(|_, w| { + w.dbg_atten_monitor() + .bits(RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT) + // We have config values for this in self, so I don't know why we're setting + // hardcoded defaults for these next two. It's what IDF does... + .bias_sleep_monitor() + .bit(RTC_CNTL_BIASSLP_MONITOR_DEFAULT) + .pd_cur_monitor() + .bit(RTC_CNTL_PD_CUR_MONITOR_DEFAULT) + }); + + rtc_cntl.bias_conf().modify(|_, w| { + w.dbg_atten_deep_slp() + .bits(self.dbg_atten_slp()) + .bias_sleep_deep_slp() + .bit(self.bias_sleep_slp()) + .pd_cur_deep_slp() + .bit(self.pd_cur_slp()) + }); + + if self.deep_slp() { + regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0); + + rtc_cntl + .dig_pwc() + .modify(|_, w| w.dg_wrap_pd_en().set_bit()); + + rtc_cntl.ana_conf().modify(|_, w| { + w.ckgen_i2c_pu() + .clear_bit() + .pll_i2c_pu() + .clear_bit() + .rfrx_pbus_pu() + .clear_bit() + .txrf_i2c_pu() + .clear_bit() + }); + + rtc_cntl + .options0() + .modify(|_, w| w.bb_i2c_force_pu().clear_bit()); + } else { + rtc_cntl.bias_conf().modify(|_, w| { + w.dg_vdd_drv_b_slp_en() + .set_bit() + .dg_vdd_drv_b_slp() + .bits(RTC_CNTL_DG_VDD_DRV_B_SLP_DEFAULT) + }); + + rtc_cntl + .dig_pwc() + .modify(|_, w| w.dg_wrap_pd_en().clear_bit()); + } + + rtc_cntl + .rtc_cntl() + .modify(|_, w| w.regulator_force_pu().bit(self.rtc_regulator_fpu())); + + rtc_cntl.clk_conf().modify(|_, w| { + w.ck8m_force_pu() + .bit(!self.int_8m_pd_en()) + .ck8m_force_nogating() + .bit(!self.int_8m_pd_en()) + }); + + // enable VDDSDIO control by state machine + + rtc_cntl.dig_pwc().modify(|_, w| { + w.vdd_spi_pwr_force() + .clear_bit() + .vdd_spi_pd_en() + .bit(self.vddsdio_pd_en()) + }); + + rtc_cntl.slp_reject_conf().modify(|_, w| { + w.deep_slp_reject_en() + .bit(self.deep_slp_reject()) + .light_slp_reject_en() + .bit(self.light_slp_reject()) + }); + + rtc_cntl + .options0() + .modify(|_, w| w.xtl_force_pu().bit(self.xtal_fpu())); + + rtc_cntl + .clk_conf() + .modify(|_, w| w.xtal_global_force_nogating().bit(self.xtal_fpu())); + } + } + + pub(crate) fn start_sleep(&self, wakeup_triggers: WakeTriggers) { + unsafe { + let rtc_cntl = &*esp32c2::RTC_CNTL::ptr(); + + // set bits for what can wake us up + rtc_cntl + .wakeup_state() + .modify(|_, w| w.wakeup_ena().bits(wakeup_triggers.0.into())); + + rtc_cntl + .state0() + .write(|w| w.sleep_en().set_bit().slp_wakeup().set_bit()); + } + } + + pub(crate) fn finish_sleep(&self) { + // In deep sleep mode, we never get here + unsafe { + let rtc_cntl = &*esp32c2::RTC_CNTL::ptr(); + + rtc_cntl.int_clr().write(|w| { + w.slp_reject() + .clear_bit_by_one() + .slp_wakeup() + .clear_bit_by_one() + }); + + // restore config if it is a light sleep + if self.lslp_mem_inf_fpu() { + rtc_sleep_pu(true); + } + } + } +} diff --git a/esp-hal/src/rtc_cntl/sleep/esp32c3.rs b/esp-hal/src/rtc_cntl/sleep/esp32c3.rs index f8fdcf3fac1..5c1cec90f5f 100644 --- a/esp-hal/src/rtc_cntl/sleep/esp32c3.rs +++ b/esp-hal/src/rtc_cntl/sleep/esp32c3.rs @@ -42,46 +42,83 @@ const I2C_ULP_HOSTID: u8 = 0; // Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP, // RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values. // Valid if RTC_CNTL_DBG_ATTEN is 0. +/// Digital bias voltage level of 0.90V. pub const RTC_CNTL_DBIAS_0V90: u32 = 13; +/// Digital bias voltage level of 0.95V. pub const RTC_CNTL_DBIAS_0V95: u32 = 16; +/// Digital bias voltage level of 1.00V. pub const RTC_CNTL_DBIAS_1V00: u32 = 18; +/// Digital bias voltage level of 1.05V. pub const RTC_CNTL_DBIAS_1V05: u32 = 20; +/// Digital bias voltage level of 1.10V. pub const RTC_CNTL_DBIAS_1V10: u32 = 23; +/// Digital bias voltage level of 1.15V. pub const RTC_CNTL_DBIAS_1V15: u32 = 25; +/// Digital bias voltage level of 1.20V. pub const RTC_CNTL_DBIAS_1V20: u32 = 28; +/// Digital bias voltage level of 1.25V. pub const RTC_CNTL_DBIAS_1V25: u32 = 30; -pub const RTC_CNTL_DBIAS_1V30: u32 = 31; //< voltage is about 1.34v in fact +/// Digital bias voltage level of approximately 1.34V. +pub const RTC_CNTL_DBIAS_1V30: u32 = 31; +/// Default attenuation setting during light sleep, with a voltage drop. pub const RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_DEFAULT: u8 = 5; +/// No attenuation (no voltage drop) during light sleep. pub const RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_NODROP: u8 = 0; +/// Default attenuation setting during deep sleep, with maximum voltage drop. pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT: u8 = 15; +/// No attenuation (no voltage drop) during deep sleep. pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_NODROP: u8 = 0; + +/// Default bias setting during sleep mode. pub const RTC_CNTL_BIASSLP_SLEEP_DEFAULT: u8 = 1; +/// Keeps the bias for ultra-low power sleep mode always on. pub const RTC_CNTL_BIASSLP_SLEEP_ON: u8 = 0; + +/// Default power-down current setting during sleep mode. pub const RTC_CNTL_PD_CUR_SLEEP_DEFAULT: u8 = 1; +/// Keeps the power-down current setting for sleep mode always on. pub const RTC_CNTL_PD_CUR_SLEEP_ON: u8 = 0; + +/// Default driver bias setting for the digital domain during sleep mode. pub const RTC_CNTL_DG_VDD_DRV_B_SLP_DEFAULT: u8 = 254; +/// Default debug attenuation setting for the monitor mode. pub const RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT: u8 = 0; +/// Default bias setting for sleep mode in the monitor mode. pub const RTC_CNTL_BIASSLP_MONITOR_DEFAULT: bool = false; +/// Default power-down current setting for the monitor mode. pub const RTC_CNTL_PD_CUR_MONITOR_DEFAULT: bool = false; +/// Default number of cycles to wait for the PLL buffer to stabilize. pub const RTC_CNTL_PLL_BUF_WAIT_DEFAULT: u8 = 20; +/// Default number of cycles to wait for the XTL buffer to stabilize. pub const RTC_CNTL_XTL_BUF_WAIT_DEFAULT: u8 = 100; +/// Default number of cycles to wait for the internal 8MHz clock to stabilize. pub const RTC_CNTL_CK8M_WAIT_DEFAULT: u8 = 20; +/// Default number of cycles required to enable the internal 8MHz clock. pub const RTC_CK8M_ENABLE_WAIT_DEFAULT: u8 = 5; +/// Minimum number of cycles for sleep duration. pub const RTC_CNTL_MIN_SLP_VAL_MIN: u8 = 2; +/// Power-up cycles for other hardware blocks. pub const OTHER_BLOCKS_POWERUP: u8 = 1; +/// Wait cycles for other hardware blocks to stabilize. pub const OTHER_BLOCKS_WAIT: u16 = 1; +/// Disables GPIO interrupt. pub const GPIO_INTR_DISABLE: u8 = 0; +/// Sets GPIO interrupt to trigger on a low level signal. pub const GPIO_INTR_LOW_LEVEL: u8 = 4; +/// Sets GPIO interrupt to trigger on a high level signal. pub const GPIO_INTR_HIGH_LEVEL: u8 = 5; +/// Specifies the function configuration for GPIO pins. pub const PIN_FUNC_GPIO: u8 = 1; +/// Index for signaling GPIO output. pub const SIG_GPIO_OUT_IDX: u32 = 128; +/// Maximum number of GPIO pins supported. pub const GPIO_NUM_MAX: usize = 22; impl WakeSource for TimerWakeupSource { @@ -256,6 +293,7 @@ impl WakeSource for RtcioWakeupSource<'_, '_> { bitfield::bitfield! { #[derive(Clone, Copy)] + /// RTC Configuration. pub struct RtcConfig(u32); impl Debug; /// Number of rtc_fast_clk cycles to wait for 8M clock to be ready @@ -264,16 +302,19 @@ bitfield::bitfield! { pub u8, xtal_wait, set_xtal_wait: 15, 8; /// Number of rtc_fast_clk cycles to wait for PLL clock to be ready pub u8, pll_wait, set_pll_wait: 23, 16; - // Perform clock control related initialization. + /// Perform clock control related initialization. pub clkctl_init, set_clkctl_init: 24; - // Perform power control related initialization. + /// Perform power control related initialization. pub pwrctl_init, set_pwrctl_init: 25; - // Force power down RTC_DBOOST + /// Force power down `RTC_DBOOST`. pub rtc_dboost_fpd, set_rtc_dboost_fpd: 26; + /// Keep the XTAL oscillator powered up in sleep. pub xtal_fpu, set_xtal_fpu: 27; + /// Keep the BBPLL oscillator powered up in sleep. pub bbpll_fpu, set_bbpll_fpu: 28; + /// Enable clock gating when the CPU is in wait-for-interrupt state. pub cpu_waiti_clk_gate, set_cpu_waiti_clk_gate: 29; - // Calibrate Ocode to make bandgap voltage more precise. + /// Calibrate Ocode to make bandgap voltage more precise. pub cali_ocode, set_cali_ocode: 30; } @@ -293,17 +334,28 @@ impl Default for RtcConfig { bitfield::bitfield! { #[derive(Clone, Copy)] + /// Configuration for RTC initialization. pub struct RtcInitConfig(u128); impl Debug; + /// Number of cycles required to power up WiFi pub u8, wifi_powerup_cycles, set_wifi_powerup_cycles: 6, 0; + /// Number of wait cycles for WiFi to stabilize pub u16, wifi_wait_cycles, set_wifi_wait_cycles: 15, 7; + /// Number of cycles required to power up Bluetooth pub u8, bt_powerup_cycles, set_bt_powerup_cycles: 22, 16; + /// Number of wait cycles for Bluetooth to stabilize pub u16, bt_wait_cycles, set_bt_wait_cycles: 31, 23; + /// Number of cycles required to power up the top CPU pub u8, cpu_top_powerup_cycles, set_cpu_top_powerup_cycles: 38, 32; + /// Number of wait cycles for the top CPU to stabilize pub u16, cpu_top_wait_cycles, set_cpu_top_wait_cycles: 47, 39; + /// Number of cycles required to power up the digital wrapper pub u8, dg_wrap_powerup_cycles, set_dg_wrap_powerup_cycles: 54, 48; + /// Number of wait cycles for the digital wrapper to stabilize pub u16, dg_wrap_wait_cycles, set_dg_wrap_wait_cycles: 63, 55; + /// Number of cycles required to power up the digital peripherals pub u8, dg_peri_powerup_cycles, set_dg_peri_powerup_cycles: 70, 64; + /// Number of wait cycles for the digital peripherals to stabilize pub u16, dg_peri_wait_cycles, set_dg_peri_wait_cycles: 79, 71; } @@ -326,6 +378,7 @@ impl Default for RtcInitConfig { bitfield::bitfield! { #[derive(Clone, Copy)] + /// Configuration for RTC sleep mode. pub struct RtcSleepConfig(u64); impl Debug; /// force normal voltage in sleep mode (digital domain memory) @@ -387,42 +440,16 @@ impl Default for RtcSleepConfig { } } -const DR_REG_NRX_BASE: u32 = 0x6001CC00; -const DR_REG_FE_BASE: u32 = 0x60006000; -const DR_REG_FE2_BASE: u32 = 0x60005000; - -const NRXPD_CTRL: u32 = DR_REG_NRX_BASE + 0x00d4; -const FE_GEN_CTRL: u32 = DR_REG_FE_BASE + 0x0090; -const FE2_TX_INTERP_CTRL: u32 = DR_REG_FE2_BASE + 0x00f0; - const SYSCON_SRAM_POWER_UP: u8 = 0x0000000F; const SYSCON_ROM_POWER_UP: u8 = 0x00000003; -const NRX_RX_ROT_FORCE_PU: u32 = 1 << 5; -const NRX_VIT_FORCE_PU: u32 = 1 << 3; -const NRX_DEMAP_FORCE_PU: u32 = 1 << 1; - -const FE_IQ_EST_FORCE_PU: u32 = 1 << 5; -const FE2_TX_INF_FORCE_PU: u32 = 1 << 10; - -fn modify_register(reg: u32, mask: u32, value: u32) { - let reg = reg as *mut u32; - - unsafe { reg.write_volatile((reg.read_volatile() & !mask) | value) }; -} - -fn register_modify_bits(reg: u32, bits: u32, set: bool) { - if set { - modify_register(reg, bits, bits); - } else { - modify_register(reg, bits, 0); - } -} - fn rtc_sleep_pu(val: bool) { let rtc_cntl = unsafe { &*esp32c3::RTC_CNTL::ptr() }; let syscon = unsafe { &*esp32c3::APB_CTRL::ptr() }; let bb = unsafe { &*esp32c3::BB::ptr() }; + let nrx = unsafe { &*esp32c3::NRX::ptr() }; + let fe = unsafe { &*esp32c3::FE::ptr() }; + let fe2 = unsafe { &*esp32c3::FE2::ptr() }; rtc_cntl .dig_pwc() @@ -440,15 +467,19 @@ fn rtc_sleep_pu(val: bool) { bb.bbpd_ctrl() .modify(|_r, w| w.fft_force_pu().bit(val).dc_est_force_pu().bit(val)); - register_modify_bits( - NRXPD_CTRL, - NRX_RX_ROT_FORCE_PU | NRX_VIT_FORCE_PU | NRX_DEMAP_FORCE_PU, - val, - ); + nrx.nrxpd_ctrl().modify(|_, w| { + w.rx_rot_force_pu() + .bit(val) + .vit_force_pu() + .bit(val) + .demap_force_pu() + .bit(val) + }); - register_modify_bits(FE_GEN_CTRL, FE_IQ_EST_FORCE_PU, val); + fe.gen_ctrl().modify(|_, w| w.iq_est_force_pu().bit(val)); - register_modify_bits(FE2_TX_INTERP_CTRL, FE2_TX_INF_FORCE_PU, val); + fe2.tx_interp_ctrl() + .modify(|_, w| w.tx_inf_force_pu().bit(val)); syscon.mem_power_up().modify(|_r, w| unsafe { w.sram_power_up() @@ -459,6 +490,7 @@ fn rtc_sleep_pu(val: bool) { } impl RtcSleepConfig { + /// Configures the RTC for deep sleep mode. pub fn deep() -> Self { // Set up for ultra-low power sleep. Wakeup sources may modify these settings. let mut cfg = Self::default(); diff --git a/esp-hal/src/rtc_cntl/sleep/esp32c6.rs b/esp-hal/src/rtc_cntl/sleep/esp32c6.rs index 3336bc34372..fb052aed945 100644 --- a/esp-hal/src/rtc_cntl/sleep/esp32c6.rs +++ b/esp-hal/src/rtc_cntl/sleep/esp32c6.rs @@ -160,11 +160,14 @@ impl WakeSource for WakeFromLpCoreWakeupSource { } } +/// Configuration for controlling the behavior during sleep modes. #[derive(Clone, Copy)] // pmu_sleep_analog_config_t pub struct AnalogSleepConfig { + /// High-power system configuration. pub hp_sys: HpAnalog, // pub lp_sys_active: LpAnalog, // unused + /// Low-power system analog configuration. pub lp_sys_sleep: LpAnalog, } @@ -298,9 +301,12 @@ impl AnalogSleepConfig { } } +/// Configuration for controlling the behavior of digital peripherals during +/// sleep modes. #[derive(Clone, Copy)] // pmu_sleep_digital_config_t pub struct DigitalSleepConfig { + /// High-power system control register configuration. pub syscntl: HpSysCntlReg, } @@ -329,11 +335,16 @@ impl DigitalSleepConfig { } } +/// Configuration for controlling the power settings of high-power and low-power +/// systems during sleep modes. #[derive(Clone, Copy)] // pmu_sleep_power_config_t pub struct PowerSleepConfig { + /// Power configuration for the high-power system during sleep. pub hp_sys: HpSysPower, + /// Power configuration for the low-power system when it is active. pub lp_sys_active: LpSysPower, + /// Power configuration for the low-power system when it is in sleep mode. pub lp_sys_sleep: LpSysPower, } @@ -424,42 +435,64 @@ impl PowerSleepConfig { } } +/// Parameters for high-power system configurations during sleep modes. #[derive(Clone, Copy)] // pmu_hp_param_t pub struct HpParam { + /// Number of cycles to wait for the modem to wake up. pub modem_wakeup_wait_cycle: u32, + /// Number of cycles to wait for the analog component stabilization. pub analog_wait_target_cycle: u16, + /// Number of cycles to wait for the digital power-down sequence. pub digital_power_down_wait_cycle: u16, + /// Number of cycles to wait for the digital power supply to stabilize. pub digital_power_supply_wait_cycle: u16, + /// Number of cycles to wait for the digital power-up sequence. pub digital_power_up_wait_cycle: u16, + /// Number of cycles to wait for the PLL to stabilize. pub pll_stable_wait_cycle: u16, + /// Number of cycles to wait for modifying the ICG control. pub modify_icg_cntl_wait_cycle: u8, + /// Number of cycles to wait for switching the ICG coйntrol. pub switch_icg_cntl_wait_cycle: u8, + /// Minimum sleep time measured in slow clock cycles. pub min_slp_slow_clk_cycle: u8, } +/// Parameters for low-power system configurations during sleep modes. #[derive(Clone, Copy)] // pmu_lp_param_t pub struct LpParam { + /// Number of cycles to wait for the digital power supply to stabilize. pub digital_power_supply_wait_cycle: u16, + /// Minimum sleep time measured in slow clock cycles. pub min_slp_slow_clk_cycle: u8, + /// Number of cycles to wait for the analog component stabilization. pub analog_wait_target_cycle: u8, + /// Number of cycles to wait for the digital power-down sequence. pub digital_power_down_wait_cycle: u8, + /// Number of cycles to wait for the digital power-up sequence. pub digital_power_up_wait_cycle: u8, } +/// Parameters for high-power and low-power system configurations during sleep +/// modes. #[derive(Clone, Copy)] // pmu_hp_lp_param_t pub struct HpLpParam { - // union of two u16 variants, I've elected to not complicate things... + /// Union of two u16 variants pub xtal_stable_wait_cycle: u16, } +/// Configuration of parameters for sleep modes #[derive(Clone, Copy)] // pmu_sleep_param_config_t pub struct ParamSleepConfig { + /// Configuration of high-power system parameters. pub hp_sys: HpParam, + /// Configuration of low-power system parameters. pub lp_sys: LpParam, + /// Shared configuration parameters for high-power and low-power systems. pub hp_lp: HpLpParam, } impl ParamSleepConfig { @@ -729,10 +762,13 @@ impl SleepTimeConfig { } } +/// Configuration for the RTC sleep behavior. #[derive(Clone, Copy)] // pmu_sleep_config_t + deep sleep flag + pd flags pub struct RtcSleepConfig { + /// Deep Sleep flag pub deep: bool, + /// Power Down flags pub pd_flags: PowerDownFlags, } @@ -761,28 +797,46 @@ bitfield::bitfield! { /// Power domains to be powered down during sleep pub struct PowerDownFlags(u32); + /// Controls the power-down status of the top power domain. pub u32, pd_top , set_pd_top : 0; + /// Controls the power-down status of the VDD_SDIO power domain. pub u32, pd_vddsdio , set_pd_vddsdio : 1; + /// Controls the power-down status of the modem power domain. pub u32, pd_modem , set_pd_modem : 2; + /// Controls the power-down status of the high-performance peripheral power domain. pub u32, pd_hp_periph, set_pd_hp_periph: 3; + /// Controls the power-down status of the CPU power domain. pub u32, pd_cpu , set_pd_cpu : 4; + /// Controls the power-down status of the high-performance always-on domain. pub u32, pd_hp_aon , set_pd_hp_aon : 5; + /// Controls the power-down status of memory group 0. pub u32, pd_mem_g0 , set_pd_mem_g0 : 6; + /// Controls the power-down status of memory group 1. pub u32, pd_mem_g1 , set_pd_mem_g1 : 7; + /// Controls the power-down status of memory group 2. pub u32, pd_mem_g2 , set_pd_mem_g2 : 8; + /// Controls the power-down status of memory group 3. pub u32, pd_mem_g3 , set_pd_mem_g3 : 9; + /// Controls the power-down status of the crystal oscillator. pub u32, pd_xtal , set_pd_xtal : 10; + /// Controls the power-down status of the fast RC oscillator. pub u32, pd_rc_fast , set_pd_rc_fast : 11; + /// Controls the power-down status of the 32kHz crystal oscillator. pub u32, pd_xtal32k , set_pd_xtal32k : 12; + /// Controls the power-down status of the 32kHz RC oscillator. pub u32, pd_rc32k , set_pd_rc32k : 13; + /// Controls the power-down status of the low-power peripheral domain. pub u32, pd_lp_periph, set_pd_lp_periph: 14; } impl PowerDownFlags { + /// Checks whether all memory groups (G0, G1, G2, G3) are powered down. pub fn pd_mem(self) -> bool { self.pd_mem_g0() && self.pd_mem_g1() && self.pd_mem_g2() && self.pd_mem_g3() } + /// Sets the power-down status for all memory groups (G0, G1, G2, G3) at + /// once. pub fn set_pd_mem(&mut self, value: bool) { self.set_pd_mem_g0(value); self.set_pd_mem_g1(value); @@ -826,10 +880,12 @@ impl MachineConstants { } impl RtcSleepConfig { + /// Returns whether the device is in deep sleep mode. pub fn deep_slp(&self) -> bool { self.deep } + /// Configures the device for deep sleep mode with ultra-low power settings. pub fn deep() -> Self { // Set up for ultra-low power sleep. Wakeup sources may modify these settings. Self { diff --git a/esp-hal/src/rtc_cntl/sleep/esp32s3.rs b/esp-hal/src/rtc_cntl/sleep/esp32s3.rs index 34011d6cf93..ea97060daaf 100644 --- a/esp-hal/src/rtc_cntl/sleep/esp32s3.rs +++ b/esp-hal/src/rtc_cntl/sleep/esp32s3.rs @@ -42,41 +42,69 @@ const I2C_DIG_REG_XPD_DIG_REG_LSB: u32 = 3; // Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP, // RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values. // Valid if RTC_CNTL_DBG_ATTEN is 0. +/// Digital bias setting for 0.90V. pub const RTC_CNTL_DBIAS_0V90: u32 = 13; +/// Digital bias setting for 0.95V. pub const RTC_CNTL_DBIAS_0V95: u32 = 16; +/// Digital bias setting for 1.00V. pub const RTC_CNTL_DBIAS_1V00: u32 = 18; +/// Digital bias setting for 1.05V. pub const RTC_CNTL_DBIAS_1V05: u32 = 20; +/// Digital bias setting for 1.10V. pub const RTC_CNTL_DBIAS_1V10: u32 = 23; +/// Digital bias setting for 1.15V. pub const RTC_CNTL_DBIAS_1V15: u32 = 25; +/// Digital bias setting for 1.20V. pub const RTC_CNTL_DBIAS_1V20: u32 = 28; +/// Digital bias setting for 1.25V. pub const RTC_CNTL_DBIAS_1V25: u32 = 30; -pub const RTC_CNTL_DBIAS_1V30: u32 = 31; //< voltage is about 1.34v in fact - +/// Digital bias setting for 1.30V. Voltage is approximately 1.34V in practice. +pub const RTC_CNTL_DBIAS_1V30: u32 = 31; +/// Default monitor debug attenuation value. pub const RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT: u8 = 0; +/// ULP co-processor touch start wait time during sleep, set to maximum. pub const RTC_CNTL_ULPCP_TOUCH_START_WAIT_IN_SLEEP: u16 = 0xFF; +/// ULP co-processor touch start wait time default value. pub const RTC_CNTL_ULPCP_TOUCH_START_WAIT_DEFAULT: u16 = 0x10; - +/// Default wait time for PLL buffer during startup. pub const RTC_CNTL_PLL_BUF_WAIT_DEFAULT: u8 = 20; +/// Default wait time for CK8M during startup. pub const RTC_CNTL_CK8M_WAIT_DEFAULT: u8 = 20; +/// Minimum sleep value. pub const RTC_CNTL_MIN_SLP_VAL_MIN: u8 = 2; +/// Deep sleep debug attenuation setting for ultra-low power mode. pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_ULTRA_LOW: u8 = 15; - +/// Power-up setting for other blocks. pub const OTHER_BLOCKS_POWERUP: u8 = 1; +/// Wait cycles for other blocks. pub const OTHER_BLOCKS_WAIT: u16 = 1; - +/// WiFi power-up cycles. pub const WIFI_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// WiFi wait cycles. pub const WIFI_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; +/// Bluetooth power-up cycles. pub const BT_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// Bluetooth wait cycles. pub const BT_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; +/// RTC power-up cycles. pub const RTC_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// RTC wait cycles. pub const RTC_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; +/// CPU top power-up cycles. pub const CPU_TOP_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// CPU top wait cycles. pub const CPU_TOP_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; +/// DG wrap power-up cycles. pub const DG_WRAP_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// DG wrap wait cycles. pub const DG_WRAP_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; +/// DG peripheral power-up cycles. pub const DG_PERI_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// DG peripheral wait cycles. pub const DG_PERI_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; +/// RTC memory power-up cycles. pub const RTC_MEM_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// RTC memory wait cycles. pub const RTC_MEM_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; impl WakeSource for TimerWakeupSource { @@ -266,6 +294,7 @@ impl Drop for RtcioWakeupSource<'_, '_> { } bitfield::bitfield! { + /// Configuration for the RTC sleep behavior. #[derive(Clone, Copy)] pub struct RtcSleepConfig(u64); impl Debug; @@ -328,42 +357,16 @@ impl Default for RtcSleepConfig { } } -const DR_REG_NRX_BASE: u32 = 0x6001CC00; -const DR_REG_FE_BASE: u32 = 0x60006000; -const DR_REG_FE2_BASE: u32 = 0x60005000; - -const NRXPD_CTRL: u32 = DR_REG_NRX_BASE + 0x00d4; -const FE_GEN_CTRL: u32 = DR_REG_FE_BASE + 0x0090; -const FE2_TX_INTERP_CTRL: u32 = DR_REG_FE2_BASE + 0x00f0; - const SYSCON_SRAM_POWER_UP: u16 = 0x7FF; const SYSCON_ROM_POWER_UP: u8 = 0x7; -const NRX_RX_ROT_FORCE_PU: u32 = 1 << 5; -const NRX_VIT_FORCE_PU: u32 = 1 << 3; -const NRX_DEMAP_FORCE_PU: u32 = 1 << 1; - -const FE_IQ_EST_FORCE_PU: u32 = 1 << 5; -const FE2_TX_INF_FORCE_PU: u32 = 1 << 10; - -fn modify_register(reg: u32, mask: u32, value: u32) { - let reg = reg as *mut u32; - - unsafe { reg.write_volatile((reg.read_volatile() & !mask) | value) }; -} - -fn register_modify_bits(reg: u32, bits: u32, set: bool) { - if set { - modify_register(reg, bits, bits); - } else { - modify_register(reg, bits, 0); - } -} - fn rtc_sleep_pu(val: bool) { let rtc_cntl = unsafe { &*esp32s3::RTC_CNTL::ptr() }; let syscon = unsafe { &*esp32s3::APB_CTRL::ptr() }; let bb = unsafe { &*esp32s3::BB::ptr() }; + let nrx = unsafe { &*esp32s3::NRX::ptr() }; + let fe = unsafe { &*esp32s3::FE::ptr() }; + let fe2 = unsafe { &*esp32s3::FE2::ptr() }; rtc_cntl .dig_pwc() @@ -385,15 +388,19 @@ fn rtc_sleep_pu(val: bool) { bb.bbpd_ctrl() .modify(|_r, w| w.fft_force_pu().bit(val).dc_est_force_pu().bit(val)); - register_modify_bits( - NRXPD_CTRL, - NRX_RX_ROT_FORCE_PU | NRX_VIT_FORCE_PU | NRX_DEMAP_FORCE_PU, - val, - ); + nrx.nrxpd_ctrl().modify(|_, w| { + w.rx_rot_force_pu() + .bit(val) + .vit_force_pu() + .bit(val) + .demap_force_pu() + .bit(val) + }); - register_modify_bits(FE_GEN_CTRL, FE_IQ_EST_FORCE_PU, val); + fe.gen_ctrl().modify(|_, w| w.iq_est_force_pu().bit(val)); - register_modify_bits(FE2_TX_INTERP_CTRL, FE2_TX_INF_FORCE_PU, val); + fe2.tx_interp_ctrl() + .modify(|_, w| w.tx_inf_force_pu().bit(val)); syscon.mem_power_up().modify(|_r, w| unsafe { w.sram_power_up() @@ -404,6 +411,7 @@ fn rtc_sleep_pu(val: bool) { } impl RtcSleepConfig { + /// Configures the RTC for deep sleep mode. pub fn deep() -> Self { // Set up for ultra-low power sleep. Wakeup sources may modify these settings. let mut cfg = Self::default(); diff --git a/esp-hal/src/rtc_cntl/sleep/mod.rs b/esp-hal/src/rtc_cntl/sleep/mod.rs index 71ee0712a24..b319fd68ce1 100644 --- a/esp-hal/src/rtc_cntl/sleep/mod.rs +++ b/esp-hal/src/rtc_cntl/sleep/mod.rs @@ -15,12 +15,12 @@ //! * `BT (Bluetooth) wake` - light sleep only use core::cell::RefCell; -#[cfg(any(esp32, esp32c3, esp32s3, esp32c6))] +#[cfg(any(esp32, esp32c3, esp32s3, esp32c6, esp32c2))] use core::time::Duration; #[cfg(any(esp32, esp32s3))] use crate::gpio::RtcPin as RtcIoWakeupPinType; -#[cfg(any(esp32c3, esp32c6))] +#[cfg(any(esp32c3, esp32c6, esp32c2))] use crate::gpio::RtcPinWithResistors as RtcIoWakeupPinType; use crate::rtc_cntl::Rtc; @@ -28,46 +28,62 @@ use crate::rtc_cntl::Rtc; #[cfg_attr(esp32s3, path = "esp32s3.rs")] #[cfg_attr(esp32c3, path = "esp32c3.rs")] #[cfg_attr(esp32c6, path = "esp32c6.rs")] +#[cfg_attr(esp32c2, path = "esp32c2.rs")] mod sleep_impl; pub use sleep_impl::*; #[derive(Debug, Default, Clone, Copy, PartialEq)] +/// Level at which a wake-up event is triggered pub enum WakeupLevel { + /// The wake-up event is triggered when the pin is low. Low, #[default] + /// The wake-up event is triggered when the pin is high. High, } +/// Represents a timer wake-up source, triggering an event after a specified +/// duration. #[derive(Debug, Default, Clone, Copy)] -#[cfg(any(esp32, esp32c3, esp32s3, esp32c6))] +#[cfg(any(esp32, esp32c3, esp32s3, esp32c6, esp32c2))] pub struct TimerWakeupSource { + /// The duration after which the wake-up event is triggered. duration: Duration, } -#[cfg(any(esp32, esp32c3, esp32s3, esp32c6))] +#[cfg(any(esp32, esp32c3, esp32s3, esp32c6, esp32c2))] impl TimerWakeupSource { + /// Creates a new timer wake-up source with the specified duration. pub fn new(duration: Duration) -> Self { Self { duration } } } +/// Errors that can occur when configuring RTC wake-up sources. #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { + /// The selected pin is not a valid RTC pin. NotRtcPin, + /// The maximum number of wake-up sources has been exceeded. TooManyWakeupSources, } +/// External wake-up source (Ext0). #[derive(Debug)] #[cfg(any(esp32, esp32s3))] pub struct Ext0WakeupSource<'a, P: RtcIoWakeupPinType> { + /// The pin used as the wake-up source. pin: RefCell<&'a mut P>, + /// The level at which the wake-up event is triggered. level: WakeupLevel, } #[cfg(any(esp32, esp32s3))] impl<'a, P: RtcIoWakeupPinType> Ext0WakeupSource<'a, P> { + /// Creates a new external wake-up source (Ext0``) with the specified pin + /// and wake-up level. pub fn new(pin: &'a mut P, level: WakeupLevel) -> Self { Self { pin: RefCell::new(pin), @@ -76,14 +92,19 @@ impl<'a, P: RtcIoWakeupPinType> Ext0WakeupSource<'a, P> { } } +/// External wake-up source (Ext1). #[cfg(any(esp32, esp32s3))] pub struct Ext1WakeupSource<'a, 'b> { + /// A collection of pins used as wake-up sources. pins: RefCell<&'a mut [&'b mut dyn RtcIoWakeupPinType]>, + /// The level at which the wake-up event is triggered across all pins. level: WakeupLevel, } #[cfg(any(esp32, esp32s3))] impl<'a, 'b> Ext1WakeupSource<'a, 'b> { + /// Creates a new external wake-up source (Ext1) with the specified pins and + /// wake-up level. pub fn new(pins: &'a mut [&'b mut dyn RtcIoWakeupPinType], level: WakeupLevel) -> Self { Self { pins: RefCell::new(pins), @@ -92,6 +113,7 @@ impl<'a, 'b> Ext1WakeupSource<'a, 'b> { } } +/// External wake-up source (Ext1). #[cfg(esp32c6)] pub struct Ext1WakeupSource<'a, 'b> { pins: RefCell<&'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]>, @@ -99,6 +121,8 @@ pub struct Ext1WakeupSource<'a, 'b> { #[cfg(esp32c6)] impl<'a, 'b> Ext1WakeupSource<'a, 'b> { + /// Creates a new external wake-up source (Ext1) with the specified pins and + /// wake-up level. pub fn new(pins: &'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]) -> Self { Self { pins: RefCell::new(pins), @@ -111,13 +135,14 @@ impl<'a, 'b> Ext1WakeupSource<'a, 'b> { /// RTC_IO wakeup allows configuring any combination of RTC_IO pins with /// arbitrary wakeup levels to wake up the chip from sleep. This wakeup source /// can be used to wake up from both light and deep sleep. -#[cfg(any(esp32c3, esp32s3))] +#[cfg(any(esp32c3, esp32s3, esp32c2))] pub struct RtcioWakeupSource<'a, 'b> { pins: RefCell<&'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]>, } -#[cfg(any(esp32c3, esp32s3))] +#[cfg(any(esp32c3, esp32s3, esp32c2))] impl<'a, 'b> RtcioWakeupSource<'a, 'b> { + /// Creates a new external wake-up source (Ext1). pub fn new(pins: &'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]) -> Self { Self { pins: RefCell::new(pins), @@ -247,6 +272,7 @@ uart_wakeup_impl!(1); #[cfg(not(pmu))] bitfield::bitfield! { + /// Represents the wakeup triggers. #[derive(Default, Clone, Copy)] pub struct WakeTriggers(u16); impl Debug; @@ -276,6 +302,7 @@ bitfield::bitfield! { #[cfg(pmu)] bitfield::bitfield! { + /// Represents the wakeup triggers. #[derive(Default, Clone, Copy)] pub struct WakeTriggers(u16); impl Debug; @@ -306,6 +333,8 @@ bitfield::bitfield! { pub usb, set_usb: 14; } +/// Trait representing a wakeup source. pub trait WakeSource { + /// Configures the RTC and applies the wakeup triggers. fn apply(&self, rtc: &Rtc<'_>, triggers: &mut WakeTriggers, sleep_config: &mut RtcSleepConfig); } diff --git a/esp-hal/src/sha.rs b/esp-hal/src/sha.rs index 7e2b276c9ef..1836cb2e0be 100644 --- a/esp-hal/src/sha.rs +++ b/esp-hal/src/sha.rs @@ -32,20 +32,19 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::sha::Sha; -//! # use esp_hal::sha::ShaMode; -//! # use core::option::Option::None; +//! # use esp_hal::sha::Sha256; //! # use nb::block; -//! let source_data = "HELLO, ESPRESSIF!".as_bytes(); -//! let mut remaining = source_data; -//! let mut hasher = Sha::new(peripherals.SHA, ShaMode::SHA256); +//! let mut source_data = "HELLO, ESPRESSIF!".as_bytes(); +//! let mut sha = Sha::new(peripherals.SHA); +//! let mut hasher = sha.start::(); //! // Short hashes can be created by decreasing the output buffer to the //! // desired length //! let mut output = [0u8; 32]; //! -//! while remaining.len() > 0 { +//! while !source_data.is_empty() { //! // All the HW Sha functions are infallible so unwrap is fine to use if //! // you use block! -//! remaining = block!(hasher.update(remaining)).unwrap(); +//! source_data = block!(hasher.update(source_data)).unwrap(); //! } //! //! // Finish can be called as many times as desired to get multiple copies of @@ -57,7 +56,11 @@ //! ## Implementation State //! - DMA-SHA Mode is not supported. -use core::{convert::Infallible, marker::PhantomData}; +use core::{borrow::BorrowMut, convert::Infallible, marker::PhantomData, mem::size_of}; + +/// Re-export digest for convenience +#[cfg(feature = "digest")] +pub use digest::Digest; use crate::{ peripheral::{Peripheral, PeripheralRef}, @@ -66,99 +69,39 @@ use crate::{ system::PeripheralClockControl, }; -// All the hash algorithms introduced in FIPS PUB 180-4 Spec. -// – SHA-1 -// – SHA-224 -// – SHA-256 -// – SHA-384 -// – SHA-512 -// – SHA-512/224 -// – SHA-512/256 -// – SHA-512/t (not implemented yet) -// Two working modes -// – Typical SHA -// – DMA-SHA (not implemented yet) - /// The SHA Accelerator driver instance -pub struct Sha<'d, DM: crate::Mode> { +pub struct Sha<'d> { sha: PeripheralRef<'d, SHA>, - mode: ShaMode, - alignment_helper: AlignmentHelper, - cursor: usize, - first_run: bool, - finished: bool, - phantom: PhantomData, -} - -/// Hash Algorithm Mode -#[derive(Debug, Clone, Copy)] -pub enum ShaMode { - SHA1, - #[cfg(not(esp32))] - SHA224, - SHA256, - #[cfg(any(esp32, esp32s2, esp32s3))] - SHA384, - #[cfg(any(esp32, esp32s2, esp32s3))] - SHA512, - #[cfg(any(esp32s2, esp32s3))] - SHA512_224, - #[cfg(any(esp32s2, esp32s3))] - SHA512_256, - // SHA512_(u16) // Max 511 -} - -// TODO: Maybe make Sha Generic (Sha) in order to allow for better -// compiler optimizations? (Requires complex const generics which isn't stable -// yet) - -#[cfg(not(esp32))] -fn mode_as_bits(mode: ShaMode) -> u8 { - match mode { - ShaMode::SHA1 => 0, - ShaMode::SHA224 => 1, - ShaMode::SHA256 => 2, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA384 => 3, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA512 => 4, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA512_224 => 5, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA512_256 => 6, - // _ => 0 // TODO: SHA512/t - } } -impl<'d> Sha<'d, crate::Blocking> { - /// Create a new instance in [crate::Blocking] mode. - #[cfg_attr(not(esp32), doc = "Optionally an interrupt handler can be bound.")] - pub fn new(sha: impl Peripheral

+ 'd, mode: ShaMode) -> Self { +impl<'d> Sha<'d> { + /// Create a new instance of the SHA Accelerator driver. + pub fn new(sha: impl Peripheral

+ 'd) -> Self { crate::into_ref!(sha); + PeripheralClockControl::reset(crate::system::Peripheral::Sha); PeripheralClockControl::enable(crate::system::Peripheral::Sha); - // Setup SHA Mode - #[cfg(not(esp32))] - sha.mode() - .write(|w| unsafe { w.mode().bits(mode_as_bits(mode)) }); + Self { sha } + } - Self { - sha, - mode, - cursor: 0, - first_run: true, - finished: false, - alignment_helper: AlignmentHelper::default(), - phantom: PhantomData, - } + /// Start a new digest. + pub fn start<'a, A: ShaAlgorithm>(&'a mut self) -> ShaDigest<'d, A, &'a mut Self> { + ShaDigest::new(self) + } + + /// Start a new digest and take ownership of the driver. + /// This is useful for storage outside a function body. i.e. in static or + /// struct. + pub fn start_owned(self) -> ShaDigest<'d, A, Self> { + ShaDigest::new(self) } } -impl<'d> crate::private::Sealed for Sha<'d, crate::Blocking> {} +impl<'d> crate::private::Sealed for Sha<'d> {} #[cfg(not(esp32))] -impl<'d> crate::InterruptConfigurable for Sha<'d, crate::Blocking> { +impl<'d> crate::InterruptConfigurable for Sha<'d> { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { unsafe { crate::interrupt::bind_interrupt(crate::peripherals::Interrupt::SHA, handler.handler()); @@ -168,8 +111,6 @@ impl<'d> crate::InterruptConfigurable for Sha<'d, crate::Blocking> { } } -// TODO: Allow/Implement SHA512_(u16) - // A few notes on this implementation with regards to 'memcpy', // - It seems that ptr::write_bytes already acts as volatile, while ptr::copy_* // does not (in this case) @@ -184,188 +125,134 @@ impl<'d> crate::InterruptConfigurable for Sha<'d, crate::Blocking> { // - This means that we need to buffer bytes coming in up to 4 u8's in order // to create a full u32 -// This implementation might fail after u32::MAX/8 bytes, to increase please see -// ::finish() length/self.cursor usage -impl<'d, DM: crate::Mode> Sha<'d, DM> { - pub fn first_run(&self) -> bool { - self.first_run - } - - pub fn finished(&self) -> bool { - self.finished - } - - #[cfg(not(esp32))] - fn process_buffer(&mut self) { - if self.first_run { - // Set SHA_START_REG - self.sha.start().write(|w| unsafe { w.bits(1) }); - self.first_run = false; - } else { - // SET SHA_CONTINUE_REG - self.sha.continue_().write(|w| unsafe { w.bits(1) }); - } - } - - #[cfg(esp32)] - fn process_buffer(&mut self) { - if self.first_run { - match self.mode { - ShaMode::SHA1 => self.sha.sha1_start().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA256 => self.sha.sha256_start().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA384 => self.sha.sha384_start().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA512 => self.sha.sha512_start().write(|w| unsafe { w.bits(1) }), - } - self.first_run = false; - } else { - match self.mode { - ShaMode::SHA1 => self.sha.sha1_continue().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA256 => self.sha.sha256_continue().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA384 => self.sha.sha384_continue().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA512 => self.sha.sha512_continue().write(|w| unsafe { w.bits(1) }), - } - } - } +/// An active digest +/// +/// This implementation might fail after u32::MAX/8 bytes, to increase please +/// see ::finish() length/self.cursor usage +pub struct ShaDigest<'d, A, S: BorrowMut>> { + sha: S, + alignment_helper: AlignmentHelper, + cursor: usize, + first_run: bool, + finished: bool, + phantom: PhantomData<(&'d (), A)>, +} - fn chunk_length(&self) -> usize { - match self.mode { - ShaMode::SHA1 | ShaMode::SHA256 => 64, - #[cfg(not(esp32))] - ShaMode::SHA224 => 64, - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] - _ => 128, - } - } +impl<'d, A: ShaAlgorithm, S: BorrowMut>> ShaDigest<'d, A, S> { + /// Creates a new digest + #[allow(unused_mut)] + pub fn new(mut sha: S) -> Self { + #[cfg(not(esp32))] + // Setup SHA Mode. + sha.borrow_mut() + .sha + .mode() + .write(|w| unsafe { w.mode().bits(A::MODE_AS_BITS) }); - #[cfg(esp32)] - fn is_busy(&self) -> bool { - match self.mode { - ShaMode::SHA1 => self.sha.sha1_busy().read().sha1_busy().bit_is_set(), - ShaMode::SHA256 => self.sha.sha256_busy().read().sha256_busy().bit_is_set(), - ShaMode::SHA384 => self.sha.sha384_busy().read().sha384_busy().bit_is_set(), - ShaMode::SHA512 => self.sha.sha512_busy().read().sha512_busy().bit_is_set(), + Self { + sha, + alignment_helper: AlignmentHelper::default(), + cursor: 0, + first_run: true, + finished: false, + phantom: PhantomData, } } + /// Restores a previously saved digest. #[cfg(not(esp32))] - fn is_busy(&self) -> bool { - self.sha.busy().read().bits() != 0 - } - - pub fn digest_length(&self) -> usize { - match self.mode { - ShaMode::SHA1 => 20, - #[cfg(not(esp32))] - ShaMode::SHA224 => 28, - ShaMode::SHA256 => 32, - #[cfg(any(esp32, esp32s2, esp32s3))] - ShaMode::SHA384 => 48, - #[cfg(any(esp32, esp32s2, esp32s3))] - ShaMode::SHA512 => 64, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA512_224 => 28, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA512_256 => 32, - } - } - - fn flush_data(&mut self) -> nb::Result<(), Infallible> { - if self.is_busy() { - return Err(nb::Error::WouldBlock); + pub fn restore(mut sha: S, ctx: &mut Context) -> Self { + // Setup SHA Mode. + sha.borrow_mut() + .sha + .mode() + .write(|w| unsafe { w.mode().bits(A::MODE_AS_BITS) }); + + // Restore the message buffer + unsafe { + core::ptr::copy_nonoverlapping( + ctx.buffer.as_ptr(), + m_mem(&sha.borrow_mut().sha, 0), + 32, + ); } - let chunk_len = self.chunk_length(); + let mut ah = ctx.alignment_helper.clone(); - let flushed = self.alignment_helper.flush_to( - #[cfg(esp32)] - self.sha.text(0).as_ptr(), - #[cfg(not(esp32))] - self.sha.m_mem(0).as_ptr(), - (self.cursor % chunk_len) / self.alignment_helper.align_size(), - ); + // Restore previously saved hash + ah.volatile_write_regset(h_mem(&sha.borrow_mut().sha, 0), &ctx.saved_digest, 64); - self.cursor = self.cursor.wrapping_add(flushed); - if flushed > 0 && self.cursor % chunk_len == 0 { - self.process_buffer(); - while self.is_busy() {} + Self { + sha, + alignment_helper: ah, + cursor: ctx.cursor, + first_run: ctx.first_run, + finished: ctx.finished, + phantom: PhantomData, } - - Ok(()) } - // This function ensures that incoming data is aligned to u32 (due to issues - // with cpy_mem) - fn write_data<'a>(&mut self, incoming: &'a [u8]) -> nb::Result<&'a [u8], Infallible> { - let mod_cursor = self.cursor % self.chunk_length(); - - let chunk_len = self.chunk_length(); - - let (remaining, bound_reached) = self.alignment_helper.aligned_volatile_copy( - #[cfg(esp32)] - self.sha.text(0).as_ptr(), - #[cfg(not(esp32))] - self.sha.m_mem(0).as_ptr(), - incoming, - chunk_len / self.alignment_helper.align_size(), - mod_cursor / self.alignment_helper.align_size(), - ); - - self.cursor = self.cursor.wrapping_add(incoming.len() - remaining.len()); - - if bound_reached { - self.process_buffer(); + /// Returns true if the hardware is processing the next message. + pub fn is_busy(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(esp32)] { + A::is_busy(&self.sha.borrow().sha) + } else { + self.sha.borrow().sha.busy().read().state().bit_is_set() + } } - - Ok(remaining) } - pub fn update<'a>(&mut self, buffer: &'a [u8]) -> nb::Result<&'a [u8], Infallible> { + /// Updates the SHA digest with the provided data buffer. + pub fn update<'a>(&mut self, incoming: &'a [u8]) -> nb::Result<&'a [u8], Infallible> { if self.is_busy() { return Err(nb::Error::WouldBlock); } - self.finished = false; - let remaining = self.write_data(buffer)?; - - Ok(remaining) + self.write_data(incoming) } - // Finish of the calculation (if not alreaedy) and copy result to output - // After `finish()` is called `update()`s will contribute to a new hash which - // can be calculated again with `finish()`. - // - // Typically output is expected to be the size of digest_length(), but smaller - // inputs can be given to get a "short hash" + /// Finish of the calculation (if not already) and copy result to output + /// After `finish()` is called `update()`s will contribute to a new hash + /// which can be calculated again with `finish()`. + /// + /// Typically, output is expected to be the size of + /// [ShaAlgorithm::DIGEST_LENGTH], but smaller inputs can be given to + /// get a "short hash" pub fn finish(&mut self, output: &mut [u8]) -> nb::Result<(), Infallible> { - // The main purpose of this function is to dynamically generate padding for the - // input. Padding: Append "1" bit, Pad zeros until 512/1024 filled - // then set the message length in the LSB (overwriting the padding) - // If not enough free space for length+1, add length at end of a new zero'd - // block - if self.is_busy() { return Err(nb::Error::WouldBlock); } - let chunk_len = self.chunk_length(); - // Store message length for padding let length = (self.cursor as u64 * 8).to_be_bytes(); nb::block!(self.update(&[0x80]))?; // Append "1" bit - nb::block!(self.flush_data())?; // Flush partial data, ensures aligned cursor + + // Flush partial data, ensures aligned cursor + { + while self.is_busy() {} + + let flushed = self.alignment_helper.flush_to( + m_mem(&self.sha.borrow_mut().sha, 0), + (self.cursor % A::CHUNK_LENGTH) / self.alignment_helper.align_size(), + ); + self.cursor = self.cursor.wrapping_add(flushed); + + if flushed > 0 && self.cursor % A::CHUNK_LENGTH == 0 { + self.process_buffer(); + while self.is_busy() {} + } + } debug_assert!(self.cursor % 4 == 0); - let mod_cursor = self.cursor % chunk_len; - if (chunk_len - mod_cursor) < core::mem::size_of::() { + let mod_cursor = self.cursor % A::CHUNK_LENGTH; + if (A::CHUNK_LENGTH - mod_cursor) < A::CHUNK_LENGTH / 8 { // Zero out remaining data if buffer is almost full (>=448/896), and process // buffer - let pad_len = chunk_len - mod_cursor; + let pad_len = A::CHUNK_LENGTH - mod_cursor; self.alignment_helper.volatile_write_bytes( - #[cfg(esp32)] - self.sha.text(0).as_ptr(), - #[cfg(not(esp32))] - self.sha.m_mem(0).as_ptr(), + m_mem(&self.sha.borrow_mut().sha, 0), 0_u8, pad_len / self.alignment_helper.align_size(), mod_cursor / self.alignment_helper.align_size(), @@ -373,33 +260,27 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { self.process_buffer(); self.cursor = self.cursor.wrapping_add(pad_len); - debug_assert_eq!(self.cursor % chunk_len, 0); + debug_assert_eq!(self.cursor % A::CHUNK_LENGTH, 0); // Spin-wait for finish while self.is_busy() {} } - let mod_cursor = self.cursor % chunk_len; // Should be zero if branched above - let pad_len = chunk_len - mod_cursor - core::mem::size_of::(); + let mod_cursor = self.cursor % A::CHUNK_LENGTH; // Should be zero if branched above + let pad_len = A::CHUNK_LENGTH - mod_cursor - size_of::(); self.alignment_helper.volatile_write_bytes( - #[cfg(esp32)] - self.sha.text(0).as_ptr(), - #[cfg(not(esp32))] - self.sha.m_mem(0).as_ptr(), - 0_u8, + m_mem(&self.sha.borrow_mut().sha, 0), + 0, pad_len / self.alignment_helper.align_size(), mod_cursor / self.alignment_helper.align_size(), ); self.alignment_helper.aligned_volatile_copy( - #[cfg(esp32)] - self.sha.text(0).as_ptr(), - #[cfg(not(esp32))] - self.sha.m_mem(0).as_ptr(), + m_mem(&self.sha.borrow_mut().sha, 0), &length, - chunk_len / self.alignment_helper.align_size(), - (chunk_len - core::mem::size_of::()) / self.alignment_helper.align_size(), + A::CHUNK_LENGTH / self.alignment_helper.align_size(), + (A::CHUNK_LENGTH - size_of::()) / self.alignment_helper.align_size(), ); self.process_buffer(); @@ -409,22 +290,14 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { // ESP32 requires additional load to retrieve output #[cfg(esp32)] { - match self.mode { - ShaMode::SHA1 => unsafe { self.sha.sha1_load().write(|w| w.bits(1)) }, - ShaMode::SHA256 => unsafe { self.sha.sha256_load().write(|w| w.bits(1)) }, - ShaMode::SHA384 => unsafe { self.sha.sha384_load().write(|w| w.bits(1)) }, - ShaMode::SHA512 => unsafe { self.sha.sha512_load().write(|w| w.bits(1)) }, - } + A::load(&mut self.sha.borrow_mut().sha); // Spin wait for result, 8-20 clock cycles according to manual while self.is_busy() {} } self.alignment_helper.volatile_read_regset( - #[cfg(esp32)] - self.sha.text(0).as_ptr(), - #[cfg(not(esp32))] - self.sha.h_mem(0).as_ptr(), + h_mem(&self.sha.borrow_mut().sha, 0), output, core::cmp::min(output.len(), 32) / self.alignment_helper.align_size(), ); @@ -435,4 +308,333 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { Ok(()) } + + /// Save the current state of the digest for later continuation. + #[cfg(not(esp32))] + pub fn save(&mut self, context: &mut Context) -> nb::Result<(), Infallible> { + if self.is_busy() { + return Err(nb::Error::WouldBlock); + } + + context.alignment_helper = self.alignment_helper.clone(); + context.cursor = self.cursor; + context.first_run = self.first_run; + context.finished = self.finished; + + // Save the content of the current hash. + self.alignment_helper.volatile_read_regset( + h_mem(&self.sha.borrow_mut().sha, 0), + &mut context.saved_digest, + 64 / self.alignment_helper.align_size(), + ); + + // Save the content of the current (probably partially written) message. + unsafe { + core::ptr::copy_nonoverlapping( + m_mem(&self.sha.borrow_mut().sha, 0), + context.buffer.as_mut_ptr(), + 32, + ); + } + + Ok(()) + } + + /// Discard the current digest and return the peripheral. + pub fn cancel(self) -> S { + self.sha + } + + /// Processes the data buffer and updates the hash state. + /// + /// This method is platform-specific and differs for ESP32 and non-ESP32 + /// platforms. + fn process_buffer(&mut self) { + #[cfg(not(esp32))] + if self.first_run { + // Set SHA_START_REG + self.sha + .borrow_mut() + .sha + .start() + .write(|w| unsafe { w.bits(1) }); + self.first_run = false; + } else { + // SET SHA_CONTINUE_REG + self.sha + .borrow_mut() + .sha + .continue_() + .write(|w| unsafe { w.bits(1) }); + } + + #[cfg(esp32)] + if self.first_run { + A::start(&mut self.sha.borrow_mut().sha); + self.first_run = false; + } else { + A::r#continue(&mut self.sha.borrow_mut().sha); + } + } + + fn write_data<'a>(&mut self, incoming: &'a [u8]) -> nb::Result<&'a [u8], Infallible> { + if self.is_busy() { + return Err(nb::Error::WouldBlock); + } + self.finished = false; + + let mod_cursor = self.cursor % A::CHUNK_LENGTH; + let chunk_len = A::CHUNK_LENGTH; + + let (remaining, bound_reached) = self.alignment_helper.aligned_volatile_copy( + m_mem(&self.sha.borrow().sha, 0), + incoming, + chunk_len / self.alignment_helper.align_size(), + mod_cursor / self.alignment_helper.align_size(), + ); + + self.cursor = self.cursor.wrapping_add(incoming.len() - remaining.len()); + + if bound_reached { + self.process_buffer(); + } + + Ok(remaining) + } +} + +#[cfg(not(esp32))] +/// Context for a SHA Accelerator driver instance +#[derive(Debug, Clone)] +pub struct Context { + alignment_helper: AlignmentHelper, + cursor: usize, + first_run: bool, + finished: bool, + /// Buffered bytes (SHA_M_n_REG) to be processed. + buffer: [u32; 32], + /// Saved digest (SHA_H_n_REG) for interleaving operation + saved_digest: [u8; 64], + phantom: PhantomData, +} + +#[cfg(not(esp32))] +impl Context { + /// Create a new empty context + pub fn new() -> Self { + Self { + cursor: 0, + first_run: true, + finished: false, + alignment_helper: AlignmentHelper::default(), + buffer: [0; 32], + saved_digest: [0; 64], + phantom: PhantomData, + } + } + + /// Indicates if the SHA context is in the first run. + /// + /// Returns `true` if this is the first time processing data with the SHA + /// instance, otherwise returns `false`. + pub fn first_run(&self) -> bool { + self.first_run + } + + /// Indicates if the SHA context has finished processing the data. + /// + /// Returns `true` if the SHA calculation is complete, otherwise returns. + pub fn finished(&self) -> bool { + self.finished + } +} + +#[cfg(not(esp32))] +impl Default for Context { + fn default() -> Self { + Self::new() + } +} + +/// This trait encapsulates the configuration for a specific SHA algorithm. +pub trait ShaAlgorithm: crate::private::Sealed { + /// Constant containing the name of the algorithm as a string. + const ALGORITHM: &'static str; + + /// The length of the chunk that the algorithm processes at a time. + /// + /// For example, in SHA-256, this would typically be 64 bytes. + const CHUNK_LENGTH: usize; + + /// The length of the resulting digest produced by the algorithm. + /// + /// For example, in SHA-256, this would be 32 bytes. + const DIGEST_LENGTH: usize; + + #[cfg(feature = "digest")] + #[doc(hidden)] + type DigestOutputSize: digest::generic_array::ArrayLength + 'static; + + #[cfg(not(esp32))] + #[doc(hidden)] + const MODE_AS_BITS: u8; + + #[cfg(esp32)] + #[doc(hidden)] + // Initiate the operation + fn start(sha: &mut crate::peripherals::SHA); + + #[cfg(esp32)] + #[doc(hidden)] + // Continue the operation + fn r#continue(sha: &mut crate::peripherals::SHA); + + #[cfg(esp32)] + #[doc(hidden)] + // Calculate the final hash + fn load(sha: &mut crate::peripherals::SHA); + + #[cfg(esp32)] + #[doc(hidden)] + // Check if peripheral is busy + fn is_busy(sha: &crate::peripherals::SHA) -> bool; +} + +/// implement digest traits if digest feature is present. +/// Note: digest has a blanket trait implementation for [digest::Digest] for any +/// element that implements FixedOutput + Default + Update + HashMarker +#[cfg(feature = "digest")] +impl<'d, A: ShaAlgorithm, S: BorrowMut>> digest::HashMarker for ShaDigest<'d, A, S> {} + +#[cfg(feature = "digest")] +impl<'d, A: ShaAlgorithm, S: BorrowMut>> digest::OutputSizeUser for ShaDigest<'d, A, S> { + type OutputSize = A::DigestOutputSize; +} + +#[cfg(feature = "digest")] +impl<'d, A: ShaAlgorithm, S: BorrowMut>> digest::Update for ShaDigest<'d, A, S> { + fn update(&mut self, data: &[u8]) { + let mut remaining = data.as_ref(); + while !remaining.is_empty() { + remaining = nb::block!(Self::update(self, remaining)).unwrap(); + } + } +} + +#[cfg(feature = "digest")] +impl<'d, A: ShaAlgorithm, S: BorrowMut>> digest::FixedOutput for ShaDigest<'d, A, S> { + fn finalize_into(mut self, out: &mut digest::Output) { + nb::block!(self.finish(out)).unwrap(); + } +} + +/// This macro implements the Sha<'a, DM> trait for a specified Sha algorithm +/// and a set of parameters +macro_rules! impl_sha { + ($name: ident, $mode_bits: tt, $digest_length: tt, $chunk_length: tt) => { + /// A SHA implementation struct. + /// + /// This struct is generated by the macro and represents a specific SHA hashing + /// algorithm (e.g., SHA-256, SHA-1). It manages the context and state required + /// for processing data using the selected hashing algorithm. + /// + /// The struct provides various functionalities such as initializing the hashing + /// process, updating the internal state with new data, and finalizing the + /// hashing operation to generate the final digest. + #[non_exhaustive] + pub struct $name; + + impl crate::private::Sealed for $name {} + + impl $crate::sha::ShaAlgorithm for $name { + const ALGORITHM: &'static str = stringify!($name); + + const CHUNK_LENGTH: usize = $chunk_length; + + const DIGEST_LENGTH: usize = $digest_length; + + #[cfg(not(esp32))] + const MODE_AS_BITS: u8 = $mode_bits; + + #[cfg(feature = "digest")] + // We use paste to append `U` to the digest size to match a const defined in + // digest + type DigestOutputSize = paste::paste!(digest::consts::[< U $digest_length >]); + + #[cfg(esp32)] + fn start(sha: &mut crate::peripherals::SHA) { + paste::paste! { + sha.[< $name:lower _start >]().write(|w| w.[< $name:lower _start >]().set_bit()); + } + } + + #[cfg(esp32)] + fn r#continue(sha: &mut crate::peripherals::SHA) { + paste::paste! { + sha.[< $name:lower _continue >]().write(|w| w.[< $name:lower _continue >]().set_bit()); + } + } + + #[cfg(esp32)] + fn load(sha: &mut crate::peripherals::SHA) { + paste::paste! { + sha.[< $name:lower _load >]().write(|w| w.[< $name:lower _load >]().set_bit()); + } + } + + #[cfg(esp32)] + fn is_busy(sha: &crate::peripherals::SHA) -> bool { + paste::paste! { + sha.[< $name:lower _busy >]().read().[< $name:lower _busy >]().bit_is_set() + } + } + } + }; +} + +// All the hash algorithms introduced in FIPS PUB 180-4 Spec. +// – SHA-1 +// – SHA-224 +// – SHA-256 +// – SHA-384 +// – SHA-512 +// – SHA-512/224 +// – SHA-512/256 +// – SHA-512/t (not implemented yet) +// Two working modes +// – Typical SHA +// – DMA-SHA (not implemented yet) +// +// TODO: Allow/Implement SHA512_(u16) +impl_sha!(Sha1, 0, 20, 64); +#[cfg(not(esp32))] +impl_sha!(Sha224, 1, 28, 64); +impl_sha!(Sha256, 2, 32, 64); +#[cfg(any(esp32, esp32s2, esp32s3))] +impl_sha!(Sha384, 3, 48, 128); +#[cfg(any(esp32, esp32s2, esp32s3))] +impl_sha!(Sha512, 4, 64, 128); +#[cfg(any(esp32s2, esp32s3))] +impl_sha!(Sha512_224, 5, 28, 128); +#[cfg(any(esp32s2, esp32s3))] +impl_sha!(Sha512_256, 6, 32, 128); + +fn h_mem(sha: &crate::peripherals::SHA, index: usize) -> *mut u32 { + cfg_if::cfg_if! { + if #[cfg(esp32)] { + sha.text(index).as_ptr() + } else { + sha.h_mem(index).as_ptr() + } + } +} + +fn m_mem(sha: &crate::peripherals::SHA, index: usize) -> *mut u32 { + cfg_if::cfg_if! { + if #[cfg(esp32)] { + sha.text(index).as_ptr() + } else { + sha.m_mem(index).as_ptr() + } + } } diff --git a/esp-hal/src/soc/esp32/cpu_control.rs b/esp-hal/src/soc/esp32/cpu_control.rs index 7319639379a..974a216e405 100644 --- a/esp-hal/src/soc/esp32/cpu_control.rs +++ b/esp-hal/src/soc/esp32/cpu_control.rs @@ -15,7 +15,7 @@ //! # use esp_hal::prelude::*; //! static mut APP_CORE_STACK: Stack<8192> = Stack::new(); //! -//! # let delay = Delay::new(&clocks); +//! # let delay = Delay::new(); //! //! let counter = Mutex::new(RefCell::new(0)); //! @@ -99,14 +99,17 @@ impl Stack { } } + /// Returns the size of the stack. pub const fn len(&self) -> usize { SIZE } + /// Provides a mutable pointer to the bottom of the stack. pub fn bottom(&mut self) -> *mut u32 { self.mem.as_mut_ptr() as *mut u32 } + /// Provides a mutable pointer to the top of the stack. pub fn top(&mut self) -> *mut u32 { unsafe { self.bottom().add(SIZE / 4) } } @@ -132,9 +135,11 @@ impl<'a> Drop for AppCoreGuard<'a> { } } +/// Represents errors that can occur while working with the core. #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { + /// The core is already running. CoreAlreadyRunning, } @@ -168,6 +173,7 @@ unsafe fn internal_park_core(core: Cpu) { } impl<'d> CpuControl<'d> { + /// Creates a new instance of `CpuControl`. pub fn new(cpu_control: impl Peripheral

+ 'd) -> CpuControl<'d> { crate::into_ref!(cpu_control); diff --git a/esp-hal/src/soc/esp32/efuse/mod.rs b/esp-hal/src/soc/esp32/efuse/mod.rs index 0a6de1bac83..ab1e40073e1 100644 --- a/esp-hal/src/soc/esp32/efuse/mod.rs +++ b/esp-hal/src/soc/esp32/efuse/mod.rs @@ -1,13 +1,15 @@ //! # Reading of eFuses (ESP32) //! //! ## Overview +//! //! The `efuse` module provides functionality for reading eFuse data //! from the `ESP32` chip, allowing access to various chip-specific information -//! such as : +//! such as: +//! //! * MAC address -//! * core count -//! * CPU frequency -//! * chip type +//! * Chip type, revision +//! * Core count +//! * Max CPU frequency //! //! and more. It is useful for retrieving chip-specific configuration and //! identification data during runtime. @@ -15,8 +17,10 @@ //! The `Efuse` struct represents the eFuse peripheral and is responsible for //! reading various eFuse fields and values. //! -//! ## Examples +//! ## Example +//! //! ### Read chip's MAC address from the eFuse storage. +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; @@ -25,7 +29,7 @@ //! # use core::writeln; //! # use core::fmt::Write; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, &clocks, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -47,20 +51,30 @@ use crate::peripherals::EFUSE; mod fields; +/// A struct representing the eFuse functionality of the chip. pub struct Efuse; +/// Representing different types of ESP32 chips. #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum ChipType { + /// Represents the ESP32 D0WDQ6 chip variant. Esp32D0wdq6, + /// Represents the ESP32 D0WDQ5 chip variant. Esp32D0wdq5, + /// Represents the ESP32 D2WDQ5 chip variant. Esp32D2wdq5, + /// Represents the ESP32 Pico D2 chip variant. Esp32Picod2, + /// Represents the ESP32 Pico D4 chip variant. Esp32Picod4, + /// Represents the ESP32 Pico v3.02 chip variant. Esp32Picov302, + /// Represents an unknown or unsupported chip variant. Unknown, } impl Efuse { + /// Reads the base MAC address from the eFuse memory. pub fn read_base_mac_address() -> [u8; 6] { Self::read_field_be(MAC) } diff --git a/esp-hal/src/soc/esp32/gpio.rs b/esp-hal/src/soc/esp32/gpio.rs index fb8b29ed161..8b62dba181b 100644 --- a/esp-hal/src/soc/esp32/gpio.rs +++ b/esp-hal/src/soc/esp32/gpio.rs @@ -8,8 +8,7 @@ //! //! Let's get through the functionality and configurations provided by this GPIO //! module: -//! - `get_io_mux_reg(gpio_num: u8) -> &'static -//! crate::peripherals::io_mux::GPIO0:`: +//! - `get_io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0:`: //! * This function returns a reference to the GPIO register associated //! with the given GPIO number. It uses unsafe code and transmutation to //! access the GPIO registers based on the provided GPIO number. @@ -46,6 +45,8 @@ //! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the //! `gpio` peripheral to access the appropriate registers. +use core::mem::transmute; + use crate::{ gpio::{ AlternateFunction, @@ -54,10 +55,11 @@ use crate::{ InterruptStatusRegisterAccessBank0, InterruptStatusRegisterAccessBank1, }, - peripherals::GPIO, + peripherals::{io_mux, GPIO, IO_MUX}, Cpu, }; +/// The total number of GPIO pins available. pub const NUM_PINS: usize = 40; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; @@ -71,155 +73,47 @@ pub(crate) const ZERO_INPUT: u8 = 0x30; pub(crate) const GPIO_FUNCTION: AlternateFunction = AlternateFunction::Function2; -pub(crate) fn get_io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO0 { +pub(crate) fn get_io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0 { unsafe { - let iomux = &*crate::peripherals::IO_MUX::PTR; + let iomux = &*IO_MUX::PTR; match gpio_num { - 0 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO0, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio0()), - 1 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO1, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio1()), - 2 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO2, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio2()), - 3 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO3, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio3()), - 4 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO4, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio4()), - 5 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO5, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio5()), - 6 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO6, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio6()), - 7 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO7, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio7()), - 8 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO8, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio8()), - 9 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO9, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio9()), - 10 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO10, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio10()), - 11 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO11, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio11()), - 12 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO12, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio12()), - 13 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO13, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio13()), - 14 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO14, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio14()), - 15 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO15, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio15()), - 16 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO16, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio16()), - 17 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO17, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio17()), - 18 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO18, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio18()), - 19 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO19, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio19()), - 20 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO20, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio20()), - 21 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO21, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio21()), - 22 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO22, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio22()), - 23 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO23, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio23()), - 24 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO24, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio24()), - 25 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO25, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio25()), - 26 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO26, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio26()), - 27 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO27, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio27()), - 32 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO32, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio32()), - 33 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO33, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio33()), - 34 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO34, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio34()), - 35 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO35, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio35()), - 36 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO36, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio36()), - 37 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO37, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio37()), - 38 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO38, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio38()), - 39 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO39, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio39()), + 0 => transmute::<&'static io_mux::GPIO0, &'static io_mux::GPIO0>(iomux.gpio0()), + 1 => transmute::<&'static io_mux::GPIO1, &'static io_mux::GPIO0>(iomux.gpio1()), + 2 => transmute::<&'static io_mux::GPIO2, &'static io_mux::GPIO0>(iomux.gpio2()), + 3 => transmute::<&'static io_mux::GPIO3, &'static io_mux::GPIO0>(iomux.gpio3()), + 4 => transmute::<&'static io_mux::GPIO4, &'static io_mux::GPIO0>(iomux.gpio4()), + 5 => transmute::<&'static io_mux::GPIO5, &'static io_mux::GPIO0>(iomux.gpio5()), + 6 => transmute::<&'static io_mux::GPIO6, &'static io_mux::GPIO0>(iomux.gpio6()), + 7 => transmute::<&'static io_mux::GPIO7, &'static io_mux::GPIO0>(iomux.gpio7()), + 8 => transmute::<&'static io_mux::GPIO8, &'static io_mux::GPIO0>(iomux.gpio8()), + 9 => transmute::<&'static io_mux::GPIO9, &'static io_mux::GPIO0>(iomux.gpio9()), + 10 => transmute::<&'static io_mux::GPIO10, &'static io_mux::GPIO0>(iomux.gpio10()), + 11 => transmute::<&'static io_mux::GPIO11, &'static io_mux::GPIO0>(iomux.gpio11()), + 12 => transmute::<&'static io_mux::GPIO12, &'static io_mux::GPIO0>(iomux.gpio12()), + 13 => transmute::<&'static io_mux::GPIO13, &'static io_mux::GPIO0>(iomux.gpio13()), + 14 => transmute::<&'static io_mux::GPIO14, &'static io_mux::GPIO0>(iomux.gpio14()), + 15 => transmute::<&'static io_mux::GPIO15, &'static io_mux::GPIO0>(iomux.gpio15()), + 16 => transmute::<&'static io_mux::GPIO16, &'static io_mux::GPIO0>(iomux.gpio16()), + 17 => transmute::<&'static io_mux::GPIO17, &'static io_mux::GPIO0>(iomux.gpio17()), + 18 => transmute::<&'static io_mux::GPIO18, &'static io_mux::GPIO0>(iomux.gpio18()), + 19 => transmute::<&'static io_mux::GPIO19, &'static io_mux::GPIO0>(iomux.gpio19()), + 20 => transmute::<&'static io_mux::GPIO20, &'static io_mux::GPIO0>(iomux.gpio20()), + 21 => transmute::<&'static io_mux::GPIO21, &'static io_mux::GPIO0>(iomux.gpio21()), + 22 => transmute::<&'static io_mux::GPIO22, &'static io_mux::GPIO0>(iomux.gpio22()), + 23 => transmute::<&'static io_mux::GPIO23, &'static io_mux::GPIO0>(iomux.gpio23()), + 24 => transmute::<&'static io_mux::GPIO24, &'static io_mux::GPIO0>(iomux.gpio24()), + 25 => transmute::<&'static io_mux::GPIO25, &'static io_mux::GPIO0>(iomux.gpio25()), + 26 => transmute::<&'static io_mux::GPIO26, &'static io_mux::GPIO0>(iomux.gpio26()), + 27 => transmute::<&'static io_mux::GPIO27, &'static io_mux::GPIO0>(iomux.gpio27()), + 32 => transmute::<&'static io_mux::GPIO32, &'static io_mux::GPIO0>(iomux.gpio32()), + 33 => transmute::<&'static io_mux::GPIO33, &'static io_mux::GPIO0>(iomux.gpio33()), + 34 => transmute::<&'static io_mux::GPIO34, &'static io_mux::GPIO0>(iomux.gpio34()), + 35 => transmute::<&'static io_mux::GPIO35, &'static io_mux::GPIO0>(iomux.gpio35()), + 36 => transmute::<&'static io_mux::GPIO36, &'static io_mux::GPIO0>(iomux.gpio36()), + 37 => transmute::<&'static io_mux::GPIO37, &'static io_mux::GPIO0>(iomux.gpio37()), + 38 => transmute::<&'static io_mux::GPIO38, &'static io_mux::GPIO0>(iomux.gpio38()), + 39 => transmute::<&'static io_mux::GPIO39, &'static io_mux::GPIO0>(iomux.gpio39()), _ => panic!(), } } @@ -910,74 +804,74 @@ crate::gpio::gpio! { } crate::gpio::analog! { - (36, 0, sensor_pads(), sense1_mux_sel, sense1_fun_sel, sense1_fun_ie) - (37, 1, sensor_pads(), sense2_mux_sel, sense2_fun_sel, sense2_fun_ie) - (38, 2, sensor_pads(), sense3_mux_sel, sense3_fun_sel, sense3_fun_ie) - (39, 3, sensor_pads(), sense4_mux_sel, sense4_fun_sel, sense4_fun_ie) - (34, 4, adc_pad(), adc1_mux_sel, adc1_fun_sel, adc1_fun_ie) - (35, 5, adc_pad(), adc2_mux_sel, adc2_fun_sel, adc1_fun_ie) - (25, 6, pad_dac1(), mux_sel, fun_sel, fun_ie, rue, rde) - (26, 7, pad_dac2(), mux_sel, fun_sel, fun_ie, rue, rde) - (33, 8, xtal_32k_pad(), x32n_mux_sel, x32n_fun_sel, x32n_fun_ie, x32n_rue, x32n_rde ) - (32, 9, xtal_32k_pad(), x32p_mux_sel, x32p_fun_sel, x32p_fun_ie, x32p_rue, x32p_rde ) - (4, 10, touch_pad0(), mux_sel, fun_sel, fun_ie, rue, rde ) - (0, 11, touch_pad1(), mux_sel, fun_sel, fun_ie, rue, rde ) - (2, 12, touch_pad2(), mux_sel, fun_sel, fun_ie, rue, rde ) - (15, 13, touch_pad3(), mux_sel, fun_sel, fun_ie, rue, rde ) - (13, 14, touch_pad4(), mux_sel, fun_sel, fun_ie, rue, rde ) - (12, 15, touch_pad5(), mux_sel, fun_sel, fun_ie, rue, rde ) - (14, 16, touch_pad6(), mux_sel, fun_sel, fun_ie, rue, rde ) - (27, 17, touch_pad7(), mux_sel, fun_sel, fun_ie, rue, rde ) + (36, 0, sensor_pads(), sense1_mux_sel, sense1_fun_sel, sense1_fun_ie) + (37, 1, sensor_pads(), sense2_mux_sel, sense2_fun_sel, sense2_fun_ie) + (38, 2, sensor_pads(), sense3_mux_sel, sense3_fun_sel, sense3_fun_ie) + (39, 3, sensor_pads(), sense4_mux_sel, sense4_fun_sel, sense4_fun_ie) + (34, 4, adc_pad(), adc1_mux_sel, adc1_fun_sel, adc1_fun_ie) + (35, 5, adc_pad(), adc2_mux_sel, adc2_fun_sel, adc1_fun_ie) + (25, 6, pad_dac1(), mux_sel, fun_sel, fun_ie, rue, rde) + (26, 7, pad_dac2(), mux_sel, fun_sel, fun_ie, rue, rde) + (33, 8, xtal_32k_pad(), x32n_mux_sel, x32n_fun_sel, x32n_fun_ie, x32n_rue, x32n_rde ) + (32, 9, xtal_32k_pad(), x32p_mux_sel, x32p_fun_sel, x32p_fun_ie, x32p_rue, x32p_rde ) + (4, 10, touch_pad0(), mux_sel, fun_sel, fun_ie, rue, rde ) + (0, 11, touch_pad1(), mux_sel, fun_sel, fun_ie, rue, rde ) + (2, 12, touch_pad2(), mux_sel, fun_sel, fun_ie, rue, rde ) + (15, 13, touch_pad3(), mux_sel, fun_sel, fun_ie, rue, rde ) + (13, 14, touch_pad4(), mux_sel, fun_sel, fun_ie, rue, rde ) + (12, 15, touch_pad5(), mux_sel, fun_sel, fun_ie, rue, rde ) + (14, 16, touch_pad6(), mux_sel, fun_sel, fun_ie, rue, rde ) + (27, 17, touch_pad7(), mux_sel, fun_sel, fun_ie, rue, rde ) } crate::gpio::rtc_pins! { - (36, 0, sensor_pads(), sense1_, sense1_hold_force ) - (37, 1, sensor_pads(), sense2_, sense2_hold_force ) - (38, 2, sensor_pads(), sense3_, sense3_hold_force ) - (39, 3, sensor_pads(), sense4_, sense4_hold_force ) - (34, 4, adc_pad(), adc1_, adc1_hold_force ) - (35, 5, adc_pad(), adc2_, adc2_hold_force ) - (25, 6, pad_dac1(), "", pdac1_hold_force, rue, rde ) - (26, 7, pad_dac2(), "", pdac2_hold_force, rue, rde ) - (33, 8, xtal_32k_pad(), x32n_, x32n_hold_force, x32n_rue, x32n_rde ) - (32, 9, xtal_32k_pad(), x32p_, x32p_hold_force, x32p_rue, x32p_rde ) - (4, 10, touch_pad0(), "", touch_pad0_hold_force, rue, rde ) - (0, 11, touch_pad1(), "", touch_pad1_hold_force, rue, rde ) - (2, 12, touch_pad2(), "", touch_pad2_hold_force, rue, rde ) - (15, 13, touch_pad3(), "", touch_pad3_hold_force, rue, rde ) - (13, 14, touch_pad4(), "", touch_pad4_hold_force, rue, rde ) - (12, 15, touch_pad5(), "", touch_pad5_hold_force, rue, rde ) - (14, 16, touch_pad6(), "", touch_pad6_hold_force, rue, rde ) - (27, 17, touch_pad7(), "", touch_pad7_hold_force, rue, rde ) + (36, 0, sensor_pads(), sense1_, sense1_hold_force ) + (37, 1, sensor_pads(), sense2_, sense2_hold_force ) + (38, 2, sensor_pads(), sense3_, sense3_hold_force ) + (39, 3, sensor_pads(), sense4_, sense4_hold_force ) + (34, 4, adc_pad(), adc1_, adc1_hold_force ) + (35, 5, adc_pad(), adc2_, adc2_hold_force ) + (25, 6, pad_dac1(), "", pdac1_hold_force, rue, rde ) + (26, 7, pad_dac2(), "", pdac2_hold_force, rue, rde ) + (33, 8, xtal_32k_pad(), x32n_, x32n_hold_force, x32n_rue, x32n_rde ) + (32, 9, xtal_32k_pad(), x32p_, x32p_hold_force, x32p_rue, x32p_rde ) + (4, 10, touch_pad0(), "", touch_pad0_hold_force, rue, rde ) + (0, 11, touch_pad1(), "", touch_pad1_hold_force, rue, rde ) + (2, 12, touch_pad2(), "", touch_pad2_hold_force, rue, rde ) + (15, 13, touch_pad3(), "", touch_pad3_hold_force, rue, rde ) + (13, 14, touch_pad4(), "", touch_pad4_hold_force, rue, rde ) + (12, 15, touch_pad5(), "", touch_pad5_hold_force, rue, rde ) + (14, 16, touch_pad6(), "", touch_pad6_hold_force, rue, rde ) + (27, 17, touch_pad7(), "", touch_pad7_hold_force, rue, rde ) } crate::gpio::touch_into! { // (touch_nr, pin_nr, rtc_pin, touch_comb_reg_nr, normal_pin) - (0, 4, 10, sar_touch_thres1, touch_out_th0, true ) - (1, 0, 11, sar_touch_thres1, touch_out_th1, true ) - (2, 2, 12, sar_touch_thres2, touch_out_th2, true ) - (3, 15, 13, sar_touch_thres2, touch_out_th3, true ) - (4, 13, 14, sar_touch_thres3, touch_out_th4, true ) - (5, 12, 15, sar_touch_thres3, touch_out_th5, true ) - (6, 14, 16, sar_touch_thres4, touch_out_th6, true ) - (7, 27, 17, sar_touch_thres4, touch_out_th7, true ) - --- - (8, 33, 8, sar_touch_thres5, touch_out_th8, false ) - (9, 32, 9, sar_touch_thres5, touch_out_th9, false ) + (0, 4, 10, sar_touch_thres1, touch_out_th0, true ) + (1, 0, 11, sar_touch_thres1, touch_out_th1, true ) + (2, 2, 12, sar_touch_thres2, touch_out_th2, true ) + (3, 15, 13, sar_touch_thres2, touch_out_th3, true ) + (4, 13, 14, sar_touch_thres3, touch_out_th4, true ) + (5, 12, 15, sar_touch_thres3, touch_out_th5, true ) + (6, 14, 16, sar_touch_thres4, touch_out_th6, true ) + (7, 27, 17, sar_touch_thres4, touch_out_th7, true ) + // --- + (8, 33, 8, sar_touch_thres5, touch_out_th8, false ) + (9, 32, 9, sar_touch_thres5, touch_out_th9, false ) } crate::gpio::touch_common! { // (touch_nr, pin_nr, touch_out_reg, touch_thres_reg ) - (0, 4, sar_touch_out1, touch_meas_out0, sar_touch_thres1, touch_out_th0) - (1, 0, sar_touch_out1, touch_meas_out1, sar_touch_thres1, touch_out_th1) - (2, 2, sar_touch_out2, touch_meas_out2, sar_touch_thres2, touch_out_th2) - (3, 15, sar_touch_out2, touch_meas_out3, sar_touch_thres2, touch_out_th3) - (4, 13, sar_touch_out3, touch_meas_out4, sar_touch_thres3, touch_out_th4) - (5, 12, sar_touch_out3, touch_meas_out5, sar_touch_thres3, touch_out_th5) - (6, 14, sar_touch_out4, touch_meas_out6, sar_touch_thres4, touch_out_th6) - (7, 27, sar_touch_out4, touch_meas_out7, sar_touch_thres4, touch_out_th7) - (8, 33, sar_touch_out5, touch_meas_out8, sar_touch_thres5, touch_out_th8) - (9, 32, sar_touch_out5, touch_meas_out9, sar_touch_thres5, touch_out_th9) + (0, 4, sar_touch_out1, touch_meas_out0, sar_touch_thres1, touch_out_th0) + (1, 0, sar_touch_out1, touch_meas_out1, sar_touch_thres1, touch_out_th1) + (2, 2, sar_touch_out2, touch_meas_out2, sar_touch_thres2, touch_out_th2) + (3, 15, sar_touch_out2, touch_meas_out3, sar_touch_thres2, touch_out_th3) + (4, 13, sar_touch_out3, touch_meas_out4, sar_touch_thres3, touch_out_th4) + (5, 12, sar_touch_out3, touch_meas_out5, sar_touch_thres3, touch_out_th5) + (6, 14, sar_touch_out4, touch_meas_out6, sar_touch_thres4, touch_out_th6) + (7, 27, sar_touch_out4, touch_meas_out7, sar_touch_thres4, touch_out_th7) + (8, 33, sar_touch_out5, touch_meas_out8, sar_touch_thres5, touch_out_th8) + (9, 32, sar_touch_out5, touch_meas_out9, sar_touch_thres5, touch_out_th9) } impl InterruptStatusRegisterAccess for InterruptStatusRegisterAccessBank0 { diff --git a/esp-hal/src/soc/esp32/mod.rs b/esp-hal/src/soc/esp32/mod.rs index be3b74f9ab3..6196075d2fa 100644 --- a/esp-hal/src/soc/esp32/mod.rs +++ b/esp-hal/src/soc/esp32/mod.rs @@ -7,11 +7,7 @@ use core::ptr::addr_of_mut; -use self::peripherals::{LPWR, TIMG0, TIMG1}; -use crate::{ - rtc_cntl::{Rtc, SocResetReason}, - timer::timg::Wdt, -}; +use crate::rtc_cntl::SocResetReason; pub mod cpu_control; pub mod efuse; @@ -33,15 +29,19 @@ macro_rules! chip { pub use chip; pub(crate) mod constants { + /// The base clock frequency for the I2S peripheral (Hertz). pub const I2S_SCLK: u32 = 160_000_000; + /// The default clock source for I2S operations. pub const I2S_DEFAULT_CLK_SRC: u32 = 2; - + /// The starting address of the Remote Control (RMT) module's RAM. pub const RMT_RAM_START: usize = 0x3ff56800; + /// The size, in bytes, of each RMT channel's dedicated RAM. pub const RMT_CHANNEL_RAM_SIZE: usize = 64; - + /// The lower bound of the system's DRAM (Data RAM) address space. pub const SOC_DRAM_LOW: u32 = 0x3FFA_E000; + /// The upper bound of the system's DRAM (Data RAM) address space. pub const SOC_DRAM_HIGH: u32 = 0x4000_0000; - + /// A reference clock tick of 1 MHz. pub const REF_TICK: fugit::HertzU32 = fugit::HertzU32::MHz(1); } @@ -107,9 +107,6 @@ pub unsafe extern "C" fn ESP32Reset() -> ! { stack_chk_guard.write_volatile(0xdeadbabe); } - crate::interrupt::setup_interrupts(); - crate::time::time_init(); - // continue with default reset handler xtensa_lx_rt::Reset(); } @@ -122,13 +119,3 @@ pub unsafe extern "C" fn ESP32Reset() -> ! { pub extern "Rust" fn __init_data() -> bool { false } - -#[export_name = "__post_init"] -unsafe fn post_init() { - // RTC domain must be enabled before we try to disable - let mut rtc = Rtc::new(LPWR::steal()); - rtc.rwdt.disable(); - - Wdt::::set_wdt_enabled(false); - Wdt::::set_wdt_enabled(false); -} diff --git a/esp-hal/src/soc/esp32/peripherals.rs b/esp-hal/src/soc/esp32/peripherals.rs index fe2be78c2be..ab9b31ce6d1 100644 --- a/esp-hal/src/soc/esp32/peripherals.rs +++ b/esp-hal/src/soc/esp32/peripherals.rs @@ -63,6 +63,7 @@ crate::peripherals! { SPI2 <= SPI2 (SPI2_DMA, SPI2), SPI3 <= SPI3 (SPI3_DMA, SPI3), SYSTEM <= DPORT, + SW_INTERRUPT <= virtual, TIMG0 <= TIMG0, TIMG1 <= TIMG1, TOUCH <= virtual, diff --git a/esp-hal/src/soc/esp32/psram.rs b/esp-hal/src/soc/esp32/psram.rs index 83c684e3882..0ad25216335 100644 --- a/esp-hal/src/soc/esp32/psram.rs +++ b/esp-hal/src/soc/esp32/psram.rs @@ -18,8 +18,12 @@ //! //! NOTE: If you want to use `PSRAM` on `ESP32` or `ESP32-S3`, it'll work only //! in `release` mode. + +/// The starting virtual address of the PSRAM (Pseudo-SRAM) region in memory. pub const PSRAM_VADDR_START: usize = 0x3F800000; +/// Retrieves the starting virtual address of the PSRAM (Pseudo-SRAM) region in +/// memory. pub fn psram_vaddr_start() -> usize { PSRAM_VADDR_START } @@ -36,8 +40,10 @@ cfg_if::cfg_if! { } } +/// The total size of the PSRAM (Pseudo-SRAM) in bytes. pub const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024; +/// Initializes the PSRAM memory on supported devices. #[cfg(any(feature = "psram-2m", feature = "psram-4m", feature = "psram-8m"))] pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral

) { utils::psram_init(); diff --git a/esp-hal/src/soc/esp32/trng.rs b/esp-hal/src/soc/esp32/trng.rs index f8bf24a4c83..aeb55fcab16 100644 --- a/esp-hal/src/soc/esp32/trng.rs +++ b/esp-hal/src/soc/esp32/trng.rs @@ -1,6 +1,8 @@ //! Helper functions for TRNG functionality -pub fn ensure_randomness() { +/// Enable true randomness by enabling the entropy source. +/// Blocks `ADC` usage. +pub(crate) fn ensure_randomness() { let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() }; let sens = unsafe { &*crate::peripherals::SENS::ptr() }; let dport = unsafe { &*crate::peripherals::DPORT::ptr() }; @@ -104,7 +106,8 @@ pub fn ensure_randomness() { } } -pub fn revert_trng() { +/// Disable true randomness. Unlocks `ADC` peripheral. +pub(crate) fn revert_trng() { let sens = unsafe { &*crate::peripherals::SENS::ptr() }; let i2s0 = unsafe { &*crate::peripherals::I2S0::ptr() }; let apb_ctrl = unsafe { &*crate::peripherals::APB_CTRL::ptr() }; diff --git a/esp-hal/src/soc/esp32c2/efuse/mod.rs b/esp-hal/src/soc/esp32c2/efuse/mod.rs index 257ceef3639..3aeb5fc0a94 100644 --- a/esp-hal/src/soc/esp32c2/efuse/mod.rs +++ b/esp-hal/src/soc/esp32c2/efuse/mod.rs @@ -3,11 +3,10 @@ //! ## Overview //! The `efuse` module provides functionality for reading eFuse data //! from the `ESP32-C2` chip, allowing access to various chip-specific -//! information such as : +//! information such as: +//! //! * MAC address -//! * core count -//! * CPU frequency -//! * chip type +//! * ADC calibration information //! //! and more. It is useful for retrieving chip-specific configuration and //! identification data during runtime. @@ -15,8 +14,10 @@ //! The `Efuse` struct represents the eFuse peripheral and is responsible for //! reading various eFuse fields and values. //! -//! ## Examples +//! ## Example +//! //! ### Read chip's MAC address from the eFuse storage. +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; @@ -25,7 +26,7 @@ //! # use core::writeln; //! # use core::fmt::Write; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, &clocks, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -45,6 +46,7 @@ use crate::{analog::adc::Attenuation, peripherals::EFUSE}; mod fields; +/// A struct representing the eFuse functionality of the chip. pub struct Efuse; impl Efuse { diff --git a/esp-hal/src/soc/esp32c2/gpio.rs b/esp-hal/src/soc/esp32c2/gpio.rs index 103f97f6c3f..630befe48fd 100644 --- a/esp-hal/src/soc/esp32c2/gpio.rs +++ b/esp-hal/src/soc/esp32c2/gpio.rs @@ -50,6 +50,7 @@ use crate::{ peripherals::GPIO, }; +/// The total number of GPIO pins available. pub const NUM_PINS: usize = 21; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; @@ -197,6 +198,15 @@ crate::gpio::gpio! { (20, 0, InputOutput (0 => U0RXD) ()) } +crate::gpio::rtc_pins! { + 0 + 1 + 2 + 3 + 4 + 5 +} + crate::gpio::analog! { 0 1 diff --git a/esp-hal/src/soc/esp32c2/mod.rs b/esp-hal/src/soc/esp32c2/mod.rs index 71a0f469765..32717ba8bc5 100644 --- a/esp-hal/src/soc/esp32c2/mod.rs +++ b/esp-hal/src/soc/esp32c2/mod.rs @@ -5,9 +5,6 @@ //! The `SOC` module provides access, functions and structures that are useful //! for interacting with various system-related peripherals on `ESP32-C2` chip. -use self::peripherals::{LPWR, TIMG0}; -use crate::{rtc_cntl::Rtc, timer::timg::Wdt}; - pub mod efuse; pub mod gpio; pub mod peripherals; @@ -30,18 +27,11 @@ pub(crate) mod registers { } pub(crate) mod constants { + /// The lower bound of the system's DRAM (Data RAM) address space. pub const SOC_DRAM_LOW: u32 = 0x3FCA_0000; + /// The upper bound of the system's DRAM (Data RAM) address space. pub const SOC_DRAM_HIGH: u32 = 0x3FCE_0000; + /// RC FAST Clock value (Hertz). pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500); } - -#[export_name = "__post_init"] -unsafe fn post_init() { - // RTC domain must be enabled before we try to disable - let mut rtc = Rtc::new(LPWR::steal()); - rtc.swd.disable(); - rtc.rwdt.disable(); - - Wdt::::set_wdt_enabled(false); -} diff --git a/esp-hal/src/soc/esp32c2/peripherals.rs b/esp-hal/src/soc/esp32c2/peripherals.rs index adf05427afc..3cd86959d96 100644 --- a/esp-hal/src/soc/esp32c2/peripherals.rs +++ b/esp-hal/src/soc/esp32c2/peripherals.rs @@ -43,6 +43,7 @@ crate::peripherals! { SPI2 <= SPI2 (SPI2), SYSTEM <= SYSTEM, SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, TIMG0 <= TIMG0, UART0 <= UART0, UART1 <= UART1, diff --git a/esp-hal/src/soc/esp32c2/trng.rs b/esp-hal/src/soc/esp32c2/trng.rs index 88c5db4b23f..92a565ae665 100644 --- a/esp-hal/src/soc/esp32c2/trng.rs +++ b/esp-hal/src/soc/esp32c2/trng.rs @@ -15,6 +15,8 @@ const ADC_SARADC_ENT_TSENS_ADDR_LSB: u8 = 2; use crate::regi2c_write_mask; +/// Enable true randomness by enabling the entropy source. +/// Blocks `ADC` usage. pub(crate) fn ensure_randomness() { let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() }; let system = unsafe { &*crate::peripherals::SYSTEM::ptr() }; @@ -100,6 +102,7 @@ pub(crate) fn ensure_randomness() { } } +/// Disable true randomness. Unlocks `ADC` peripheral. pub(crate) fn revert_trng() { let apb_saradc = unsafe { &*crate::peripherals::APB_SARADC::ptr() }; let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() }; diff --git a/esp-hal/src/soc/esp32c3/efuse/mod.rs b/esp-hal/src/soc/esp32c3/efuse/mod.rs index cf4463bbabf..f1f3b0e2fb5 100644 --- a/esp-hal/src/soc/esp32c3/efuse/mod.rs +++ b/esp-hal/src/soc/esp32c3/efuse/mod.rs @@ -1,13 +1,13 @@ //! # Reading of eFuses (ESP32-C3) //! //! ## Overview +//! //! The `efuse` module provides functionality for reading eFuse data //! from the `ESP32-C3` chip, allowing access to various chip-specific -//! information such as : +//! information such as: +//! //! * MAC address -//! * core count -//! * CPU frequency -//! * chip type +//! * ADC calibration data //! //! and more. It is useful for retrieving chip-specific configuration and //! identification data during runtime. @@ -15,8 +15,10 @@ //! The `Efuse` struct represents the eFuse peripheral and is responsible for //! reading various eFuse fields and values. //! -//! ## Examples +//! ## Example +//! //! ### Read chip's MAC address from the eFuse storage. +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; @@ -25,7 +27,7 @@ //! # use core::writeln; //! # use core::fmt::Write; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, &clocks, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -45,6 +47,7 @@ use crate::{analog::adc::Attenuation, peripherals::EFUSE}; mod fields; +/// A struct representing the eFuse functionality of the chip. pub struct Efuse; impl Efuse { diff --git a/esp-hal/src/soc/esp32c3/gpio.rs b/esp-hal/src/soc/esp32c3/gpio.rs index aca0ebe4878..4c6a3091301 100644 --- a/esp-hal/src/soc/esp32c3/gpio.rs +++ b/esp-hal/src/soc/esp32c3/gpio.rs @@ -51,6 +51,7 @@ use crate::{ peripherals::GPIO, }; +/// The total number of GPIO pins available. pub const NUM_PINS: usize = 22; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; diff --git a/esp-hal/src/soc/esp32c3/mod.rs b/esp-hal/src/soc/esp32c3/mod.rs index b4e9fc23dd1..2d45d4e47fd 100644 --- a/esp-hal/src/soc/esp32c3/mod.rs +++ b/esp-hal/src/soc/esp32c3/mod.rs @@ -9,9 +9,6 @@ //! * I2S_SCLK: 160_000_000 - I2S clock frequency //! * I2S_DEFAULT_CLK_SRC: 2 - I2S clock source -use self::peripherals::{LPWR, TIMG0, TIMG1}; -use crate::{rtc_cntl::Rtc, timer::timg::Wdt}; - pub mod efuse; pub mod gpio; pub mod peripherals; @@ -34,27 +31,25 @@ pub(crate) mod registers { } pub(crate) mod constants { + /// The base clock frequency for the I2S peripheral (Hertz). pub const I2S_SCLK: u32 = 160_000_000; + /// The default clock source for I2S operations. pub const I2S_DEFAULT_CLK_SRC: u8 = 2; + /// The starting address of the Remote Control (RMT) module's RAM. pub const RMT_RAM_START: usize = 0x60016400; + /// The size, in bytes, of each RMT channel's dedicated RAM. pub const RMT_CHANNEL_RAM_SIZE: usize = 48; + /// RMT Clock source value. pub const RMT_CLOCK_SRC: u8 = 1; + /// RMT Clock source frequence. pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(80); + /// The lower bound of the system's DRAM (Data RAM) address space. pub const SOC_DRAM_LOW: u32 = 0x3FC8_0000; + /// The upper bound of the system's DRAM (Data RAM) address space. pub const SOC_DRAM_HIGH: u32 = 0x3FCE_0000; + /// RC FAST Clock value (Hertz). pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500); } - -#[export_name = "__post_init"] -unsafe fn post_init() { - // RTC domain must be enabled before we try to disable - let mut rtc = Rtc::new(LPWR::steal()); - rtc.swd.disable(); - rtc.rwdt.disable(); - - Wdt::::set_wdt_enabled(false); - Wdt::::set_wdt_enabled(false); -} diff --git a/esp-hal/src/soc/esp32c3/peripherals.rs b/esp-hal/src/soc/esp32c3/peripherals.rs index 4ffd2aa402a..dd5308df0c2 100644 --- a/esp-hal/src/soc/esp32c3/peripherals.rs +++ b/esp-hal/src/soc/esp32c3/peripherals.rs @@ -50,6 +50,7 @@ crate::peripherals! { SPI2 <= SPI2 (SPI2), SYSTEM <= SYSTEM, SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, TIMG0 <= TIMG0, TIMG1 <= TIMG1, TWAI0 <= TWAI0, diff --git a/esp-hal/src/soc/esp32c3/trng.rs b/esp-hal/src/soc/esp32c3/trng.rs index b78ed8d49a4..a1e7f5d281b 100644 --- a/esp-hal/src/soc/esp32c3/trng.rs +++ b/esp-hal/src/soc/esp32c3/trng.rs @@ -15,6 +15,8 @@ const ADC_SARADC_ENT_TSENS_ADDR_LSB: u8 = 2; use crate::regi2c_write_mask; +/// Enable true randomness by enabling the entropy source. +/// Blocks `ADC` usage. pub(crate) fn ensure_randomness() { let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() }; let system = unsafe { &*crate::peripherals::SYSTEM::ptr() }; @@ -100,6 +102,7 @@ pub(crate) fn ensure_randomness() { } } +/// Disable true randomness. Unlocks `ADC` peripheral. pub(crate) fn revert_trng() { let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() }; let apb_saradc = unsafe { &*crate::peripherals::APB_SARADC::ptr() }; diff --git a/esp-hal/src/soc/esp32c6/efuse/fields.rs b/esp-hal/src/soc/esp32c6/efuse/fields.rs index c9867e0d251..793d9ab7f9c 100644 --- a/esp-hal/src/soc/esp32c6/efuse/fields.rs +++ b/esp-hal/src/soc/esp32c6/efuse/fields.rs @@ -235,6 +235,8 @@ pub const SPI_DOWNLOAD_MSPI_DIS: EfuseField = EfuseField::new(EfuseBlock::Block0 /// `[DIS_CAN]` Represents whether TWAI function is disabled or enabled. 1: /// disabled. 0: enabled pub const DIS_TWAI: EfuseField = EfuseField::new(EfuseBlock::Block0, 46, 1); +#[allow(unknown_lints)] +#[allow(clippy::too_long_first_doc_paragraph)] /// `[]` Represents whether the selection between usb_to_jtag and pad_to_jtag /// through strapping gpio15 when both EFUSE_DIS_PAD_JTAG and EFUSE_DIS_USB_JTAG /// are equal to 0 is enabled or disabled. 1: enabled. 0: disabled diff --git a/esp-hal/src/soc/esp32c6/efuse/mod.rs b/esp-hal/src/soc/esp32c6/efuse/mod.rs index 97711a5a3e4..0caa975a253 100644 --- a/esp-hal/src/soc/esp32c6/efuse/mod.rs +++ b/esp-hal/src/soc/esp32c6/efuse/mod.rs @@ -3,11 +3,11 @@ //! ## Overview //! The `efuse` module provides functionality for reading eFuse data //! from the `ESP32-C6` chip, allowing access to various chip-specific -//! information such as : +//! information such as: +//! //! * MAC address -//! * core count -//! * CPU frequency -//! * chip type +//! * ADC calibration data +//! * Chip version //! //! and more. It is useful for retrieving chip-specific configuration and //! identification data during runtime. @@ -15,8 +15,10 @@ //! The `Efuse` struct represents the eFuse peripheral and is responsible for //! reading various eFuse fields and values. //! -//! ## Examples +//! ## Example +//! //! ### Read chip's MAC address from the eFuse storage. +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; @@ -25,7 +27,7 @@ //! # use core::writeln; //! # use core::fmt::Write; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, &clocks, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -45,6 +47,7 @@ use crate::{analog::adc::Attenuation, peripherals::EFUSE}; mod fields; +/// A struct representing the eFuse functionality of the chip. pub struct Efuse; impl Efuse { diff --git a/esp-hal/src/soc/esp32c6/gpio.rs b/esp-hal/src/soc/esp32c6/gpio.rs index 74d524f10d5..4e688237e06 100644 --- a/esp-hal/src/soc/esp32c6/gpio.rs +++ b/esp-hal/src/soc/esp32c6/gpio.rs @@ -51,6 +51,7 @@ use crate::{ peripherals::GPIO, }; +/// The total number of GPIO pins available. pub const NUM_PINS: usize = 31; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; diff --git a/esp-hal/src/soc/esp32c6/lp_core.rs b/esp-hal/src/soc/esp32c6/lp_core.rs index 24330a3492f..151e47268f1 100644 --- a/esp-hal/src/soc/esp32c6/lp_core.rs +++ b/esp-hal/src/soc/esp32c6/lp_core.rs @@ -23,8 +23,10 @@ use esp32c6 as pac; use crate::peripheral::{Peripheral, PeripheralRef}; +/// Represents the possible wakeup sources for the LP (Low Power) core. #[derive(Debug, Clone, Copy)] pub enum LpCoreWakeupSource { + /// Wakeup source from the HP (High Performance) CPU. HpCpu, } @@ -39,6 +41,7 @@ pub enum LpCoreClockSource { XtalD2Clk, } +/// Represents the Low Power (LP) core peripheral. pub struct LpCore<'d> { _lp_core: PeripheralRef<'d, crate::soc::peripherals::LP_CORE>, } diff --git a/esp-hal/src/soc/esp32c6/mod.rs b/esp-hal/src/soc/esp32c6/mod.rs index 2f7d2138554..01e80a95356 100644 --- a/esp-hal/src/soc/esp32c6/mod.rs +++ b/esp-hal/src/soc/esp32c6/mod.rs @@ -10,9 +10,6 @@ //! * I2S_DEFAULT_CLK_SRC: 2 - I2S clock source //! * I2S_SCLK: 160_000_000 - I2S clock frequency -use self::peripherals::{LPWR, TIMG0, TIMG1}; -use crate::{rtc_cntl::Rtc, timer::timg::Wdt}; - pub mod efuse; pub mod gpio; pub mod lp_core; @@ -36,31 +33,31 @@ pub(crate) mod registers { } pub(crate) mod constants { + /// The default clock source for the timer group. pub const TIMG_DEFAULT_CLK_SRC: u8 = 1; + /// The clock frequency for the I2S peripheral in Hertz. pub const I2S_SCLK: u32 = 160_000_000; + /// The default clock source for the I2S peripheral. pub const I2S_DEFAULT_CLK_SRC: u8 = 2; + /// The starting address of the RMT (Remote Control) peripheral's RAM. pub const RMT_RAM_START: usize = 0x60006400; + /// The size of each RMT channel's RAM in bytes. pub const RMT_CHANNEL_RAM_SIZE: usize = 48; + /// The default clock source for the RMT peripheral. pub const RMT_CLOCK_SRC: u8 = 1; + /// The frequency of the RMT clock source in Hertz. pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(80); + /// The clock frequency for the Parallel IO peripheral in Hertz. pub const PARL_IO_SCLK: u32 = 240_000_000; + /// The lower address boundary for system DRAM. pub const SOC_DRAM_LOW: u32 = 0x4080_0000; + /// The upper address boundary for system DRAM. pub const SOC_DRAM_HIGH: u32 = 0x4088_0000; - pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500); -} - -#[export_name = "__post_init"] -unsafe fn post_init() { - // RTC domain must be enabled before we try to disable - let mut rtc = Rtc::new(LPWR::steal()); - rtc.swd.disable(); - rtc.rwdt.disable(); - - Wdt::::set_wdt_enabled(false); - Wdt::::set_wdt_enabled(false); + /// RC FAST Clock value (Hertz). + pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17_500); } diff --git a/esp-hal/src/soc/esp32c6/peripherals.rs b/esp-hal/src/soc/esp32c6/peripherals.rs index 6952cdc0d00..ef74459ed22 100644 --- a/esp-hal/src/soc/esp32c6/peripherals.rs +++ b/esp-hal/src/soc/esp32c6/peripherals.rs @@ -76,6 +76,7 @@ crate::peripherals! { SPI2 <= SPI2 (SPI2), SYSTEM <= PCR, SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, TEE <= TEE, TIMG0 <= TIMG0, TIMG1 <= TIMG1, diff --git a/esp-hal/src/soc/esp32c6/radio_clocks.rs b/esp-hal/src/soc/esp32c6/radio_clocks.rs index 2ce532a7842..f9319969bcc 100644 --- a/esp-hal/src/soc/esp32c6/radio_clocks.rs +++ b/esp-hal/src/soc/esp32c6/radio_clocks.rs @@ -342,11 +342,9 @@ fn init_clocks() { pmu.hp_active_icg_modem() .modify(|_, w| w.hp_active_dig_icg_modem_code().bits(2)); pmu.imm_modem_icg() - .as_ptr() - .write_volatile(pmu.imm_modem_icg().as_ptr().read_volatile() | 1 << 31); + .write(|w| w.update_dig_icg_modem_en().set_bit()); pmu.imm_sleep_sysclk() - .as_ptr() - .write_volatile(pmu.imm_sleep_sysclk().as_ptr().read_volatile() | 1 << 28); + .write(|w| w.update_dig_icg_switch().set_bit()); let modem_syscon = &*esp32c6::MODEM_SYSCON::PTR; modem_syscon.clk_conf_power_st().modify(|_, w| { diff --git a/esp-hal/src/soc/esp32c6/trng.rs b/esp-hal/src/soc/esp32c6/trng.rs index c08b363e2fd..576f5c0442b 100644 --- a/esp-hal/src/soc/esp32c6/trng.rs +++ b/esp-hal/src/soc/esp32c6/trng.rs @@ -60,6 +60,8 @@ const ADC_SARADC2_ENCAL_REF_ADDR: u8 = 0x7; const ADC_SARADC2_ENCAL_REF_ADDR_MSB: u8 = 6; const ADC_SARADC2_ENCAL_REF_ADDR_LSB: u8 = 6; +/// Enable true randomness by enabling the entropy source. +/// Blocks `ADC` usage. pub(crate) fn ensure_randomness() { let pcr = unsafe { &*crate::peripherals::PCR::ptr() }; let pmu = unsafe { &*crate::peripherals::PMU::ptr() }; @@ -190,10 +192,9 @@ pub(crate) fn ensure_randomness() { } } -#[allow(unused)] +/// Disable true randomness. Unlocks `ADC` peripheral. pub(crate) fn revert_trng() { let apb_saradc = unsafe { &*crate::peripherals::APB_SARADC::ptr() }; - let pmu = unsafe { &*crate::peripherals::PMU::ptr() }; let pcr = unsafe { &*crate::peripherals::PCR::ptr() }; unsafe { @@ -273,8 +274,6 @@ pub(crate) fn revert_trng() { 0, ); - pmu.rf_pwc().modify(|_, w| w.perif_i2c_rstb().clear_bit()); - pcr.saradc_clkm_conf().modify(|_, w| w.bits(0x00404000)); pcr.saradc_conf().modify(|_, w| w.bits(0x5)); diff --git a/esp-hal/src/soc/esp32h2/efuse/mod.rs b/esp-hal/src/soc/esp32h2/efuse/mod.rs index 81df45fad38..73bdcd57b31 100644 --- a/esp-hal/src/soc/esp32h2/efuse/mod.rs +++ b/esp-hal/src/soc/esp32h2/efuse/mod.rs @@ -1,13 +1,13 @@ //! # Reading of eFuses (ESP32-H2) //! //! ## Overview +//! //! The `efuse` module provides functionality for reading eFuse data //! from the `ESP32-H2` chip, allowing access to various chip-specific -//! information such as : +//! information such as: +//! //! * MAC address -//! * core count -//! * CPU frequency -//! * chip type +//! * ADC calibration data //! //! and more. It is useful for retrieving chip-specific configuration and //! identification data during runtime. @@ -15,8 +15,10 @@ //! The `Efuse` struct represents the eFuse peripheral and is responsible for //! reading various eFuse fields and values. //! -//! ## Examples +//! ## Example +//! //! ### Read chip's MAC address from the eFuse storage. +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; @@ -25,7 +27,7 @@ //! # use core::writeln; //! # use core::fmt::Write; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, &clocks, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -45,6 +47,7 @@ use crate::peripherals::EFUSE; mod fields; +/// A struct representing the eFuse functionality of the chip. pub struct Efuse; impl Efuse { diff --git a/esp-hal/src/soc/esp32h2/gpio.rs b/esp-hal/src/soc/esp32h2/gpio.rs index d6f2710eec1..519b6f56b6a 100644 --- a/esp-hal/src/soc/esp32h2/gpio.rs +++ b/esp-hal/src/soc/esp32h2/gpio.rs @@ -52,6 +52,7 @@ use crate::{ }; // https://github.com/espressif/esp-idf/blob/df9310a/components/soc/esp32h2/gpio_periph.c#L42 +/// The total number of GPIO pins available. pub const NUM_PINS: usize = 28; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; diff --git a/esp-hal/src/soc/esp32h2/mod.rs b/esp-hal/src/soc/esp32h2/mod.rs index 457e0ff9f15..8ebcaadee84 100644 --- a/esp-hal/src/soc/esp32h2/mod.rs +++ b/esp-hal/src/soc/esp32h2/mod.rs @@ -10,9 +10,6 @@ //! * I2S_DEFAULT_CLK_SRC: 1 - I2S clock source //! * I2S_SCLK: 96_000_000 - I2S clock frequency -use self::peripherals::{LPWR, TIMG0, TIMG1}; -use crate::{rtc_cntl::Rtc, timer::timg::Wdt}; - pub mod efuse; pub mod gpio; pub mod peripherals; @@ -35,31 +32,32 @@ pub(crate) mod registers { } pub(crate) mod constants { + /// Default clock source for the timer group (TIMG) peripheral. pub const TIMG_DEFAULT_CLK_SRC: u8 = 2; + /// Default clock source for the I2S peripheral. pub const I2S_DEFAULT_CLK_SRC: u8 = 1; + /// Clock frequency for the I2S peripheral, in Hertz. pub const I2S_SCLK: u32 = 96_000_000; + /// Start address of the RMT (Remote Control) peripheral's RAM. pub const RMT_RAM_START: usize = 0x60007400; + /// Size of the RAM allocated per RMT channel, in bytes. pub const RMT_CHANNEL_RAM_SIZE: usize = 48; + /// Clock source for the RMT peripheral (false = default source). pub const RMT_CLOCK_SRC: bool = false; + /// Frequency of the RMT clock source, in Hertz. pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(32); + /// System clock frequency for the parallel I/O (PARL IO) peripheral, in + /// Hertz. pub const PARL_IO_SCLK: u32 = 96_000_000; + /// Start address of the system's DRAM (low range). pub const SOC_DRAM_LOW: u32 = 0x4080_0000; + /// End address of the system's DRAM (high range). pub const SOC_DRAM_HIGH: u32 = 0x4085_0000; + /// RC FAST Clock value (Hertz). pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500); } - -#[export_name = "__post_init"] -unsafe fn post_init() { - // RTC domain must be enabled before we try to disable - let mut rtc = Rtc::new(LPWR::steal()); - rtc.swd.disable(); - rtc.rwdt.disable(); - - Wdt::::set_wdt_enabled(false); - Wdt::::set_wdt_enabled(false); -} diff --git a/esp-hal/src/soc/esp32h2/peripherals.rs b/esp-hal/src/soc/esp32h2/peripherals.rs index 205c1bad47c..7fbda39c981 100644 --- a/esp-hal/src/soc/esp32h2/peripherals.rs +++ b/esp-hal/src/soc/esp32h2/peripherals.rs @@ -68,6 +68,7 @@ crate::peripherals! { SPI2 <= SPI2 (SPI2), SYSTEM <= PCR, SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, TEE <= TEE, TIMG0 <= TIMG0, TIMG1 <= TIMG1, diff --git a/esp-hal/src/soc/esp32h2/radio_clocks.rs b/esp-hal/src/soc/esp32h2/radio_clocks.rs index d9067deab70..101e7ee20ad 100644 --- a/esp-hal/src/soc/esp32h2/radio_clocks.rs +++ b/esp-hal/src/soc/esp32h2/radio_clocks.rs @@ -131,11 +131,9 @@ fn init_clocks() { pmu.hp_active_icg_modem() .modify(|_, w| w.hp_active_dig_icg_modem_code().bits(2)); pmu.imm_modem_icg() - .as_ptr() - .write_volatile(pmu.imm_modem_icg().as_ptr().read_volatile() | 1 << 31); + .write(|w| w.update_dig_icg_modem_en().set_bit()); pmu.imm_sleep_sysclk() - .as_ptr() - .write_volatile(pmu.imm_sleep_sysclk().as_ptr().read_volatile() | 1 << 28); + .write(|w| w.update_dig_icg_switch().set_bit()); (*esp32h2::MODEM_LPCON::PTR).clk_conf().modify(|_, w| { w.clk_i2c_mst_en() diff --git a/esp-hal/src/soc/esp32h2/trng.rs b/esp-hal/src/soc/esp32h2/trng.rs index 0b1e25e1188..e0c3aab93c5 100644 --- a/esp-hal/src/soc/esp32h2/trng.rs +++ b/esp-hal/src/soc/esp32h2/trng.rs @@ -56,6 +56,8 @@ const REGI2C_RTC_WR_CNTL_S: u8 = 24; const REGI2C_RTC_DATA_V: u8 = 0xFF; const REGI2C_RTC_DATA_S: u8 = 16; +/// Enable true randomness by enabling the entropy source. +/// Blocks `ADC` usage. pub(crate) fn ensure_randomness() { let pcr = unsafe { &*crate::peripherals::PCR::ptr() }; let pmu = unsafe { &*crate::peripherals::PMU::ptr() }; @@ -177,6 +179,7 @@ pub(crate) fn ensure_randomness() { } } +/// Disable true randomness. Unlocks `ADC` peripheral. pub(crate) fn revert_trng() { let apb_saradc = unsafe { &*crate::peripherals::APB_SARADC::ptr() }; let pcr = unsafe { &*crate::peripherals::PCR::ptr() }; diff --git a/esp-hal/src/soc/esp32s2/efuse/mod.rs b/esp-hal/src/soc/esp32s2/efuse/mod.rs index e93849c107b..6dd75ceca7c 100644 --- a/esp-hal/src/soc/esp32s2/efuse/mod.rs +++ b/esp-hal/src/soc/esp32s2/efuse/mod.rs @@ -1,9 +1,11 @@ //! # Reading of eFuses (ESP32-S2) //! //! ## Overview +//! //! The `efuse` module provides functionality for reading eFuse data //! from the `ESP32-S2` chip, allowing access to various chip-specific -//! information such as : +//! information such as: +//! //! * MAC address //! * core count //! * CPU frequency @@ -15,8 +17,10 @@ //! The `Efuse` struct represents the eFuse peripheral and is responsible for //! reading various eFuse fields and values. //! -//! ## Examples +//! ## Example +//! //! ### Read chip's MAC address from the eFuse storage. +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; @@ -25,7 +29,7 @@ //! # use core::writeln; //! # use core::fmt::Write; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, &clocks, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -45,6 +49,7 @@ use crate::peripherals::EFUSE; mod fields; +/// A struct representing the eFuse functionality of the chip. pub struct Efuse; impl Efuse { diff --git a/esp-hal/src/soc/esp32s2/gpio.rs b/esp-hal/src/soc/esp32s2/gpio.rs index 78cd541c23e..3b5d3daac12 100644 --- a/esp-hal/src/soc/esp32s2/gpio.rs +++ b/esp-hal/src/soc/esp32s2/gpio.rs @@ -8,8 +8,7 @@ //! //! Let's get through the functionality and configurations provided by this GPIO //! module: -//! - `get_io_mux_reg(gpio_num: u8) -> &'static -//! crate::peripherals::io_mux::GPIO0:`: +//! - `get_io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0:`: //! * This function returns a reference to the GPIO register associated //! with the given GPIO number. It uses unsafe code and transmutation to //! access the GPIO registers based on the provided GPIO number. @@ -53,6 +52,8 @@ //! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the //! `gpio` peripheral to access the appropriate registers. +use core::mem::transmute; + use crate::{ gpio::{ AlternateFunction, @@ -61,9 +62,10 @@ use crate::{ InterruptStatusRegisterAccessBank0, InterruptStatusRegisterAccessBank1, }, - peripherals::GPIO, + peripherals::{io_mux, GPIO, IO_MUX}, }; +/// The total number of GPIO pins available. pub const NUM_PINS: usize = 47; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; @@ -77,167 +79,50 @@ pub(crate) const ZERO_INPUT: u8 = 0x3c; pub(crate) const GPIO_FUNCTION: AlternateFunction = AlternateFunction::Function1; -pub(crate) const fn get_io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO0 { +pub(crate) const fn get_io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0 { unsafe { - let iomux = &*crate::peripherals::IO_MUX::PTR; + let iomux = &*IO_MUX::PTR; match gpio_num { - 0 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO0, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio0()), - 1 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO1, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio1()), - 2 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO2, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio2()), - 3 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO3, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio3()), - 4 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO4, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio4()), - 5 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO5, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio5()), - 6 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO6, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio6()), - 7 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO7, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio7()), - 8 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO8, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio8()), - 9 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO9, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio9()), - 10 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO10, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio10()), - 11 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO11, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio11()), - 12 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO12, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio12()), - 13 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO13, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio13()), - 14 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO14, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio14()), - 15 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO15, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio15()), - 16 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO16, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio16()), - 17 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO17, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio17()), - 18 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO18, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio18()), - 19 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO19, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio19()), - 20 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO20, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio20()), - 21 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO21, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio21()), - 26 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO26, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio26()), - 27 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO27, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio27()), - 32 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO32, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio32()), - 33 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO33, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio33()), - 34 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO34, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio34()), - 35 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO35, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio35()), - 36 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO36, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio36()), - 37 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO37, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio37()), - 38 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO38, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio38()), - 39 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO39, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio39()), - 40 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO40, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio40()), - 41 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO41, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio41()), - 42 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO42, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio42()), - 43 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO43, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio43()), - 44 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO44, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio44()), - 45 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO45, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio45()), - 46 => core::mem::transmute::< - &'static crate::peripherals::io_mux::GPIO46, - &'static crate::peripherals::io_mux::GPIO0, - >(iomux.gpio46()), + 0 => transmute::<&'static io_mux::GPIO0, &'static io_mux::GPIO0>(iomux.gpio0()), + 1 => transmute::<&'static io_mux::GPIO1, &'static io_mux::GPIO0>(iomux.gpio1()), + 2 => transmute::<&'static io_mux::GPIO2, &'static io_mux::GPIO0>(iomux.gpio2()), + 3 => transmute::<&'static io_mux::GPIO3, &'static io_mux::GPIO0>(iomux.gpio3()), + 4 => transmute::<&'static io_mux::GPIO4, &'static io_mux::GPIO0>(iomux.gpio4()), + 5 => transmute::<&'static io_mux::GPIO5, &'static io_mux::GPIO0>(iomux.gpio5()), + 6 => transmute::<&'static io_mux::GPIO6, &'static io_mux::GPIO0>(iomux.gpio6()), + 7 => transmute::<&'static io_mux::GPIO7, &'static io_mux::GPIO0>(iomux.gpio7()), + 8 => transmute::<&'static io_mux::GPIO8, &'static io_mux::GPIO0>(iomux.gpio8()), + 9 => transmute::<&'static io_mux::GPIO9, &'static io_mux::GPIO0>(iomux.gpio9()), + 10 => transmute::<&'static io_mux::GPIO10, &'static io_mux::GPIO0>(iomux.gpio10()), + 11 => transmute::<&'static io_mux::GPIO11, &'static io_mux::GPIO0>(iomux.gpio11()), + 12 => transmute::<&'static io_mux::GPIO12, &'static io_mux::GPIO0>(iomux.gpio12()), + 13 => transmute::<&'static io_mux::GPIO13, &'static io_mux::GPIO0>(iomux.gpio13()), + 14 => transmute::<&'static io_mux::GPIO14, &'static io_mux::GPIO0>(iomux.gpio14()), + 15 => transmute::<&'static io_mux::GPIO15, &'static io_mux::GPIO0>(iomux.gpio15()), + 16 => transmute::<&'static io_mux::GPIO16, &'static io_mux::GPIO0>(iomux.gpio16()), + 17 => transmute::<&'static io_mux::GPIO17, &'static io_mux::GPIO0>(iomux.gpio17()), + 18 => transmute::<&'static io_mux::GPIO18, &'static io_mux::GPIO0>(iomux.gpio18()), + 19 => transmute::<&'static io_mux::GPIO19, &'static io_mux::GPIO0>(iomux.gpio19()), + 20 => transmute::<&'static io_mux::GPIO20, &'static io_mux::GPIO0>(iomux.gpio20()), + 21 => transmute::<&'static io_mux::GPIO21, &'static io_mux::GPIO0>(iomux.gpio21()), + 26 => transmute::<&'static io_mux::GPIO26, &'static io_mux::GPIO0>(iomux.gpio26()), + 27 => transmute::<&'static io_mux::GPIO27, &'static io_mux::GPIO0>(iomux.gpio27()), + 32 => transmute::<&'static io_mux::GPIO32, &'static io_mux::GPIO0>(iomux.gpio32()), + 33 => transmute::<&'static io_mux::GPIO33, &'static io_mux::GPIO0>(iomux.gpio33()), + 34 => transmute::<&'static io_mux::GPIO34, &'static io_mux::GPIO0>(iomux.gpio34()), + 35 => transmute::<&'static io_mux::GPIO35, &'static io_mux::GPIO0>(iomux.gpio35()), + 36 => transmute::<&'static io_mux::GPIO36, &'static io_mux::GPIO0>(iomux.gpio36()), + 37 => transmute::<&'static io_mux::GPIO37, &'static io_mux::GPIO0>(iomux.gpio37()), + 38 => transmute::<&'static io_mux::GPIO38, &'static io_mux::GPIO0>(iomux.gpio38()), + 39 => transmute::<&'static io_mux::GPIO39, &'static io_mux::GPIO0>(iomux.gpio39()), + 40 => transmute::<&'static io_mux::GPIO40, &'static io_mux::GPIO0>(iomux.gpio40()), + 41 => transmute::<&'static io_mux::GPIO41, &'static io_mux::GPIO0>(iomux.gpio41()), + 42 => transmute::<&'static io_mux::GPIO42, &'static io_mux::GPIO0>(iomux.gpio42()), + 43 => transmute::<&'static io_mux::GPIO43, &'static io_mux::GPIO0>(iomux.gpio43()), + 44 => transmute::<&'static io_mux::GPIO44, &'static io_mux::GPIO0>(iomux.gpio44()), + 45 => transmute::<&'static io_mux::GPIO45, &'static io_mux::GPIO0>(iomux.gpio45()), + 46 => transmute::<&'static io_mux::GPIO46, &'static io_mux::GPIO0>(iomux.gpio46()), _ => ::core::unreachable!(), } } @@ -558,5 +443,5 @@ impl InterruptStatusRegisterAccess for InterruptStatusRegisterAccessBank1 { } // implement marker traits on USB pins -impl crate::otg_fs::UsbDm for Gpio19 {} -impl crate::otg_fs::UsbDp for Gpio20 {} +impl crate::otg_fs::UsbDm for GpioPin<19> {} +impl crate::otg_fs::UsbDp for GpioPin<20> {} diff --git a/esp-hal/src/soc/esp32s2/mod.rs b/esp-hal/src/soc/esp32s2/mod.rs index a19d919f8f2..ffae3496d1a 100644 --- a/esp-hal/src/soc/esp32s2/mod.rs +++ b/esp-hal/src/soc/esp32s2/mod.rs @@ -11,11 +11,7 @@ use core::ptr::addr_of_mut; -use self::peripherals::{LPWR, TIMG0, TIMG1}; -use crate::{ - rtc_cntl::{Rtc, SocResetReason}, - timer::timg::Wdt, -}; +use crate::rtc_cntl::SocResetReason; pub mod efuse; pub mod gpio; @@ -38,15 +34,19 @@ macro_rules! chip { pub use chip; pub(crate) mod constants { + /// System clock frequency for the I2S peripheral, in Hertz. pub const I2S_SCLK: u32 = 160_000_000; + /// Default clock source for the I2S peripheral. pub const I2S_DEFAULT_CLK_SRC: u32 = 2; - + /// Start address of the RMT (Remote Control) peripheral's RAM. pub const RMT_RAM_START: usize = 0x3f416400; + /// Size of the RAM allocated per RMT channel, in bytes. pub const RMT_CHANNEL_RAM_SIZE: usize = 64; - + /// Start address of the system's DRAM (low range). pub const SOC_DRAM_LOW: u32 = 0x3FFB_0000; + /// End address of the system's DRAM (high range). pub const SOC_DRAM_HIGH: u32 = 0x4000_0000; - + /// Reference clock tick frequency, set to 1 MHz. pub const REF_TICK: fugit::HertzU32 = fugit::HertzU32::MHz(1); } @@ -112,8 +112,6 @@ pub unsafe extern "C" fn ESP32Reset() -> ! { stack_chk_guard.write_volatile(0xdeadbabe); } - crate::interrupt::setup_interrupts(); - // continue with default reset handler xtensa_lx_rt::Reset(); } @@ -126,13 +124,3 @@ pub unsafe extern "C" fn ESP32Reset() -> ! { pub extern "Rust" fn __init_data() -> bool { false } - -#[export_name = "__post_init"] -unsafe fn post_init() { - // RTC domain must be enabled before we try to disable - let mut rtc = Rtc::new(LPWR::steal()); - rtc.rwdt.disable(); - - Wdt::::set_wdt_enabled(false); - Wdt::::set_wdt_enabled(false); -} diff --git a/esp-hal/src/soc/esp32s2/peripherals.rs b/esp-hal/src/soc/esp32s2/peripherals.rs index 7ed7c128248..31d989cf28f 100644 --- a/esp-hal/src/soc/esp32s2/peripherals.rs +++ b/esp-hal/src/soc/esp32s2/peripherals.rs @@ -58,6 +58,7 @@ crate::peripherals! { SYSCON <= SYSCON, SYSTEM <= SYSTEM, SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, TIMG0 <= TIMG0, TIMG1 <= TIMG1, TWAI0 <= TWAI0, diff --git a/esp-hal/src/soc/esp32s2/psram.rs b/esp-hal/src/soc/esp32s2/psram.rs index a47fcbd6d2d..a02b059f6e5 100644 --- a/esp-hal/src/soc/esp32s2/psram.rs +++ b/esp-hal/src/soc/esp32s2/psram.rs @@ -17,6 +17,7 @@ //! `4MB`, and `8MB`. const PSRAM_VADDR: u32 = 0x3f500000; +/// Returns the start address of the PSRAM virtual address space. pub fn psram_vaddr_start() -> usize { PSRAM_VADDR_START } @@ -33,11 +34,16 @@ cfg_if::cfg_if! { } } +/// The total size of the PSRAM in bytes, calculated from the PSRAM size +/// constant. pub const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024; +/// The start address of the PSRAM virtual address space. pub const PSRAM_VADDR_START: usize = PSRAM_VADDR as usize; +/// Initialize PSRAM to be used for data. #[cfg(any(feature = "psram-2m", feature = "psram-4m", feature = "psram-8m"))] +#[procmacros::ram] pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral

) { #[allow(unused)] enum CacheLayout { diff --git a/esp-hal/src/soc/esp32s2/trng.rs b/esp-hal/src/soc/esp32s2/trng.rs index 2999ac1d339..48eeb6f9d33 100644 --- a/esp-hal/src/soc/esp32s2/trng.rs +++ b/esp-hal/src/soc/esp32s2/trng.rs @@ -45,6 +45,8 @@ const I2C_RTC_BBPLL_MASK: u32 = 1 << 17; const I2C_RTC_SAR_MASK: u32 = 1 << 18; const I2C_RTC_BOD_MASK: u32 = 1 << 22; +/// Enable true randomness by enabling the entropy source. +/// Blocks `ADC` usage. pub(crate) fn ensure_randomness() { let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() }; let dport = unsafe { &*crate::peripherals::SYSTEM::ptr() }; @@ -157,7 +159,8 @@ pub(crate) fn ensure_randomness() { } } -pub fn revert_trng() { +/// Disable true randomness. Unlocks `ADC` peripheral. +pub(crate) fn revert_trng() { let dport = unsafe { &*crate::peripherals::SYSTEM::ptr() }; let apb_saradc = unsafe { &*crate::peripherals::APB_SARADC::ptr() }; let sens = unsafe { &*crate::peripherals::SENS::ptr() }; diff --git a/esp-hal/src/soc/esp32s2/ulp_core.rs b/esp-hal/src/soc/esp32s2/ulp_core.rs index cedfef686e0..6515635347a 100644 --- a/esp-hal/src/soc/esp32s2/ulp_core.rs +++ b/esp-hal/src/soc/esp32s2/ulp_core.rs @@ -17,17 +17,21 @@ #![doc = crate::before_snippet!()] //! const CODE: &[u8] = &[ //! 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x01, 0x81, 0x45, 0x85, 0x05, -//! 0x0c, 0xc1, 0xf5, 0xbf, 0x00, 0x00, 0x00, 0x00, -//! ]; +//! 0x0c, 0xc1, 0xf5, 0xbf, 0x00, 0x00, 0x00, 0x00, +//! ]; //! let mut ulp_core = -//! esp_hal::ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE); +//! esp_hal::ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE); //! // ulp_core.stop(); currently not implemented //! //! // copy code to RTC ram //! let lp_ram = 0x5000_0000 as *mut u8; //! unsafe { -//! core::ptr::copy_nonoverlapping(CODE as *const _ as *const u8, lp_ram, -//! CODE.len()); } +//! core::ptr::copy_nonoverlapping( +//! CODE as *const _ as *const u8, +//! lp_ram, +//! CODE.len(), +//! ); +//! } //! //! // start ULP core //! ulp_core.run(esp_hal::ulp_core::UlpCoreWakeupSource::HpCpu); @@ -42,16 +46,20 @@ use esp32s2 as pac; use crate::peripheral::{Peripheral, PeripheralRef}; +/// Enum representing the possible wakeup sources for the ULP core. #[derive(Debug, Clone, Copy)] pub enum UlpCoreWakeupSource { + /// Wakeup source from the HP (High Performance) CPU. HpCpu, } +/// Structure representing the ULP (Ultra-Low Power) core. pub struct UlpCore<'d> { _lp_core: PeripheralRef<'d, crate::soc::peripherals::ULP_RISCV_CORE>, } impl<'d> UlpCore<'d> { + /// Creates a new instance of the `UlpCore` struct. pub fn new(lp_core: impl Peripheral

+ 'd) -> Self { crate::into_ref!(lp_core); @@ -70,6 +78,7 @@ impl<'d> UlpCore<'d> { // ulp_stop(); // } + /// Runs the ULP core with the specified wakeup source. pub fn run(&mut self, wakeup_src: UlpCoreWakeupSource) { ulp_run(wakeup_src); } diff --git a/esp-hal/src/soc/esp32s3/cpu_control.rs b/esp-hal/src/soc/esp32s3/cpu_control.rs index efe7397681b..1628dce2f87 100644 --- a/esp-hal/src/soc/esp32s3/cpu_control.rs +++ b/esp-hal/src/soc/esp32s3/cpu_control.rs @@ -13,11 +13,9 @@ //! # use esp_hal::cpu_control::{CpuControl, Stack}; //! # use core::{cell::RefCell, ptr::addr_of_mut}; //! # use critical_section::Mutex; -//! # use esp_hal::prelude::*; +//! # let delay = Delay::new(); //! static mut APP_CORE_STACK: Stack<8192> = Stack::new(); //! -//! # let delay = Delay::new(&clocks); -//! //! let counter = Mutex::new(RefCell::new(0)); //! //! let mut cpu_control = CpuControl::new(peripherals.CPU_CTRL); @@ -25,8 +23,10 @@ //! cpu1_task(&delay, &counter); //! }; //! let _guard = cpu_control -//! .start_app_core(unsafe { &mut *addr_of_mut!(APP_CORE_STACK) }, -//! cpu1_fnctn) .unwrap(); +//! .start_app_core( +//! unsafe { &mut *addr_of_mut!(APP_CORE_STACK) }, +//! cpu1_fnctn, +//! ).unwrap(); //! //! loop { //! delay.delay(1.secs()); @@ -37,7 +37,7 @@ //! // Where `cpu1_task()` may be defined as: //! # use esp_hal::delay::Delay; //! # use core::cell::RefCell; -//! # use esp_hal::prelude::*; +//! //! fn cpu1_task( //! delay: &Delay, //! counter: &critical_section::Mutex>, @@ -100,14 +100,17 @@ impl Stack { } } + /// Returns the length of the stack in bytes. pub const fn len(&self) -> usize { SIZE } + /// Returns a mutable pointer to the bottom of the stack. pub fn bottom(&mut self) -> *mut u32 { self.mem.as_mut_ptr() as *mut u32 } + /// Returns a mutable pointer to the top of the stack. pub fn top(&mut self) -> *mut u32 { unsafe { self.bottom().add(SIZE / 4) } } @@ -132,9 +135,11 @@ impl<'a> Drop for AppCoreGuard<'a> { } } +/// Represents errors that can occur while working with the core. #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { + /// The core is already running. CoreAlreadyRunning, } @@ -168,6 +173,7 @@ unsafe fn internal_park_core(core: Cpu) { } impl<'d> CpuControl<'d> { + /// Creates a new instance of `CpuControl`. pub fn new(cpu_control: impl Peripheral

+ 'd) -> CpuControl<'d> { crate::into_ref!(cpu_control); diff --git a/esp-hal/src/soc/esp32s3/efuse/mod.rs b/esp-hal/src/soc/esp32s3/efuse/mod.rs index 8acdc754326..d4303daf85c 100644 --- a/esp-hal/src/soc/esp32s3/efuse/mod.rs +++ b/esp-hal/src/soc/esp32s3/efuse/mod.rs @@ -1,13 +1,13 @@ //! # Reading of eFuses (ESP32-S3) //! //! ## Overview +//! //! The `efuse` module provides functionality for reading eFuse data //! from the `ESP32-S3` chip, allowing access to various chip-specific -//! information such as : +//! information such as: +//! //! * MAC address -//! * core count -//! * CPU frequency -//! * chip type +//! * Chip revision //! //! and more. It is useful for retrieving chip-specific configuration and //! identification data during runtime. @@ -15,8 +15,10 @@ //! The `Efuse` struct represents the eFuse peripheral and is responsible for //! reading various eFuse fields and values. //! -//! ## Examples +//! ## Example +//! //! ### Read chip's MAC address from the eFuse storage. +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; @@ -25,7 +27,7 @@ //! # use core::writeln; //! # use core::fmt::Write; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, &clocks, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -45,6 +47,7 @@ use crate::{analog::adc::Attenuation, peripherals::EFUSE}; mod fields; +/// A struct representing the eFuse functionality of the chip. pub struct Efuse; impl Efuse { diff --git a/esp-hal/src/soc/esp32s3/gpio.rs b/esp-hal/src/soc/esp32s3/gpio.rs index 96f0c6a3658..9c4598bfe8c 100644 --- a/esp-hal/src/soc/esp32s3/gpio.rs +++ b/esp-hal/src/soc/esp32s3/gpio.rs @@ -52,6 +52,7 @@ use crate::{ peripherals::GPIO, }; +/// The total number of GPIO pins available. pub const NUM_PINS: usize = 49; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; @@ -404,46 +405,46 @@ crate::gpio::gpio! { } crate::gpio::analog! { - ( 0, 0, touch_pad0(), mux_sel, fun_sel, fun_ie, rue, rde) - ( 1, 1, touch_pad1(), mux_sel, fun_sel, fun_ie, rue, rde) - ( 2, 2, touch_pad2(), mux_sel, fun_sel, fun_ie, rue, rde) - ( 3, 3, touch_pad3(), mux_sel, fun_sel, fun_ie, rue, rde) - ( 4, 4, touch_pad4(), mux_sel, fun_sel, fun_ie, rue, rde) - ( 5, 5, touch_pad5(), mux_sel, fun_sel, fun_ie, rue, rde) - ( 6, 6, touch_pad6(), mux_sel, fun_sel, fun_ie, rue, rde) - ( 7, 7, touch_pad7(), mux_sel, fun_sel, fun_ie, rue, rde) - ( 8, 8, touch_pad8(), mux_sel, fun_sel, fun_ie, rue, rde) - ( 9, 9, touch_pad9(), mux_sel, fun_sel, fun_ie, rue, rde) - (10, 10, touch_pad10(), mux_sel, fun_sel, fun_ie, rue, rde) - (11, 11, touch_pad11(), mux_sel, fun_sel, fun_ie, rue, rde) - (12, 12, touch_pad12(), mux_sel, fun_sel, fun_ie, rue, rde) - (13, 13, touch_pad13(), mux_sel, fun_sel, fun_ie, rue, rde) - (14, 14, touch_pad14(), mux_sel, fun_sel, fun_ie, rue, rde) - (15, 15, xtal_32p_pad(), x32p_mux_sel, x32p_fun_sel, x32p_fun_ie, x32p_rue, x32p_rde) - (16, 16, xtal_32n_pad(), x32n_mux_sel, x32n_fun_sel, x32n_fun_ie, x32n_rue, x32n_rde) - (17, 17, pad_dac1(), pdac1_mux_sel,pdac1_fun_sel,pdac1_fun_ie, pdac1_rue, pdac1_rde) - (18, 18, pad_dac2(), pdac2_mux_sel,pdac2_fun_sel,pdac2_fun_ie, pdac2_rue, pdac2_rde) - (19, 19, rtc_pad19(), mux_sel, fun_sel, fun_ie, rue, rde) - (20, 20, rtc_pad20(), mux_sel, fun_sel, fun_ie, rue, rde) - (21, 21, rtc_pad21(), mux_sel, fun_sel, fun_ie, rue, rde) + ( 0, 0, touch_pad(0), mux_sel, fun_sel, fun_ie, rue, rde) + ( 1, 1, touch_pad(1), mux_sel, fun_sel, fun_ie, rue, rde) + ( 2, 2, touch_pad(2), mux_sel, fun_sel, fun_ie, rue, rde) + ( 3, 3, touch_pad(3), mux_sel, fun_sel, fun_ie, rue, rde) + ( 4, 4, touch_pad(4), mux_sel, fun_sel, fun_ie, rue, rde) + ( 5, 5, touch_pad(5), mux_sel, fun_sel, fun_ie, rue, rde) + ( 6, 6, touch_pad(6), mux_sel, fun_sel, fun_ie, rue, rde) + ( 7, 7, touch_pad(7), mux_sel, fun_sel, fun_ie, rue, rde) + ( 8, 8, touch_pad(8), mux_sel, fun_sel, fun_ie, rue, rde) + ( 9, 9, touch_pad(9), mux_sel, fun_sel, fun_ie, rue, rde) + (10, 10, touch_pad(10), mux_sel, fun_sel, fun_ie, rue, rde) + (11, 11, touch_pad(11), mux_sel, fun_sel, fun_ie, rue, rde) + (12, 12, touch_pad(12), mux_sel, fun_sel, fun_ie, rue, rde) + (13, 13, touch_pad(13), mux_sel, fun_sel, fun_ie, rue, rde) + (14, 14, touch_pad(14), mux_sel, fun_sel, fun_ie, rue, rde) + (15, 15, xtal_32p_pad(), x32p_mux_sel, x32p_fun_sel, x32p_fun_ie, x32p_rue, x32p_rde) + (16, 16, xtal_32n_pad(), x32n_mux_sel, x32n_fun_sel, x32n_fun_ie, x32n_rue, x32n_rde) + (17, 17, pad_dac1(), pdac1_mux_sel,pdac1_fun_sel,pdac1_fun_ie, pdac1_rue, pdac1_rde) + (18, 18, pad_dac2(), pdac2_mux_sel,pdac2_fun_sel,pdac2_fun_ie, pdac2_rue, pdac2_rde) + (19, 19, rtc_pad19(), mux_sel, fun_sel, fun_ie, rue, rde) + (20, 20, rtc_pad20(), mux_sel, fun_sel, fun_ie, rue, rde) + (21, 21, rtc_pad21(), mux_sel, fun_sel, fun_ie, rue, rde) } crate::gpio::rtc_pins! { - ( 0, 0, touch_pad0(), "", touch_pad0_hold, rue, rde) - ( 1, 1, touch_pad1(), "", touch_pad1_hold, rue, rde) - ( 2, 2, touch_pad2(), "", touch_pad2_hold, rue, rde) - ( 3, 3, touch_pad3(), "", touch_pad3_hold, rue, rde) - ( 4, 4, touch_pad4(), "", touch_pad4_hold, rue, rde) - ( 5, 5, touch_pad5(), "", touch_pad5_hold, rue, rde) - ( 6, 6, touch_pad6(), "", touch_pad6_hold, rue, rde) - ( 7, 7, touch_pad7(), "", touch_pad7_hold, rue, rde) - ( 8, 8, touch_pad8(), "", touch_pad8_hold, rue, rde) - ( 9, 9, touch_pad9(), "", touch_pad9_hold, rue, rde) - (10, 10, touch_pad10(), "", touch_pad10_hold, rue, rde) - (11, 11, touch_pad11(), "", touch_pad11_hold, rue, rde) - (12, 12, touch_pad12(), "", touch_pad12_hold, rue, rde) - (13, 13, touch_pad13(), "", touch_pad13_hold, rue, rde) - (14, 14, touch_pad14(), "", touch_pad14_hold, rue, rde) + ( 0, 0, touch_pad(0), "", touch_pad0_hold, rue, rde) + ( 1, 1, touch_pad(1), "", touch_pad1_hold, rue, rde) + ( 2, 2, touch_pad(2), "", touch_pad2_hold, rue, rde) + ( 3, 3, touch_pad(3), "", touch_pad3_hold, rue, rde) + ( 4, 4, touch_pad(4), "", touch_pad4_hold, rue, rde) + ( 5, 5, touch_pad(5), "", touch_pad5_hold, rue, rde) + ( 6, 6, touch_pad(6), "", touch_pad6_hold, rue, rde) + ( 7, 7, touch_pad(7), "", touch_pad7_hold, rue, rde) + ( 8, 8, touch_pad(8), "", touch_pad8_hold, rue, rde) + ( 9, 9, touch_pad(9), "", touch_pad9_hold, rue, rde) + (10, 10, touch_pad(10), "", touch_pad10_hold, rue, rde) + (11, 11, touch_pad(11), "", touch_pad11_hold, rue, rde) + (12, 12, touch_pad(12), "", touch_pad12_hold, rue, rde) + (13, 13, touch_pad(13), "", touch_pad13_hold, rue, rde) + (14, 14, touch_pad(14), "", touch_pad14_hold, rue, rde) (15, 15, xtal_32p_pad(), x32p_, x32p_hold, x32p_rue, x32p_rde) (16, 16, xtal_32n_pad(), x32n_, x32n_hold, x32n_rue, x32n_rde) (17, 17, pad_dac1(), pdac1_, pdac1_hold, pdac1_rue, pdac1_rde) @@ -492,5 +493,5 @@ impl InterruptStatusRegisterAccess for InterruptStatusRegisterAccessBank1 { } // implement marker traits on USB pins -impl crate::otg_fs::UsbDm for Gpio19 {} -impl crate::otg_fs::UsbDp for Gpio20 {} +impl crate::otg_fs::UsbDm for GpioPin<19> {} +impl crate::otg_fs::UsbDp for GpioPin<20> {} diff --git a/esp-hal/src/soc/esp32s3/mod.rs b/esp-hal/src/soc/esp32s3/mod.rs index f711304123c..43f62fa64a5 100644 --- a/esp-hal/src/soc/esp32s3/mod.rs +++ b/esp-hal/src/soc/esp32s3/mod.rs @@ -11,11 +11,7 @@ use core::ptr::addr_of_mut; -use self::peripherals::{LPWR, TIMG0, TIMG1}; -use crate::{ - rtc_cntl::{Rtc, SocResetReason}, - timer::timg::Wdt, -}; +use crate::rtc_cntl::SocResetReason; pub mod cpu_control; pub mod efuse; @@ -39,17 +35,26 @@ macro_rules! chip { pub use chip; pub(crate) mod constants { + /// The base clock frequency for the I2S peripheral (Hertz). pub const I2S_SCLK: u32 = 160_000_000; + /// The default clock source for I2S operations. pub const I2S_DEFAULT_CLK_SRC: u8 = 2; + /// The starting address of the Remote Control (RMT) module's RAM. pub const RMT_RAM_START: usize = 0x60016800; + /// The size, in bytes, of each RMT channel's dedicated RAM. pub const RMT_CHANNEL_RAM_SIZE: usize = 48; + /// RMT Clock source value. pub const RMT_CLOCK_SRC: u8 = 1; + /// RMT Clock source frequence. pub const RMT_CLOCK_SRC_FREQ: fugit::HertzU32 = fugit::HertzU32::MHz(80); + /// The lower bound of the system's DRAM (Data RAM) address space. pub const SOC_DRAM_LOW: u32 = 0x3FC8_8000; + /// The upper bound of the system's DRAM (Data RAM) address space. pub const SOC_DRAM_HIGH: u32 = 0x3FD0_0000; + /// A reference clock tick of 1 MHz. pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500); } @@ -146,8 +151,6 @@ pub unsafe extern "C" fn ESP32Reset() -> ! { stack_chk_guard.write_volatile(0xdeadbabe); } - crate::interrupt::setup_interrupts(); - // continue with default reset handler xtensa_lx_rt::Reset(); } @@ -161,16 +164,7 @@ pub extern "Rust" fn __init_data() -> bool { false } -#[export_name = "__post_init"] -unsafe fn post_init() { - // RTC domain must be enabled before we try to disable - let mut rtc = Rtc::new(LPWR::steal()); - rtc.rwdt.disable(); - - Wdt::::set_wdt_enabled(false); - Wdt::::set_wdt_enabled(false); -} - +/// Write back a specific range of data in the cache. #[doc(hidden)] #[link_section = ".rwtext"] pub unsafe fn cache_writeback_addr(addr: u32, size: u32) { @@ -185,6 +179,7 @@ pub unsafe fn cache_writeback_addr(addr: u32, size: u32) { Cache_Resume_DCache_Autoload(autoload); } +/// Invalidate a specific range of addresses in the cache. #[doc(hidden)] #[link_section = ".rwtext"] pub unsafe fn cache_invalidate_addr(addr: u32, size: u32) { @@ -194,6 +189,7 @@ pub unsafe fn cache_invalidate_addr(addr: u32, size: u32) { Cache_Invalidate_Addr(addr, size); } +/// Get the size of a cache line in the DCache. #[doc(hidden)] #[link_section = ".rwtext"] pub unsafe fn cache_get_dcache_line_size() -> u32 { diff --git a/esp-hal/src/soc/esp32s3/peripherals.rs b/esp-hal/src/soc/esp32s3/peripherals.rs index 8671a1e516c..9e0592abf52 100644 --- a/esp-hal/src/soc/esp32s3/peripherals.rs +++ b/esp-hal/src/soc/esp32s3/peripherals.rs @@ -63,6 +63,7 @@ crate::peripherals! { SPI3 <= SPI3 (SPI3), SYSTEM <= SYSTEM, SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, TIMG0 <= TIMG0, TIMG1 <= TIMG1, TWAI0 <= TWAI0, diff --git a/esp-hal/src/soc/esp32s3/psram.rs b/esp-hal/src/soc/esp32s3/psram.rs index f9f986af9de..c910895e941 100644 --- a/esp-hal/src/soc/esp32s3/psram.rs +++ b/esp-hal/src/soc/esp32s3/psram.rs @@ -18,6 +18,7 @@ static mut PSRAM_VADDR: u32 = 0x3C000000; +/// Returns the start address of the PSRAM virtual address space. pub fn psram_vaddr_start() -> usize { unsafe { PSRAM_VADDR as usize } } @@ -42,6 +43,8 @@ cfg_if::cfg_if! { } } +/// The total size of the PSRAM in bytes, calculated from the PSRAM size +/// constant. pub const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024; /// Initialize PSRAM to be used for data. @@ -54,6 +57,7 @@ pub const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024; feature = "opsram-8m", feature = "opsram-16m" ))] +#[procmacros::ram] pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral

) { const CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE: u32 = 0x4000; const CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS: u8 = 8; @@ -862,14 +866,21 @@ pub(crate) mod utils { fn esp_rom_opiflash_pin_config(); } + /// Represents the operational mode registers of an OPI PSRAM. #[derive(Default)] #[repr(C)] struct OpiPsramModeReg { + // Mode register 0 (MR0). pub mr0: u8, + // Mode register 1 (MR1). pub mr1: u8, + // Mode register 2 (MR2). pub mr2: u8, + // Mode register 3 (MR3). pub mr3: u8, + // Mode register 4 (MR4). pub mr4: u8, + // Mode register 8 (MR8). pub mr8: u8, } diff --git a/esp-hal/src/soc/esp32s3/trng.rs b/esp-hal/src/soc/esp32s3/trng.rs index c67933c8f5c..fc98ddda813 100644 --- a/esp-hal/src/soc/esp32s3/trng.rs +++ b/esp-hal/src/soc/esp32s3/trng.rs @@ -17,6 +17,8 @@ const ADC_SARADC_DTEST_RTC_ADDR_LSB: u32 = 0; use crate::regi2c_write_mask; +/// Enable true randomness by enabling the entropy source. +/// Blocks `ADC` usage. pub(crate) fn ensure_randomness() { unsafe { let rtc_cntl = &*crate::peripherals::RTC_CNTL::ptr(); @@ -117,7 +119,8 @@ pub(crate) fn ensure_randomness() { } } -pub fn revert_trng() { +/// Disable true randomness. Unlocks `ADC` peripheral. +pub(crate) fn revert_trng() { let system = unsafe { &*crate::peripherals::SYSTEM::ptr() }; let apb_saradc = unsafe { &*crate::peripherals::APB_SARADC::ptr() }; let sens = unsafe { &*crate::peripherals::SENS::ptr() }; diff --git a/esp-hal/src/soc/esp32s3/ulp_core.rs b/esp-hal/src/soc/esp32s3/ulp_core.rs index f704a2a171b..deae3f75a92 100644 --- a/esp-hal/src/soc/esp32s3/ulp_core.rs +++ b/esp-hal/src/soc/esp32s3/ulp_core.rs @@ -1,6 +1,7 @@ //! Control the ULP core //! //! ## Overview +//! //! The `ULP CORE` peripheral allows control over the `Ultra-Low Power //! (ULP) core` in `ESP` chips. The ULP core is a low-power processor //! designed for executing tasks in deep sleep mode, enabling efficient power @@ -11,22 +12,28 @@ //! operation. The `UlpCore` struct is initialized with a peripheral reference //! to the `ULP CORE` instance. //! -//! ## Examples +//! ## Example +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! const CODE: &[u8] = &[ //! 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x01, 0x81, 0x45, 0x85, 0x05, -//! 0x0c, 0xc1, 0xf5, 0xbf, 0x00, 0x00, 0x00, 0x00, -//! ]; +//! 0x0c, 0xc1, 0xf5, 0xbf, 0x00, 0x00, 0x00, 0x00, +//! ]; +//! //! let mut ulp_core = -//! esp_hal::ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE); +//! esp_hal::ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE); //! ulp_core.stop(); //! //! // copy code to RTC ram //! let lp_ram = 0x5000_0000 as *mut u8; //! unsafe { -//! core::ptr::copy_nonoverlapping(CODE as *const _ as *const u8, lp_ram, -//! CODE.len()); } +//! core::ptr::copy_nonoverlapping( +//! CODE as *const _ as *const u8, +//! lp_ram, +//! CODE.len(), +//! ); +//! } //! //! // start ULP core //! ulp_core.run(esp_hal::ulp_core::UlpCoreWakeupSource::HpCpu); @@ -42,16 +49,20 @@ use esp32s3 as pac; use crate::peripheral::{Peripheral, PeripheralRef}; +/// Enum representing the possible wakeup sources for the ULP core. #[derive(Debug, Clone, Copy)] pub enum UlpCoreWakeupSource { + /// Wakeup source from the HP (High Performance) CPU. HpCpu, } +/// Structure representing the ULP (Ultra-Low Power) core. pub struct UlpCore<'d> { _lp_core: PeripheralRef<'d, crate::soc::peripherals::ULP_RISCV_CORE>, } impl<'d> UlpCore<'d> { + /// Creates a new instance of the `UlpCore` struct. pub fn new(lp_core: impl Peripheral

+ 'd) -> Self { crate::into_ref!(lp_core); @@ -66,10 +77,12 @@ impl<'d> UlpCore<'d> { this } + /// Stops the ULP core. pub fn stop(&mut self) { ulp_stop(); } + /// Runs the ULP core with the specified wakeup source. pub fn run(&mut self, wakeup_src: UlpCoreWakeupSource) { ulp_run(wakeup_src); } diff --git a/esp-hal/src/soc/mod.rs b/esp-hal/src/soc/mod.rs index 1e7c93b41a2..d364895a771 100644 --- a/esp-hal/src/soc/mod.rs +++ b/esp-hal/src/soc/mod.rs @@ -24,8 +24,10 @@ mod efuse_field; static MAC_OVERRIDE_STATE: AtomicU8 = AtomicU8::new(0); static mut MAC_OVERRIDE: [u8; 6] = [0; 6]; +/// Error indicating issues with setting the MAC address. #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum SetMacError { + /// The MAC address has already been set and cannot be changed. AlreadySet, } @@ -72,6 +74,13 @@ pub(crate) fn is_valid_ram_address(address: u32) -> bool { (self::constants::SOC_DRAM_LOW..=self::constants::SOC_DRAM_HIGH).contains(&address) } +#[allow(unused)] +pub(crate) fn is_slice_in_dram(slice: &[T]) -> bool { + let start = slice.as_ptr() as u32; + let end = start + slice.len() as u32; + self::constants::SOC_DRAM_LOW <= start && end <= self::constants::SOC_DRAM_HIGH +} + #[allow(unused)] pub(crate) fn is_valid_psram_address(address: u32) -> bool { #[cfg(psram)] diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index ef9556c9512..237ef8e54f6 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -1,39 +1,42 @@ //! # Serial Peripheral Interface - Master Mode //! //! ## Overview +//! //! In this mode, the SPI acts as master and initiates the SPI transactions. //! //! ## Configuration +//! //! The peripheral can be used in full-duplex and half-duplex mode and can //! leverage DMA for data transfers. It can also be used in blocking or async. //! //! ### Exclusive access to the SPI bus +//! //! If all you want to do is to communicate to a single device, and you initiate //! transactions yourself, there are a number of ways to achieve this: //! //! - Use the [`FullDuplex`](embedded_hal_02::spi::FullDuplex) trait to //! read/write single bytes at a time, -//! - Use the [`SpiBus`](embedded_hal::spi::SpiBus) trait (requires the -//! "embedded-hal" feature) and its associated functions to initiate -//! transactions with simultaneous reads and writes, or +//! - Use the [`SpiBus`](embedded_hal::spi::SpiBus) trait and its associated +//! functions to initiate transactions with simultaneous reads and writes, or //! - Use the `ExclusiveDevice` struct from [`embedded-hal-bus`] or `SpiDevice` //! from [`embassy-embedded-hal`]. //! -//! //! ### Shared SPI access +//! //! If you have multiple devices on the same SPI bus that each have their own CS //! line, you may want to have a look at the implementations provided by //! [`embedded-hal-bus`] and [`embassy-embedded-hal`]. //! //! ## Usage +//! //! The module implements several third-party traits from embedded-hal@0.2.x, //! embedded-hal@1.x.x and embassy-embedded-hal //! //! ## Example +//! //! ### SPI Initialization //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use crate::esp_hal::prelude::_fugit_RateExtU32; //! # use esp_hal::spi::SpiMode; //! # use esp_hal::spi::master::Spi; //! # use esp_hal::gpio::Io; @@ -47,7 +50,6 @@ //! peripherals.SPI2, //! 100.kHz(), //! SpiMode::Mode0, -//! &mut clocks, //! ) //! .with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs)); //! # } @@ -58,6 +60,7 @@ use core::marker::PhantomData; +pub use dma::*; #[cfg(not(any(esp32, esp32s2)))] use enumset::EnumSet; #[cfg(not(any(esp32, esp32s2)))] @@ -67,19 +70,18 @@ use fugit::HertzU32; use procmacros::ram; use super::{ + DmaError, DuplexMode, Error, FullDuplexMode, HalfDuplexMode, - IsFullDuplex, - IsHalfDuplex, SpiBitOrder, SpiDataMode, SpiMode, }; use crate::{ clock::Clocks, - dma::{DescriptorChain, DmaPeripheral, Rx, Tx}, + dma::{DmaDescriptor, DmaPeripheral, Rx, Tx}, gpio::{InputPin, InputSignal, OutputPin, OutputSignal}, interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, @@ -88,20 +90,15 @@ use crate::{ system::PeripheralClockControl, }; -/// Prelude for the SPI (Master) driver -pub mod prelude { - #[cfg(spi3)] - pub use super::dma::WithDmaSpi3 as _esp_hal_spi_master_dma_WithDmaSpi3; - pub use super::{ - dma::WithDmaSpi2 as _esp_hal_spi_master_dma_WithDmaSpi2, - Instance as _esp_hal_spi_master_Instance, - InstanceDma as _esp_hal_spi_master_InstanceDma, - }; -} - +/// Enumeration of possible SPI interrupt events. #[cfg(not(any(esp32, esp32s2)))] #[derive(EnumSetType)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SpiInterrupt { + /// Indicates that the SPI transaction has completed successfully. + /// + /// This interrupt is triggered when an SPI transaction has finished + /// transmitting and receiving data. TransDone, } @@ -117,26 +114,46 @@ const EMPTY_WRITE_PAD: u8 = 0x00u8; #[allow(unused)] const MAX_DMA_SIZE: usize = 32736; -/// SPI command, 1 to 16 bits. +/// SPI commands, each consisting of a 16-bit command value and a data mode. /// +/// Used to define specific commands sent over the SPI bus. /// Can be [Command::None] if command phase should be suppressed. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Command { + /// No command is sent. None, + /// Command1. Command1(u16, SpiDataMode), + /// Command2. Command2(u16, SpiDataMode), + /// Command3. Command3(u16, SpiDataMode), + /// Command4. Command4(u16, SpiDataMode), + /// Command5. Command5(u16, SpiDataMode), + /// Command6. Command6(u16, SpiDataMode), + /// Command7. Command7(u16, SpiDataMode), + /// Command8. Command8(u16, SpiDataMode), + /// Command9. Command9(u16, SpiDataMode), + /// Command10. Command10(u16, SpiDataMode), + /// Command11. Command11(u16, SpiDataMode), + /// Command12. Command12(u16, SpiDataMode), + /// Command13. Command13(u16, SpiDataMode), + /// Command14. Command14(u16, SpiDataMode), + /// Command15. Command15(u16, SpiDataMode), + /// Command16. Command16(u16, SpiDataMode), } @@ -212,42 +229,78 @@ impl Command { } } -/// SPI address, 1 to 32 bits. +/// SPI address, ranging from 1 to 32 bits, paired with a data mode. /// +/// This can be used to specify the address phase of SPI transactions. /// Can be [Address::None] if address phase should be suppressed. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Address { + /// No address phase. None, + /// Address with 1-bit. Address1(u32, SpiDataMode), + /// Address with 2-bit. Address2(u32, SpiDataMode), + /// Address with 3-bit. Address3(u32, SpiDataMode), + /// Address with 4-bit. Address4(u32, SpiDataMode), + /// Address with 5-bit. Address5(u32, SpiDataMode), + /// Address with 6-bit. Address6(u32, SpiDataMode), + /// Address with 7-bit. Address7(u32, SpiDataMode), + /// Address with 8-bit. Address8(u32, SpiDataMode), + /// Address with 9-bit. Address9(u32, SpiDataMode), + /// Address with 10-bit. Address10(u32, SpiDataMode), + /// Address with 11-bit. Address11(u32, SpiDataMode), + /// Address with 12-bit. Address12(u32, SpiDataMode), + /// Address with 13-bit. Address13(u32, SpiDataMode), + /// Address with 14-bit. Address14(u32, SpiDataMode), + /// Address with 15-bit. Address15(u32, SpiDataMode), + /// Address with 16-bit. Address16(u32, SpiDataMode), + /// Address with 17-bit. Address17(u32, SpiDataMode), + /// Address with 18-bit. Address18(u32, SpiDataMode), + /// Address with 19-bit. Address19(u32, SpiDataMode), + /// Address with 20-bit. Address20(u32, SpiDataMode), + /// Address with 21-bit. Address21(u32, SpiDataMode), + /// Address with 22-bit. Address22(u32, SpiDataMode), + /// Address with 23-bit. Address23(u32, SpiDataMode), + /// Address with 24-bit. Address24(u32, SpiDataMode), + /// Address with 25-bit. Address25(u32, SpiDataMode), + /// Address with 26-bit. Address26(u32, SpiDataMode), + /// Address with 27-bit. Address27(u32, SpiDataMode), + /// Address with 28-bit. Address28(u32, SpiDataMode), + /// Address with 29-bit. Address29(u32, SpiDataMode), + /// Address with 30-bit. Address30(u32, SpiDataMode), + /// Address with 31-bit. Address31(u32, SpiDataMode), + /// Address with 32-bit. Address32(u32, SpiDataMode), } @@ -373,6 +426,8 @@ impl Address { /// Read and Write in half duplex mode. pub trait HalfDuplexReadWrite { + /// The associated error type that will be returned in the event of a + /// failure. type Error; /// Half-duplex read. @@ -402,10 +457,9 @@ pub struct Spi<'d, T, M> { _mode: PhantomData, } -impl<'d, T, M> Spi<'d, T, M> +impl<'d, T> Spi<'d, T, FullDuplexMode> where T: Instance, - M: IsFullDuplex, { /// Read bytes from SPI. /// @@ -454,12 +508,15 @@ where spi: impl Peripheral

+ 'd, frequency: HertzU32, mode: SpiMode, - clocks: &Clocks<'d>, ) -> Spi<'d, T, FullDuplexMode> { crate::into_ref!(spi); - Self::new_internal(spi, frequency, mode, clocks) + Self::new_internal(spi, frequency, mode) } + /// Assign the SCK (Serial Clock) pin for the SPI instance. + /// + /// Sets the specified pin to push-pull output and connects it to the SPI + /// clock signal. pub fn with_sck(self, sck: impl Peripheral

+ 'd) -> Self { crate::into_ref!(sck); sck.set_to_push_pull_output(private::Internal); @@ -468,6 +525,10 @@ where self } + /// Assign the MOSI (Master Out Slave In) pin for the SPI instance. + /// + /// Sets the specified pin to push-pull output and connects it to the SPI + /// MOSI signal. pub fn with_mosi(self, mosi: impl Peripheral

+ 'd) -> Self { crate::into_ref!(mosi); mosi.set_to_push_pull_output(private::Internal); @@ -476,14 +537,21 @@ where self } + /// Assign the MISO (Master In Slave Out) pin for the SPI instance. + /// + /// Sets the specified pin to input and connects it to the SPI MISO signal. pub fn with_miso(self, miso: impl Peripheral

+ 'd) -> Self { crate::into_ref!(miso); - miso.set_to_input(private::Internal); + miso.init_input(false, false, private::Internal); miso.connect_input_to_peripheral(self.spi.miso_signal(), private::Internal); self } + /// Assign the CS (Chip Select) pin for the SPI instance. + /// + /// Sets the specified pin to push-pull output and connects it to the SPI CS + /// signal. pub fn with_cs(self, cs: impl Peripheral

+ 'd) -> Self { crate::into_ref!(cs); cs.set_to_push_pull_output(private::Internal); @@ -525,7 +593,7 @@ where if let Some(miso) = miso { crate::into_ref!(miso); - miso.set_to_input(private::Internal); + miso.init_input(false, false, private::Internal); miso.connect_input_to_peripheral(self.spi.miso_signal(), private::Internal); } @@ -542,23 +610,27 @@ where spi: PeripheralRef<'d, T>, frequency: HertzU32, mode: SpiMode, - clocks: &Clocks<'d>, ) -> Spi<'d, T, FullDuplexMode> { + spi.reset_peripheral(); spi.enable_peripheral(); let mut spi = Spi { spi, _mode: PhantomData, }; - spi.spi.setup(frequency, clocks); + spi.spi.setup(frequency); spi.spi.init(); spi.spi.set_data_mode(mode); spi } - pub fn change_bus_frequency(&mut self, frequency: HertzU32, clocks: &Clocks<'d>) { - self.spi.ch_bus_freq(frequency, clocks); + /// Change the bus frequency of the SPI instance. + /// + /// This method allows user to update the bus frequency for the SPI + /// communication after the instance has been created. + pub fn change_bus_frequency(&mut self, frequency: HertzU32) { + self.spi.ch_bus_freq(frequency); } } @@ -574,12 +646,15 @@ where spi: impl Peripheral

+ 'd, frequency: HertzU32, mode: SpiMode, - clocks: &Clocks<'d>, ) -> Spi<'d, T, HalfDuplexMode> { crate::into_ref!(spi); - Self::new_internal(spi, frequency, mode, clocks) + Self::new_internal(spi, frequency, mode) } + /// Assign the SCK (Serial Clock) pin for the SPI instance. + /// + /// Sets the specified pin to push-pull output and connects it to the SPI + /// clock signal. pub fn with_sck(self, sck: impl Peripheral

+ 'd) -> Self { crate::into_ref!(sck); sck.set_to_push_pull_output(private::Internal); @@ -588,6 +663,11 @@ where self } + /// Assign the MOSI (Master Out Slave In) pin for the SPI instance in + /// half-duplex mode. + /// + /// Enables both input and output functionality for the pin, and connects it + /// to the MOSI signal and SIO0 input signal. pub fn with_mosi( self, mosi: impl Peripheral

+ 'd, @@ -601,6 +681,11 @@ where self } + /// Assign the MISO (Master In Slave Out) pin for the SPI instance in + /// half-duplex mode. + /// + /// Enables both input and output functionality for the pin, and connects it + /// to the MISO signal and SIO1 input signal. pub fn with_miso( self, miso: impl Peripheral

+ 'd, @@ -614,6 +699,10 @@ where self } + /// Assign the SIO2 pin for the SPI instance. + /// + /// Enables both input and output functionality for the pin, and connects it + /// to the SIO2 output and input signals. pub fn with_sio2( self, sio2: impl Peripheral

+ 'd, @@ -627,6 +716,10 @@ where self } + /// Assign the SIO3 pin for the SPI instance. + /// + /// Enables both input and output functionality for the pin, and connects it + /// to the SIO3 output and input signals. pub fn with_sio3( self, sio3: impl Peripheral

+ 'd, @@ -640,6 +733,10 @@ where self } + /// Assign the CS (Chip Select) pin for the SPI instance. + /// + /// Sets the specified pin to push-pull output and connects it to the SPI CS + /// signal. pub fn with_cs(self, cs: impl Peripheral

+ 'd) -> Self { crate::into_ref!(cs); cs.set_to_push_pull_output(private::Internal); @@ -719,23 +816,27 @@ where spi: PeripheralRef<'d, T>, frequency: HertzU32, mode: SpiMode, - clocks: &Clocks<'d>, ) -> Spi<'d, T, HalfDuplexMode> { + spi.reset_peripheral(); spi.enable_peripheral(); let mut spi = Spi { spi, _mode: PhantomData, }; - spi.spi.setup(frequency, clocks); + spi.spi.setup(frequency); spi.spi.init(); spi.spi.set_data_mode(mode); spi } - pub fn change_bus_frequency(&mut self, frequency: HertzU32, clocks: &Clocks<'d>) { - self.spi.ch_bus_freq(frequency, clocks); + /// Change the bus frequency of the SPI instance in half-duplex mode. + /// + /// This method allows you to update the bus frequency for the SPI + /// communication after the instance has been created. + pub fn change_bus_frequency(&mut self, frequency: HertzU32) { + self.spi.ch_bus_freq(frequency); } /// Set the bit order for the SPI instance. @@ -747,10 +848,9 @@ where } } -impl HalfDuplexReadWrite for Spi<'_, T, M> +impl HalfDuplexReadWrite for Spi<'_, T, HalfDuplexMode> where T: Instance, - M: IsHalfDuplex, { type Error = Error; @@ -794,11 +894,9 @@ where } } -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::spi::FullDuplex for Spi<'_, T, M> +impl embedded_hal_02::spi::FullDuplex for Spi<'_, T, FullDuplexMode> where T: Instance, - M: IsFullDuplex, { type Error = Error; @@ -811,11 +909,9 @@ where } } -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::spi::Transfer for Spi<'_, T, M> +impl embedded_hal_02::blocking::spi::Transfer for Spi<'_, T, FullDuplexMode> where T: Instance, - M: IsFullDuplex, { type Error = Error; @@ -824,11 +920,9 @@ where } } -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::spi::Write for Spi<'_, T, M> +impl embedded_hal_02::blocking::spi::Write for Spi<'_, T, FullDuplexMode> where T: Instance, - M: IsFullDuplex, { type Error = Error; @@ -838,151 +932,130 @@ where } } -pub mod dma { +mod dma { + use core::{ + cmp::min, + sync::atomic::{fence, Ordering}, + }; + use super::*; #[cfg(spi3)] use crate::dma::Spi3Peripheral; use crate::{ dma::{ - dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx}, + asynch::{DmaRxFuture, DmaTxFuture}, Channel, - ChannelRx, - ChannelTx, - DescriptorChain, DmaChannel, - DmaDescriptor, - DmaTransferRx, - DmaTransferRxOwned, - DmaTransferTx, - DmaTransferTxOwned, - DmaTransferTxRx, - DmaTransferTxRxOwned, - ReadBuffer, + DmaRxBuf, + DmaTxBuf, + RxPrivate, Spi2Peripheral, SpiPeripheral, TxPrivate, - WriteBuffer, }, InterruptConfigurable, Mode, }; - pub trait WithDmaSpi2<'d, C, M, DmaMode> + impl<'d, M> Spi<'d, crate::peripherals::SPI2, M> where - C: DmaChannel, - C::P: SpiPeripheral, M: DuplexMode, - DmaMode: Mode, { - fn with_dma( - self, - channel: Channel<'d, C, DmaMode>, - tx_descriptors: &'static mut [DmaDescriptor], - rx_descriptors: &'static mut [DmaDescriptor], - ) -> SpiDma<'d, crate::peripherals::SPI2, C, M, DmaMode>; - } - - #[cfg(spi3)] - pub trait WithDmaSpi3<'d, C, M, DmaMode> - where - C: DmaChannel, - C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, - { - fn with_dma( - self, - channel: Channel<'d, C, DmaMode>, - tx_descriptors: &'static mut [DmaDescriptor], - rx_descriptors: &'static mut [DmaDescriptor], - ) -> SpiDma<'d, crate::peripherals::SPI3, C, M, DmaMode>; - } - - impl<'d, C, M, DmaMode> WithDmaSpi2<'d, C, M, DmaMode> for Spi<'d, crate::peripherals::SPI2, M> - where - C: DmaChannel, - C::P: SpiPeripheral + Spi2Peripheral, - M: DuplexMode, - DmaMode: Mode, - { - fn with_dma( + /// Configures the SPI instance to use DMA with the specified channel. + /// + /// This method prepares the SPI instance for DMA transfers. It + /// initializes the DMA channel for transmission and returns an + /// instance of `SpiDma` that supports DMA operations. + pub fn with_dma( self, mut channel: Channel<'d, C, DmaMode>, - tx_descriptors: &'static mut [DmaDescriptor], - rx_descriptors: &'static mut [DmaDescriptor], - ) -> SpiDma<'d, crate::peripherals::SPI2, C, M, DmaMode> { - channel.tx.init_channel(); // no need to call this for both, TX and RX + ) -> SpiDma<'d, crate::peripherals::SPI2, C, M, DmaMode> + where + C: DmaChannel, + C::P: SpiPeripheral + Spi2Peripheral, + DmaMode: Mode, + { + channel.tx.init_channel(); // no need to call this for both, RX and TX SpiDma { spi: self.spi, channel, - tx_chain: DescriptorChain::new(tx_descriptors), - rx_chain: DescriptorChain::new(rx_descriptors), _mode: PhantomData, } } } #[cfg(spi3)] - impl<'d, C, M, DmaMode> WithDmaSpi3<'d, C, M, DmaMode> for Spi<'d, crate::peripherals::SPI3, M> + impl<'d, M> Spi<'d, crate::peripherals::SPI3, M> where - C: DmaChannel, - C::P: SpiPeripheral + Spi3Peripheral, M: DuplexMode, - DmaMode: Mode, { - fn with_dma( + /// Configures the SPI3 instance to use DMA with the specified channel. + /// + /// This method prepares the SPI instance for DMA transfers using SPI3 + /// and returns an instance of `SpiDma` that supports DMA + /// operations. + pub fn with_dma( self, mut channel: Channel<'d, C, DmaMode>, - tx_descriptors: &'static mut [DmaDescriptor], - rx_descriptors: &'static mut [DmaDescriptor], - ) -> SpiDma<'d, crate::peripherals::SPI3, C, M, DmaMode> { - channel.tx.init_channel(); // no need to call this for both, TX and RX + ) -> SpiDma<'d, crate::peripherals::SPI3, C, M, DmaMode> + where + C: DmaChannel, + C::P: SpiPeripheral + Spi3Peripheral, + DmaMode: Mode, + { + channel.tx.init_channel(); // no need to call this for both, RX and TX SpiDma { spi: self.spi, channel, - tx_chain: DescriptorChain::new(tx_descriptors), - rx_chain: DescriptorChain::new(rx_descriptors), _mode: PhantomData, } } } /// A DMA capable SPI instance. - pub struct SpiDma<'d, T, C, M, DmaMode> + /// + /// Using `SpiDma` is not recommended unless you wish + /// to manage buffers yourself. It's recommended to use + /// [`SpiDmaBus`] via `with_buffers` to get access + /// to a DMA capable SPI bus that implements the + /// embedded-hal traits. + pub struct SpiDma<'d, T, C, D, M> where C: DmaChannel, C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, + D: DuplexMode, + M: Mode, { pub(crate) spi: PeripheralRef<'d, T>, - pub(crate) channel: Channel<'d, C, DmaMode>, - tx_chain: DescriptorChain, - rx_chain: DescriptorChain, - _mode: PhantomData, + pub(crate) channel: Channel<'d, C, M>, + _mode: PhantomData, } - impl<'d, T, C, M, DmaMode> core::fmt::Debug for SpiDma<'d, T, C, M, DmaMode> + impl<'d, T, C, D, M> core::fmt::Debug for SpiDma<'d, T, C, D, M> where C: DmaChannel, C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, + D: DuplexMode, + M: Mode, { + /// Formats the `SpiDma` instance for debugging purposes. + /// + /// This method returns a debug struct with the name "SpiDma" without + /// exposing internal details. fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("SpiDma").finish() } } - impl<'d, T, C, M, DmaMode> SpiDma<'d, T, C, M, DmaMode> + impl<'d, T, C, D, M> SpiDma<'d, T, C, D, M> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, C: DmaChannel, C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, + D: DuplexMode, + M: Mode, { /// Sets the interrupt handler /// @@ -1016,308 +1089,311 @@ pub mod dma { } } - impl<'d, T, C, M, DmaMode> crate::private::Sealed for SpiDma<'d, T, C, M, DmaMode> + impl<'d, T, C, D, M> crate::private::Sealed for SpiDma<'d, T, C, D, M> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, C: DmaChannel, C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, + D: DuplexMode, + M: Mode, { } - impl<'d, T, C, M, DmaMode> InterruptConfigurable for SpiDma<'d, T, C, M, DmaMode> + impl<'d, T, C, D, M> InterruptConfigurable for SpiDma<'d, T, C, D, M> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, C: DmaChannel, C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, + D: DuplexMode, + M: Mode, { + /// Configures the interrupt handler for the DMA-enabled SPI instance. fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { SpiDma::set_interrupt_handler(self, handler); } } - impl<'d, T, C, M, DmaMode> SpiDma<'d, T, C, M, DmaMode> + impl<'d, T, C, D, M> SpiDma<'d, T, C, D, M> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, C: DmaChannel, C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, + D: DuplexMode, + M: Mode, { - pub fn change_bus_frequency(&mut self, frequency: HertzU32, clocks: &Clocks<'d>) { - self.spi.ch_bus_freq(frequency, clocks); + /// Changes the SPI bus frequency for the DMA-enabled SPI instance. + pub fn change_bus_frequency(&mut self, frequency: HertzU32) { + self.spi.ch_bus_freq(frequency); } } - impl<'d, T, C, M, DmaMode> DmaSupport for SpiDma<'d, T, C, M, DmaMode> + impl<'d, T, C, D, M> SpiDma<'d, T, C, D, M> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, C: DmaChannel, C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, + D: DuplexMode, + M: Mode, { - fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { - self.spi.flush().ok(); - } - - fn peripheral_dma_stop(&mut self) { - unreachable!("unsupported") + /// Configures the DMA buffers for the SPI instance. + /// + /// This method sets up both RX and TX buffers for DMA transfers. + /// It returns an instance of `SpiDmaBus` that can be used for SPI + /// communication. + pub fn with_buffers( + self, + dma_rx_buf: DmaRxBuf, + dma_tx_buf: DmaTxBuf, + ) -> SpiDmaBus<'d, T, C, D, M> { + SpiDmaBus::new(self, dma_rx_buf, dma_tx_buf) } } - impl<'d, T, C, M, DmaMode> DmaSupportTx for SpiDma<'d, T, C, M, DmaMode> + /// A structure representing a DMA transfer for SPI. + /// + /// This structure holds references to the SPI instance, DMA buffers, and + /// transfer status. + pub struct SpiDmaTransfer<'d, T, C, D, M, Buf> where - T: InstanceDma, ChannelRx<'d, C>>, C: DmaChannel, C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, + D: DuplexMode, + M: Mode, { - type TX = ChannelTx<'d, C>; + spi_dma: SpiDma<'d, T, C, D, M>, + dma_buf: Buf, + is_rx: bool, + is_tx: bool, - fn tx(&mut self) -> &mut Self::TX { - &mut self.channel.tx - } - - fn chain(&mut self) -> &mut DescriptorChain { - &mut self.tx_chain - } + rx_future_awaited: bool, + tx_future_awaited: bool, } - impl<'d, T, C, M, DmaMode> DmaSupportRx for SpiDma<'d, T, C, M, DmaMode> + impl<'d, T, C, D, M, Buf> SpiDmaTransfer<'d, T, C, D, M, Buf> where - T: InstanceDma, ChannelRx<'d, C>>, + T: Instance, C: DmaChannel, C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, + D: DuplexMode, + M: Mode, { - type RX = ChannelRx<'d, C>; + fn new(spi_dma: SpiDma<'d, T, C, D, M>, dma_buf: Buf, is_rx: bool, is_tx: bool) -> Self { + Self { + spi_dma, + dma_buf, + is_rx, + is_tx, + rx_future_awaited: false, + tx_future_awaited: false, + } + } - fn rx(&mut self) -> &mut Self::RX { - &mut self.channel.rx + /// Checks if the DMA transfer is complete. + /// + /// This method returns `true` if both RX and TX operations are done, + /// and the SPI instance is no longer busy. + pub fn is_done(&self) -> bool { + if self.is_tx && !self.tx_future_awaited && !self.spi_dma.channel.tx.is_done() { + return false; + } + if self.spi_dma.spi.busy() { + return false; + } + if self.is_rx && !self.rx_future_awaited { + // If this is an asymmetric transfer and the RX side is smaller, the RX channel + // will never be "done" as it won't have enough descriptors/buffer to receive + // the EOF bit from the SPI. So instead the RX channel will hit + // a "descriptor empty" which means the DMA is written as much + // of the received data as possible into the buffer and + // discarded the rest. The user doesn't care about this discarded data. + + if !self.spi_dma.channel.rx.is_done() + && !self.spi_dma.channel.rx.has_dscr_empty_error() + { + return false; + } + } + true } - fn chain(&mut self) -> &mut DescriptorChain { - &mut self.rx_chain + /// Waits for the DMA transfer to complete. + /// + /// This method blocks until the transfer is finished and returns the + /// `SpiDma` instance and the associated buffer. + pub fn wait(mut self) -> (SpiDma<'d, T, C, D, M>, Buf) { + self.spi_dma.spi.flush().ok(); + fence(Ordering::Acquire); + (self.spi_dma, self.dma_buf) } } - impl<'d, T, C, M, DmaMode> SpiDma<'d, T, C, M, DmaMode> + impl<'d, T, C, D, Buf> SpiDmaTransfer<'d, T, C, D, crate::Async, Buf> where - T: InstanceDma, ChannelRx<'d, C>>, + T: Instance, C: DmaChannel, C::P: SpiPeripheral, - M: IsFullDuplex, - DmaMode: Mode, + D: DuplexMode, { - /// Perform a DMA write. + /// Waits for the DMA transfer to complete asynchronously. /// - /// This will return a [DmaTransferTx]. The maximum amount of data to be - /// sent is 32736 bytes. - #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn dma_write<'t, TXBUF>( - &'t mut self, - words: &'t TXBUF, - ) -> Result, super::Error> - where - TXBUF: ReadBuffer, - { - self.dma_write_start(words)?; - Ok(DmaTransferTx::new(self)) + /// This method awaits the completion of both RX and TX operations. + pub async fn wait_for_done(&mut self) { + if self.is_tx && !self.tx_future_awaited { + let _ = DmaTxFuture::new(&mut self.spi_dma.channel.tx).await; + self.tx_future_awaited = true; + } + + // As a future enhancement, setup Spi Future in here as well. + + if self.is_rx && !self.rx_future_awaited { + let _ = DmaRxFuture::new(&mut self.spi_dma.channel.rx).await; + self.rx_future_awaited = true; + } } + } + impl<'d, T, C, M> SpiDma<'d, T, C, FullDuplexMode, M> + where + T: InstanceDma, + C: DmaChannel, + C::P: SpiPeripheral, + M: Mode, + { /// Perform a DMA write. /// - /// This will return a [DmaTransferTxOwned] owning the buffer and the + /// This will return a [SpiDmaTransfer] owning the buffer and the /// SPI instance. The maximum amount of data to be sent is 32736 /// bytes. + #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn dma_write_owned( + pub fn dma_write( mut self, - words: TXBUF, - ) -> Result, super::Error> - where - TXBUF: ReadBuffer, + buffer: DmaTxBuf, + ) -> Result, (Error, Self, DmaTxBuf)> { - self.dma_write_start(&words)?; - Ok(DmaTransferTxOwned::new(self, words)) - } - - #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - fn dma_write_start<'t, TXBUF>(&'t mut self, words: &'t TXBUF) -> Result<(), super::Error> - where - TXBUF: ReadBuffer, - { - let (ptr, len) = unsafe { words.read_buffer() }; - - if len > MAX_DMA_SIZE { - return Err(super::Error::MaxDmaTransferSizeExceeded); + let bytes_to_write = buffer.len(); + if bytes_to_write > MAX_DMA_SIZE { + return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } - unsafe { + let result = unsafe { self.spi.start_write_bytes_dma( - &mut self.tx_chain, - ptr, - len, + buffer.first(), + bytes_to_write, &mut self.channel.tx, - )?; + true, + ) + }; + if let Err(e) = result { + return Err((e, self, buffer)); } - Ok(()) - } - /// Perform a DMA read. - /// - /// This will return a [DmaTransferRx]. The maximum amount of data to be - /// received is 32736 bytes. - #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn dma_read<'t, RXBUF>( - &'t mut self, - words: &'t mut RXBUF, - ) -> Result, super::Error> - where - RXBUF: WriteBuffer, - { - self.dma_read_start(words)?; - Ok(DmaTransferRx::new(self)) + Ok(SpiDmaTransfer::new(self, buffer, false, true)) } /// Perform a DMA read. /// - /// This will return a [DmaTransferRxOwned] owning the buffer and + /// This will return a [SpiDmaTransfer] owning the buffer and /// the SPI instance. The maximum amount of data to be /// received is 32736 bytes. + #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn dma_read_owned( + pub fn dma_read( mut self, - mut words: RXBUF, - ) -> Result, super::Error> - where - RXBUF: WriteBuffer, - { - self.dma_read_start(&mut words)?; - Ok(DmaTransferRxOwned::new(self, words)) - } - - #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - fn dma_read_start<'t, RXBUF>(&'t mut self, words: &'t mut RXBUF) -> Result<(), super::Error> - where - RXBUF: WriteBuffer, + buffer: DmaRxBuf, + ) -> Result, (Error, Self, DmaRxBuf)> { - let (ptr, len) = unsafe { words.write_buffer() }; - - if len > MAX_DMA_SIZE { - return Err(super::Error::MaxDmaTransferSizeExceeded); + let bytes_to_read = buffer.len(); + if bytes_to_read > MAX_DMA_SIZE { + return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } - unsafe { + let result = unsafe { self.spi.start_read_bytes_dma( - &mut self.rx_chain, - ptr, - len, + buffer.first(), + bytes_to_read, &mut self.channel.rx, - )?; + true, + ) + }; + if let Err(e) = result { + return Err((e, self, buffer)); } - Ok(()) - } - - /// Perform a DMA transfer. - /// - /// This will return a [DmaTransferTxRx]. - /// The maximum amount of data to be sent/received is 32736 bytes. - pub fn dma_transfer<'t, TXBUF, RXBUF>( - &'t mut self, - words: &'t TXBUF, - read_buffer: &'t mut RXBUF, - ) -> Result, super::Error> - where - TXBUF: ReadBuffer, - RXBUF: WriteBuffer, - { - self.dma_transfer_start(words, read_buffer)?; - Ok(DmaTransferTxRx::new(self)) + Ok(SpiDmaTransfer::new(self, buffer, true, false)) } /// Perform a DMA transfer /// - /// This will return a [DmaTransferTxRxOwned] owning the buffers and + /// This will return a [SpiDmaTransfer] owning the buffers and /// the SPI instance. The maximum amount of data to be /// sent/received is 32736 bytes. - pub fn dma_transfer_owned( + #[allow(clippy::type_complexity)] + pub fn dma_transfer( mut self, - words: TXBUF, - mut read_buffer: RXBUF, - ) -> Result, super::Error> - where - TXBUF: ReadBuffer, - RXBUF: WriteBuffer, - { - self.dma_transfer_start(&words, &mut read_buffer)?; - Ok(DmaTransferTxRxOwned::new(self, words, read_buffer)) - } - - fn dma_transfer_start<'t, TXBUF, RXBUF>( - &'t mut self, - words: &'t TXBUF, - read_buffer: &'t mut RXBUF, - ) -> Result<(), super::Error> - where - TXBUF: ReadBuffer, - RXBUF: WriteBuffer, - { - let (write_ptr, write_len) = unsafe { words.read_buffer() }; - let (read_ptr, read_len) = unsafe { read_buffer.write_buffer() }; - - if write_len > MAX_DMA_SIZE || read_len > MAX_DMA_SIZE { - return Err(super::Error::MaxDmaTransferSizeExceeded); - } - - unsafe { + rx_buffer: DmaRxBuf, + tx_buffer: DmaTxBuf, + ) -> Result< + SpiDmaTransfer<'d, T, C, FullDuplexMode, M, (DmaRxBuf, DmaTxBuf)>, + (Error, Self, DmaRxBuf, DmaTxBuf), + > { + let bytes_to_read = rx_buffer.len(); + let bytes_to_write = tx_buffer.len(); + + if bytes_to_write > MAX_DMA_SIZE || bytes_to_read > MAX_DMA_SIZE { + return Err(( + Error::MaxDmaTransferSizeExceeded, + self, + rx_buffer, + tx_buffer, + )); + } + + let result = unsafe { self.spi.start_transfer_dma( - &mut self.tx_chain, - &mut self.rx_chain, - write_ptr, - write_len, - read_ptr, - read_len, - &mut self.channel.tx, + rx_buffer.first(), + tx_buffer.first(), + bytes_to_read, + bytes_to_write, &mut self.channel.rx, - )?; + &mut self.channel.tx, + ) + }; + if let Err(e) = result { + return Err((e, self, rx_buffer, tx_buffer)); } - Ok(()) + Ok(SpiDmaTransfer::new( + self, + (rx_buffer, tx_buffer), + true, + true, + )) } } - impl<'d, T, C, M, DmaMode> SpiDma<'d, T, C, M, DmaMode> + impl<'d, T, C, M> SpiDma<'d, T, C, HalfDuplexMode, M> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, C: DmaChannel, C::P: SpiPeripheral, - M: IsHalfDuplex, - DmaMode: Mode, + M: Mode, { + /// Perform a half-duplex read operation using DMA. + #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn read<'t, RXBUF>( - &'t mut self, + pub fn read( + mut self, data_mode: SpiDataMode, cmd: Command, address: Address, dummy: u8, - buffer: &'t mut RXBUF, - ) -> Result, super::Error> - where - RXBUF: WriteBuffer, + buffer: DmaRxBuf, + ) -> Result, (Error, Self, DmaRxBuf)> { - let (ptr, len) = unsafe { buffer.write_buffer() }; - - if len > MAX_DMA_SIZE { - return Err(super::Error::MaxDmaTransferSizeExceeded); + let bytes_to_read = buffer.len(); + if bytes_to_read > MAX_DMA_SIZE { + return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } self.spi.init_half_duplex( @@ -1326,7 +1402,7 @@ pub mod dma { !address.is_none(), false, dummy != 0, - len == 0, + bytes_to_read == 0, ); self.spi .init_spi_data_mode(cmd.mode(), address.mode(), data_mode); @@ -1370,33 +1446,36 @@ pub mod dma { .modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) }); } - unsafe { + let result = unsafe { self.spi.start_read_bytes_dma( - &mut self.rx_chain, - ptr, - len, + buffer.first(), + bytes_to_read, &mut self.channel.rx, - )?; + false, + ) + }; + if let Err(e) = result { + return Err((e, self, buffer)); } - Ok(DmaTransferRx::new(self)) + + Ok(SpiDmaTransfer::new(self, buffer, bytes_to_read > 0, false)) } + /// Perform a half-duplex write operation using DMA. + #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn write<'t, TXBUF>( - &'t mut self, + pub fn write( + mut self, data_mode: SpiDataMode, cmd: Command, address: Address, dummy: u8, - buffer: &'t TXBUF, - ) -> Result, super::Error> - where - TXBUF: ReadBuffer, + buffer: DmaTxBuf, + ) -> Result, (Error, Self, DmaTxBuf)> { - let (ptr, len) = unsafe { buffer.read_buffer() }; - - if len > MAX_DMA_SIZE { - return Err(super::Error::MaxDmaTransferSizeExceeded); + let bytes_to_write = buffer.len(); + if bytes_to_write > MAX_DMA_SIZE { + return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } self.spi.init_half_duplex( @@ -1405,7 +1484,7 @@ pub mod dma { !address.is_none(), false, dummy != 0, - len == 0, + bytes_to_write == 0, ); self.spi .init_spi_data_mode(cmd.mode(), address.mode(), data_mode); @@ -1449,396 +1528,673 @@ pub mod dma { .modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) }); } - unsafe { + let result = unsafe { self.spi.start_write_bytes_dma( - &mut self.tx_chain, - ptr, - len, + buffer.first(), + bytes_to_write, &mut self.channel.tx, - )?; + false, + ) + }; + if let Err(e) = result { + return Err((e, self, buffer)); } - Ok(DmaTransferTx::new(self)) + + Ok(SpiDmaTransfer::new(self, buffer, false, bytes_to_write > 0)) } } - #[cfg(feature = "embedded-hal-02")] - impl<'d, T, C, M, DmaMode> embedded_hal_02::blocking::spi::Transfer - for SpiDma<'d, T, C, M, DmaMode> + #[derive(Default)] + enum State<'d, T, C, D, M> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, C: DmaChannel, C::P: SpiPeripheral, - M: IsFullDuplex, - DmaMode: Mode, + D: DuplexMode, + M: Mode, { - type Error = super::Error; + Idle(SpiDma<'d, T, C, D, M>, DmaRxBuf, DmaTxBuf), + Reading(SpiDmaTransfer<'d, T, C, D, M, DmaRxBuf>, DmaTxBuf), + Writing(SpiDmaTransfer<'d, T, C, D, M, DmaTxBuf>, DmaRxBuf), + Transferring(SpiDmaTransfer<'d, T, C, D, M, (DmaRxBuf, DmaTxBuf)>), + #[default] + TemporarilyRemoved, + } - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { - self.spi.transfer_in_place_dma( - &mut self.tx_chain, - &mut self.rx_chain, - words, - &mut self.channel.tx, - &mut self.channel.rx, - ) - } + /// A DMA-capable SPI bus. + /// + /// This structure is responsible for managing SPI transfers using DMA + /// buffers. + pub struct SpiDmaBus<'d, T, C, D, M> + where + T: InstanceDma, + C: DmaChannel, + C::P: SpiPeripheral, + D: DuplexMode, + M: Mode, + { + state: State<'d, T, C, D, M>, } - #[cfg(feature = "embedded-hal-02")] - impl<'d, T, C, M, DmaMode> embedded_hal_02::blocking::spi::Write - for SpiDma<'d, T, C, M, DmaMode> + impl<'d, T, C, D, M> SpiDmaBus<'d, T, C, D, M> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, C: DmaChannel, C::P: SpiPeripheral, - M: IsFullDuplex, - DmaMode: Mode, + D: DuplexMode, + M: Mode, { - type Error = super::Error; + /// Creates a new `SpiDmaBus` with the specified SPI instance and DMA + /// buffers. + pub fn new( + spi: SpiDma<'d, T, C, D, M>, + dma_rx_buf: DmaRxBuf, + dma_tx_buf: DmaTxBuf, + ) -> Self { + Self { + state: State::Idle(spi, dma_rx_buf, dma_tx_buf), + } + } - fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - self.spi - .write_bytes_dma(&mut self.tx_chain, words, &mut self.channel.tx)?; - self.spi.flush()?; - Ok(()) + /// Sets the interrupt handler + /// + /// Interrupts are not enabled at the peripheral level here. + pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) { + let (mut spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + spi_dma.set_interrupt_handler(handler); + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + } + + /// Listen for the given interrupts + #[cfg(not(any(esp32, esp32s2)))] + pub fn listen(&mut self, interrupts: EnumSet) { + let (mut spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + spi_dma.listen(interrupts); + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + } + + /// Unlisten the given interrupts + #[cfg(not(any(esp32, esp32s2)))] + pub fn unlisten(&mut self, interrupts: EnumSet) { + let (mut spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + spi_dma.unlisten(interrupts); + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + } + + /// Gets asserted interrupts + #[cfg(not(any(esp32, esp32s2)))] + pub fn interrupts(&mut self) -> EnumSet { + let (mut spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + let interrupts = spi_dma.interrupts(); + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + + interrupts + } + + /// Resets asserted interrupts + #[cfg(not(any(esp32, esp32s2)))] + pub fn clear_interrupts(&mut self, interrupts: EnumSet) { + let (mut spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + spi_dma.clear_interrupts(interrupts); + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + } + + /// Changes the SPI bus frequency for the DMA-enabled SPI instance. + pub fn change_bus_frequency(&mut self, frequency: HertzU32) { + let (mut spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + spi_dma.change_bus_frequency(frequency); + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + } + + fn wait_for_idle(&mut self) -> (SpiDma<'d, T, C, D, M>, DmaRxBuf, DmaTxBuf) { + match core::mem::take(&mut self.state) { + State::Idle(spi, rx_buf, tx_buf) => (spi, rx_buf, tx_buf), + State::Reading(transfer, tx_buf) => { + let (spi, rx_buf) = transfer.wait(); + (spi, rx_buf, tx_buf) + } + State::Writing(transfer, rx_buf) => { + let (spi, tx_buf) = transfer.wait(); + (spi, rx_buf, tx_buf) + } + State::Transferring(transfer) => { + let (spi, (rx_buf, tx_buf)) = transfer.wait(); + (spi, rx_buf, tx_buf) + } + State::TemporarilyRemoved => unreachable!(), + } } } - #[cfg(feature = "embedded-hal-02")] - impl, const SIZE: usize> - embedded_hal_02::blocking::spi::Transfer for crate::FlashSafeDma + impl<'d, T, C, D, M> InterruptConfigurable for SpiDmaBus<'d, T, C, D, M> + where + T: InstanceDma, + C: DmaChannel, + C::P: SpiPeripheral, + D: DuplexMode, + M: Mode, { - type Error = T::Error; - - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { - self.inner.transfer(words) + /// Configures the interrupt handler for the DMA-enabled SPI instance. + fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { + let (mut spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + SpiDma::set_interrupt_handler(&mut spi_dma, handler); + self.state = State::Idle(spi_dma, rx_buf, tx_buf); } } - #[cfg(feature = "embedded-hal-02")] - impl, const SIZE: usize> - embedded_hal_02::blocking::spi::Write for crate::FlashSafeDma + impl<'d, T, C, D, M> crate::private::Sealed for SpiDmaBus<'d, T, C, D, M> + where + T: InstanceDma, + C: DmaChannel, + C::P: SpiPeripheral, + D: DuplexMode, + M: Mode, { - type Error = T::Error; + } - fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - if !crate::soc::is_valid_ram_address(&words[0] as *const _ as u32) { - for chunk in words.chunks(SIZE) { - self.buffer[..chunk.len()].copy_from_slice(chunk); - self.inner.write(&self.buffer[..chunk.len()])?; + impl<'d, T, C, M> SpiDmaBus<'d, T, C, FullDuplexMode, M> + where + T: InstanceDma, + C: DmaChannel, + C::P: SpiPeripheral, + M: Mode, + { + /// Reads data from the SPI bus using DMA. + pub fn read(&mut self, words: &mut [u8]) -> Result<(), Error> { + let (mut spi_dma, mut rx_buf, mut tx_buf) = self.wait_for_idle(); + + for chunk in words.chunks_mut(rx_buf.capacity()) { + rx_buf.set_length(chunk.len()); + + match spi_dma.dma_read(rx_buf) { + Ok(transfer) => self.state = State::Reading(transfer, tx_buf), + Err((e, spi, rx)) => { + self.state = State::Idle(spi, rx, tx_buf); + return Err(e); + } } + (spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + + let bytes_read = rx_buf.read_received_data(chunk); + debug_assert_eq!(bytes_read, chunk.len()); + } + + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + + Ok(()) + } + + /// Writes data to the SPI bus using DMA. + pub fn write(&mut self, words: &[u8]) -> Result<(), Error> { + let (mut spi_dma, mut rx_buf, mut tx_buf) = self.wait_for_idle(); + + for chunk in words.chunks(tx_buf.capacity()) { + tx_buf.fill(chunk); + + match spi_dma.dma_write(tx_buf) { + Ok(transfer) => self.state = State::Writing(transfer, rx_buf), + Err((e, spi, tx)) => { + self.state = State::Idle(spi, rx_buf, tx); + return Err(e); + } + } + (spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + } + + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + + Ok(()) + } + + /// Transfers data to and from the SPI bus simultaneously using DMA. + pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { + let (mut spi_dma, mut rx_buf, mut tx_buf) = self.wait_for_idle(); + + let chunk_size = min(tx_buf.capacity(), rx_buf.capacity()); + + let common_length = min(read.len(), write.len()); + let (read_common, read_remainder) = read.split_at_mut(common_length); + let (write_common, write_remainder) = write.split_at(common_length); + + for (read_chunk, write_chunk) in read_common + .chunks_mut(chunk_size) + .zip(write_common.chunks(chunk_size)) + { + tx_buf.fill(write_chunk); + rx_buf.set_length(read_chunk.len()); + + match spi_dma.dma_transfer(rx_buf, tx_buf) { + Ok(transfer) => self.state = State::Transferring(transfer), + Err((e, spi, rx, tx)) => { + self.state = State::Idle(spi, rx, tx); + return Err(e); + } + } + (spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + + let bytes_read = rx_buf.read_received_data(read_chunk); + debug_assert_eq!(bytes_read, read_chunk.len()); + } + + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + + if !read_remainder.is_empty() { + self.read(read_remainder) + } else if !write_remainder.is_empty() { + self.write(write_remainder) } else { - self.inner.write(words)?; - }; + Ok(()) + } + } + + /// Transfers data in place on the SPI bus using DMA. + pub fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> { + let (mut spi_dma, mut rx_buf, mut tx_buf) = self.wait_for_idle(); + + let chunk_size = min(tx_buf.capacity(), rx_buf.capacity()); + + for chunk in words.chunks_mut(chunk_size) { + tx_buf.fill(chunk); + rx_buf.set_length(chunk.len()); + match spi_dma.dma_transfer(rx_buf, tx_buf) { + Ok(transfer) => self.state = State::Transferring(transfer), + Err((e, spi, rx, tx)) => { + self.state = State::Idle(spi, rx, tx); + return Err(e); + } + } + (spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + + let bytes_read = rx_buf.read_received_data(chunk); + debug_assert_eq!(bytes_read, chunk.len()); + } + + self.state = State::Idle(spi_dma, rx_buf, tx_buf); Ok(()) } } - #[cfg(feature = "embedded-hal-02")] - impl, const SIZE: usize> - embedded_hal_02::spi::FullDuplex for crate::FlashSafeDma + impl<'d, T, C, M> HalfDuplexReadWrite for SpiDmaBus<'d, T, C, HalfDuplexMode, M> where - Self: embedded_hal_02::blocking::spi::Transfer, - Self: embedded_hal_02::blocking::spi::Write, + T: InstanceDma, + C: DmaChannel, + C::P: SpiPeripheral, + M: Mode, { - type Error = T::Error; + type Error = super::Error; - fn read(&mut self) -> nb::Result { - use embedded_hal_02::blocking::spi::Transfer; - let mut buf = [0; 1]; - self.transfer(&mut buf)?; - Ok(buf[0]) + /// Half-duplex read. + fn read( + &mut self, + data_mode: SpiDataMode, + cmd: Command, + address: Address, + dummy: u8, + buffer: &mut [u8], + ) -> Result<(), Self::Error> { + let (mut spi_dma, mut rx_buf, mut tx_buf) = self.wait_for_idle(); + if buffer.len() > rx_buf.capacity() { + return Err(super::Error::DmaError(DmaError::Overflow)); + } + + rx_buf.set_length(buffer.len()); + + match spi_dma.read(data_mode, cmd, address, dummy, rx_buf) { + Ok(transfer) => self.state = State::Reading(transfer, tx_buf), + Err((e, spi, rx)) => { + self.state = State::Idle(spi, rx, tx_buf); + return Err(e); + } + } + (spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + + let bytes_read = rx_buf.read_received_data(buffer); + debug_assert_eq!(bytes_read, buffer.len()); + + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + + Ok(()) } - fn send(&mut self, word: u8) -> nb::Result<(), Self::Error> { - use embedded_hal_02::blocking::spi::Write; - self.write(&[word])?; + /// Half-duplex write. + fn write( + &mut self, + data_mode: SpiDataMode, + cmd: Command, + address: Address, + dummy: u8, + buffer: &[u8], + ) -> Result<(), Self::Error> { + let (mut spi_dma, mut rx_buf, mut tx_buf) = self.wait_for_idle(); + if buffer.len() > tx_buf.capacity() { + return Err(super::Error::DmaError(DmaError::Overflow)); + } + + tx_buf.fill(buffer); + + match spi_dma.write(data_mode, cmd, address, dummy, tx_buf) { + Ok(transfer) => self.state = State::Writing(transfer, rx_buf), + Err((e, spi, tx)) => { + self.state = State::Idle(spi, rx_buf, tx); + return Err(e); + } + } + (spi_dma, rx_buf, tx_buf) = self.wait_for_idle(); + + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + Ok(()) } } - #[cfg(feature = "async")] + impl<'d, T, C> embedded_hal_02::blocking::spi::Transfer + for SpiDmaBus<'d, T, C, FullDuplexMode, crate::Blocking> + where + T: InstanceDma, + C: DmaChannel, + C::P: SpiPeripheral, + { + type Error = super::Error; + + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + self.transfer_in_place(words)?; + Ok(words) + } + } + + impl<'d, T, C> embedded_hal_02::blocking::spi::Write + for SpiDmaBus<'d, T, C, FullDuplexMode, crate::Blocking> + where + T: InstanceDma, + C: DmaChannel, + C::P: SpiPeripheral, + { + type Error = super::Error; + + fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + self.write(words)?; + Ok(()) + } + } + + /// Async functionality mod asynch { + use core::{cmp::min, mem::take}; + use super::*; - impl<'d, T, C, M> embedded_hal_async::spi::SpiBus for SpiDma<'d, T, C, M, crate::Async> + impl<'d, T, C> SpiDmaBus<'d, T, C, FullDuplexMode, crate::Async> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, C: DmaChannel, C::P: SpiPeripheral, - M: IsFullDuplex, { - async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { - let mut future = crate::dma::asynch::DmaRxFuture::new(&mut self.channel.rx); - unsafe { - self.spi.start_read_bytes_dma( - &mut self.rx_chain, - words.as_mut_ptr(), - words.len(), - future.rx(), - )?; + async fn wait_for_idle_async( + &mut self, + ) -> ( + SpiDma<'d, T, C, FullDuplexMode, crate::Async>, + DmaRxBuf, + DmaTxBuf, + ) { + match &mut self.state { + State::Idle(_, _, _) => (), + State::Reading(transfer, _) => transfer.wait_for_done().await, + State::Writing(transfer, _) => transfer.wait_for_done().await, + State::Transferring(transfer) => transfer.wait_for_done().await, + State::TemporarilyRemoved => unreachable!(), + } + match take(&mut self.state) { + State::Idle(spi, rx_buf, tx_buf) => (spi, rx_buf, tx_buf), + State::Reading(transfer, tx_buf) => { + let (spi, rx_buf) = transfer.wait(); + (spi, rx_buf, tx_buf) + } + State::Writing(transfer, rx_buf) => { + let (spi, tx_buf) = transfer.wait(); + (spi, rx_buf, tx_buf) + } + State::Transferring(transfer) => { + let (spi, (rx_buf, tx_buf)) = transfer.wait(); + (spi, rx_buf, tx_buf) + } + State::TemporarilyRemoved => unreachable!(), } - future.await?; - - Ok(()) } - async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - for chunk in words.chunks(MAX_DMA_SIZE) { - let mut future = crate::dma::asynch::DmaTxFuture::new(&mut self.channel.tx); - unsafe { - self.spi.start_write_bytes_dma( - &mut self.tx_chain, - chunk.as_ptr(), - chunk.len(), - future.tx(), - )?; - } - future.await?; + /// Fill the given buffer with data from the bus. + pub async fn read_async(&mut self, words: &mut [u8]) -> Result<(), super::Error> { + // Get previous transfer. + let (mut spi_dma, mut rx_buf, mut tx_buf) = self.wait_for_idle_async().await; + + for chunk in words.chunks_mut(rx_buf.capacity()) { + rx_buf.set_length(chunk.len()); + + match spi_dma.dma_read(rx_buf) { + Ok(transfer) => { + self.state = State::Reading(transfer, tx_buf); + } + Err((e, spi, rx)) => { + self.state = State::Idle(spi, rx, tx_buf); + return Err(e); + } + }; - self.spi.flush()?; + (spi_dma, rx_buf, tx_buf) = self.wait_for_idle_async().await; + + let bytes_read = rx_buf.read_received_data(chunk); + debug_assert_eq!(bytes_read, chunk.len()); } + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + Ok(()) } - async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { - let mut idx = 0; - loop { - let write_idx = isize::min(idx, write.len() as isize); - let write_len = usize::min(write.len() - idx as usize, MAX_DMA_SIZE); - - let read_idx = isize::min(idx, read.len() as isize); - let read_len = usize::min(read.len() - idx as usize, MAX_DMA_SIZE); - - let mut tx_future = crate::dma::asynch::DmaTxFuture::new(&mut self.channel.tx); - let mut rx_future = crate::dma::asynch::DmaRxFuture::new(&mut self.channel.rx); - - unsafe { - self.spi.start_transfer_dma( - &mut self.tx_chain, - &mut self.rx_chain, - write.as_ptr().offset(write_idx), - write_len, - read.as_mut_ptr().offset(read_idx), - read_len, - tx_future.tx(), - rx_future.rx(), - )?; - } - let (tx_res, rx_res) = embassy_futures::join::join(tx_future, rx_future).await; - tx_res?; - rx_res?; + /// Transmit the given buffer to the bus. + pub async fn write_async(&mut self, words: &[u8]) -> Result<(), super::Error> { + // Get previous transfer. + let (mut spi_dma, mut rx_buf, mut tx_buf) = self.wait_for_idle_async().await; - self.spi.flush()?; + for chunk in words.chunks(tx_buf.capacity()) { + tx_buf.fill(chunk); - idx += MAX_DMA_SIZE as isize; - if idx >= write.len() as isize && idx >= read.len() as isize { - break; - } + match spi_dma.dma_write(tx_buf) { + Ok(transfer) => { + self.state = State::Writing(transfer, rx_buf); + } + Err((e, spi, tx)) => { + self.state = State::Idle(spi, rx_buf, tx); + return Err(e); + } + }; + + (spi_dma, rx_buf, tx_buf) = self.wait_for_idle_async().await; } + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + Ok(()) } - async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { - for chunk in words.chunks_mut(MAX_DMA_SIZE) { - let mut tx_future = crate::dma::asynch::DmaTxFuture::new(&mut self.channel.tx); - let mut rx_future = crate::dma::asynch::DmaRxFuture::new(&mut self.channel.rx); - - unsafe { - self.spi.start_transfer_dma( - &mut self.tx_chain, - &mut self.rx_chain, - chunk.as_ptr(), - chunk.len(), - chunk.as_mut_ptr(), - chunk.len(), - tx_future.tx(), - rx_future.rx(), - )?; - } + /// Transfer by writing out a buffer and reading the response from + /// the bus into another buffer. + pub async fn transfer_async( + &mut self, + read: &mut [u8], + write: &[u8], + ) -> Result<(), super::Error> { + // Get previous transfer. + let (mut spi_dma, mut rx_buf, mut tx_buf) = self.wait_for_idle_async().await; + + let chunk_size = min(tx_buf.capacity(), rx_buf.capacity()); + + let common_length = min(read.len(), write.len()); + let (read_common, read_remainder) = read.split_at_mut(common_length); + let (write_common, write_remainder) = write.split_at(common_length); + + for (read_chunk, write_chunk) in read_common + .chunks_mut(chunk_size) + .zip(write_common.chunks(chunk_size)) + { + tx_buf.fill(write_chunk); + rx_buf.set_length(read_chunk.len()); + + match spi_dma.dma_transfer(rx_buf, tx_buf) { + Ok(transfer) => { + self.state = State::Transferring(transfer); + } + Err((e, spi, rx, tx)) => { + self.state = State::Idle(spi, rx, tx); + return Err(e); + } + }; + + (spi_dma, rx_buf, tx_buf) = self.wait_for_idle_async().await; + + let bytes_read = rx_buf.read_received_data(read_chunk); + assert_eq!(bytes_read, read_chunk.len()); + } + + self.state = State::Idle(spi_dma, rx_buf, tx_buf); - let (tx_res, rx_res) = embassy_futures::join::join(tx_future, rx_future).await; - tx_res?; - rx_res?; + if !read_remainder.is_empty() { + self.read_async(read_remainder).await + } else if !write_remainder.is_empty() { + self.write_async(write_remainder).await + } else { + Ok(()) + } + } - self.spi.flush()?; + /// Transfer by writing out a buffer and reading the response from + /// the bus into the same buffer. + pub async fn transfer_in_place_async( + &mut self, + words: &mut [u8], + ) -> Result<(), super::Error> { + // Get previous transfer. + let (mut spi_dma, mut rx_buf, mut tx_buf) = self.wait_for_idle_async().await; + + for chunk in words.chunks_mut(tx_buf.capacity()) { + tx_buf.fill(chunk); + rx_buf.set_length(chunk.len()); + + match spi_dma.dma_transfer(rx_buf, tx_buf) { + Ok(transfer) => { + self.state = State::Transferring(transfer); + } + Err((e, spi, rx, tx)) => { + self.state = State::Idle(spi, rx, tx); + return Err(e); + } + }; + + match &mut self.state { + State::Transferring(transfer) => transfer.wait_for_done().await, + _ => unreachable!(), + }; + + (spi_dma, rx_buf, tx_buf) = match take(&mut self.state) { + State::Transferring(transfer) => { + let (spi, (rx_buf, tx_buf)) = transfer.wait(); + (spi, rx_buf, tx_buf) + } + _ => unreachable!(), + }; + + let bytes_read = rx_buf.read_received_data(chunk); + debug_assert_eq!(bytes_read, chunk.len()); } + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + Ok(()) } - async fn flush(&mut self) -> Result<(), Self::Error> { - self.spi.flush() + /// Flush any pending data in the SPI peripheral. + pub async fn flush_async(&mut self) -> Result<(), super::Error> { + // Get previous transfer. + let (spi_dma, rx_buf, tx_buf) = self.wait_for_idle_async().await; + self.state = State::Idle(spi_dma, rx_buf, tx_buf); + Ok(()) } } - impl embedded_hal_async::spi::SpiBus - for crate::FlashSafeDma + impl<'d, T, C> embedded_hal_async::spi::SpiBus for SpiDmaBus<'d, T, C, FullDuplexMode, crate::Async> + where + T: InstanceDma, + C: DmaChannel, + C::P: SpiPeripheral, { async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { - self.inner.read(words).await + self.read_async(words).await } async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - if !crate::soc::is_valid_ram_address(&words[0] as *const _ as u32) { - for chunk in words.chunks(SIZE) { - self.buffer[..chunk.len()].copy_from_slice(chunk); - self.inner.write(&self.buffer[..chunk.len()]).await?; - } - } else { - self.inner.write(words).await?; - } - Ok(()) + self.write_async(words).await } - async fn flush(&mut self) -> Result<(), Self::Error> { - self.inner.flush().await + async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { + self.transfer_async(read, write).await } async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { - self.inner.transfer_in_place(words).await + self.transfer_in_place_async(words).await } - async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { - if !crate::soc::is_valid_ram_address(&write[0] as *const _ as u32) { - for (read, write) in read.chunks_mut(SIZE).zip(write.chunks(SIZE)) { - self.buffer[..write.len()].copy_from_slice(write); - self.inner - .transfer(read, &self.buffer[..write.len()]) - .await?; - } - } else { - self.inner.transfer(read, write).await?; - } - Ok(()) + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush_async().await } } } - #[cfg(feature = "embedded-hal")] mod ehal1 { use embedded_hal::spi::{ErrorType, SpiBus}; use super::*; - impl<'d, T, C, M, DmaMode> ErrorType for SpiDma<'d, T, C, M, DmaMode> + impl<'d, T, C, M> ErrorType for SpiDmaBus<'d, T, C, FullDuplexMode, M> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, C: DmaChannel, C::P: SpiPeripheral, - M: IsFullDuplex, - DmaMode: Mode, + M: Mode, { type Error = Error; } - impl<'d, T, C, M> SpiBus for SpiDma<'d, T, C, M, crate::Blocking> + impl<'d, T, C, M> SpiBus for SpiDmaBus<'d, T, C, FullDuplexMode, M> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, C: DmaChannel, C::P: SpiPeripheral, - M: IsFullDuplex, + M: Mode, { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { - self.spi.transfer_dma( - &mut self.tx_chain, - &mut self.rx_chain, - &[], - words, - &mut self.channel.tx, - &mut self.channel.rx, - )?; - self.flush() + self.read(words) } fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - self.spi - .write_bytes_dma(&mut self.tx_chain, words, &mut self.channel.tx)?; - self.flush() + self.write(words) } fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { - self.spi.transfer_dma( - &mut self.tx_chain, - &mut self.rx_chain, - write, - read, - &mut self.channel.tx, - &mut self.channel.rx, - )?; - self.flush() + self.transfer(read, write) } - /// Transfer data in place. - /// - /// Writes data from `words` out on the bus and stores the reply - /// into `words`. A convenient wrapper around - /// [`write`](SpiBus::write), [`flush`](SpiBus::flush) and - /// [`read`](SpiBus::read). fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { - self.spi.transfer_in_place_dma( - &mut self.tx_chain, - &mut self.rx_chain, - words, - &mut self.channel.tx, - &mut self.channel.rx, - )?; - self.flush() + self.transfer_in_place(words) } fn flush(&mut self) -> Result<(), Self::Error> { - self.spi.flush() - } - } - - impl ErrorType for crate::FlashSafeDma { - type Error = T::Error; - } - - impl SpiBus for crate::FlashSafeDma { - fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { - self.inner.read(words) - } - - fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - if !crate::soc::is_valid_ram_address(&words[0] as *const _ as u32) { - for chunk in words.chunks(SIZE) { - self.buffer[..chunk.len()].copy_from_slice(chunk); - self.inner.write(&self.buffer[..chunk.len()])?; - } - } else { - self.inner.write(words)?; - } + // All operations currently flush so this is no-op. Ok(()) } - - fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { - if !crate::soc::is_valid_ram_address(&write[0] as *const _ as u32) { - for (read, write) in read.chunks_mut(SIZE).zip(write.chunks(SIZE)) { - self.buffer[..write.len()].copy_from_slice(write); - self.inner.transfer(read, &self.buffer[..write.len()])?; - } - } else { - self.inner.transfer(read, write)?; - } - Ok(()) - } - - fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { - self.inner.transfer_in_place(words) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.inner.flush() - } } } } -#[cfg(feature = "embedded-hal")] mod ehal1 { use embedded_hal::spi::SpiBus; use embedded_hal_nb::spi::FullDuplex; @@ -1849,10 +2205,9 @@ mod ehal1 { type Error = super::Error; } - impl FullDuplex for Spi<'_, T, M> + impl FullDuplex for Spi<'_, T, FullDuplexMode> where T: Instance, - M: IsFullDuplex, { fn read(&mut self) -> nb::Result { self.spi.read_byte() @@ -1863,10 +2218,9 @@ mod ehal1 { } } - impl SpiBus for Spi<'_, T, M> + impl SpiBus for Spi<'_, T, FullDuplexMode> where T: Instance, - M: IsFullDuplex, { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.spi.read_bytes(words) @@ -1938,99 +2292,22 @@ mod ehal1 { } #[doc(hidden)] -pub trait InstanceDma: Instance -where - TX: Tx, - RX: Rx, -{ - fn transfer_in_place_dma<'w>( - &mut self, - tx_chain: &mut DescriptorChain, - rx_chain: &mut DescriptorChain, - words: &'w mut [u8], - tx: &mut TX, - rx: &mut RX, - ) -> Result<&'w [u8], Error> { - for chunk in words.chunks_mut(MAX_DMA_SIZE) { - unsafe { - self.start_transfer_dma( - tx_chain, - rx_chain, - chunk.as_ptr(), - chunk.len(), - chunk.as_mut_ptr(), - chunk.len(), - tx, - rx, - )?; - } - - while !tx.is_done() && !rx.is_done() {} - self.flush().unwrap(); - } - - Ok(words) - } - - fn transfer_dma<'w>( - &mut self, - tx_chain: &mut DescriptorChain, - rx_chain: &mut DescriptorChain, - write_buffer: &'w [u8], - read_buffer: &'w mut [u8], - tx: &mut TX, - rx: &mut RX, - ) -> Result<&'w [u8], Error> { - let mut idx = 0; - loop { - let write_idx = isize::min(idx, write_buffer.len() as isize); - let write_len = usize::min(write_buffer.len() - idx as usize, MAX_DMA_SIZE); - - let read_idx = isize::min(idx, read_buffer.len() as isize); - let read_len = usize::min(read_buffer.len() - idx as usize, MAX_DMA_SIZE); - - unsafe { - self.start_transfer_dma( - tx_chain, - rx_chain, - write_buffer.as_ptr().offset(write_idx), - write_len, - read_buffer.as_mut_ptr().offset(read_idx), - read_len, - tx, - rx, - )?; - } - - while !tx.is_done() && !rx.is_done() {} - self.flush().unwrap(); - - idx += MAX_DMA_SIZE as isize; - if idx >= write_buffer.len() as isize && idx >= read_buffer.len() as isize { - break; - } - } - - Ok(read_buffer) - } - +pub trait InstanceDma: Instance { #[allow(clippy::too_many_arguments)] - unsafe fn start_transfer_dma( + unsafe fn start_transfer_dma( &mut self, - tx_chain: &mut DescriptorChain, - rx_chain: &mut DescriptorChain, - write_buffer_ptr: *const u8, - write_buffer_len: usize, - read_buffer_ptr: *mut u8, + rx_desc: *mut DmaDescriptor, + tx_desc: *mut DmaDescriptor, read_buffer_len: usize, - tx: &mut TX, + write_buffer_len: usize, rx: &mut RX, + tx: &mut TX, ) -> Result<(), Error> { let reg_block = self.register_block(); self.configure_datalen(usize::max(read_buffer_len, write_buffer_len) as u32 * 8); - tx.is_done(); rx.is_done(); + tx.is_done(); // re-enable the MISO and MOSI reg_block @@ -2040,14 +2317,12 @@ where self.enable_dma(); self.update(); - reset_dma_before_load_dma_dscr(reg_block); self.clear_dma_interrupts(); - tx_chain.fill_for_tx(false, write_buffer_ptr, write_buffer_len)?; - tx.prepare_transfer_without_start(self.dma_peripheral(), tx_chain) - .and_then(|_| tx.start_transfer())?; - rx_chain.fill_for_rx(false, read_buffer_ptr, read_buffer_len)?; - rx.prepare_transfer_without_start(self.dma_peripheral(), rx_chain) + reset_dma_before_load_dma_dscr(reg_block); + rx.prepare_transfer(self.dma_peripheral(), rx_desc) .and_then(|_| rx.start_transfer())?; + tx.prepare_transfer(self.dma_peripheral(), tx_desc) + .and_then(|_| tx.start_transfer())?; reset_dma_before_usr_cmd(reg_block); @@ -2056,89 +2331,79 @@ where Ok(()) } - fn write_bytes_dma<'w>( - &mut self, - chain: &mut DescriptorChain, - words: &'w [u8], - tx: &mut TX, - ) -> Result<&'w [u8], Error> { - for chunk in words.chunks(MAX_DMA_SIZE) { - unsafe { - self.start_write_bytes_dma(chain, chunk.as_ptr(), chunk.len(), tx)?; - } - - while !tx.is_done() {} - self.flush().unwrap(); // seems "is_done" doesn't work as intended? - } - - Ok(words) - } - #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - unsafe fn start_write_bytes_dma( + unsafe fn start_write_bytes_dma( &mut self, - chain: &mut DescriptorChain, - ptr: *const u8, + first_desc: *mut DmaDescriptor, len: usize, tx: &mut TX, + full_duplex: bool, ) -> Result<(), Error> { let reg_block = self.register_block(); self.configure_datalen(len as u32 * 8); tx.is_done(); - // disable MISO and re-enable MOSI - reg_block - .user() - .modify(|_, w| w.usr_miso().bit(false).usr_mosi().bit(true)); + // disable MISO and re-enable MOSI (DON'T do it for half-duplex) + if full_duplex { + reg_block + .user() + .modify(|_, w| w.usr_miso().bit(false).usr_mosi().bit(true)); + } self.enable_dma(); self.update(); reset_dma_before_load_dma_dscr(reg_block); self.clear_dma_interrupts(); - chain.fill_for_tx(false, ptr, len)?; - unsafe { - tx.prepare_transfer_without_start(self.dma_peripheral(), chain) - .and_then(|_| tx.start_transfer())?; - } + tx.prepare_transfer(self.dma_peripheral(), first_desc)?; + tx.start_transfer()?; reset_dma_before_usr_cmd(reg_block); + // Wait for at least one clock cycle for the DMA to fill the SPI async FIFO, + // before starting the SPI + #[cfg(riscv)] + riscv::asm::delay(1); + #[cfg(xtensa)] + xtensa_lx::timer::delay(1); + reg_block.cmd().modify(|_, w| w.usr().set_bit()); Ok(()) } #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - unsafe fn start_read_bytes_dma( + unsafe fn start_read_bytes_dma( &mut self, - chain: &mut DescriptorChain, - ptr: *mut u8, - len: usize, + desc: *mut DmaDescriptor, + data_length: usize, rx: &mut RX, + full_duplex: bool, ) -> Result<(), Error> { let reg_block = self.register_block(); - self.configure_datalen(len as u32 * 8); + self.configure_datalen(data_length as u32 * 8); rx.is_done(); - // re-enable MISO and disable MOSI - reg_block - .user() - .modify(|_, w| w.usr_miso().bit(true).usr_mosi().bit(false)); + // re-enable MISO and disable MOSI (DON'T do it for half-duplex) + if full_duplex { + reg_block + .user() + .modify(|_, w| w.usr_miso().bit(true).usr_mosi().bit(false)); + } self.enable_dma(); self.update(); reset_dma_before_load_dma_dscr(reg_block); - self.clear_dma_interrupts(); - chain.fill_for_rx(false, ptr, len)?; - rx.prepare_transfer_without_start(self.dma_peripheral(), chain) - .and_then(|_| rx.start_transfer())?; + self.clear_dma_interrupts(); reset_dma_before_usr_cmd(reg_block); + rx.prepare_transfer(self.dma_peripheral(), desc)?; + rx.start_transfer()?; + reg_block.cmd().modify(|_, w| w.usr().set_bit()); Ok(()) @@ -2251,20 +2516,10 @@ fn reset_dma_before_load_dma_dscr(reg_block: &RegisterBlock) { }); } -impl InstanceDma for crate::peripherals::SPI2 -where - TX: Tx, - RX: Rx, -{ -} +impl InstanceDma for crate::peripherals::SPI2 {} #[cfg(spi3)] -impl InstanceDma for crate::peripherals::SPI3 -where - TX: Tx, - RX: Rx, -{ -} +impl InstanceDma for crate::peripherals::SPI3 {} #[doc(hidden)] pub trait ExtendedInstance: Instance { @@ -2295,6 +2550,8 @@ pub trait Instance: private::Sealed { fn enable_peripheral(&self); + fn reset_peripheral(&self); + fn spi_num(&self) -> u8; /// Initialize for full-duplex 1 bit mode @@ -2569,12 +2826,16 @@ pub trait Instance: private::Sealed { } // taken from https://github.com/apache/incubator-nuttx/blob/8267a7618629838231256edfa666e44b5313348e/arch/risc-v/src/esp32c3/esp32c3_spi.c#L496 - fn setup(&mut self, frequency: HertzU32, clocks: &Clocks<'_>) { - #[cfg(not(esp32h2))] - let apb_clk_freq: HertzU32 = HertzU32::Hz(clocks.apb_clock.to_Hz()); - // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK - #[cfg(esp32h2)] - let apb_clk_freq: HertzU32 = HertzU32::Hz(clocks.pll_48m_clock.to_Hz()); + fn setup(&mut self, frequency: HertzU32) { + let clocks = Clocks::get(); + cfg_if::cfg_if! { + if #[cfg(esp32h2)] { + // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK + let apb_clk_freq = HertzU32::Hz(clocks.pll_48m_clock.to_Hz()); + } else { + let apb_clk_freq = HertzU32::Hz(clocks.apb_clock.to_Hz()); + } + } let reg_val: u32; let duty_cycle = 128; @@ -2768,7 +3029,7 @@ pub trait Instance: private::Sealed { self } - fn ch_bus_freq(&mut self, frequency: HertzU32, clocks: &Clocks<'_>) { + fn ch_bus_freq(&mut self, frequency: HertzU32) { // Disable clock source #[cfg(not(any(esp32, esp32s2)))] self.register_block().clk_gate().modify(|_, w| { @@ -2781,7 +3042,7 @@ pub trait Instance: private::Sealed { }); // Change clock frequency - self.setup(frequency, clocks); + self.setup(frequency); // Enable clock source #[cfg(not(any(esp32, esp32s2)))] @@ -3255,6 +3516,11 @@ impl Instance for crate::peripherals::SPI2 { PeripheralClockControl::enable(crate::system::Peripheral::Spi2); } + #[inline(always)] + fn reset_peripheral(&self) { + PeripheralClockControl::reset(crate::system::Peripheral::Spi2); + } + #[inline(always)] fn spi_num(&self) -> u8 { 2 @@ -3332,6 +3598,11 @@ impl Instance for crate::peripherals::SPI2 { PeripheralClockControl::enable(crate::system::Peripheral::Spi2); } + #[inline(always)] + fn reset_peripheral(&self) { + PeripheralClockControl::reset(crate::system::Peripheral::Spi2); + } + #[inline(always)] fn spi_num(&self) -> u8 { 2 @@ -3375,7 +3646,7 @@ impl Instance for crate::peripherals::SPI3 { #[inline(always)] fn set_interrupt_handler(&mut self, handler: InterruptHandler) { self.bind_spi3_interrupt(handler.handler()); - crate::interrupt::enable(crate::peripherals::Interrupt::SPI2, handler.priority()).unwrap(); + crate::interrupt::enable(crate::peripherals::Interrupt::SPI3, handler.priority()).unwrap(); } #[inline(always)] @@ -3403,6 +3674,11 @@ impl Instance for crate::peripherals::SPI3 { PeripheralClockControl::enable(crate::system::Peripheral::Spi3) } + #[inline(always)] + fn reset_peripheral(&self) { + PeripheralClockControl::reset(crate::system::Peripheral::Spi3) + } + #[inline(always)] fn spi_num(&self) -> u8 { 3 @@ -3447,6 +3723,11 @@ impl Instance for crate::peripherals::SPI2 { PeripheralClockControl::enable(crate::system::Peripheral::Spi2) } + #[inline(always)] + fn reset_peripheral(&self) { + PeripheralClockControl::reset(crate::system::Peripheral::Spi2) + } + #[inline(always)] fn spi_num(&self) -> u8 { 2 @@ -3496,7 +3777,7 @@ impl Instance for crate::peripherals::SPI3 { #[inline(always)] fn set_interrupt_handler(&mut self, handler: InterruptHandler) { self.bind_spi3_interrupt(handler.handler()); - crate::interrupt::enable(crate::peripherals::Interrupt::SPI2, handler.priority()).unwrap(); + crate::interrupt::enable(crate::peripherals::Interrupt::SPI3, handler.priority()).unwrap(); } #[inline(always)] @@ -3524,6 +3805,11 @@ impl Instance for crate::peripherals::SPI3 { PeripheralClockControl::enable(crate::system::Peripheral::Spi3) } + #[inline(always)] + fn reset_peripheral(&self) { + PeripheralClockControl::reset(crate::system::Peripheral::Spi3) + } + #[inline(always)] fn spi_num(&self) -> u8 { 3 diff --git a/esp-hal/src/spi/mod.rs b/esp-hal/src/spi/mod.rs index 0335284f566..363bb7c09c3 100644 --- a/esp-hal/src/spi/mod.rs +++ b/esp-hal/src/spi/mod.rs @@ -19,10 +19,17 @@ pub mod slave; #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { + /// Error occurred due to a DMA-related issue. DmaError(DmaError), + /// Error indicating that the maximum DMA transfer size was exceeded. MaxDmaTransferSizeExceeded, + /// Error indicating that the FIFO size was exceeded during SPI + /// communication. FifoSizeExeeded, + /// Error indicating that the operation is unsupported by the current + /// implementation. Unsupported, + /// An unknown error occurred during SPI communication. Unknown, } @@ -32,20 +39,31 @@ impl From for Error { } } -#[cfg(feature = "embedded-hal")] impl embedded_hal::spi::Error for Error { fn kind(&self) -> embedded_hal::spi::ErrorKind { embedded_hal::spi::ErrorKind::Other } } -/// SPI modes +/// SPI communication modes, defined by clock polarity (CPOL) and clock phase +/// (CPHA). +/// +/// These modes control the clock signal's idle state and when data is sampled +/// and shifted. #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SpiMode { + /// Mode 0 (CPOL = 0, CPHA = 0): Clock is low when idle, data is captured on + /// the rising edge and propagated on the falling edge. Mode0, + /// Mode 1 (CPOL = 0, CPHA = 1): Clock is low when idle, data is captured on + /// the falling edge and propagated on the rising edge. Mode1, + /// Mode 2 (CPOL = 1, CPHA = 0): Clock is high when idle, data is captured + /// on the falling edge and propagated on the rising edge. Mode2, + /// Mode 3 (CPOL = 1, CPHA = 1): Clock is high when idle, data is captured + /// on the rising edge and propagated on the falling edge. Mode3, } @@ -53,33 +71,33 @@ pub enum SpiMode { #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SpiBitOrder { + /// Most Significant Bit (MSB) is transmitted first. MSBFirst, + /// Least Significant Bit (LSB) is transmitted first. LSBFirst, } -pub trait DuplexMode {} -pub trait IsFullDuplex: DuplexMode {} -pub trait IsHalfDuplex: DuplexMode {} +/// Trait marker for defining SPI duplex modes. +pub trait DuplexMode: crate::private::Sealed {} /// SPI data mode -/// -/// Single = 1 bit, 2 wires -/// Dual = 2 bit, 2 wires -/// Quad = 4 bit, 4 wires -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SpiDataMode { + /// `Single` Data Mode - 1 bit, 2 wires. Single, + /// `Dual` Data Mode - 2 bit, 2 wires Dual, + /// `Quad` Data Mode - 4 bit, 4 wires Quad, } /// Full-duplex operation pub struct FullDuplexMode {} impl DuplexMode for FullDuplexMode {} -impl IsFullDuplex for FullDuplexMode {} +impl crate::private::Sealed for FullDuplexMode {} /// Half-duplex operation pub struct HalfDuplexMode {} impl DuplexMode for HalfDuplexMode {} -impl IsHalfDuplex for HalfDuplexMode {} +impl crate::private::Sealed for HalfDuplexMode {} diff --git a/esp-hal/src/spi/slave.rs b/esp-hal/src/spi/slave.rs index 748e0365ee2..3a915d7f41d 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -1,14 +1,18 @@ //! # Serial Peripheral Interface - Slave Mode //! //! ## Overview +//! //! In this mode, the SPI acts as slave and transfers data with its master when //! its CS is asserted. //! //! ## Configuration +//! //! The SPI slave driver allows using full-duplex and can only be used with DMA. //! //! ## Example +//! //! ### SPI Slave with DMA +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::dma::DmaPriority; @@ -26,7 +30,7 @@ //! let mosi = io.pins.gpio2; //! let cs = io.pins.gpio3; //! -//! let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = +//! let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = //! dma_buffers!(32000); let mut spi = Spi::new( //! peripherals.SPI2, //! sclk, @@ -38,20 +42,21 @@ //! .with_dma(dma_channel.configure( //! false, //! DmaPriority::Priority0, -//! ), tx_descriptors, rx_descriptors); +//! ), rx_descriptors, tx_descriptors); //! -//! let mut send = tx_buffer; //! let mut receive = rx_buffer; +//! let mut send = tx_buffer; //! //! let transfer = spi -//! .dma_transfer(&mut send, &mut receive) -//! .unwrap(); +//! .dma_transfer(&mut receive, &mut send) +//! .unwrap(); //! //! transfer.wait().unwrap(); //! # } //! ``` //! //! ## Implementation State +//! //! There are several options for working with the SPI peripheral in slave mode, //! but the code currently only supports single transfers (not segmented //! transfers), full duplex, single bit (not dual or quad SPI), and DMA mode @@ -110,16 +115,16 @@ where mode: SpiMode, ) -> Spi<'d, T, FullDuplexMode> { crate::into_ref!(spi, sck, mosi, miso, cs); - sck.set_to_input(private::Internal); + sck.init_input(false, false, private::Internal); sck.connect_input_to_peripheral(spi.sclk_signal(), private::Internal); - mosi.set_to_input(private::Internal); + mosi.init_input(false, false, private::Internal); mosi.connect_input_to_peripheral(spi.mosi_signal(), private::Internal); miso.set_to_push_pull_output(private::Internal); miso.connect_peripheral_to_output(spi.miso_signal(), private::Internal); - cs.set_to_input(private::Internal); + cs.init_input(false, false, private::Internal); cs.connect_input_to_peripheral(spi.cs_signal(), private::Internal); Self::new_internal(spi, mode) @@ -143,6 +148,7 @@ where } } +/// DMA (Direct Memory Access) funtionality (Slave). pub mod dma { use super::*; #[cfg(spi3)] @@ -157,8 +163,8 @@ pub mod dma { DmaChannel, DmaDescriptor, DmaTransferRx, + DmaTransferRxTx, DmaTransferTx, - DmaTransferTxRx, ReadBuffer, RxPrivate, Spi2Peripheral, @@ -169,20 +175,24 @@ pub mod dma { Mode, }; + /// Trait for configuring DMA with SPI2 peripherals in slave mode. pub trait WithDmaSpi2<'d, C, DmaMode> where C: DmaChannel, C::P: SpiPeripheral, DmaMode: Mode, { + /// Configures the SPI2 peripheral with the provided DMA channel and + /// descriptors. fn with_dma( self, channel: Channel<'d, C, DmaMode>, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], ) -> SpiDma<'d, crate::peripherals::SPI2, C, DmaMode>; } + /// Trait for configuring DMA with SPI3 peripherals in slave mode. #[cfg(spi3)] pub trait WithDmaSpi3<'d, C, DmaMode> where @@ -190,11 +200,13 @@ pub mod dma { C::P: SpiPeripheral, DmaMode: Mode, { + /// Configures the SPI3 peripheral with the provided DMA channel and + /// descriptors. fn with_dma( self, channel: Channel<'d, C, DmaMode>, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], ) -> SpiDma<'d, crate::peripherals::SPI3, C, DmaMode>; } @@ -208,16 +220,16 @@ pub mod dma { fn with_dma( self, mut channel: Channel<'d, C, DmaMode>, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], ) -> SpiDma<'d, crate::peripherals::SPI2, C, DmaMode> { channel.tx.init_channel(); // no need to call this for both, TX and RX SpiDma { spi: self.spi, channel, - tx_chain: DescriptorChain::new(tx_descriptors), rx_chain: DescriptorChain::new(rx_descriptors), + tx_chain: DescriptorChain::new(tx_descriptors), } } } @@ -233,16 +245,16 @@ pub mod dma { fn with_dma( self, mut channel: Channel<'d, C, DmaMode>, - tx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], ) -> SpiDma<'d, crate::peripherals::SPI3, C, DmaMode> { channel.tx.init_channel(); // no need to call this for both, TX and RX SpiDma { spi: self.spi, channel, - tx_chain: DescriptorChain::new(tx_descriptors), rx_chain: DescriptorChain::new(rx_descriptors), + tx_chain: DescriptorChain::new(tx_descriptors), } } } @@ -256,8 +268,8 @@ pub mod dma { { pub(crate) spi: PeripheralRef<'d, T>, pub(crate) channel: Channel<'d, C, DmaMode>, - tx_chain: DescriptorChain, rx_chain: DescriptorChain, + tx_chain: DescriptorChain, } impl<'d, T, C, DmaMode> core::fmt::Debug for SpiDma<'d, T, C, DmaMode> @@ -273,12 +285,12 @@ pub mod dma { impl<'d, T, C, DmaMode> DmaSupport for SpiDma<'d, T, C, DmaMode> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, ChannelTx<'d, C>>, C: DmaChannel, C::P: SpiPeripheral, DmaMode: Mode, { - fn peripheral_wait_dma(&mut self, is_tx: bool, is_rx: bool) { + fn peripheral_wait_dma(&mut self, is_rx: bool, is_tx: bool) { while !((!is_tx || self.channel.tx.is_done()) && (!is_rx || self.channel.rx.is_done()) && !self.spi.is_bus_busy()) @@ -294,7 +306,7 @@ pub mod dma { impl<'d, T, C, DmaMode> DmaSupportTx for SpiDma<'d, T, C, DmaMode> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, ChannelTx<'d, C>>, C: DmaChannel, C::P: SpiPeripheral, DmaMode: Mode, @@ -312,7 +324,7 @@ pub mod dma { impl<'d, T, C, DmaMode> DmaSupportRx for SpiDma<'d, T, C, DmaMode> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, ChannelTx<'d, C>>, C: DmaChannel, C::P: SpiPeripheral, DmaMode: Mode, @@ -330,7 +342,7 @@ pub mod dma { impl<'d, T, C, DmaMode> SpiDma<'d, T, C, DmaMode> where - T: InstanceDma, ChannelRx<'d, C>>, + T: InstanceDma, ChannelTx<'d, C>>, C: DmaChannel, C::P: SpiPeripheral, DmaMode: Mode, @@ -344,7 +356,7 @@ pub mod dma { pub fn dma_write<'t, TXBUF>( &'t mut self, words: &'t TXBUF, - ) -> Result, Error> + ) -> Result, Error> where TXBUF: ReadBuffer, { @@ -370,7 +382,7 @@ pub mod dma { pub fn dma_read<'t, RXBUF>( &'t mut self, words: &'t mut RXBUF, - ) -> Result, Error> + ) -> Result, Error> where RXBUF: WriteBuffer, { @@ -389,19 +401,19 @@ pub mod dma { /// Register buffers for a DMA transfer. /// - /// This will return a [DmaTransferTxRx]. The maximum amount of data to + /// This will return a [DmaTransferRxTx]. The maximum amount of data to /// be sent/received is 32736 bytes. /// /// The data transfer is driven by the SPI master's sclk signal and cs /// line. - pub fn dma_transfer<'t, TXBUF, RXBUF>( + pub fn dma_transfer<'t, RXBUF, TXBUF>( &'t mut self, words: &'t TXBUF, read_buffer: &'t mut RXBUF, - ) -> Result, Error> + ) -> Result, Error> where - TXBUF: ReadBuffer, RXBUF: WriteBuffer, + TXBUF: ReadBuffer, { let (write_ptr, write_len) = unsafe { words.read_buffer() }; let (read_ptr, read_len) = unsafe { read_buffer.write_buffer() }; @@ -413,54 +425,54 @@ pub mod dma { unsafe { self.spi .start_transfer_dma( - &mut self.tx_chain, &mut self.rx_chain, - write_ptr, - write_len, + &mut self.tx_chain, read_ptr, read_len, - &mut self.channel.tx, + write_ptr, + write_len, &mut self.channel.rx, + &mut self.channel.tx, ) - .map(move |_| DmaTransferTxRx::new(self)) + .map(move |_| DmaTransferRxTx::new(self)) } } } } #[doc(hidden)] -pub trait InstanceDma: Instance +pub trait InstanceDma: Instance where - TX: Tx, RX: Rx, + TX: Tx, { #[allow(clippy::too_many_arguments)] unsafe fn start_transfer_dma( &mut self, - tx_chain: &mut DescriptorChain, rx_chain: &mut DescriptorChain, - write_buffer_ptr: *const u8, - write_buffer_len: usize, + tx_chain: &mut DescriptorChain, read_buffer_ptr: *mut u8, read_buffer_len: usize, - tx: &mut TX, + write_buffer_ptr: *const u8, + write_buffer_len: usize, rx: &mut RX, + tx: &mut TX, ) -> Result<(), Error> { let reg_block = self.register_block(); - tx.is_done(); rx.is_done(); + tx.is_done(); self.enable_dma(); reset_dma_before_load_dma_dscr(reg_block); - tx_chain.fill_for_tx(false, write_buffer_ptr, write_buffer_len)?; - tx.prepare_transfer_without_start(self.dma_peripheral(), tx_chain)?; - rx_chain.fill_for_rx(false, read_buffer_ptr, read_buffer_len)?; rx.prepare_transfer_without_start(self.dma_peripheral(), rx_chain)?; + tx_chain.fill_for_tx(false, write_buffer_ptr, write_buffer_len)?; + tx.prepare_transfer_without_start(self.dma_peripheral(), tx_chain)?; + reset_dma_before_usr_cmd(reg_block); reg_block @@ -668,18 +680,18 @@ fn reset_dma_before_load_dma_dscr(reg_block: &RegisterBlock) { .modify(|_, w| w.dma_infifo_full_clr().clear_bit()); } -impl InstanceDma for crate::peripherals::SPI2 +impl InstanceDma for crate::peripherals::SPI2 where - TX: Tx, RX: Rx, + TX: Tx, { } #[cfg(spi3)] -impl InstanceDma for crate::peripherals::SPI3 +impl InstanceDma for crate::peripherals::SPI3 where - TX: Tx, RX: Rx, + TX: Tx, { } diff --git a/esp-hal/src/system.rs b/esp-hal/src/system.rs index 8eb105dd3c2..4b031e91c17 100755 --- a/esp-hal/src/system.rs +++ b/esp-hal/src/system.rs @@ -1,276 +1,123 @@ //! # System Control //! //! ## Overview -//! This `system` driver provides an interface to control and configure various -//! system-related features and peripherals on ESP chips. It includes -//! functionality to control peripheral clocks, manage software interrupts, -//! configure chip clocks, and control radio peripherals. //! -//! ### Software Interrupts -//! The `SoftwareInterruptControl` struct gives access to the available software -//! interrupts. -//! -//! The `SoftwareInterrupt` struct allows raising or resetting software -//! interrupts using the `raise()` and `reset()` methods. -//! -//! ### Peripheral Clock Control -//! The `PeripheralClockControl` struct controls the enablement of peripheral -//! clocks. -//! -//! It provides an `enable()` method to enable and reset specific peripherals. -//! The available peripherals are represented by the `Peripheral` enum -//! -//! ## Examples -//! ```rust, no_run -#![doc = crate::before_snippet!()] -//! let peripherals = Peripherals::take(); -//! let system = SystemControl::new(peripherals.SYSTEM); -//! let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); -//! # } -//! ``` +//! This `system` module defines the available radio peripherals and provides an +//! interface to control and configure radio clocks. -use crate::{ - interrupt::InterruptHandler, - peripheral::PeripheralRef, - peripherals::SYSTEM, - InterruptConfigurable, -}; +use crate::peripherals::SYSTEM; -/// Peripherals which can be enabled via `PeripheralClockControl` +/// Peripherals which can be enabled via `PeripheralClockControl`. +/// +/// This enum represents various hardware peripherals that can be enabled +/// by the system's clock control. Depending on the target device, different +/// peripherals will be available for enabling. +// FIXME: This enum needs to be public because it's exposed via a bunch of traits, but it's not +// useful to users. +#[doc(hidden)] pub enum Peripheral { + /// SPI2 peripheral. #[cfg(spi2)] Spi2, + /// SPI3 peripheral. #[cfg(spi3)] Spi3, + /// External I2C0 peripheral. #[cfg(i2c0)] I2cExt0, + /// External I2C1 peripheral. #[cfg(i2c1)] I2cExt1, + /// RMT peripheral (Remote Control). #[cfg(rmt)] Rmt, + /// LEDC peripheral (LED PWM Controller). #[cfg(ledc)] Ledc, + /// MCPWM0 peripheral (Motor Control PWM 0). #[cfg(mcpwm0)] Mcpwm0, + /// MCPWM1 peripheral (Motor Control PWM 1). #[cfg(mcpwm1)] Mcpwm1, + /// PCNT peripheral (Pulse Counter). #[cfg(pcnt)] Pcnt, + /// APB SAR ADC peripheral. #[cfg(apb_saradc)] ApbSarAdc, + /// General DMA (GDMA) peripheral. #[cfg(gdma)] Gdma, + /// Peripheral DMA (PDMA) peripheral. #[cfg(pdma)] Dma, + /// I2S0 peripheral (Inter-IC Sound). #[cfg(i2s0)] I2s0, + /// I2S1 peripheral (Inter-IC Sound). #[cfg(i2s1)] I2s1, + /// USB0 peripheral. #[cfg(usb0)] Usb, + /// AES peripheral (Advanced Encryption Standard). #[cfg(aes)] Aes, + /// TWAI0 peripheral. #[cfg(twai0)] Twai0, + /// TWAI1 peripheral. #[cfg(twai1)] Twai1, + /// Timer Group 0 peripheral. #[cfg(timg0)] Timg0, + /// Timer Group 1 peripheral. #[cfg(timg1)] Timg1, + /// Low-power watchdog timer (WDT) peripheral. #[cfg(lp_wdt)] Wdt, + /// SHA peripheral (Secure Hash Algorithm). #[cfg(sha)] Sha, + /// USB Device peripheral. #[cfg(usb_device)] UsbDevice, + /// UART0 peripheral. #[cfg(uart0)] Uart0, + /// UART1 peripheral. #[cfg(uart1)] Uart1, + /// UART2 peripheral. #[cfg(uart2)] Uart2, + /// RSA peripheral (Rivest-Shamir-Adleman encryption). #[cfg(rsa)] Rsa, + /// Parallel IO peripheral. #[cfg(parl_io)] ParlIo, + /// HMAC peripheral (Hash-based Message Authentication Code). #[cfg(hmac)] Hmac, + /// ECC peripheral (Elliptic Curve Cryptography). #[cfg(ecc)] Ecc, + /// SOC ETM peripheral (Event Task Manager). #[cfg(soc_etm)] Etm, + /// TRACE0 peripheral (Debug trace). #[cfg(trace0)] Trace0, + /// LCD Camera peripheral. #[cfg(lcd_cam)] LcdCam, -} - -/// The `DPORT`/`PCR`/`SYSTEM` peripheral split into its different logical -/// components. -pub struct SystemControl<'d> { - _inner: PeripheralRef<'d, SYSTEM>, - pub clock_control: SystemClockControl, - pub software_interrupt_control: SoftwareInterruptControl, -} - -impl<'d> SystemControl<'d> { - /// Construct a new instance of [`SystemControl`]. - pub fn new(system: impl crate::peripheral::Peripheral

+ 'd) -> Self { - crate::into_ref!(system); - - Self { - _inner: system, - clock_control: SystemClockControl::new(), - software_interrupt_control: SoftwareInterruptControl::new(), - } - } -} - -/// A software interrupt can be triggered by software. -#[non_exhaustive] -pub struct SoftwareInterrupt; - -impl SoftwareInterrupt { - /// Sets the interrupt handler for this software-interrupt - pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - let interrupt = match NUM { - 0 => crate::peripherals::Interrupt::FROM_CPU_INTR0, - 1 => crate::peripherals::Interrupt::FROM_CPU_INTR1, - 2 => crate::peripherals::Interrupt::FROM_CPU_INTR2, - 3 => crate::peripherals::Interrupt::FROM_CPU_INTR3, - _ => unreachable!(), - }; - - unsafe { - crate::interrupt::bind_interrupt(interrupt, handler.handler()); - crate::interrupt::enable(interrupt, handler.priority()).unwrap(); - } - } - - /// Trigger this software-interrupt - pub fn raise(&self) { - #[cfg(not(any(esp32c6, esp32h2)))] - let system = unsafe { &*SYSTEM::PTR }; - #[cfg(any(esp32c6, esp32h2))] - let system = unsafe { &*crate::peripherals::INTPRI::PTR }; - - match NUM { - 0 => { - system - .cpu_intr_from_cpu_0() - .write(|w| w.cpu_intr_from_cpu_0().set_bit()); - } - 1 => { - system - .cpu_intr_from_cpu_1() - .write(|w| w.cpu_intr_from_cpu_1().set_bit()); - } - 2 => { - system - .cpu_intr_from_cpu_2() - .write(|w| w.cpu_intr_from_cpu_2().set_bit()); - } - 3 => { - system - .cpu_intr_from_cpu_3() - .write(|w| w.cpu_intr_from_cpu_3().set_bit()); - } - _ => unreachable!(), - } - } - - /// Resets this software-interrupt - pub fn reset(&self) { - #[cfg(not(any(esp32c6, esp32h2)))] - let system = unsafe { &*SYSTEM::PTR }; - #[cfg(any(esp32c6, esp32h2))] - let system = unsafe { &*crate::peripherals::INTPRI::PTR }; - - match NUM { - 0 => { - system - .cpu_intr_from_cpu_0() - .write(|w| w.cpu_intr_from_cpu_0().clear_bit()); - } - 1 => { - system - .cpu_intr_from_cpu_1() - .write(|w| w.cpu_intr_from_cpu_1().clear_bit()); - } - 2 => { - system - .cpu_intr_from_cpu_2() - .write(|w| w.cpu_intr_from_cpu_2().clear_bit()); - } - 3 => { - system - .cpu_intr_from_cpu_3() - .write(|w| w.cpu_intr_from_cpu_3().clear_bit()); - } - _ => unreachable!(), - } - } - - /// Unsafely create an instance of this peripheral out of thin air. - /// - /// # Safety - /// - /// You must ensure that you're only using one instance of this type at a - /// time. - #[inline] - pub unsafe fn steal() -> Self { - Self - } -} - -impl crate::peripheral::Peripheral for SoftwareInterrupt { - type P = SoftwareInterrupt; - - #[inline] - unsafe fn clone_unchecked(&mut self) -> Self::P { - Self::steal() - } -} - -impl crate::private::Sealed for SoftwareInterrupt {} - -impl InterruptConfigurable for SoftwareInterrupt { - fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { - SoftwareInterrupt::set_interrupt_handler(self, handler); - } -} - -/// This gives access to the available software interrupts. -#[cfg_attr( - multi_core, - doc = r#" -Please note: Software interrupt 3 is reserved -for inter-processor communication when the `embassy` -feature is enabled."# -)] -#[non_exhaustive] -pub struct SoftwareInterruptControl { - pub software_interrupt0: SoftwareInterrupt<0>, - pub software_interrupt1: SoftwareInterrupt<1>, - pub software_interrupt2: SoftwareInterrupt<2>, - #[cfg(not(all(feature = "embassy", multi_core)))] - pub software_interrupt3: SoftwareInterrupt<3>, -} - -impl SoftwareInterruptControl { - fn new() -> Self { - SoftwareInterruptControl { - software_interrupt0: SoftwareInterrupt {}, - software_interrupt1: SoftwareInterrupt {}, - software_interrupt2: SoftwareInterrupt {}, - // the thread-executor uses SW-INT3 when used on a multi-core system - // we cannot easily require `software_interrupt3` there since it's created - // before `main` via proc-macro so we cfg it away from users - #[cfg(not(all(feature = "embassy", multi_core)))] - software_interrupt3: SoftwareInterrupt {}, - } - } + /// Systimer peripheral. + #[cfg(systimer)] + Systimer, } /// Controls the enablement of peripheral clocks. @@ -491,6 +338,11 @@ impl PeripheralClockControl { perip_clk_en1.modify(|_, w| w.lcd_cam_clk_en().set_bit()); perip_rst_en1.modify(|_, w| w.lcd_cam_rst().clear_bit()); } + #[cfg(systimer)] + Peripheral::Systimer => { + perip_clk_en0.modify(|_, w| w.systimer_clk_en().set_bit()); + perip_rst_en0.modify(|_, w| w.systimer_rst().clear_bit()); + } }); } @@ -695,6 +547,11 @@ impl PeripheralClockControl { perip_rst_en1.modify(|_, w| w.lcd_cam_rst().set_bit()); perip_rst_en1.modify(|_, w| w.lcd_cam_rst().clear_bit()); } + #[cfg(systimer)] + Peripheral::Systimer => { + perip_rst_en0.modify(|_, w| w.systimer_rst().set_bit()); + perip_rst_en0.modify(|_, w| w.systimer_rst().clear_bit()); + } }); } } @@ -913,6 +770,15 @@ impl PeripheralClockControl { .trace_conf() .modify(|_, w| w.trace_rst_en().clear_bit()); } + #[cfg(systimer)] + Peripheral::Systimer => { + system + .systimer_conf() + .modify(|_, w| w.systimer_clk_en().set_bit()); + system + .systimer_conf() + .modify(|_, w| w.systimer_rst_en().clear_bit()); + } } } @@ -1105,47 +971,32 @@ impl PeripheralClockControl { .trace_conf() .modify(|_, w| w.trace_rst_en().clear_bit()); } + #[cfg(systimer)] + Peripheral::Systimer => { + system + .systimer_conf() + .modify(|_, w| w.systimer_rst_en().set_bit()); + system + .systimer_conf() + .modify(|_, w| w.systimer_rst_en().clear_bit()); + } } } } -/// Controls the configuration of the chip's clocks. -pub struct SystemClockControl { - _private: (), -} - -impl SystemClockControl { - pub fn new() -> Self { - Self { _private: () } - } -} - -impl Default for SystemClockControl { - fn default() -> Self { - Self::new() - } -} - -impl crate::peripheral::Peripheral for SystemClockControl { - type P = SystemClockControl; - - #[inline] - unsafe fn clone_unchecked(&mut self) -> Self::P { - SystemClockControl { _private: () } - } -} - -impl crate::private::Sealed for SystemClockControl {} - /// Enumeration of the available radio peripherals for this chip. #[cfg(any(bt, ieee802154, wifi))] pub enum RadioPeripherals { + /// Represents the PHY (Physical Layer) peripheral. #[cfg(phy)] Phy, + /// Represents the Bluetooth peripheral. #[cfg(bt)] Bt, + /// Represents the WiFi peripheral. #[cfg(wifi)] Wifi, + /// Represents the IEEE 802.15.4 peripheral. #[cfg(ieee802154)] Ieee802154, } @@ -1168,5 +1019,6 @@ pub trait RadioClockController { /// Initialize BLE RTC clocks fn ble_rtc_clk_init(&mut self); + /// Reset the Resolvable Private Address (RPA). fn reset_rpa(&mut self); } diff --git a/esp-hal/src/time.rs b/esp-hal/src/time.rs index 26ad3180359..6e8b16ef521 100644 --- a/esp-hal/src/time.rs +++ b/esp-hal/src/time.rs @@ -1,18 +1,8 @@ //! # Time //! -//! ## Overview -//! The `time` module offers a way to get the system uptime. -//! -//! ## Examples -//! ```rust, no_run -#![doc = crate::before_snippet!()] -//! # use esp_hal::time; -//! let time = time::current_time(); -//! # } -//! ``` -#![warn(missing_docs)] +//! The `time` module offers a way to get the system now. -/// Provides time since system start in microseconds precision +/// Provides time since system start in microseconds precision. /// /// The counter won’t measure time in sleep-mode. /// @@ -20,7 +10,7 @@ #[cfg_attr(esp32, doc = "36_558 years")] #[cfg_attr(esp32s2, doc = "7_311 years")] #[cfg_attr(not(any(esp32, esp32s2)), doc = "more than 7 years")] -pub fn current_time() -> fugit::Instant { +pub fn now() -> fugit::Instant { #[cfg(esp32)] let (ticks, div) = { // on ESP32 use LACT @@ -51,7 +41,7 @@ pub fn current_time() -> fugit::Instant { let ticks = crate::timer::systimer::SystemTimer::now(); ( ticks, - (crate::timer::systimer::SystemTimer::TICKS_PER_SECOND / 1_000_000), + (crate::timer::systimer::SystemTimer::ticks_per_second() / 1_000_000), ) }; @@ -60,9 +50,10 @@ pub fn current_time() -> fugit::Instant { #[cfg(esp32)] pub(crate) fn time_init() { + let apb = crate::Clocks::get().apb_clock.to_Hz(); // we assume 80MHz APB clock source - there is no way to configure it in a // different way currently - const APB_FREQUENCY: u32 = 80_000_000u32; + assert_eq!(apb, 80_000_000u32); let tg0 = unsafe { crate::peripherals::TIMG0::steal() }; @@ -73,7 +64,7 @@ pub(crate) fn time_init() { // 16 MHz counter tg0.lactconfig() - .modify(|_, w| unsafe { w.divider().bits((APB_FREQUENCY / 16_000_000u32) as u16) }); + .modify(|_, w| unsafe { w.divider().bits((apb / 16_000_000u32) as u16) }); tg0.lactconfig().modify(|_, w| { w.increase().bit(true); w.autoreload().bit(true); diff --git a/esp-hal/src/timer/mod.rs b/esp-hal/src/timer/mod.rs index 5cf4391105f..fa69e8b3898 100644 --- a/esp-hal/src/timer/mod.rs +++ b/esp-hal/src/timer/mod.rs @@ -11,12 +11,14 @@ )] #![cfg_attr(feature = "esp32", doc = "See the [timg] module for more information.")] //! ## Examples +//! //! ### One-shot Timer +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::timer::{OneShotTimer, PeriodicTimer, timg::TimerGroup}; -//! # use esp_hal::prelude::*; -//! let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); +//! # +//! let timg0 = TimerGroup::new(peripherals.TIMG0); //! let one_shot = OneShotTimer::new(timg0.timer0); //! //! one_shot.delay_millis(500); @@ -27,8 +29,8 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::timer::{PeriodicTimer, timg::TimerGroup}; -//! # use esp_hal::prelude::*; -//! let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); +//! # +//! let timg0 = TimerGroup::new(peripherals.TIMG0); //! let mut periodic = PeriodicTimer::new(timg0.timer0); //! //! periodic.start(1.secs()); @@ -38,8 +40,6 @@ //! # } //! ``` -#![deny(missing_docs)] - use fugit::{ExtU64, Instant, MicrosDurationU64}; use crate::{ @@ -212,7 +212,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl<'d, T, UXX> embedded_hal_02::blocking::delay::DelayMs for OneShotTimer<'d, T> where T: Timer, @@ -223,7 +222,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl<'d, T, UXX> embedded_hal_02::blocking::delay::DelayUs for OneShotTimer<'d, T> where T: Timer, @@ -234,7 +232,6 @@ where } } -#[cfg(feature = "embedded-hal")] impl<'d, T> embedded_hal::delay::DelayNs for OneShotTimer<'d, T> where T: Timer, @@ -329,7 +326,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl<'d, T> embedded_hal_02::timer::CountDown for PeriodicTimer<'d, T> where T: Timer, @@ -348,7 +344,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl<'d, T> embedded_hal_02::timer::Cancel for PeriodicTimer<'d, T> where T: Timer, @@ -360,33 +355,29 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl<'d, T> embedded_hal_02::timer::Periodic for PeriodicTimer<'d, T> where T: Timer {} /// A type-erased timer /// /// You can create an instance of this by just calling `.into()` on a timer. -#[allow(missing_docs)] pub enum ErasedTimer { + /// Timer 0 of the TIMG0 peripheral in blocking mode. Timg0Timer0(timg::Timer, Blocking>), + /// Timer 1 of the TIMG0 peripheral in blocking mode. #[cfg(timg_timer1)] Timg0Timer1(timg::Timer, Blocking>), + /// Timer 0 of the TIMG1 peripheral in blocking mode. #[cfg(timg1)] Timg1Timer0(timg::Timer, Blocking>), + /// Timer 1 of the TIMG1 peripheral in blocking mode. #[cfg(all(timg1, timg_timer1))] Timg1Timer1(timg::Timer, Blocking>), + /// Systimer Alarm in periodic mode with blocking behavior. #[cfg(systimer)] - SystimerAlarm0Periodic(systimer::Alarm), - #[cfg(systimer)] - SystimerAlarm1Periodic(systimer::Alarm), - #[cfg(systimer)] - SystimerAlarm2Periodic(systimer::Alarm), - #[cfg(systimer)] - SystimerAlarm0Target(systimer::Alarm), - #[cfg(systimer)] - SystimerAlarm1Target(systimer::Alarm), + SystimerAlarmPeriodic(systimer::Alarm<'static, systimer::Periodic, Blocking>), + /// Systimer Target in periodic mode with blocking behavior. #[cfg(systimer)] - SystimerAlarm2Target(systimer::Alarm), + SystimerAlarmTarget(systimer::Alarm<'static, systimer::Target, Blocking>), } impl crate::private::Sealed for ErasedTimer {} @@ -419,44 +410,16 @@ impl From, Blocking>> for Er } #[cfg(systimer)] -impl From> for ErasedTimer { - fn from(value: systimer::Alarm) -> Self { - Self::SystimerAlarm0Periodic(value) +impl From> for ErasedTimer { + fn from(value: systimer::Alarm<'static, systimer::Periodic, Blocking>) -> Self { + Self::SystimerAlarmPeriodic(value) } } #[cfg(systimer)] -impl From> for ErasedTimer { - fn from(value: systimer::Alarm) -> Self { - Self::SystimerAlarm1Periodic(value) - } -} - -#[cfg(systimer)] -impl From> for ErasedTimer { - fn from(value: systimer::Alarm) -> Self { - Self::SystimerAlarm2Periodic(value) - } -} - -#[cfg(systimer)] -impl From> for ErasedTimer { - fn from(value: systimer::Alarm) -> Self { - Self::SystimerAlarm0Target(value) - } -} - -#[cfg(systimer)] -impl From> for ErasedTimer { - fn from(value: systimer::Alarm) -> Self { - Self::SystimerAlarm1Target(value) - } -} - -#[cfg(systimer)] -impl From> for ErasedTimer { - fn from(value: systimer::Alarm) -> Self { - Self::SystimerAlarm2Target(value) +impl From> for ErasedTimer { + fn from(value: systimer::Alarm<'static, systimer::Target, Blocking>) -> Self { + Self::SystimerAlarmTarget(value) } } @@ -475,17 +438,9 @@ impl Timer for ErasedTimer { #[cfg(all(timg1,timg_timer1))] ErasedTimer::Timg1Timer1(inner) => inner, #[cfg(systimer)] - ErasedTimer::SystimerAlarm0Periodic(inner) => inner, - #[cfg(systimer)] - ErasedTimer::SystimerAlarm1Periodic(inner) => inner, - #[cfg(systimer)] - ErasedTimer::SystimerAlarm2Periodic(inner) => inner, - #[cfg(systimer)] - ErasedTimer::SystimerAlarm0Target(inner) => inner, - #[cfg(systimer)] - ErasedTimer::SystimerAlarm1Target(inner) => inner, + ErasedTimer::SystimerAlarmPeriodic(inner) => inner, #[cfg(systimer)] - ErasedTimer::SystimerAlarm2Target(inner) => inner, + ErasedTimer::SystimerAlarmTarget(inner) => inner, } { fn start(&self); fn stop(&self); diff --git a/esp-hal/src/timer/systimer.rs b/esp-hal/src/timer/systimer.rs index aac957bfe81..4107bb3e04b 100644 --- a/esp-hal/src/timer/systimer.rs +++ b/esp-hal/src/timer/systimer.rs @@ -8,128 +8,159 @@ //! operating system, or simply as a general-purpose timer. //! //! ## Configuration +//! //! The timer consists of two counters, `UNIT0` and `UNIT1`. The counter values //! can be monitored by 3 comparators, `COMP0`, `COMP1`, and `COMP2`. //! //! [Alarm]s can be configured in two modes: [Target] (one-shot) and [Periodic]. //! //! ## Examples -//! ### General-purpose Timer -//! ```rust, no_run -#![doc = crate::before_snippet!()] -//! # use esp_hal::timer::systimer::SystemTimer; -//! # use esp_hal::timer::timg::TimerGroup; -//! # use crate::esp_hal::prelude::_esp_hal_timer_Timer; -//! # use esp_hal::prelude::*; -//! let systimer = SystemTimer::new(peripherals.SYSTIMER); //! -//! // Get the current timestamp, in microseconds: -//! let now = SystemTimer::now(); +//! ### Splitting up the System Timer into three alarms //! -//! let timg0 = TimerGroup::new( -//! peripherals.TIMG0, -//! &clocks, -//! ); +//! Use the [split][SystemTimer::split] method to create three alarms from the +//! System Timer, contained in a [SysTimerAlarms] struct. //! -//! let mut timer0 = timg0.timer0; -//! timer0.set_interrupt_handler(tg0_t0_level); +//! ```rust, no_run +#![doc = crate::before_snippet!()] +//! use esp_hal::timer::systimer::{ +//! SystemTimer, +//! Periodic, +//! }; //! -//! // Wait for timeout: -//! timer0.load_value(1.secs()); -//! timer0.start(); +//! let systimer = SystemTimer::new( +//! peripherals.SYSTIMER, +//! ).split::(); //! -//! while !timer0.is_interrupt_set() { -//! // Wait -//! } +//! // Reconfigure a periodic alarm to be a target alarm +//! let target_alarm = systimer.alarm0.into_target(); //! # } +//! ``` +//! +//! ### General-purpose Timer +//! ```rust, no_run +#![doc = crate::before_snippet!()] +//! use esp_hal::timer::systimer::{ +//! Alarm, +//! FrozenUnit, +//! SpecificUnit, +//! SystemTimer, +//! }; +//! +//! let mut systimer = SystemTimer::new(peripherals.SYSTIMER); //! +//! // Get the current timestamp, in microseconds: +//! let now = SystemTimer::now(); //! -//! # use core::cell::RefCell; -//! # use critical_section::Mutex; -//! # use procmacros::handler; -//! # use esp_hal::interrupt::InterruptHandler; -//! # use esp_hal::interrupt; -//! # use esp_hal::peripherals::TIMG0; -//! # use esp_hal::timer::timg::{Timer, Timer0}; -//! # use crate::esp_hal::prelude::_esp_hal_timer_Timer; -//! # static TIMER0: Mutex, esp_hal::Blocking>>>> = Mutex::new(RefCell::new(None)); -//! #[handler] -//! fn tg0_t0_level() { -//! critical_section::with(|cs| { -//! let mut timer0 = TIMER0.borrow_ref_mut(cs); -//! let timer0 = timer0.as_mut().unwrap(); +//! let frozen_unit = FrozenUnit::new(&mut systimer.unit0); +//! let alarm0 = Alarm::new(systimer.comparator0, &frozen_unit); //! -//! timer0.clear_interrupt(); +//! alarm0.set_target( +//! SystemTimer::now() + SystemTimer::ticks_per_second() * 2 +//! ); +//! alarm0.enable_interrupt(true); //! -//! // Counter value should be a very small number as the alarm triggered a -//! // counter reload to 0 and ETM stopped the counter quickly after -//! // esp_println::println!("counter in interrupt: {}", timer0.now()); -//! }); +//! while !alarm0.is_interrupt_set() { +//! // Wait for the interrupt to be set //! } +//! +//! alarm0.clear_interrupt(); +//! # } //! ``` -use core::marker::PhantomData; +use core::{ + fmt::{Debug, Formatter}, + marker::PhantomData, + ptr::addr_of_mut, +}; use fugit::{Instant, MicrosDurationU32, MicrosDurationU64}; use super::{Error, Timer as _}; use crate::{ interrupt::{self, InterruptHandler}, + lock, peripheral::Peripheral, - peripherals::{ - systimer::{TARGET_CONF, TRGT}, - Interrupt, - SYSTIMER, - }, + peripherals::{Interrupt, SYSTIMER}, + system::{Peripheral as PeripheralEnable, PeripheralClockControl}, Async, Blocking, + Cpu, InterruptConfigurable, + LockState, Mode, }; /// System Timer driver. -pub struct SystemTimer<'d, DM> -where - DM: Mode, -{ - _phantom: PhantomData<&'d ()>, - /// Alarm 0. - pub alarm0: Alarm, - /// Alarm 1. - pub alarm1: Alarm, - /// Alarm 2. - pub alarm2: Alarm, +pub struct SystemTimer<'d> { + /// Unit 0 + pub unit0: SpecificUnit<'d, 0>, + + #[cfg(not(esp32s2))] + /// Unit 1 + pub unit1: SpecificUnit<'d, 1>, + + /// Comparator 0. + pub comparator0: SpecificComparator<'d, 0>, + + /// Comparator 1. + pub comparator1: SpecificComparator<'d, 1>, + + /// Comparator 2. + pub comparator2: SpecificComparator<'d, 2>, } -impl<'d> SystemTimer<'d, Blocking> { +impl<'d> SystemTimer<'d> { cfg_if::cfg_if! { if #[cfg(esp32s2)] { /// Bitmask to be applied to the raw register value. pub const BIT_MASK: u64 = u64::MAX; - /// The ticks per second the underlying peripheral uses. - pub const TICKS_PER_SECOND: u64 = 80_000_000; // Bitmask to be applied to the raw period register value. const PERIOD_MASK: u64 = 0x1FFF_FFFF; } else { /// Bitmask to be applied to the raw register value. pub const BIT_MASK: u64 = 0xF_FFFF_FFFF_FFFF; - /// The ticks per second the underlying peripheral uses. - pub const TICKS_PER_SECOND: u64 = 16_000_000; // Bitmask to be applied to the raw period register value. const PERIOD_MASK: u64 = 0x3FF_FFFF; } } - /// Create a new instance in [crate::Blocking] mode. + /// Returns the tick frequency of the underlying timer unit. + pub fn ticks_per_second() -> u64 { + cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + const MULTIPLIER: u64 = 2_000_000; + } else if #[cfg(esp32h2)] { + // The counters and comparators are driven using `XTAL_CLK`. + // The average clock frequency is fXTAL_CLK/2, which is 16 MHz. + // The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle. + const MULTIPLIER: u64 = 10_000_000 / 20; + } else { + // The counters and comparators are driven using `XTAL_CLK`. + // The average clock frequency is fXTAL_CLK/2.5, which is 16 MHz. + // The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle. + const MULTIPLIER: u64 = 10_000_000 / 25; + } + } + let xtal_freq_mhz = crate::clock::Clocks::xtal_freq().to_MHz(); + xtal_freq_mhz as u64 * MULTIPLIER + } + + /// Create a new instance. pub fn new(_systimer: impl Peripheral

+ 'd) -> Self { + // Don't reset Systimer as it will break `time::now`, only enable it + PeripheralClockControl::enable(PeripheralEnable::Systimer); + #[cfg(soc_etm)] etm::enable_etm(); Self { - _phantom: PhantomData, - alarm0: Alarm::new(), - alarm1: Alarm::new(), - alarm2: Alarm::new(), + unit0: SpecificUnit::new(), + #[cfg(not(esp32s2))] + unit1: SpecificUnit::new(), + comparator0: SpecificComparator::new(), + comparator1: SpecificComparator::new(), + comparator2: SpecificComparator::new(), } } @@ -138,33 +169,557 @@ impl<'d> SystemTimer<'d, Blocking> { // This should be safe to access from multiple contexts // worst case scenario the second accessor ends up reading // an older time stamp + + let unit = unsafe { SpecificUnit::<'_, 0>::conjure() }; + + unit.update(); + loop { + if let Some(value) = unit.poll_count() { + break value; + } + } + } +} + +impl SystemTimer<'static> { + /// Split the System Timer into three alarms. + /// + /// This is a convenience method to create `'static` alarms of the same + /// type. You are encouraged to use [Alarm::new] over this very specific + /// helper. + pub fn split(self) -> SysTimerAlarms { + static mut UNIT0: Option> = None; + let unit0 = unsafe { &mut *addr_of_mut!(UNIT0) }; + + let unit0 = unit0.insert(self.unit0.into()); + let unit = FrozenUnit::new(unit0); + + SysTimerAlarms { + alarm0: Alarm::new(self.comparator0.into(), &unit), + alarm1: Alarm::new(self.comparator1.into(), &unit), + alarm2: Alarm::new(self.comparator2.into(), &unit), + #[cfg(not(esp32s2))] + unit1: self.unit1, + } + } + + /// Split the System Timer into three alarms. + /// + /// This is a convenience method to create `'static` alarms of the same + /// type. You are encouraged to use [Alarm::new_async] over this very + /// specific helper. + pub fn split_async(self) -> SysTimerAlarms { + static mut UNIT0: Option> = None; + let unit0 = unsafe { &mut *addr_of_mut!(UNIT0) }; + + let unit0 = unit0.insert(self.unit0.into()); + let unit = FrozenUnit::new(unit0); + + SysTimerAlarms { + alarm0: Alarm::new_async(self.comparator0.into(), &unit), + alarm1: Alarm::new_async(self.comparator1.into(), &unit), + alarm2: Alarm::new_async(self.comparator2.into(), &unit), + #[cfg(not(esp32s2))] + unit1: self.unit1, + } + } +} + +/// A +#[cfg_attr(esp32s2, doc = "64-bit")] +#[cfg_attr(not(esp32s2), doc = "52-bit")] +/// counter. +pub trait Unit { + /// Returns the unit number. + fn channel(&self) -> u8; + + #[cfg(not(esp32s2))] + /// Configures when this counter can run. + /// It can be configured to stall or continue running when CPU stalls + /// or enters on-chip-debugging mode + fn configure(&self, config: UnitConfig) { + let systimer = unsafe { &*SYSTIMER::ptr() }; + let conf = systimer.conf(); + + lock(&CONF_LOCK, || { + conf.modify(|_, w| match config { + UnitConfig::Disabled => match self.channel() { + 0 => w.timer_unit0_work_en().clear_bit(), + 1 => w.timer_unit1_work_en().clear_bit(), + _ => unreachable!(), + }, + UnitConfig::DisabledIfCpuIsStalled(cpu) => match self.channel() { + 0 => w + .timer_unit0_work_en() + .set_bit() + .timer_unit0_core0_stall_en() + .bit(cpu == Cpu::ProCpu) + .timer_unit0_core1_stall_en() + .bit(cpu != Cpu::ProCpu), + 1 => w + .timer_unit1_work_en() + .set_bit() + .timer_unit1_core0_stall_en() + .bit(cpu == Cpu::ProCpu) + .timer_unit1_core1_stall_en() + .bit(cpu != Cpu::ProCpu), + _ => unreachable!(), + }, + UnitConfig::Enabled => match self.channel() { + 0 => w + .timer_unit0_work_en() + .set_bit() + .timer_unit0_core0_stall_en() + .clear_bit() + .timer_unit0_core1_stall_en() + .clear_bit(), + 1 => w + .timer_unit1_work_en() + .set_bit() + .timer_unit1_core0_stall_en() + .clear_bit() + .timer_unit1_core1_stall_en() + .clear_bit(), + _ => unreachable!(), + }, + }); + }); + } + + /// Set the value of the counter immediately. If the unit is at work, + /// the counter will continue to count up from the new reloaded value. + /// + /// This can be used to load back the sleep time recorded by RTC timer + /// via software after Light-sleep + fn set_count(&self, value: u64) { + let systimer = unsafe { &*SYSTIMER::ptr() }; + #[cfg(not(esp32s2))] + { + let unitload = systimer.unitload(self.channel() as _); + let unit_load = systimer.unit_load(self.channel() as _); + + unitload.hi().write(|w| w.load_hi().set((value << 32) as _)); + unitload + .lo() + .write(|w| w.load_lo().set((value & 0xFFFF_FFFF) as _)); + + unit_load.write(|w| w.load().set_bit()); + } + #[cfg(esp32s2)] + { + systimer + .load_hi() + .write(|w| w.load_hi().set((value << 32) as _)); + systimer + .load_lo() + .write(|w| w.load_lo().set((value & 0xFFFF_FFFF) as _)); + + systimer.load().write(|w| w.load().set_bit()); + } + } + + /// Update the value returned by [Self::poll_count] to be the current value + /// of the counter. + /// + /// This can be used to read the current value of the timer. + fn update(&self) { let systimer = unsafe { &*SYSTIMER::ptr() }; - systimer.unit0_op().modify(|_, w| w.update().set_bit()); + systimer + .unit_op(self.channel() as _) + .modify(|_, w| w.update().set_bit()); + } + + /// Return the count value at the time of the last call to [Self::update]. + /// + /// Returns None if the update isn't ready to read if update has never been + /// called. + fn poll_count(&self) -> Option { + let systimer = unsafe { &*SYSTIMER::ptr() }; + if systimer + .unit_op(self.channel() as _) + .read() + .value_valid() + .bit_is_set() + { + let unit_value = systimer.unit_value(self.channel() as _); + + let lo = unit_value.lo().read().bits(); + let hi = unit_value.hi().read().bits(); + + Some(((hi as u64) << 32) | lo as u64) + } else { + None + } + } + + /// Convenience method to call [Self::update] and [Self::poll_count]. + fn read_count(&self) -> u64 { + // This can be a shared reference as long as this type isn't Sync. - while !systimer.unit0_op().read().value_valid().bit_is_set() {} + self.update(); + loop { + if let Some(count) = self.poll_count() { + break count; + } + } + } +} - let value_lo = systimer.unit0_value().lo().read().bits(); - let value_hi = systimer.unit0_value().hi().read().bits(); +/// A specific [Unit]. i.e. Either unit 0 or unit 1. +#[derive(Debug)] +pub struct SpecificUnit<'d, const CHANNEL: u8>(PhantomData<&'d ()>); - ((value_hi as u64) << 32) | value_lo as u64 +impl<'d, const CHANNEL: u8> SpecificUnit<'d, CHANNEL> { + fn new() -> Self { + Self(PhantomData) } } -impl<'d> SystemTimer<'d, Async> { - /// Create a new instance in [crate::Async] mode. - pub fn new_async(_systimer: impl Peripheral

+ 'd) -> Self { - #[cfg(soc_etm)] - etm::enable_etm(); +impl<'d, const CHANNEL: u8> Unit for SpecificUnit<'d, CHANNEL> { + fn channel(&self) -> u8 { + CHANNEL + } +} - Self { - alarm0: Alarm::new(), - alarm1: Alarm::new(), - alarm2: Alarm::new(), - _phantom: PhantomData, +/// Any [Unit]. Could be either unit 0 or unit 1. +#[derive(Debug)] +pub struct AnyUnit<'d>(PhantomData<&'d ()>, u8); + +impl<'d> Unit for AnyUnit<'d> { + fn channel(&self) -> u8 { + self.1 + } +} + +impl<'d, const CHANNEL: u8> From> for AnyUnit<'d> { + fn from(_value: SpecificUnit<'d, CHANNEL>) -> Self { + Self(PhantomData, CHANNEL) + } +} + +impl<'d, const CHANNEL: u8> TryFrom> for SpecificUnit<'d, CHANNEL> { + type Error = u8; + + fn try_from(value: AnyUnit<'d>) -> Result { + if value.1 == CHANNEL { + Ok(SpecificUnit::new()) + } else { + Err(value.1) + } + } +} + +/// A comparator that can generate alarms/interrupts based on values of a unit. +pub trait Comparator { + /// Returns the comparators number. + fn channel(&self) -> u8; + + /// Enables/disables the comparator. If enabled, this means + /// it will generate interrupt based on its configuration. + fn set_enable(&self, enable: bool) { + let systimer = unsafe { &*SYSTIMER::ptr() }; + + lock(&CONF_LOCK, || { + #[cfg(not(esp32s2))] + systimer.conf().modify(|_, w| match self.channel() { + 0 => w.target0_work_en().bit(enable), + 1 => w.target1_work_en().bit(enable), + 2 => w.target2_work_en().bit(enable), + _ => unreachable!(), + }); + }); + + // Note: The ESP32-S2 doesn't require a lock because each + // comparator's enable bit in a different register. + #[cfg(esp32s2)] + systimer + .target_conf(self.channel() as usize) + .modify(|_r, w| w.work_en().bit(enable)); + } + + /// Returns true if the comparator has been enabled. This means + /// it will generate interrupt based on its configuration. + fn is_enabled(&self) -> bool { + #[cfg(not(esp32s2))] + { + let systimer = unsafe { &*SYSTIMER::ptr() }; + let conf = systimer.conf().read(); + match self.channel() { + 0 => conf.target0_work_en().bit(), + 1 => conf.target1_work_en().bit(), + 2 => conf.target2_work_en().bit(), + _ => unreachable!(), + } + } + + #[cfg(esp32s2)] + { + let tconf = unsafe { + let systimer = &*SYSTIMER::ptr(); + systimer.target_conf(self.channel() as usize) + }; + tconf.read().work_en().bit() + } + } + + /// Sets the unit this comparator uses as a reference count. + #[cfg(not(esp32s2))] + fn set_unit(&self, is_unit0: bool) { + let tconf = unsafe { + let systimer = &*SYSTIMER::ptr(); + systimer.target_conf(self.channel() as usize) + }; + tconf.modify(|_, w| w.timer_unit_sel().bit(is_unit0)); + } + + /// Set the mode of the comparator to be either target or periodic. + fn set_mode(&self, mode: ComparatorMode) { + let tconf = unsafe { + let systimer = &*SYSTIMER::ptr(); + systimer.target_conf(self.channel() as usize) + }; + let is_period_mode = match mode { + ComparatorMode::Period => true, + ComparatorMode::Target => false, + }; + tconf.modify(|_, w| w.period_mode().bit(is_period_mode)); + } + + /// Get the current mode of the comparator, which is either target or + /// periodic. + fn get_mode(&self) -> ComparatorMode { + let tconf = unsafe { + let systimer = &*SYSTIMER::ptr(); + systimer.target_conf(self.channel() as usize) + }; + if tconf.read().period_mode().bit() { + ComparatorMode::Period + } else { + ComparatorMode::Target + } + } + + /// Set how often the comparator should generate an interrupt when in + /// periodic mode. + fn set_period(&self, value: u32) { + unsafe { + let systimer = &*SYSTIMER::ptr(); + let tconf = systimer.target_conf(self.channel() as usize); + tconf.modify(|_, w| w.period().bits(value)); + #[cfg(not(esp32s2))] + { + let comp_load = systimer.comp_load(self.channel() as usize); + comp_load.write(|w| w.load().set_bit()); + } + } + } + + /// Set when the comparator should generate an interrupt in target mode. + fn set_target(&self, value: u64) { + let systimer = unsafe { &*SYSTIMER::ptr() }; + let target = systimer.trgt(self.channel() as usize); + target.hi().write(|w| w.hi().set((value >> 32) as u32)); + target + .lo() + .write(|w| w.lo().set((value & 0xFFFF_FFFF) as u32)); + #[cfg(not(esp32s2))] + { + let comp_load = systimer.comp_load(self.channel() as usize); + comp_load.write(|w| w.load().set_bit()); + } + } + + /// Get the actual target value of the comparator. + fn get_actual_target(&self) -> u64 { + let target = unsafe { + let systimer = &*SYSTIMER::ptr(); + systimer.trgt(self.channel() as usize) + }; + let hi = target.hi().read().hi().bits(); + let lo = target.lo().read().lo().bits(); + + ((hi as u64) << 32) | (lo as u64) + } + + /// Set the interrupt handler for this comparator. + fn set_interrupt_handler(&self, handler: InterruptHandler) { + let interrupt = match self.channel() { + 0 => Interrupt::SYSTIMER_TARGET0, + 1 => Interrupt::SYSTIMER_TARGET1, + 2 => Interrupt::SYSTIMER_TARGET2, + _ => unreachable!(), + }; + + #[cfg(not(esp32s2))] + unsafe { + interrupt::bind_interrupt(interrupt, handler.handler()); + } + + #[cfg(esp32s2)] + { + // ESP32-S2 Systimer interrupts are edge triggered. Our interrupt + // handler calls each of the handlers, regardless of which one triggered the + // interrupt. This mess registers an intermediate handler that + // checks if an interrupt is active before calling the associated + // handler functions. + + static mut HANDLERS: [Option; 3] = [None, None, None]; + + #[crate::prelude::ram] + unsafe extern "C" fn _handle_interrupt() { + if unsafe { &*SYSTIMER::PTR } + .int_raw() + .read() + .target(CH) + .bit_is_set() + { + let handler = unsafe { HANDLERS[CH as usize] }; + if let Some(handler) = handler { + handler(); + } + } + } + + unsafe { + HANDLERS[self.channel() as usize] = Some(handler.handler()); + let handler = match self.channel() { + 0 => _handle_interrupt::<0>, + 1 => _handle_interrupt::<1>, + 2 => _handle_interrupt::<2>, + _ => unreachable!(), + }; + interrupt::bind_interrupt(interrupt, handler); + } + } + unwrap!(interrupt::enable(interrupt, handler.priority())); + } +} + +/// A specific [Comparator]. i.e. Either comparator 0, comparator 1, etc. +#[derive(Debug)] +pub struct SpecificComparator<'d, const CHANNEL: u8>(PhantomData<&'d ()>); + +impl<'d, const CHANNEL: u8> SpecificComparator<'d, CHANNEL> { + fn new() -> Self { + Self(PhantomData) + } +} + +impl<'d, const CHANNEL: u8> Comparator for SpecificComparator<'d, CHANNEL> { + fn channel(&self) -> u8 { + CHANNEL + } +} + +/// Any [Comparator]. Could be either comparator 0, comparator 1, etc. +#[derive(Debug)] +pub struct AnyComparator<'d>(PhantomData<&'d ()>, u8); + +impl<'d> Comparator for AnyComparator<'d> { + fn channel(&self) -> u8 { + self.1 + } +} + +impl<'d, const CHANNEL: u8> From> for AnyComparator<'d> { + fn from(_value: SpecificComparator<'d, CHANNEL>) -> Self { + Self(PhantomData, CHANNEL) + } +} + +impl<'d, const CHANNEL: u8> TryFrom> for SpecificComparator<'d, CHANNEL> { + type Error = u8; + + fn try_from(value: AnyComparator<'d>) -> Result { + if value.1 == CHANNEL { + Ok(SpecificComparator::new()) + } else { + Err(value.1) } } } +/// The configuration of a unit. +#[derive(Copy, Clone)] +pub enum UnitConfig { + /// Unit is not counting. + Disabled, + + /// Unit is counting unless the Cpu is stalled. + DisabledIfCpuIsStalled(Cpu), + + /// Unit is counting. + Enabled, +} + +/// The modes of a comparator. +#[derive(Copy, Clone)] +pub enum ComparatorMode { + /// The comparator will generate interrupts periodically. + Period, + + /// The comparator will generate an interrupt when the unit reaches the + /// target. + Target, +} + +impl SpecificUnit<'static, 0> { + /// Conjure a system timer unit out of thin air. + /// + /// # Safety + /// + /// Users must take care to ensure that only one reference to the unit is + /// in scope at any given time. + pub const unsafe fn conjure() -> Self { + Self(PhantomData) + } +} + +#[cfg(not(esp32s2))] +impl SpecificUnit<'static, 1> { + /// Conjure a system timer unit out of thin air. + /// + /// # Safety + /// + /// Users must take care to ensure that only one reference to the unit is + /// in scope at any given time. + pub const unsafe fn conjure() -> Self { + Self(PhantomData) + } +} + +/// A unit whose value cannot be updated. +pub struct FrozenUnit<'d, U: Unit>(&'d U); + +impl<'d, U: Unit> FrozenUnit<'d, U> { + /// Creates a frozen unit. You will no longer be allowed + /// direct access to this unit until all the alarms created + /// from the unit are dropped. + pub fn new(unit: &'d mut U) -> Self { + Self(unit) + } + + fn borrow(&self) -> &'d U { + self.0 + } +} + +/// Alarms created from the System Timer peripheral. +pub struct SysTimerAlarms { + /// Alarm 0 + pub alarm0: Alarm<'static, MODE, DM>, + /// Alarm 1 + pub alarm1: Alarm<'static, MODE, DM>, + /// Alarm 2 + pub alarm2: Alarm<'static, MODE, DM>, + + /// Unit 1 + /// + /// Leftover unit which wasn't used to create the three alarms. + #[cfg(not(esp32s2))] + pub unit1: SpecificUnit<'static, 1>, +} + /// A marker for a [Alarm] in target mode. #[derive(Debug)] pub struct Target; @@ -174,114 +729,73 @@ pub struct Target; pub struct Periodic; /// A single alarm. -#[derive(Debug)] -pub struct Alarm +pub struct Alarm<'d, MODE, DM, COMP = AnyComparator<'d>, UNIT = AnyUnit<'d>> where DM: Mode, { + comparator: COMP, + unit: &'d UNIT, _pd: PhantomData<(MODE, DM)>, } -impl Alarm +impl<'d, T, DM, COMP: Comparator, UNIT: Unit> Debug for Alarm<'d, T, DM, COMP, UNIT> where DM: Mode, { - fn new() -> Self { - Self { _pd: PhantomData } + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Alarm") + .field("comparator", &self.comparator.channel()) + .field("unit", &self.unit.channel()) + .finish() } +} - fn configure(&self, conf: impl FnOnce(&TARGET_CONF, &TRGT)) { - unsafe { - let systimer = &*SYSTIMER::ptr(); - let tconf = systimer.target_conf(CHANNEL as usize); - let target = systimer.trgt(CHANNEL as usize); - - #[cfg(esp32s2)] - systimer.step().write(|w| w.xtal_step().bits(0x1)); // run at XTAL freq, not 80 * XTAL freq - - #[cfg(not(esp32s2))] - { - tconf.write(|w| w.timer_unit_sel().clear_bit()); // default, use unit 0 - systimer - .conf() - .modify(|_, w| w.timer_unit0_core0_stall_en().clear_bit()); - } - - conf(tconf, target); - - #[cfg(not(esp32s2))] - { - systimer - .comp_load(CHANNEL as usize) - .write(|w| w.load().set_bit()); - - systimer.conf().modify(|_r, w| match CHANNEL { - 0 => w.target0_work_en().set_bit(), - 1 => w.target1_work_en().set_bit(), - 2 => w.target2_work_en().set_bit(), - _ => unreachable!(), - }); - } - - #[cfg(esp32s2)] - tconf.modify(|_r, w| w.work_en().set_bit()); - } - } - - fn set_interrupt_handler_internal(&self, handler: InterruptHandler) { - match CHANNEL { - 0 => unsafe { - interrupt::bind_interrupt(Interrupt::SYSTIMER_TARGET0, handler.handler()); - unwrap!(interrupt::enable( - Interrupt::SYSTIMER_TARGET0, - handler.priority(), - )); - }, - 1 => unsafe { - interrupt::bind_interrupt(Interrupt::SYSTIMER_TARGET1, handler.handler()); - unwrap!(interrupt::enable( - Interrupt::SYSTIMER_TARGET1, - handler.priority(), - )); - }, - 2 => unsafe { - interrupt::bind_interrupt(Interrupt::SYSTIMER_TARGET2, handler.handler()); - unwrap!(interrupt::enable( - Interrupt::SYSTIMER_TARGET2, - handler.priority(), - )); - }, - _ => unreachable!(), +impl<'d, T, COMP: Comparator, UNIT: Unit> Alarm<'d, T, Blocking, COMP, UNIT> { + /// Creates a new alarm from a comparator and unit, in blocking mode. + pub fn new(comparator: COMP, unit: &FrozenUnit<'d, UNIT>) -> Self { + Self { + comparator, + unit: unit.borrow(), + _pd: PhantomData, } } } -impl Alarm { - /// Set the interrupt handler for this alarm. - pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - self.set_interrupt_handler_internal(handler) +impl<'d, T, COMP: Comparator, UNIT: Unit> Alarm<'d, T, Async, COMP, UNIT> { + /// Creates a new alarm from a comparator and unit, in async mode. + pub fn new_async(comparator: COMP, unit: &FrozenUnit<'d, UNIT>) -> Self { + Self { + comparator, + unit: unit.0, + _pd: PhantomData, + } } } -impl InterruptConfigurable for Alarm { +impl<'d, T, COMP: Comparator, UNIT: Unit> InterruptConfigurable + for Alarm<'d, T, Blocking, COMP, UNIT> +{ fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - self.set_interrupt_handler_internal(handler) + self.comparator.set_interrupt_handler(handler) } } -impl Alarm +impl<'d, DM, COMP: Comparator, UNIT: Unit> Alarm<'d, Target, DM, COMP, UNIT> where DM: Mode, { /// Set the target value of this [Alarm] pub fn set_target(&self, timestamp: u64) { - self.configure(|tconf, target| unsafe { - tconf.write(|w| w.period_mode().clear_bit()); // target mode - target.hi().write(|w| w.hi().bits((timestamp >> 32) as u32)); - target - .lo() - .write(|w| w.lo().set((timestamp & 0xFFFF_FFFF) as u32)); - }); + #[cfg(esp32s2)] + unsafe { + let systimer = &*SYSTIMER::ptr(); + // run at XTAL freq, not 80 * XTAL freq + systimer.step().write(|w| w.xtal_step().bits(0x1)); + } + + self.comparator.set_mode(ComparatorMode::Target); + self.comparator.set_target(timestamp); + self.comparator.set_enable(true); } /// Block waiting until the timer reaches the `timestamp` @@ -291,123 +805,70 @@ where let r = unsafe { &*crate::peripherals::SYSTIMER::PTR }.int_raw(); loop { - if r.read().target(CHANNEL).bit_is_set() { + if r.read().target(self.comparator.channel()).bit_is_set() { break; } } } /// Converts this [Alarm] into [Periodic] mode - pub fn into_periodic(self) -> Alarm { - Alarm { _pd: PhantomData } + pub fn into_periodic(self) -> Alarm<'d, Periodic, DM, COMP, UNIT> { + Alarm { + comparator: self.comparator, + unit: self.unit, + _pd: PhantomData, + } } } -impl Alarm +impl<'d, DM, COMP: Comparator, UNIT: Unit> Alarm<'d, Periodic, DM, COMP, UNIT> where DM: Mode, { /// Set the period of this [Alarm] pub fn set_period(&self, period: MicrosDurationU32) { + #[cfg(esp32s2)] + unsafe { + let systimer = &*SYSTIMER::ptr(); + // run at XTAL freq, not 80 * XTAL freq + systimer.step().write(|w| w.xtal_step().bits(0x1)); + } + let us = period.ticks(); - let ticks = us * (SystemTimer::TICKS_PER_SECOND / 1_000_000) as u32; + let ticks = us * (SystemTimer::ticks_per_second() / 1_000_000) as u32; - self.configure(|tconf, target| { - tconf.write(|w| unsafe { w.period_mode().set_bit().period().bits(ticks) }); - target.hi().write(|w| w.hi().set(0)); - target.lo().write(|w| w.lo().set(0)); - }); + self.comparator.set_mode(ComparatorMode::Period); + self.comparator.set_period(ticks); + self.comparator.set_enable(true); } /// Converts this [Alarm] into [Target] mode - pub fn into_target(self) -> Alarm { - Alarm { _pd: PhantomData } - } -} - -impl Alarm -where - DM: Mode, -{ - /// Conjure an alarm out of thin air. - /// - /// # Safety - /// - /// Users must take care to ensure that only one reference to the timer is - /// in scope at any given time. - pub const unsafe fn conjure() -> Self { - Self { _pd: PhantomData } + pub fn into_target(self) -> Alarm<'d, Target, DM, COMP, UNIT> { + Alarm { + comparator: self.comparator, + unit: self.unit, + _pd: PhantomData, + } } } -impl Alarm +impl<'d, T, DM, COMP: Comparator, UNIT: Unit> crate::private::Sealed + for Alarm<'d, T, DM, COMP, UNIT> where DM: Mode, { - /// Conjure an alarm out of thin air. - /// - /// # Safety - /// - /// Users must take care to ensure that only one reference to the timer is - /// in scope at any given time. - pub const unsafe fn conjure() -> Self { - Self { _pd: PhantomData } - } } -impl Alarm -where - DM: Mode, -{ - /// Conjure an alarm out of thin air. - /// - /// # Safety - /// - /// Users must take care to ensure that only one reference to the timer is - /// in scope at any given time. - pub const unsafe fn conjure() -> Self { - Self { _pd: PhantomData } - } -} - -impl crate::private::Sealed for Alarm where DM: Mode {} - -impl super::Timer for Alarm +impl<'d, T, DM, COMP: Comparator, UNIT: Unit> super::Timer for Alarm<'d, T, DM, COMP, UNIT> where DM: Mode, { fn start(&self) { - let systimer = unsafe { &*SYSTIMER::PTR }; - - #[cfg(esp32s2)] - systimer - .target_conf(CHANNEL as usize) - .modify(|_, w| w.work_en().set_bit()); - - #[cfg(not(esp32s2))] - systimer.conf().modify(|_, w| match CHANNEL { - 0 => w.target0_work_en().set_bit(), - 1 => w.target1_work_en().set_bit(), - 2 => w.target2_work_en().set_bit(), - _ => unreachable!(), - }); + self.comparator.set_enable(true); } fn stop(&self) { - let systimer = unsafe { &*SYSTIMER::PTR }; - - #[cfg(esp32s2)] - systimer - .target_conf(CHANNEL as usize) - .modify(|_, w| w.work_en().clear_bit()); - - #[cfg(not(esp32s2))] - systimer.conf().modify(|_, w| match CHANNEL { - 0 => w.target0_work_en().clear_bit(), - 1 => w.target1_work_en().clear_bit(), - 2 => w.target2_work_en().clear_bit(), - _ => unreachable!(), - }); + self.comparator.set_enable(false); } fn reset(&self) { @@ -421,9 +882,6 @@ where #[cfg(not(esp32s2))] { - systimer - .target_conf(CHANNEL as usize) - .modify(|_, w| w.timer_unit_sel().clear_bit()); // default, use unit 0 systimer .conf() .modify(|_, w| w.timer_unit0_core0_stall_en().clear_bit()); @@ -431,59 +889,33 @@ where } fn is_running(&self) -> bool { - let systimer = unsafe { &*SYSTIMER::PTR }; - - #[cfg(esp32s2)] - { - systimer - .target_conf(CHANNEL as usize) - .read() - .work_en() - .bit_is_set() - } - - #[cfg(not(esp32s2))] - match CHANNEL { - 0 => systimer.conf().read().target0_work_en().bit_is_set(), - 1 => systimer.conf().read().target1_work_en().bit_is_set(), - 2 => systimer.conf().read().target2_work_en().bit_is_set(), - _ => unreachable!(), - } + self.comparator.is_enabled() } fn now(&self) -> Instant { // This should be safe to access from multiple contexts; worst case // scenario the second accessor ends up reading an older time stamp. - let systimer = unsafe { &*SYSTIMER::PTR }; - - systimer.unit0_op().modify(|_, w| w.update().set_bit()); - while !systimer.unit0_op().read().value_valid().bit_is_set() { - // Wait - } + self.unit.update(); - let value_lo = systimer.unit0_value().lo().read().bits(); - let value_hi = systimer.unit0_value().hi().read().bits(); + let ticks = loop { + if let Some(value) = self.unit.poll_count() { + break value; + } + }; - let ticks = ((value_hi as u64) << 32) | value_lo as u64; - let us = ticks / (SystemTimer::TICKS_PER_SECOND / 1_000_000); + let us = ticks / (SystemTimer::ticks_per_second() / 1_000_000); Instant::::from_ticks(us) } fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error> { - let systimer = unsafe { &*SYSTIMER::PTR }; - - let auto_reload = systimer - .target_conf(CHANNEL as usize) - .read() - .period_mode() - .bit_is_set(); + let mode = self.comparator.get_mode(); let us = value.ticks(); - let ticks = us * (SystemTimer::TICKS_PER_SECOND / 1_000_000); + let ticks = us * (SystemTimer::ticks_per_second() / 1_000_000); - if auto_reload { + if matches!(mode, ComparatorMode::Period) { // Period mode // The `SYSTIMER_TARGETx_PERIOD` field is 26-bits wide (or @@ -493,28 +925,17 @@ where return Err(Error::InvalidTimeout); } - systimer - .target_conf(CHANNEL as usize) - .modify(|_, w| unsafe { w.period().bits(ticks as u32) }); - - #[cfg(not(esp32s2))] - systimer - .comp_load(CHANNEL as usize) - .write(|w| w.load().set_bit()); + self.comparator.set_period(ticks as u32); // Clear and then set SYSTIMER_TARGETx_PERIOD_MODE to configure COMPx into // period mode - systimer - .target_conf(CHANNEL as usize) - .modify(|_, w| w.period_mode().clear_bit()); - systimer - .target_conf(CHANNEL as usize) - .modify(|_, w| w.period_mode().set_bit()); + self.comparator.set_mode(ComparatorMode::Target); + self.comparator.set_mode(ComparatorMode::Period); } else { // Target mode - systimer.unit0_op().modify(|_, w| w.update().set_bit()); - while !systimer.unit0_op().read().value_valid().bit_is_set() { + self.unit.update(); + while self.unit.poll_count().is_none() { // Wait for value registers to update } @@ -526,25 +947,10 @@ where return Err(Error::InvalidTimeout); } - let hi = systimer.unit0_value().hi().read().bits(); - let lo = systimer.unit0_value().lo().read().bits(); - - let v = (((hi & 0xF_FFFF) as u64) << 32) | lo as u64; + let v = self.unit.poll_count().unwrap(); let t = v + ticks; - systimer - .trgt(CHANNEL as usize) - .hi() - .write(|w| unsafe { w.hi().bits((t >> 32) as u32) }); - systimer - .trgt(CHANNEL as usize) - .lo() - .write(|w| unsafe { w.lo().bits(t as u32) }); - - #[cfg(not(esp32s2))] - systimer - .comp_load(CHANNEL as usize) - .write(|w| w.load().set_bit()); + self.comparator.set_target(t); } Ok(()) @@ -552,28 +958,33 @@ where fn enable_auto_reload(&self, auto_reload: bool) { // If `auto_reload` is true use Period Mode, otherwise use Target Mode: - unsafe { &*SYSTIMER::PTR } - .target_conf(CHANNEL as usize) - .modify(|_, w| w.period_mode().bit(auto_reload)); + let mode = if auto_reload { + ComparatorMode::Period + } else { + ComparatorMode::Target + }; + self.comparator.set_mode(mode) } fn enable_interrupt(&self, state: bool) { - unsafe { &*SYSTIMER::PTR } - .int_ena() - .modify(|_, w| w.target(CHANNEL).bit(state)); + lock(&INT_ENA_LOCK, || { + unsafe { &*SYSTIMER::PTR } + .int_ena() + .modify(|_, w| w.target(self.comparator.channel()).bit(state)); + }); } fn clear_interrupt(&self) { unsafe { &*SYSTIMER::PTR } .int_clr() - .write(|w| w.target(CHANNEL).clear_bit_by_one()); + .write(|w| w.target(self.comparator.channel()).clear_bit_by_one()); } fn is_interrupt_set(&self) -> bool { unsafe { &*SYSTIMER::PTR } .int_raw() .read() - .target(CHANNEL) + .target(self.comparator.channel()) .bit_is_set() } @@ -582,11 +993,11 @@ where } fn set_interrupt_handler(&self, handler: InterruptHandler) { - Alarm::set_interrupt_handler_internal(self, handler); + self.comparator.set_interrupt_handler(handler); } } -impl Peripheral for Alarm +impl<'d, T, DM, COMP: Comparator, UNIT: Unit> Peripheral for Alarm<'d, T, DM, COMP, UNIT> where DM: Mode, { @@ -598,8 +1009,10 @@ where } } +static CONF_LOCK: LockState = LockState::new(); +static INT_ENA_LOCK: LockState = LockState::new(); + // Async functionality of the system timer. -#[cfg(feature = "async")] mod asynch { use core::{ pin::Pin, @@ -617,15 +1030,16 @@ mod asynch { const INIT: AtomicWaker = AtomicWaker::new(); static WAKERS: [AtomicWaker; NUM_ALARMS] = [INIT; NUM_ALARMS]; - pub(crate) struct AlarmFuture<'a, const N: u8> { - phantom: PhantomData<&'a Alarm>, + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub(crate) struct AlarmFuture<'a, COMP: Comparator, UNIT: Unit> { + alarm: &'a Alarm<'a, Periodic, crate::Async, COMP, UNIT>, } - impl<'a, const N: u8> AlarmFuture<'a, N> { - pub(crate) fn new(alarm: &'a Alarm) -> Self { + impl<'a, COMP: Comparator, UNIT: Unit> AlarmFuture<'a, COMP, UNIT> { + pub(crate) fn new(alarm: &'a Alarm<'a, Periodic, crate::Async, COMP, UNIT>) -> Self { alarm.clear_interrupt(); - let (interrupt, handler) = match N { + let (interrupt, handler) = match alarm.comparator.channel() { 0 => (Interrupt::SYSTIMER_TARGET0, target0_handler), 1 => (Interrupt::SYSTIMER_TARGET1, target1_handler), _ => (Interrupt::SYSTIMER_TARGET2, target2_handler), @@ -638,25 +1052,23 @@ mod asynch { alarm.enable_interrupt(true); - Self { - phantom: PhantomData, - } + Self { alarm } } fn event_bit_is_clear(&self) -> bool { unsafe { &*crate::peripherals::SYSTIMER::PTR } .int_ena() .read() - .target(N) + .target(self.alarm.comparator.channel()) .bit_is_clear() } } - impl<'a, const N: u8> core::future::Future for AlarmFuture<'a, N> { + impl<'a, COMP: Comparator, UNIT: Unit> core::future::Future for AlarmFuture<'a, COMP, UNIT> { type Output = (); fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { - WAKERS[N as usize].register(ctx.waker()); + WAKERS[self.alarm.comparator.channel() as usize].register(ctx.waker()); if self.event_bit_is_clear() { Poll::Ready(()) @@ -666,8 +1078,8 @@ mod asynch { } } - impl embedded_hal_async::delay::DelayNs - for Alarm + impl<'d, COMP: Comparator, UNIT: Unit> embedded_hal_async::delay::DelayNs + for Alarm<'d, Periodic, crate::Async, COMP, UNIT> { async fn delay_ns(&mut self, ns: u32) { let period = MicrosDurationU32::from_ticks(ns / 1000); @@ -685,27 +1097,33 @@ mod asynch { #[handler] fn target0_handler() { - unsafe { &*crate::peripherals::SYSTIMER::PTR } - .int_ena() - .modify(|_, w| w.target0().clear_bit()); + lock(&INT_ENA_LOCK, || { + unsafe { &*crate::peripherals::SYSTIMER::PTR } + .int_ena() + .modify(|_, w| w.target0().clear_bit()); + }); WAKERS[0].wake(); } #[handler] fn target1_handler() { - unsafe { &*crate::peripherals::SYSTIMER::PTR } - .int_ena() - .modify(|_, w| w.target1().clear_bit()); + lock(&INT_ENA_LOCK, || { + unsafe { &*crate::peripherals::SYSTIMER::PTR } + .int_ena() + .modify(|_, w| w.target1().clear_bit()); + }); WAKERS[1].wake(); } #[handler] fn target2_handler() { - unsafe { &*crate::peripherals::SYSTIMER::PTR } - .int_ena() - .modify(|_, w| w.target2().clear_bit()); + lock(&INT_ENA_LOCK, || { + unsafe { &*crate::peripherals::SYSTIMER::PTR } + .int_ena() + .modify(|_, w| w.target2().clear_bit()); + }); WAKERS[2].wake(); } @@ -717,9 +1135,9 @@ pub mod etm { //! //! ## Overview //! - //! The system timer supports the Event Task Matrix (ETM) function, which - //! allows the system timer’s ETM events to trigger any - //! peripherals’ ETM tasks. + //! The system timer supports the Event Task Matrix (ETM) function, which + //! allows the system timer’s ETM events to trigger any peripherals’ ETM + //! tasks. //! //! The system timer can generate the following ETM events: //! - SYSTIMER_EVT_CNT_CMPx: Indicates the alarm pulses generated by @@ -731,8 +1149,9 @@ pub mod etm { //! # use esp_hal::timer::systimer::{etm::SysTimerEtmEvent, SystemTimer}; //! # use fugit::ExtU32; //! let syst = SystemTimer::new(peripherals.SYSTIMER); - //! let mut alarm0 = syst.alarm0.into_periodic(); - //! alarm0.set_period(1.secs()); + //! let syst_alarms = syst.split(); + //! let mut alarm0 = syst_alarms.alarm0.into_periodic(); + //! alarm0.set_period(1u32.secs()); //! //! let timer_event = SysTimerEtmEvent::new(&mut alarm0); //! # } @@ -741,31 +1160,35 @@ pub mod etm { use super::*; /// An ETM controlled SYSTIMER event - pub struct SysTimerEtmEvent<'a, M, DM: crate::Mode, const N: u8> { - alarm: &'a mut Alarm, + pub struct SysTimerEtmEvent<'a, 'd, M, DM: crate::Mode, COMP, UNIT> { + alarm: &'a mut Alarm<'d, M, DM, COMP, UNIT>, } - impl<'a, M, DM: crate::Mode, const N: u8> SysTimerEtmEvent<'a, M, DM, N> { + impl<'a, 'd, M, DM: crate::Mode, COMP: Comparator, UNIT: Unit> + SysTimerEtmEvent<'a, 'd, M, DM, COMP, UNIT> + { /// Creates an ETM event from the given [Alarm] - pub fn new(alarm: &'a mut Alarm) -> Self { + pub fn new(alarm: &'a mut Alarm<'d, M, DM, COMP, UNIT>) -> Self { Self { alarm } } /// Execute closure f with mutable access to the wrapped [Alarm]. - pub fn with(&self, f: impl FnOnce(&&'a mut Alarm) -> R) -> R { + pub fn with(&self, f: impl FnOnce(&&'a mut Alarm<'d, M, DM, COMP, UNIT>) -> R) -> R { let alarm = &self.alarm; f(alarm) } } - impl<'a, M, DM: crate::Mode, const N: u8> crate::private::Sealed - for SysTimerEtmEvent<'a, M, DM, N> + impl<'a, 'd, M, DM: crate::Mode, COMP: Comparator, UNIT: Unit> crate::private::Sealed + for SysTimerEtmEvent<'a, 'd, M, DM, COMP, UNIT> { } - impl<'a, M, DM: crate::Mode, const N: u8> crate::etm::EtmEvent for SysTimerEtmEvent<'a, M, DM, N> { + impl<'a, 'd, M, DM: crate::Mode, COMP: Comparator, UNIT: Unit> crate::etm::EtmEvent + for SysTimerEtmEvent<'a, 'd, M, DM, COMP, UNIT> + { fn id(&self) -> u8 { - 50 + N + 50 + self.alarm.comparator.channel() } } diff --git a/esp-hal/src/timer/timg.rs b/esp-hal/src/timer/timg.rs index bfea536f99a..3323c97ecc7 100644 --- a/esp-hal/src/timer/timg.rs +++ b/esp-hal/src/timer/timg.rs @@ -1,6 +1,7 @@ //! # Timer Group (TIMG) //! //! ## Overview +//! //! The Timer Group (TIMG) peripherals contain one or more general-purpose //! timers, plus one or more watchdog timers. //! @@ -8,6 +9,7 @@ //! auto-reload-capable up-down counter. //! //! ## Configuration +//! //! The timers have configurable alarms, which are triggered when the internal //! counter of the timers reaches a specific target value. The timers are //! clocked using the APB clock source. @@ -18,15 +20,15 @@ //! - Generate one-shot alarms; trigger events once //! - Free-running; fetching a high-resolution timestamp on demand //! -//! //! ## Examples +//! //! ### General-purpose Timer +//! //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::timer::timg::TimerGroup; -//! # use crate::esp_hal::prelude::_esp_hal_timer_Timer; -//! # use esp_hal::prelude::*; -//! let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); +//! +//! let timg0 = TimerGroup::new(peripherals.TIMG0); //! let timer0 = timg0.timer0; //! //! // Get the current timestamp, in microseconds: @@ -39,15 +41,17 @@ //! while !timer0.is_interrupt_set() { //! // Wait //! } +//! +//! timer0.clear_interrupt(); //! # } //! ``` //! -//! #### Watchdog Timer +//! ### Watchdog Timer //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::timer::timg::TimerGroup; -//! # use esp_hal::prelude::*; -//! let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); +//! +//! let timg0 = TimerGroup::new(peripherals.TIMG0); //! let mut wdt = timg0.wdt; //! //! wdt.set_timeout(5_000.millis()); @@ -74,6 +78,7 @@ use crate::soc::constants::TIMG_DEFAULT_CLK_SRC; use crate::{ clock::Clocks, interrupt::{self, InterruptHandler}, + lock, peripheral::{Peripheral, PeripheralRef}, peripherals::{timg0::RegisterBlock, Interrupt, TIMG0}, private::Sealed, @@ -81,9 +86,12 @@ use crate::{ Async, Blocking, InterruptConfigurable, + LockState, Mode, }; +static INT_ENA_LOCK: LockState = LockState::new(); + /// A timer group consisting of up to 2 timers (chip dependent) and a watchdog /// timer. pub struct TimerGroup<'d, T, DM> @@ -106,6 +114,8 @@ pub trait TimerGroupInstance { fn id() -> u8; fn register_block() -> *const RegisterBlock; fn configure_src_clk(); + fn enable_peripheral(); + fn reset_peripheral(); fn configure_wdt_src_clk(); } @@ -144,6 +154,14 @@ impl TimerGroupInstance for TIMG0 { // ESP32 has only APB clock source, do nothing } + fn enable_peripheral() { + crate::system::PeripheralClockControl::enable(crate::system::Peripheral::Timg0) + } + + fn reset_peripheral() { + // for TIMG0 do nothing for now because the reset breaks `time::now` + } + #[inline(always)] #[cfg(any(esp32c2, esp32c3))] fn configure_wdt_src_clk() { @@ -206,6 +224,16 @@ impl TimerGroupInstance for TIMG1 { // ESP32-C2 and ESP32-C3 don't have t1config only t0config, do nothing } + #[inline(always)] + fn enable_peripheral() { + crate::system::PeripheralClockControl::enable(crate::system::Peripheral::Timg1) + } + + #[inline(always)] + fn reset_peripheral() { + crate::system::PeripheralClockControl::reset(crate::system::Peripheral::Timg1) + } + #[inline(always)] #[cfg(any(esp32c6, esp32h2))] fn configure_wdt_src_clk() { @@ -222,25 +250,35 @@ impl TimerGroupInstance for TIMG1 { } } -impl<'d, T> TimerGroup<'d, T, Blocking> +impl<'d, T, DM> TimerGroup<'d, T, DM> where T: TimerGroupInstance, + DM: crate::Mode, { /// Construct a new instance of [`TimerGroup`] in blocking mode - pub fn new(_timer_group: impl Peripheral

+ 'd, clocks: &Clocks<'d>) -> Self { + pub fn new_inner(_timer_group: impl Peripheral

+ 'd) -> Self { crate::into_ref!(_timer_group); + T::reset_peripheral(); + T::enable_peripheral(); + T::configure_src_clk(); - // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK + let clocks = Clocks::get(); + cfg_if::cfg_if! { + if #[cfg(esp32h2)] { + // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK + let apb_clk_freq = clocks.pll_48m_clock; + } else { + let apb_clk_freq = clocks.apb_clock; + } + } + let timer0 = Timer::new( Timer0 { phantom: PhantomData, }, - #[cfg(not(esp32h2))] - clocks.apb_clock, - #[cfg(esp32h2)] - clocks.pll_48m_clock, + apb_clk_freq, ); #[cfg(timg_timer1)] @@ -248,7 +286,7 @@ where Timer1 { phantom: PhantomData, }, - clocks.apb_clock, + apb_clk_freq, ); Self { @@ -261,42 +299,23 @@ where } } +impl<'d, T> TimerGroup<'d, T, Blocking> +where + T: TimerGroupInstance, +{ + /// Construct a new instance of [`TimerGroup`] in blocking mode + pub fn new(_timer_group: impl Peripheral

+ 'd) -> Self { + Self::new_inner(_timer_group) + } +} + impl<'d, T> TimerGroup<'d, T, Async> where T: TimerGroupInstance, { /// Construct a new instance of [`TimerGroup`] in asynchronous mode - pub fn new_async(_timer_group: impl Peripheral

+ 'd, clocks: &Clocks<'d>) -> Self { - crate::into_ref!(_timer_group); - - T::configure_src_clk(); - - // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK - let timer0 = Timer::new( - Timer0 { - phantom: PhantomData, - }, - #[cfg(not(esp32h2))] - clocks.apb_clock, - #[cfg(esp32h2)] - clocks.pll_48m_clock, - ); - - #[cfg(timg_timer1)] - let timer1 = Timer::new( - Timer1 { - phantom: PhantomData, - }, - clocks.apb_clock, - ); - - Self { - _timer_group, - timer0, - #[cfg(timg_timer1)] - timer1, - wdt: Wdt::new(), - } + pub fn new_async(_timer_group: impl Peripheral

+ 'd) -> Self { + Self::new_inner(_timer_group) } } @@ -343,7 +362,7 @@ where } } - /// Block until the timer has elasped. + /// Block until the timer has elapsed. pub fn wait(&mut self) { while !self.has_elapsed() {} } @@ -468,14 +487,16 @@ where .config() .modify(|_, w| w.level_int_en().set_bit()); - self.register_block() - .int_ena_timers() - .modify(|_, w| w.t(self.timer_number()).bit(state)); + lock(&INT_ENA_LOCK, || { + self.register_block() + .int_ena() + .modify(|_, w| w.t(self.timer_number()).bit(state)); + }); } fn clear_interrupt(&self) { self.register_block() - .int_clr_timers() + .int_clr() .write(|w| w.t(self.timer_number()).clear_bit_by_one()); } @@ -499,7 +520,7 @@ where fn is_interrupt_set(&self) -> bool { self.register_block() - .int_raw_timers() + .int_raw() .read() .t(self.timer_number()) .bit_is_set() @@ -693,20 +714,24 @@ where .config() .modify(|_, w| w.level_int_en().set_bit()); - self.register_block() - .int_ena_timers() - .modify(|_, w| w.t(T).set_bit()); + lock(&INT_ENA_LOCK, || { + self.register_block() + .int_ena() + .modify(|_, w| w.t(T).set_bit()); + }); } fn unlisten(&self) { - self.register_block() - .int_ena_timers() - .modify(|_, w| w.t(T).clear_bit()); + lock(&INT_ENA_LOCK, || { + self.register_block() + .int_ena() + .modify(|_, w| w.t(T).clear_bit()); + }); } fn clear_interrupt(&self) { self.register_block() - .int_clr_timers() + .int_clr() .write(|w| w.t(T).clear_bit_by_one()); } @@ -739,11 +764,7 @@ where } fn is_interrupt_set(&self) -> bool { - self.register_block() - .int_raw_timers() - .read() - .t(T) - .bit_is_set() + self.register_block().int_raw().read().t(T).bit_is_set() } fn set_divider(&self, divider: u16) { @@ -800,7 +821,6 @@ where (1_000_000 * micros / period as u64) as u64 } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::timer::CountDown for Timer where T: Instance + super::Timer, @@ -825,7 +845,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::timer::Cancel for Timer where T: Instance + super::Timer, @@ -846,7 +865,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::timer::Periodic for Timer where T: Instance + super::Timer, @@ -1013,7 +1031,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::watchdog::WatchdogDisable for Wdt where TG: TimerGroupInstance, @@ -1024,7 +1041,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::watchdog::WatchdogEnable for Wdt where TG: TimerGroupInstance, @@ -1041,7 +1057,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::watchdog::Watchdog for Wdt where TG: TimerGroupInstance, diff --git a/esp-hal/src/touch.rs b/esp-hal/src/touch.rs index ed3215fb082..3005d2e2984 100644 --- a/esp-hal/src/touch.rs +++ b/esp-hal/src/touch.rs @@ -13,7 +13,7 @@ //! # use esp_hal::gpio::Io; //! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! let touch_pin0 = io.pins.gpio2; -//! let touch = Touch::continous_mode(peripherals.TOUCH, None); +//! let touch = Touch::continuous_mode(peripherals.TOUCH, None); //! let mut touchpad = TouchPad::new(touch_pin0, &touch); //! // ... give the peripheral some time for the measurement //! let touch_val = touchpad.read(); @@ -27,8 +27,6 @@ //! - Touch sensor slope control //! - Deep Sleep support (wakeup from Deep Sleep) -#![deny(missing_docs)] - use core::marker::PhantomData; use crate::{ @@ -36,11 +34,12 @@ use crate::{ peripheral::{Peripheral, PeripheralRef}, peripherals::{RTC_CNTL, SENS, TOUCH}, private::{Internal, Sealed}, + rtc_cntl::Rtc, + Async, Blocking, + InterruptConfigurable, Mode, }; -#[cfg(feature = "async")] -use crate::{rtc_cntl::Rtc, Async, InterruptConfigurable}; /// A marker trait describing the mode the touch pad is set to. pub trait TouchMode: Sealed {} @@ -51,16 +50,16 @@ pub trait TouchMode: Sealed {} #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OneShot; -/// Marker struct for the touch peripherals continous reading mode. In the +/// Marker struct for the touch peripherals continuous reading mode. In the /// technical reference manual, this is referred to as "start FSM via timer". #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Continous; +pub struct Continuous; impl TouchMode for OneShot {} -impl TouchMode for Continous {} +impl TouchMode for Continuous {} impl Sealed for OneShot {} -impl Sealed for Continous {} +impl Sealed for Continuous {} /// Touchpad threshold type. #[derive(Debug, Copy, Clone)] @@ -82,7 +81,7 @@ pub struct TouchConfig { /// Duration of a single measurement (in cycles of the 8 MHz touch clock). /// Defaults to `0x7fff` pub measurement_duration: Option, - /// Sleep cycles for the touch timer in [`Continous`]-mode. Defaults to + /// Sleep cycles for the touch timer in [`Continuous`]-mode. Defaults to /// `0x100` pub sleep_cycles: Option, } @@ -148,8 +147,8 @@ impl<'d, TOUCHMODE: TouchMode, MODE: Mode> Touch<'d, TOUCHMODE, MODE> { }); } - /// Common parts of the continous mode initialization. - fn initialize_common_continoous(config: Option) { + /// Common parts of the continuous mode initialization. + fn initialize_common_continuous(config: Option) { let rtccntl = unsafe { &*RTC_CNTL::ptr() }; let sens = unsafe { &*SENS::ptr() }; @@ -242,8 +241,8 @@ impl<'d> Touch<'d, OneShot, Blocking> { } } } -impl<'d> Touch<'d, Continous, Blocking> { - /// Initializes the touch peripheral in continous mode and returns this +impl<'d> Touch<'d, Continuous, Blocking> { + /// Initializes the touch peripheral in continuous mode and returns this /// marker struct. Optionally accepts configuration options. /// /// ## Example @@ -255,16 +254,16 @@ impl<'d> Touch<'d, Continous, Blocking> { /// measurement_duration: Some(0x3000), /// ..Default::default() /// }); - /// let touch = Touch::continous_mode(peripherals.TOUCH, touch_cfg); + /// let touch = Touch::continuous_mode(peripherals.TOUCH, touch_cfg); /// # } /// ``` - pub fn continous_mode( + pub fn continuous_mode( touch_peripheral: impl Peripheral

+ 'd, config: Option, ) -> Self { crate::into_ref!(touch_peripheral); - Self::initialize_common_continoous(config); + Self::initialize_common_continuous(config); Self { _inner: touch_peripheral, @@ -273,9 +272,8 @@ impl<'d> Touch<'d, Continous, Blocking> { } } } -#[cfg(feature = "async")] -impl<'d> Touch<'d, Continous, Async> { - /// Initializes the touch peripheral in continous async mode and returns +impl<'d> Touch<'d, Continuous, Async> { + /// Initializes the touch peripheral in continuous async mode and returns /// this marker struct. /// /// ## Warning: @@ -296,6 +294,7 @@ impl<'d> Touch<'d, Continous, Async> { /// /// ```rust, no_run #[doc = crate::before_snippet!()] + /// # use esp_hal::rtc_cntl::Rtc; /// # use esp_hal::touch::{Touch, TouchConfig}; /// let mut rtc = Rtc::new(peripherals.LPWR); /// let touch = Touch::async_mode(peripherals.TOUCH, &mut rtc, None); @@ -308,7 +307,7 @@ impl<'d> Touch<'d, Continous, Async> { ) -> Self { crate::into_ref!(touch_peripheral); - Self::initialize_common_continoous(config); + Self::initialize_common_continuous(config); rtc.set_interrupt_handler(asynch::handle_touch_interrupt); @@ -495,7 +494,6 @@ fn internal_disable_interrupt(touch_nr: u8) { } } -#[cfg(feature = "async")] fn internal_disable_interrupts() { let sens = unsafe { &*SENS::ptr() }; sens.sar_touch_enable() @@ -525,7 +523,6 @@ fn internal_is_interrupt_set(touch_nr: u8) -> bool { internal_pins_touched() & (1 << touch_nr) != 0 } -#[cfg(feature = "async")] mod asynch { use core::{ sync::atomic::{AtomicU16, Ordering}, @@ -546,6 +543,7 @@ mod asynch { // Helper variable to store which pins need handling. static TOUCHED_PINS: AtomicU16 = AtomicU16::new(0); + #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TouchFuture { touch_nr: u8, } diff --git a/esp-hal/src/trace.rs b/esp-hal/src/trace.rs index ef6d2a35ca0..a0c735b1e4e 100644 --- a/esp-hal/src/trace.rs +++ b/esp-hal/src/trace.rs @@ -42,13 +42,16 @@ use crate::{ /// Errors returned from [Trace::stop_trace] #[derive(Debug, Clone, Copy)] pub enum Error { + /// Attempted to stop a trace which had not been started yet NotStarted, } /// Returned by [Trace::stop_trace] #[derive(Debug, Clone, Copy)] pub struct TraceResult { + /// Start index of the valid data pub valid_start_index: usize, + /// Length of the valid data pub valid_length: usize, } @@ -66,6 +69,7 @@ where pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { crate::into_ref!(peripheral); + PeripheralClockControl::reset(crate::system::Peripheral::Trace0); PeripheralClockControl::enable(crate::system::Peripheral::Trace0); Self { @@ -199,7 +203,9 @@ where } } +/// Trace peripheral instance pub trait Instance: crate::private::Sealed { + /// Get a reference to the peripheral's underlying register block fn register_block(&self) -> &RegisterBlock; } diff --git a/esp-hal/src/twai/filter.rs b/esp-hal/src/twai/filter.rs index bbdf2780351..3e05a1cfcd0 100644 --- a/esp-hal/src/twai/filter.rs +++ b/esp-hal/src/twai/filter.rs @@ -1,6 +1,7 @@ //! Two-wire Automotive Interface (TWAI) Filters //! //! ## Overview +//! //! The TWAI controller contains a hardware acceptance filter which can be used //! to filter messages of a particular ID. A node that filters out a message //! does not receive the message, but will still acknowledge it. Acceptance @@ -8,14 +9,22 @@ //! the bus that are irrelevant to the node. //! //! ## Configuration +//! //! The acceptance filters are configured using two 32-bit values known as the //! acceptance code and the acceptance mask. use super::{ExtendedId, StandardId}; #[derive(Debug, PartialEq, Eq)] +/// Represents the type of filtering to be applied to incoming TWAI frames. pub enum FilterType { + /// Uses the acceptance code and mask to define a single filter, which + /// allows for the first two data bytes of a standard frame to be filtered, + /// or the entirety of an extended frame's 29-bit ID. Single, + /// Uses the acceptance code and mask to define two separate filters + /// allowing for increased flexibility of ID's to accept, but does not allow + /// for all 29-bits of an extended ID to be filtered. Dual, } @@ -32,6 +41,7 @@ pub enum FilterType { pub trait Filter { /// The type of the filter. const FILTER_TYPE: FilterType; + /// Returns filter type. fn filter_type(&self) -> FilterType { Self::FILTER_TYPE } @@ -40,6 +50,7 @@ pub trait Filter { fn to_registers(&self) -> [u8; 8]; } +/// A type representing the bitmask used to filter incoming TWAI frames. pub type BitFilter = [u8; N]; // Convert a byte from a bytestring into a bit inside a given code and mask. @@ -97,9 +108,16 @@ impl SingleStandardFilter { /// /// Example matching only even IDs, allowing any rtr value and any payload /// data: - /// ```rust, ignore + /// ```rust, no_run + #[doc = crate::before_snippet!()] + /// # use esp_hal::twai::filter::SingleStandardFilter; /// const FILTER: SingleStandardFilter = - /// SingleStandardFilter::new(b"xxxxxxxxxx0", b"x", [b"xxxxxxxx", b"xxxxxxxx"]); + /// SingleStandardFilter::new( + /// b"xxxxxxxxxx0", + /// b"x", + /// [b"xxxxxxxx", b"xxxxxxxx"] + /// ); + /// # } /// ``` pub const fn new(id: &BitFilter<11>, rtr: &BitFilter<1>, payload: [&BitFilter<8>; 2]) -> Self { // The bit values we desire to match against. This determines whether we want a diff --git a/esp-hal/src/twai/mod.rs b/esp-hal/src/twai/mod.rs index 38a1c1744fc..c8e3d943c3b 100644 --- a/esp-hal/src/twai/mod.rs +++ b/esp-hal/src/twai/mod.rs @@ -1,6 +1,7 @@ //! # Two-wire Automotive Interface (TWAI) //! //! ## Overview +//! //! The TWAI is a multi-master, multi-cast communication protocol with error //! detection and signaling and inbuilt message priorities and arbitration. The //! TWAI protocol is suited for automotive and industrial applications. @@ -14,41 +15,43 @@ //! up the timing parameters, configuring acceptance filters, handling //! interrupts, and transmitting/receiving messages on the TWAI bus. //! -//! This driver manages the ISO 11898-1 (CAN Specification 2.0) compatible TWAI +//! This driver manages the ISO 11898-1 compatible TWAI //! controllers. It supports Standard Frame Format (11-bit) and Extended Frame //! Format (29-bit) frame identifiers. //! -//! ## Example +//! ## Examples +//! //! ### Transmitting and Receiving Messages +//! //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::twai; //! # use embedded_can::Id; -//! # use esp_hal::twai::filter::SingleStandardFilter; +//! # use esp_hal::twai; //! # use esp_hal::twai::filter; +//! # use esp_hal::twai::filter::SingleStandardFilter; //! # use esp_hal::twai::TwaiConfiguration; //! # use esp_hal::twai::BaudRate; +//! # use esp_hal::twai::TwaiMode; //! # use esp_hal::gpio::Io; //! # use embedded_can::Frame; -//! # use core::option::Option::None; //! # use nb::block; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! // Use GPIO pins 2 and 3 to connect to the respective pins on the CAN +//! // Use GPIO pins 2 and 3 to connect to the respective pins on the TWAI //! // transceiver. -//! let can_tx_pin = io.pins.gpio2; //! let can_rx_pin = io.pins.gpio3; +//! let can_tx_pin = io.pins.gpio2; //! -//! // The speed of the CAN bus. -//! const CAN_BAUDRATE: twai::BaudRate = BaudRate::B1000K; +//! // The speed of the TWAI bus. +//! const TWAI_BAUDRATE: twai::BaudRate = BaudRate::B1000K; //! //! // Begin configuring the TWAI peripheral. The peripheral is in a reset like //! // state that prevents transmission but allows configuration. //! let mut can_config = twai::TwaiConfiguration::new( //! peripherals.TWAI0, -//! can_tx_pin, //! can_rx_pin, -//! &clocks, -//! CAN_BAUDRATE, +//! can_tx_pin, +//! TWAI_BAUDRATE, +//! TwaiMode::Normal //! ); //! //! // Partially filter the incoming messages to reduce overhead of receiving @@ -71,12 +74,65 @@ //! } //! # } //! ``` +//! +//! ### Self-testing (self reception of transmitted messages) +//! ```rust, no_run +#![doc = crate::before_snippet!()] +//! # use embedded_can::Id; +//! # use esp_hal::twai; +//! # use esp_hal::twai::filter; +//! # use esp_hal::twai::filter::SingleStandardFilter; +//! # use esp_hal::twai::TwaiConfiguration; +//! # use esp_hal::twai::BaudRate; +//! # use esp_hal::twai::EspTwaiFrame; +//! # use esp_hal::twai::StandardId; +//! # use esp_hal::twai::TwaiMode; +//! # use esp_hal::gpio::Io; +//! # use embedded_can::Frame; +//! # use nb::block; +//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); +//! // Use GPIO pins 2 and 3 to connect to the respective pins on the TWAI +//! // transceiver. +//! let can_rx_pin = io.pins.gpio3; +//! let can_tx_pin = io.pins.gpio2; +//! +//! // The speed of the TWAI bus. +//! const TWAI_BAUDRATE: twai::BaudRate = BaudRate::B1000K; +//! +//! // Begin configuring the TWAI peripheral. +//! let mut can_config = twai::TwaiConfiguration::new( +//! peripherals.TWAI0, +//! can_rx_pin, +//! can_tx_pin, +//! TWAI_BAUDRATE, +//! TwaiMode::SelfTest +//! ); +//! +//! // Partially filter the incoming messages to reduce overhead of receiving +//! // undesired messages +//! const FILTER: twai::filter::SingleStandardFilter = +//! SingleStandardFilter::new(b"xxxxxxxxxx0", b"x", +//! [b"xxxxxxxx", b"xxxxxxxx"]); +//! can_config.set_filter(FILTER); +//! +//! // Start the peripheral. This locks the configuration settings of the +//! // peripheral and puts it into operation mode, allowing packets to be sent +//! // and received. +//! let mut can = can_config.start(); +//! +//! let frame = EspTwaiFrame::new_self_reception(StandardId::ZERO.into(), +//! &[1, 2, 3]).unwrap(); +//! // Wait for a frame to be received. +//! let frame = block!(can.receive()).unwrap(); +//! +//! # loop {} +//! # } +//! ``` use core::marker::PhantomData; use self::filter::{Filter, FilterType}; use crate::{ - clock::Clocks, gpio::{InputPin, InputSignal, OutputPin, OutputSignal}, interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, @@ -87,12 +143,12 @@ use crate::{ pub mod filter; -/// CAN error kind +/// TWAI error kind /// -/// This represents a common set of CAN operation errors. HAL implementations +/// This represents a common set of TWAI operation errors. HAL implementations /// are free to define more specific or additional error types. However, by -/// providing a mapping to these common CAN errors, generic code can still react -/// to them. +/// providing a mapping to these common TWAI errors, generic code can still +/// react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[non_exhaustive] pub enum ErrorKind { @@ -142,7 +198,6 @@ impl core::fmt::Display for ErrorKind { } } -#[cfg(feature = "embedded-hal-02")] impl From for embedded_hal_02::can::ErrorKind { fn from(value: ErrorKind) -> Self { match value { @@ -157,14 +212,12 @@ impl From for embedded_hal_02::can::ErrorKind { } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::can::Error for ErrorKind { fn kind(&self) -> embedded_hal_02::can::ErrorKind { (*self).into() } } -#[cfg(feature = "embedded-hal")] impl From for embedded_can::ErrorKind { fn from(value: ErrorKind) -> Self { match value { @@ -179,22 +232,32 @@ impl From for embedded_can::ErrorKind { } } -#[cfg(feature = "embedded-hal")] impl embedded_can::Error for ErrorKind { fn kind(&self) -> embedded_can::ErrorKind { (*self).into() } } -/// Standard 11-bit CAN Identifier (`0..=0x7FF`). +/// Specifies in which mode the TWAI controller will operate. +pub enum TwaiMode { + /// Normal operating mode + Normal, + /// Self-test mode (no acknowledgement required for a successful message + /// transmission) + SelfTest, + /// Listen only operating mode + ListenOnly, +} + +/// Standard 11-bit TWAI Identifier (`0..=0x7FF`). #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct StandardId(u16); impl StandardId { - /// CAN ID `0`, the highest priority. + /// TWAI ID `0`, the highest priority. pub const ZERO: Self = StandardId(0); - /// CAN ID `0x7FF`, the lowest priority. + /// TWAI ID `0x7FF`, the lowest priority. pub const MAX: Self = StandardId(0x7FF); /// Tries to create a `StandardId` from a raw 16-bit integer. @@ -220,50 +283,46 @@ impl StandardId { StandardId(raw) } - /// Returns this CAN Identifier as a raw 16-bit integer. + /// Returns TWAI Identifier as a raw 16-bit integer. #[inline] pub fn as_raw(&self) -> u16 { self.0 } } -#[cfg(feature = "embedded-hal-02")] impl From for embedded_hal_02::can::StandardId { fn from(value: StandardId) -> Self { embedded_hal_02::can::StandardId::new(value.as_raw()).unwrap() } } -#[cfg(feature = "embedded-hal-02")] impl From for StandardId { fn from(value: embedded_hal_02::can::StandardId) -> Self { StandardId::new(value.as_raw()).unwrap() } } -#[cfg(feature = "embedded-hal")] impl From for embedded_can::StandardId { fn from(value: StandardId) -> Self { embedded_can::StandardId::new(value.as_raw()).unwrap() } } -#[cfg(feature = "embedded-hal")] impl From for StandardId { fn from(value: embedded_can::StandardId) -> Self { StandardId::new(value.as_raw()).unwrap() } } -/// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`). +/// Extended 29-bit TWAI Identifier (`0..=1FFF_FFFF`). #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct ExtendedId(u32); impl ExtendedId { - /// CAN ID `0`, the highest priority. + /// TWAI ID `0`, the highest priority. pub const ZERO: Self = ExtendedId(0); - /// CAN ID `0x1FFFFFFF`, the lowest priority. + /// TWAI ID `0x1FFFFFFF`, the lowest priority. pub const MAX: Self = ExtendedId(0x1FFF_FFFF); /// Tries to create a `ExtendedId` from a raw 32-bit integer. @@ -289,7 +348,7 @@ impl ExtendedId { ExtendedId(raw) } - /// Returns this CAN Identifier as a raw 32-bit integer. + /// Returns TWAI Identifier as a raw 32-bit integer. #[inline] pub fn as_raw(&self) -> u32 { self.0 @@ -302,35 +361,31 @@ impl ExtendedId { } } -#[cfg(feature = "embedded-hal-02")] impl From for embedded_hal_02::can::ExtendedId { fn from(value: ExtendedId) -> Self { embedded_hal_02::can::ExtendedId::new(value.0).unwrap() } } -#[cfg(feature = "embedded-hal-02")] impl From for ExtendedId { fn from(value: embedded_hal_02::can::ExtendedId) -> Self { ExtendedId::new(value.as_raw()).unwrap() } } -#[cfg(feature = "embedded-hal")] impl From for embedded_can::ExtendedId { fn from(value: ExtendedId) -> Self { embedded_can::ExtendedId::new(value.0).unwrap() } } -#[cfg(feature = "embedded-hal")] impl From for ExtendedId { fn from(value: embedded_can::ExtendedId) -> Self { ExtendedId::new(value.as_raw()).unwrap() } } -/// A CAN Identifier (standard or extended). +/// A TWAI Identifier (standard or extended). #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Id { /// Standard 11-bit Identifier (`0..=0x7FF`). @@ -353,7 +408,6 @@ impl From for Id { } } -#[cfg(feature = "embedded-hal-02")] impl From for embedded_hal_02::can::Id { fn from(value: Id) -> Self { match value { @@ -363,7 +417,6 @@ impl From for embedded_hal_02::can::Id { } } -#[cfg(feature = "embedded-hal-02")] impl From for Id { fn from(value: embedded_hal_02::can::Id) -> Self { match value { @@ -373,7 +426,6 @@ impl From for Id { } } -#[cfg(feature = "embedded-hal")] impl From for embedded_can::Id { fn from(value: Id) -> Self { match value { @@ -383,7 +435,6 @@ impl From for embedded_can::Id { } } -#[cfg(feature = "embedded-hal")] impl From for Id { fn from(value: embedded_can::Id) -> Self { match value { @@ -400,11 +451,13 @@ pub struct EspTwaiFrame { dlc: usize, data: [u8; 8], is_remote: bool, + self_reception: bool, } impl EspTwaiFrame { + /// Creates a new `EspTwaiFrame` with the specified ID and data payload. pub fn new(id: Id, data: &[u8]) -> Option { - // CAN2.0 frames cannot contain more than 8 bytes of data. + // TWAI frames cannot contain more than 8 bytes of data. if data.len() > 8 { return None; } @@ -418,11 +471,14 @@ impl EspTwaiFrame { data: d, dlc: data.len(), is_remote: false, + self_reception: false, }) } + /// Creates a new `EspTwaiFrame` for a transmission request with the + /// specified ID and data length (DLC). pub fn new_remote(id: Id, dlc: usize) -> Option { - // CAN2.0 frames cannot have more than 8 bytes. + // TWAI frames cannot have more than 8 bytes. if dlc > 8 { return None; } @@ -432,6 +488,27 @@ impl EspTwaiFrame { data: [0; 8], dlc, is_remote: true, + self_reception: false, + }) + } + + /// Creates a new `EspTwaiFrame` ready for self-reception with the specified + /// ID and data payload. + pub fn new_self_reception(id: Id, data: &[u8]) -> Option { + if data.len() > 8 { + return None; + } + + let mut d: [u8; 8] = [0; 8]; + let (left, _unused) = d.split_at_mut(data.len()); + left.clone_from_slice(data); + + Some(EspTwaiFrame { + id, + data: d, + dlc: data.len(), + is_remote: false, + self_reception: true, }) } @@ -455,11 +532,11 @@ impl EspTwaiFrame { data, dlc, is_remote: false, + self_reception: true, } } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::can::Frame for EspTwaiFrame { fn new(id: impl Into, data: &[u8]) -> Option { let id: embedded_hal_02::can::Id = id.into(); @@ -500,7 +577,6 @@ impl embedded_hal_02::can::Frame for EspTwaiFrame { } } -#[cfg(feature = "embedded-hal")] impl embedded_can::Frame for EspTwaiFrame { fn new(id: impl Into, data: &[u8]) -> Option { let id: embedded_can::Id = id.into(); @@ -543,10 +619,23 @@ impl embedded_can::Frame for EspTwaiFrame { /// The underlying timings for the TWAI peripheral. pub struct TimingConfig { + /// The baudrate prescaler is used to determine the period of each time + /// quantum by dividing the TWAI controller's source clock. pub baud_rate_prescaler: u16, + + /// The synchronization jump width is used to determine the maximum number + /// of time quanta a single bit time can be lengthened/shortened for + /// synchronization purposes. pub sync_jump_width: u8, + + /// Timing segment 1 consists of 1 to 16 time quanta before sample point. pub tseg_1: u8, + + /// Timing Segment 2 consists of 1 to 8 time quanta after sample point. pub tseg_2: u8, + + /// Enabling triple sampling causes 3 time quanta to be sampled per bit + /// instead of 1. pub triple_sample: bool, } @@ -554,33 +643,24 @@ pub struct TimingConfig { /// Currently these timings are sourced from the ESP IDF C driver which assumes /// an APB clock of 80MHz. pub enum BaudRate { + /// A baud rate of 125 Kbps. B125K, + /// A baud rate of 250 Kbps. B250K, + /// A baud rate of 500 Kbps. B500K, + /// A baud rate of 1 Mbps. B1000K, + /// A custom baud rate defined by the user. + /// + /// This variant allows users to specify their own timing configuration + /// using a `TimingConfig` struct. Custom(TimingConfig), } impl BaudRate { /// Convert the BaudRate into the timings that the peripheral needs. - // These timings are copied from the ESP IDF C driver. - // #define TWAI_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = - // 8, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = - // 4, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_100KBITS() {.brp = 40, .tseg_1 = 15, .tseg_2 = - // 4, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_125KBITS() {.brp = 32, .tseg_1 = 15, .tseg_2 = - // 4, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_250KBITS() {.brp = 16, .tseg_1 = 15, .tseg_2 = - // 4, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_500KBITS() {.brp = 8, .tseg_1 = 15, .tseg_2 = 4, - // .sjw = 3, .triple_sampling = false} #define TWAI_TIMING_CONFIG_800KBITS() - // {.brp = 4, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} - // #define TWAI_TIMING_CONFIG_1MBITS() {.brp = 4, .tseg_1 = 15, .tseg_2 = 4, - // .sjw = 3, .triple_sampling = false} - // - // see https://github.com/espressif/esp-idf/tree/master/components/hal/include/hal/twai_types.h + // See: https://github.com/espressif/esp-idf/tree/master/components/hal/include/hal/twai_types.h const fn timing(self) -> TimingConfig { #[allow(unused_mut)] let mut timing = match self { @@ -638,13 +718,17 @@ where { fn new_internal( _peripheral: impl Peripheral

+ 'd, - tx_pin: impl Peripheral

+ 'd, rx_pin: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, + tx_pin: impl Peripheral

+ 'd, baud_rate: BaudRate, no_transceiver: bool, + mode: TwaiMode, ) -> Self { + // Set up the GPIO pins. + crate::into_ref!(tx_pin, rx_pin); + // Enable the peripheral clock for the TWAI peripheral. + T::reset_peripheral(); T::enable_peripheral(); // Set RESET bit to 1 @@ -652,20 +736,32 @@ where .mode() .write(|w| w.reset_mode().set_bit()); - // Set up the GPIO pins. - crate::into_ref!(tx_pin, rx_pin); if no_transceiver { tx_pin.set_to_open_drain_output(crate::private::Internal); } tx_pin.set_to_push_pull_output(crate::private::Internal); tx_pin.connect_peripheral_to_output(T::OUTPUT_SIGNAL, crate::private::Internal); - rx_pin.set_to_input(crate::private::Internal); + rx_pin.init_input(false, false, crate::private::Internal); rx_pin.connect_input_to_peripheral(T::INPUT_SIGNAL, crate::private::Internal); - // Set mod to listen only first - T::register_block() - .mode() - .modify(|_, w| w.listen_only_mode().set_bit()); + // Set the operating mode based on provided option + match mode { + TwaiMode::Normal => { + // Do nothing special, the default state is Normal mode. + } + TwaiMode::SelfTest => { + // Set the self-test mode (no acknowledgement required) + T::register_block() + .mode() + .modify(|_, w| w.self_test_mode().set_bit()); + } + TwaiMode::ListenOnly => { + // Set listen-only mode + T::register_block() + .mode() + .modify(|_, w| w.listen_only_mode().set_bit()); + } + } // Set TEC to 0 T::register_block() @@ -687,7 +783,7 @@ where phantom: PhantomData, }; - cfg.set_baud_rate(baud_rate, clocks); + cfg.set_baud_rate(baud_rate); cfg } @@ -701,11 +797,14 @@ where /// Set the bitrate of the bus. /// /// Note: The timings currently assume a APB_CLK of 80MHz. - fn set_baud_rate(&mut self, baud_rate: BaudRate, _clocks: &Clocks<'d>) { + fn set_baud_rate(&mut self, baud_rate: BaudRate) { // TWAI is clocked from the APB_CLK according to Table 6-4 [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf) // Included timings are all for 80MHz so assert that we are running at 80MHz. #[cfg(not(esp32c6))] - assert!(_clocks.apb_clock == fugit::HertzU32::MHz(80)); + { + let apb_clock = crate::clock::Clocks::get().apb_clock; + assert!(apb_clock == fugit::HertzU32::MHz(80)); + } // Unpack the baud rate timings and convert them to the values needed for the // register. Many of the registers have a minimum value of 1 which is @@ -784,11 +883,11 @@ where .modify(|_, w| w.reset_mode().clear_bit()); Twai { - tx: TwaiTx { + rx: TwaiRx { _peripheral: PhantomData, phantom: PhantomData, }, - rx: TwaiRx { + tx: TwaiTx { _peripheral: PhantomData, phantom: PhantomData, }, @@ -804,14 +903,14 @@ where /// Create a new instance of [TwaiConfiguration] /// /// You will need to use a transceiver to connect to the TWAI bus - pub fn new( + pub fn new( peripheral: impl Peripheral

+ 'd, - tx_pin: impl Peripheral

+ 'd, rx_pin: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, + tx_pin: impl Peripheral

+ 'd, baud_rate: BaudRate, + mode: TwaiMode, ) -> Self { - Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, false) + Self::new_internal(peripheral, rx_pin, tx_pin, baud_rate, false, mode) } /// Create a new instance of [TwaiConfiguration] meant to connect two ESP32s @@ -819,14 +918,14 @@ where /// /// You don't need a transceiver by following the description in the /// `twai.rs` example - pub fn new_no_transceiver( + pub fn new_no_transceiver( peripheral: impl Peripheral

+ 'd, - tx_pin: impl Peripheral

+ 'd, rx_pin: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, + tx_pin: impl Peripheral

+ 'd, baud_rate: BaudRate, + mode: TwaiMode, ) -> Self { - Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, true) + Self::new_internal(peripheral, rx_pin, tx_pin, baud_rate, true, mode) } } @@ -841,7 +940,6 @@ where } } -#[cfg(feature = "async")] impl<'d, T> TwaiConfiguration<'d, T, crate::Async> where T: Instance, @@ -849,14 +947,14 @@ where /// Create a new instance of [TwaiConfiguration] in async mode /// /// You will need to use a transceiver to connect to the TWAI bus - pub fn new_async( + pub fn new_async( peripheral: impl Peripheral

+ 'd, - tx_pin: impl Peripheral

+ 'd, rx_pin: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, + tx_pin: impl Peripheral

+ 'd, baud_rate: BaudRate, + mode: TwaiMode, ) -> Self { - let mut this = Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, false); + let mut this = Self::new_internal(peripheral, rx_pin, tx_pin, baud_rate, false, mode); this.internal_set_interrupt_handler(T::async_handler()); this } @@ -866,14 +964,14 @@ where /// /// You don't need a transceiver by following the description in the /// `twai.rs` example - pub fn new_async_no_transceiver( + pub fn new_async_no_transceiver( peripheral: impl Peripheral

+ 'd, - tx_pin: impl Peripheral

+ 'd, rx_pin: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, + tx_pin: impl Peripheral

+ 'd, baud_rate: BaudRate, + mode: TwaiMode, ) -> Self { - let mut this = Self::new_internal(peripheral, tx_pin, rx_pin, clocks, baud_rate, true); + let mut this = Self::new_internal(peripheral, rx_pin, tx_pin, baud_rate, true, mode); this.internal_set_interrupt_handler(T::async_handler()); this } @@ -909,10 +1007,12 @@ where } } + /// Returns the value of the receive error counter. pub fn receive_error_count(&self) -> u8 { T::register_block().rx_err_cnt().read().rx_err_cnt().bits() } + /// Returns the value of the transmit error counter. pub fn transmit_error_count(&self) -> u8 { T::register_block().tx_err_cnt().read().tx_err_cnt().bits() } @@ -952,22 +1052,24 @@ where } } + /// Sends the specified `EspTwaiFrame` over the TWAI bus. pub fn transmit(&mut self, frame: &EspTwaiFrame) -> nb::Result<(), EspTwaiError> { self.tx.transmit(frame) } + /// Receives a TWAI frame from the TWAI bus. pub fn receive(&mut self) -> nb::Result { self.rx.receive() } /// Consumes this `Twai` instance and splits it into transmitting and /// receiving halves. - pub fn split(self) -> (TwaiTx<'d, T, DM>, TwaiRx<'d, T, DM>) { - (self.tx, self.rx) + pub fn split(self) -> (TwaiRx<'d, T, DM>, TwaiTx<'d, T, DM>) { + (self.rx, self.tx) } } -/// Interface to the CAN transmitter part. +/// Interface to the TWAI transmitter part. pub struct TwaiTx<'d, T, DM: crate::Mode> { _peripheral: PhantomData<&'d T>, phantom: PhantomData, @@ -1009,7 +1111,7 @@ where } } -/// Interface to the CAN receiver part. +/// Interface to the TWAI receiver part. pub struct TwaiRx<'d, T, DM: crate::Mode> { _peripheral: PhantomData<&'d T>, phantom: PhantomData, @@ -1020,7 +1122,7 @@ where T: OperationInstance, DM: crate::Mode, { - // Receive a frame + /// Receive a frame pub fn receive(&mut self) -> nb::Result { let register_block = T::register_block(); let status = register_block.status().read(); @@ -1051,13 +1153,17 @@ where } } +/// Represents errors that can occur in the TWAI driver. +/// This enum defines the possible errors that can be encountered when +/// interacting with the TWAI peripheral. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum EspTwaiError { + /// TWAI peripheral has entered a bus-off state. BusOff, + /// Encapsulates errors defined by the embedded-hal crate. EmbeddedHAL(ErrorKind), } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::can::Error for EspTwaiError { fn kind(&self) -> embedded_hal_02::can::ErrorKind { match self { @@ -1067,7 +1173,6 @@ impl embedded_hal_02::can::Error for EspTwaiError { } } -#[cfg(feature = "embedded-hal")] impl embedded_can::Error for EspTwaiError { fn kind(&self) -> embedded_can::ErrorKind { match self { @@ -1109,7 +1214,6 @@ unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { } } -#[cfg(feature = "embedded-hal-02")] impl<'d, T, DM> embedded_hal_02::can::Can for Twai<'d, T, DM> where T: OperationInstance, @@ -1134,7 +1238,6 @@ where } } -#[cfg(feature = "embedded-hal")] impl<'d, T, DM> embedded_can::nb::Can for Twai<'d, T, DM> where T: OperationInstance, @@ -1159,26 +1262,40 @@ where } } +/// TWAI peripheral instance. pub trait Instance: crate::private::Sealed { + /// The system peripheral associated with this TWAI instance. const SYSTEM_PERIPHERAL: system::Peripheral; + /// The identifier number for this TWAI instance. const NUMBER: usize; + /// Input signal. const INPUT_SIGNAL: InputSignal; + /// Output signal. const OUTPUT_SIGNAL: OutputSignal; - + /// The interrupt associated with this TWAI instance. const INTERRUPT: crate::peripherals::Interrupt; - #[cfg(feature = "async")] + + /// Provides an asynchronous interrupt handler for TWAI instance. fn async_handler() -> InterruptHandler; + /// Returns a reference to the register block for TWAI instance. fn register_block() -> &'static RegisterBlock; + /// Enables the TWAI peripheral. fn enable_peripheral(); + /// Resets the TWAI peripheral. + fn reset_peripheral(); + + /// Enables interrupts for the TWAI peripheral. fn enable_interrupts(); } +/// An extension of the `Instance` trait that provides additional operations +/// for managing and interacting with the TWAI peripheral. pub trait OperationInstance: Instance { - #[cfg(feature = "async")] + /// Returns a reference to the asynchronous state for this TWAI instance. fn async_state() -> &'static asynch::TwaiAsyncState { &asynch::TWAI_STATE[Self::NUMBER] } @@ -1264,9 +1381,19 @@ pub trait OperationInstance: Instance { // Is RTR frame, so no data is included. } - // Set the transmit request command, this will lock the transmit buffer until - // the transmission is complete or aborted. - register_block.cmd().write(|w| w.tx_req().set_bit()); + // Trigger the appropriate transmission request based on self_reception flag + if frame.self_reception { + #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] + register_block.cmd().write(|w| w.self_rx_req().set_bit()); + #[cfg(not(any(esp32, esp32c3, esp32s2, esp32s3)))] + register_block + .cmd() + .write(|w| w.self_rx_request().set_bit()); + } else { + // Set the transmit request command, this will lock the transmit buffer until + // the transmission is complete or aborted. + register_block.cmd().write(|w| w.tx_req().set_bit()); + } } /// Read a frame from the peripheral. @@ -1344,7 +1471,6 @@ impl Instance for crate::peripherals::TWAI0 { const INTERRUPT: crate::peripherals::Interrupt = crate::peripherals::Interrupt::TWAI0; - #[cfg(feature = "async")] fn async_handler() -> InterruptHandler { asynch::twai0 } @@ -1354,6 +1480,10 @@ impl Instance for crate::peripherals::TWAI0 { unsafe { &*crate::peripherals::TWAI0::PTR } } + fn reset_peripheral() { + PeripheralClockControl::reset(crate::system::Peripheral::Twai0); + } + fn enable_peripheral() { PeripheralClockControl::enable(crate::system::Peripheral::Twai0); } @@ -1388,7 +1518,6 @@ impl Instance for crate::peripherals::TWAI0 { const INTERRUPT: crate::peripherals::Interrupt = crate::peripherals::Interrupt::TWAI0; - #[cfg(feature = "async")] fn async_handler() -> InterruptHandler { asynch::twai0 } @@ -1398,6 +1527,10 @@ impl Instance for crate::peripherals::TWAI0 { unsafe { &*crate::peripherals::TWAI0::PTR } } + fn reset_peripheral() { + PeripheralClockControl::enable(crate::system::Peripheral::Twai0); + } + fn enable_peripheral() { PeripheralClockControl::enable(crate::system::Peripheral::Twai0); } @@ -1432,7 +1565,6 @@ impl Instance for crate::peripherals::TWAI1 { const INTERRUPT: crate::peripherals::Interrupt = crate::peripherals::Interrupt::TWAI1; - #[cfg(feature = "async")] fn async_handler() -> InterruptHandler { asynch::twai1 } @@ -1442,6 +1574,10 @@ impl Instance for crate::peripherals::TWAI1 { unsafe { &*crate::peripherals::TWAI1::PTR } } + fn reset_peripheral() { + PeripheralClockControl::enable(crate::system::Peripheral::Twai1); + } + fn enable_peripheral() { PeripheralClockControl::enable(crate::system::Peripheral::Twai1); } @@ -1466,7 +1602,6 @@ impl Instance for crate::peripherals::TWAI1 { #[cfg(esp32c6)] impl OperationInstance for crate::peripherals::TWAI1 {} -#[cfg(feature = "async")] mod asynch { use core::{future::poll_fn, task::Poll}; @@ -1513,10 +1648,11 @@ mod asynch { where T: OperationInstance, { + /// Transmits an `EspTwaiFrame` asynchronously over the TWAI bus. pub async fn transmit_async(&mut self, frame: &EspTwaiFrame) -> Result<(), EspTwaiError> { self.tx.transmit_async(frame).await } - + /// Receives an `EspTwaiFrame` asynchronously over the TWAI bus. pub async fn receive_async(&mut self) -> Result { self.rx.receive_async().await } @@ -1526,6 +1662,7 @@ mod asynch { where T: OperationInstance, { + /// Transmits an `EspTwaiFrame` asynchronously over the TWAI bus. pub async fn transmit_async(&mut self, frame: &EspTwaiFrame) -> Result<(), EspTwaiError> { T::enable_interrupts(); poll_fn(|cx| { @@ -1555,6 +1692,7 @@ mod asynch { where T: OperationInstance, { + /// Receives an `EspTwaiFrame` asynchronously over the TWAI bus. pub async fn receive_async(&mut self) -> Result { T::enable_interrupts(); poll_fn(|cx| { diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index e2395b0f51d..a3e7d75de1b 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -1,6 +1,7 @@ //! # Universal Asynchronous Receiver/Transmitter (UART) //! //! ## Overview +//! //! The UART is a hardware peripheral which handles communication using serial //! communication interfaces, such as RS232 and RS485. This peripheral provides //! a cheap and ubiquitous method for full- and half-duplex communication @@ -13,28 +14,33 @@ //! protocols. //! //! ## Configuration +//! //! Each UART controller is individually configurable, and the usual setting //! such as baud rate, data bits, parity, and stop bits can easily be -//! configured. Additionally, the transmit (TX) and receive (RX) pins need to +//! configured. Additionally, the receive (RX) and transmit (TX) pins need to //! be specified. //! //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use core::option::Option::Some; -//! # use esp_hal::uart::{config::Config, Uart}; +//! # use esp_hal::uart::Uart; //! use esp_hal::gpio::Io; +//! //! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! -//! let mut uart1 = Uart::new(peripherals.UART1, &clocks, io.pins.gpio1, -//! io.pins.gpio2).unwrap(); +//! let mut uart1 = Uart::new( +//! peripherals.UART1, +//! io.pins.gpio1, +//! io.pins.gpio2, +//! ).unwrap(); //! # } //! ``` //! //! The UART controller can be configured to invert the polarity of the pins. -//! This is achived by inverting the desired pins, and then constucting the +//! This is achieved by inverting the desired pins, and then constructing the //! UART instance using the inverted pins. //! //! ## Usage +//! //! The UART driver implements a number of third-party traits, with the //! intention of making the HAL inter-compatible with various device drivers //! from the community. This includes, but is not limited to, the [embedded-hal] @@ -50,35 +56,33 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::uart::{config::Config, Uart}; -//! use esp_hal::gpio::Io; +//! # use esp_hal::gpio::Io; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! # let mut uart1 = Uart::new_with_config( //! # peripherals.UART1, //! # Config::default(), -//! # &clocks, //! # io.pins.gpio1, //! # io.pins.gpio2, //! # ).unwrap(); //! // Write bytes out over the UART: -//! uart1.write_bytes("Hello, world!".as_bytes()).expect("write error!"); +//! uart1.write_bytes(b"Hello, world!").expect("write error!"); //! # } //! ``` //! -//! ### Splitting the UART into TX and RX Components +//! ### Splitting the UART into RX and TX Components //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::uart::{config::Config, Uart}; -//! use esp_hal::gpio::Io; +//! # use esp_hal::gpio::Io; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! # let mut uart1 = Uart::new_with_config( //! # peripherals.UART1, //! # Config::default(), -//! # &clocks, //! # io.pins.gpio1, //! # io.pins.gpio2, //! # ).unwrap(); //! // The UART can be split into separate Transmit and Receive components: -//! let (mut tx, mut rx) = uart1.split(); +//! let (mut rx, mut tx) = uart1.split(); //! //! // Each component can be used individually to interact with the UART: //! tx.write_bytes(&[42u8]).expect("write error!"); @@ -86,30 +90,34 @@ //! # } //! ``` //! -//! ### Inverting TX and RX Pins +//! ### Inverting RX and TX Pins //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{config::Config, Uart}; -//! use esp_hal::gpio::{Io, any_pin::AnyPin}; +//! # use esp_hal::uart::Uart; +//! use esp_hal::gpio::{AnyPin, Io}; +//! //! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! -//! let tx = AnyPin::new_inverted(io.pins.gpio1); //! let rx = AnyPin::new_inverted(io.pins.gpio2); -//! let mut uart1 = Uart::new(peripherals.UART1, &clocks, tx, rx).unwrap(); +//! let tx = AnyPin::new_inverted(io.pins.gpio1); +//! let mut uart1 = Uart::new( +//! peripherals.UART1, +//! rx, +//! tx, +//! ).unwrap(); //! # } //! ``` //! -//! ### Constructing TX and RX Components +//! ### Constructing RX and TX Components //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{config::Config, UartTx, UartRx}; -//! use esp_hal::gpio::{Io, any_pin::AnyPin}; +//! # use esp_hal::uart::{UartTx, UartRx}; +//! use esp_hal::gpio::{AnyPin, Io}; +//! //! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! -//! let tx = UartTx::new(peripherals.UART0, &clocks, -//! io.pins.gpio1).unwrap(); -//! let rx = UartRx::new(peripherals.UART1, &clocks, -//! io.pins.gpio2).unwrap(); +//! let tx = UartTx::new(peripherals.UART0, io.pins.gpio1).unwrap(); +//! let rx = UartRx::new(peripherals.UART1, io.pins.gpio2).unwrap(); //! # } //! ``` //! @@ -126,10 +134,7 @@ use crate::{ gpio::{InputPin, InputSignal, OutputPin, OutputSignal}, interrupt::InterruptHandler, peripheral::Peripheral, - peripherals::{ - uart0::{fifo::FIFO_SPEC, RegisterBlock}, - Interrupt, - }, + peripherals::{uart0::RegisterBlock, Interrupt}, private::Internal, system::PeripheralClockControl, Blocking, @@ -148,26 +153,61 @@ use crate::soc::constants::REF_TICK; // Default TX and RX pins for Uart/Serial communication (UART0) cfg_if::cfg_if! { if #[cfg(esp32)] { - pub type DefaultTxPin = crate::gpio::Gpio1; - pub type DefaultRxPin = crate::gpio::Gpio3; + /// Default TX pin for UART0 on ESP32. + /// Corresponds to GPIO1. + pub type DefaultTxPin = crate::gpio::GpioPin<1>; + + /// Default RX pin for UART0 on ESP32. + /// Corresponds to GPIO3. + pub type DefaultRxPin = crate::gpio::GpioPin<3>; } else if #[cfg(esp32c2)] { - pub type DefaultTxPin = crate::gpio::Gpio20; - pub type DefaultRxPin = crate::gpio::Gpio19; + /// Default TX pin for UART0 on ESP32-C2. + /// Corresponds to GPIO20. + pub type DefaultTxPin = crate::gpio::GpioPin<20>; + + /// Default RX pin for UART0 on ESP32-C2. + /// Corresponds to GPIO19. + pub type DefaultRxPin = crate::gpio::GpioPin<19>; } else if #[cfg(esp32c3)] { - pub type DefaultTxPin = crate::gpio::Gpio21; - pub type DefaultRxPin = crate::gpio::Gpio20; - }else if #[cfg(esp32c6)] { - pub type DefaultTxPin = crate::gpio::Gpio16; - pub type DefaultRxPin = crate::gpio::Gpio17; - }else if #[cfg(esp32h2)] { - pub type DefaultTxPin = crate::gpio::Gpio24; - pub type DefaultRxPin = crate::gpio::Gpio23; + /// Default TX pin for UART0 on ESP32-C3. + /// Corresponds to GPIO21. + pub type DefaultTxPin = crate::gpio::GpioPin<21>; + + /// Default RX pin for UART0 on ESP32-C3. + /// Corresponds to GPIO20. + pub type DefaultRxPin = crate::gpio::GpioPin<20>; + } else if #[cfg(esp32c6)] { + /// Default TX pin for UART0 on ESP32-C6. + /// Corresponds to GPIO16. + pub type DefaultTxPin = crate::gpio::GpioPin<16>; + + /// Default RX pin for UART0 on ESP32-C6. + /// Corresponds to GPIO17. + pub type DefaultRxPin = crate::gpio::GpioPin<17>; + } else if #[cfg(esp32h2)] { + /// Default TX pin for UART0 on ESP32-H2. + /// Corresponds to GPIO24. + pub type DefaultTxPin = crate::gpio::GpioPin<24>; + + /// Default RX pin for UART0 on ESP32-H2. + /// Corresponds to GPIO23. + pub type DefaultRxPin = crate::gpio::GpioPin<23>; } else if #[cfg(esp32s2)] { - pub type DefaultTxPin = crate::gpio::Gpio43; - pub type DefaultRxPin = crate::gpio::Gpio44; + /// Default TX pin for UART0 on ESP32-S2. + /// Corresponds to GPIO43. + pub type DefaultTxPin = crate::gpio::GpioPin<43>; + + /// Default RX pin for UART0 on ESP32-S2. + /// Corresponds to GPIO44. + pub type DefaultRxPin = crate::gpio::GpioPin<44>; } else if #[cfg(esp32s3)] { - pub type DefaultTxPin = crate::gpio::Gpio43; - pub type DefaultRxPin = crate::gpio::Gpio44; + /// Default TX pin for UART0 on ESP32-S3. + /// Corresponds to GPIO43. + pub type DefaultTxPin = crate::gpio::GpioPin<43>; + + /// Default RX pin for UART0 on ESP32-S3. + /// Corresponds to GPIO44. + pub type DefaultRxPin = crate::gpio::GpioPin<44>; } } @@ -175,27 +215,42 @@ cfg_if::cfg_if! { #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { - /// An invalid configuration argument was provided + /// An invalid configuration argument was provided. + /// + /// This error occurs when an incorrect or invalid argument is passed during + /// the configuration of the UART peripheral. InvalidArgument, - /// The RX FIFO overflowed - #[cfg(feature = "async")] + + /// The RX FIFO overflowed. RxFifoOvf, - #[cfg(feature = "async")] + + /// A glitch was detected on the RX line. + /// + /// This error occurs when an unexpected or erroneous signal (glitch) is + /// detected on the UART RX line, which could lead to incorrect data + /// reception. RxGlitchDetected, - #[cfg(feature = "async")] + + /// A framing error was detected on the RX line. + /// + /// This error occurs when the received data does not conform to the + /// expected UART frame format. RxFrameError, - #[cfg(feature = "async")] + + /// A parity error was detected on the RX line. + /// + /// This error occurs when the parity bit in the received data does not + /// match the expected parity configuration. + /// with the `async` feature. RxParityError, } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::Error for Error { fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { embedded_hal_nb::serial::ErrorKind::Other } } -#[cfg(feature = "embedded-io")] impl embedded_io::Error for Error { fn kind(&self) -> embedded_io::ErrorKind { embedded_io::ErrorKind::Other @@ -231,33 +286,55 @@ pub mod config { const UART_TOUT_THRESH_DEFAULT: u8 = 10; /// Number of data bits + /// + /// This enum represents the various configurations for the number of data + /// bits used in UART communication. The number of data bits defines the + /// length of each transmitted or received data frame. #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DataBits { + /// 5 data bits per frame. DataBits5 = 0, + /// 6 data bits per frame. DataBits6 = 1, + /// 7 data bits per frame. DataBits7 = 2, + /// 8 data bits per frame (most common). DataBits8 = 3, } /// Parity check + /// + /// Parity is a form of error detection in UART communication, used to + /// ensure that the data has not been corrupted during transmission. The + /// parity bit is added to the data bits to make the number of 1-bits + /// either even or odd. #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Parity { + /// No parity bit is used (most common). ParityNone, + /// Even parity: the parity bit is set to make the total number of + /// 1-bits even. ParityEven, + /// Odd parity: the parity bit is set to make the total number of 1-bits + /// odd. ParityOdd, } /// Number of stop bits + /// + /// The stop bit(s) signal the end of a data packet in UART communication. + /// This enum defines the possible configurations for the number of stop + /// bits. #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum StopBits { - /// 1 stop bit + /// 1 stop bit. STOP1 = 1, - /// 1.5 stop bits + /// 1.5 stop bits. STOP1P5 = 2, - /// 2 stop bits + /// 2 stop bits. STOP2 = 3, } @@ -265,51 +342,68 @@ pub mod config { #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { + /// The baud rate (speed) of the UART communication in bits per second + /// (bps). pub baudrate: u32, + /// Number of data bits in each frame (5, 6, 7, or 8 bits). pub data_bits: DataBits, + /// Parity setting (None, Even, or Odd). pub parity: Parity, + /// Number of stop bits in each frame (1, 1.5, or 2 bits). pub stop_bits: StopBits, + /// Clock source used by the UART peripheral. pub clock_source: super::ClockSource, + /// Threshold level at which the RX FIFO is considered full. pub rx_fifo_full_threshold: u16, + /// Optional timeout value for RX operations. pub rx_timeout: Option, } impl Config { + /// Sets the baud rate for the UART configuration. pub fn baudrate(mut self, baudrate: u32) -> Self { self.baudrate = baudrate; self } + /// Configures the UART to use no parity check. pub fn parity_none(mut self) -> Self { self.parity = Parity::ParityNone; self } + /// Configures the UART to use even parity check. pub fn parity_even(mut self) -> Self { self.parity = Parity::ParityEven; self } + /// Configures the UART to use odd parity check. pub fn parity_odd(mut self) -> Self { self.parity = Parity::ParityOdd; self } + /// Sets the number of data bits for the UART configuration. pub fn data_bits(mut self, data_bits: DataBits) -> Self { self.data_bits = data_bits; self } + /// Sets the number of stop bits for the UART configuration. pub fn stop_bits(mut self, stop_bits: StopBits) -> Self { self.stop_bits = stop_bits; self } + /// Sets the clock source for the UART configuration. pub fn clock_source(mut self, source: super::ClockSource) -> Self { self.clock_source = source; self } + /// Calculates the total symbol length in bits based on the configured + /// data bits, parity, and stop bits. pub fn symbol_length(&self) -> u8 { let mut length: u8 = 1; // start bit length += match self.data_bits { @@ -329,11 +423,13 @@ pub mod config { length } + /// Sets the RX FIFO full threshold for the UART configuration. pub fn rx_fifo_full_threshold(mut self, threshold: u16) -> Self { self.rx_fifo_full_threshold = threshold; self } + /// Sets the RX timeout for the UART configuration. pub fn rx_timeout(mut self, timeout: Option) -> Self { self.rx_timeout = timeout; self @@ -359,14 +455,27 @@ pub mod config { /// Configuration for the AT-CMD detection functionality pub struct AtCmdConfig { + /// Optional idle time before the AT command detection begins, in clock + /// cycles. pub pre_idle_count: Option, + /// Optional idle time after the AT command detection ends, in clock + /// cycles. pub post_idle_count: Option, + /// Optional timeout between characters in the AT command, in clock + /// cycles. pub gap_timeout: Option, + /// The character that triggers the AT command detection. pub cmd_char: u8, + /// Optional number of characters to detect as part of the AT command. pub char_num: Option, } impl AtCmdConfig { + /// Creates a new `AtCmdConfig` with the specified configuration. + /// + /// This function sets up the AT command detection parameters, including + /// pre- and post-idle times, a gap timeout, the triggering command + /// character, and the number of characters to detect. pub fn new( pre_idle_count: Option, post_idle_count: Option, @@ -387,8 +496,8 @@ pub mod config { /// UART (Full-duplex) pub struct Uart<'d, T, M> { - tx: UartTx<'d, T, M>, rx: UartRx<'d, T, M>, + tx: UartTx<'d, T, M>, } /// UART (Transmit) @@ -464,10 +573,9 @@ where /// Create a new UART TX instance in [`Blocking`] mode. pub fn new( uart: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, tx: impl Peripheral

+ 'd, ) -> Result { - Self::new_with_config(uart, Default::default(), clocks, tx) + Self::new_with_config(uart, Default::default(), tx) } /// Create a new UART TX instance with configuration options in @@ -475,15 +583,13 @@ where pub fn new_with_config( uart: impl Peripheral

+ 'd, config: Config, - clocks: &Clocks<'d>, tx: impl Peripheral

+ 'd, ) -> Result { crate::into_ref!(tx); tx.set_to_push_pull_output(Internal); tx.connect_peripheral_to_output(T::tx_signal(), Internal); - let (uart_tx, _) = - Uart::<'d, T, Blocking>::new_with_config_inner(uart, config, clocks)?.split(); + let (_, uart_tx) = Uart::<'d, T, Blocking>::new_with_config_inner(uart, config)?.split(); Ok(uart_tx) } @@ -507,7 +613,7 @@ where /// Configure CTS pin pub fn with_cts(self, cts: impl Peripheral

+ 'd) -> Self { crate::into_ref!(cts); - cts.set_to_input(Internal); + cts.init_input(false, false, Internal); cts.connect_input_to_peripheral(T::cts_signal(), Internal); self @@ -545,7 +651,7 @@ where if T::get_rx_fifo_count() > 0 { let value = unsafe { let fifo = (T::register_block().fifo().as_ptr() as *mut u8).offset(offset) - as *mut crate::peripherals::generic::Reg; + as *mut crate::peripherals::uart0::FIFO; (*fifo).read().rxfifo_rd_byte().bits() }; @@ -565,7 +671,7 @@ where while T::get_rx_fifo_count() > 0 && count < buf.len() { let value = unsafe { let fifo = (T::register_block().fifo().as_ptr() as *mut u8).offset(offset) - as *mut crate::peripherals::generic::Reg; + as *mut crate::peripherals::uart0::FIFO; (*fifo).read().rxfifo_rd_byte().bits() }; buf[count] = value; @@ -672,10 +778,9 @@ where /// Create a new UART RX instance in [`Blocking`] mode. pub fn new( uart: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, rx: impl Peripheral

+ 'd, ) -> Result { - Self::new_with_config(uart, Default::default(), clocks, rx) + Self::new_with_config(uart, Default::default(), rx) } /// Create a new UART RX instance with configuration options in @@ -683,15 +788,13 @@ where pub fn new_with_config( uart: impl Peripheral

+ 'd, config: Config, - clocks: &Clocks<'d>, rx: impl Peripheral

+ 'd, ) -> Result { crate::into_ref!(rx); - rx.set_to_input(Internal); + rx.init_input(false, false, Internal); rx.connect_input_to_peripheral(T::rx_signal(), Internal); - let (_, uart_rx) = - Uart::<'d, T, Blocking>::new_with_config_inner(uart, config, clocks)?.split(); + let (uart_rx, _) = Uart::<'d, T, Blocking>::new_with_config_inner(uart, config)?.split(); Ok(uart_rx) } @@ -706,51 +809,48 @@ where pub fn new_with_config( uart: impl Peripheral

+ 'd, config: Config, - clocks: &Clocks<'d>, - tx: impl Peripheral

+ 'd, rx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { crate::into_ref!(tx); crate::into_ref!(rx); tx.set_to_push_pull_output(Internal); tx.connect_peripheral_to_output(T::tx_signal(), Internal); - rx.set_to_input(Internal); + rx.init_input(false, false, Internal); rx.connect_input_to_peripheral(T::rx_signal(), Internal); - Self::new_with_config_inner(uart, config, clocks) + Self::new_with_config_inner(uart, config) } /// Create a new UART instance with defaults in [`Blocking`] mode. pub fn new( uart: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, - tx: impl Peripheral

+ 'd, rx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { crate::into_ref!(tx); crate::into_ref!(rx); tx.set_to_push_pull_output(Internal); tx.connect_peripheral_to_output(T::tx_signal(), Internal); - rx.set_to_input(Internal); + rx.init_input(false, false, Internal); rx.connect_input_to_peripheral(T::rx_signal(), Internal); - Self::new_inner(uart, clocks) + Self::new_inner(uart) } /// Create a new UART instance with defaults in [`Blocking`] mode. /// Verify that the default pins (DefaultTxPin and DefaultRxPin) are used. pub fn new_with_default_pins( uart: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, - tx: &mut DefaultTxPin, rx: &mut DefaultRxPin, + tx: &mut DefaultTxPin, ) -> Result { tx.set_to_push_pull_output(Internal); tx.connect_peripheral_to_output(T::tx_signal(), Internal); - rx.set_to_input(Internal); + rx.init_input(false, false, Internal); rx.connect_input_to_peripheral(T::rx_signal(), Internal); - Self::new_inner(uart, clocks) + Self::new_inner(uart) } } @@ -762,23 +862,22 @@ where pub(crate) fn new_with_config_inner( _uart: impl Peripheral

+ 'd, config: Config, - clocks: &Clocks<'d>, ) -> Result { Self::init(); let mut serial = Uart { - tx: UartTx::new_inner(), rx: UartRx::new_inner( #[cfg(not(esp32))] config.symbol_length(), ), + tx: UartTx::new_inner(), }; serial .rx .set_rx_fifo_full_threshold(config.rx_fifo_full_threshold)?; serial.rx.set_rx_timeout(config.rx_timeout)?; - serial.change_baud_internal(config.baudrate, config.clock_source, clocks); + serial.change_baud_internal(config.baudrate, config.clock_source); serial.change_data_bits(config.data_bits); serial.change_parity(config.parity); serial.change_stop_bits(config.stop_bits); @@ -810,14 +909,14 @@ where } } - fn new_inner(uart: impl Peripheral

+ 'd, clocks: &Clocks<'d>) -> Result { - Self::new_with_config_inner(uart, Default::default(), clocks) + fn new_inner(uart: impl Peripheral

+ 'd) -> Result { + Self::new_with_config_inner(uart, Default::default()) } /// Configure CTS pin pub fn with_cts(self, cts: impl Peripheral

+ 'd) -> Self { crate::into_ref!(cts); - cts.set_to_input(Internal); + cts.init_input(false, false, Internal); cts.connect_input_to_peripheral(T::cts_signal(), Internal); self @@ -836,8 +935,8 @@ where /// /// This is particularly useful when having two tasks correlating to /// transmitting and receiving. - pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) { - (self.tx, self.rx) + pub fn split(self) -> (UartRx<'d, T, M>, UartTx<'d, T, M>) { + (self.rx, self.tx) } /// Write bytes out over the UART @@ -1042,7 +1141,8 @@ where } #[cfg(any(esp32c2, esp32c3, esp32s3))] - fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks<'d>) { + fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource) { + let clocks = Clocks::get(); let clk = match clock_source { ClockSource::Apb => clocks.apb_clock.to_Hz(), ClockSource::Xtal => clocks.xtal_clock.to_Hz(), @@ -1079,7 +1179,8 @@ where } #[cfg(any(esp32c6, esp32h2))] - fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks<'d>) { + fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource) { + let clocks = Clocks::get(); let clk = match clock_source { ClockSource::Apb => clocks.apb_clock.to_Hz(), ClockSource::Xtal => clocks.xtal_clock.to_Hz(), @@ -1150,7 +1251,8 @@ where } #[cfg(any(esp32, esp32s2))] - fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks<'d>) { + fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource) { + let clocks = Clocks::get(); let clk = match clock_source { ClockSource::Apb => clocks.apb_clock.to_Hz(), ClockSource::RefTick => REF_TICK.to_Hz(), /* ESP32(/-S2) TRM, section 3.2.4.2 @@ -1189,8 +1291,8 @@ where } /// Modify UART baud rate and reset TX/RX fifo. - pub fn change_baud(&mut self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks<'d>) { - self.change_baud_internal(baudrate, clock_source, clocks); + pub fn change_baud(&mut self, baudrate: u32, clock_source: ClockSource) { + self.change_baud_internal(baudrate, clock_source); self.txfifo_reset(); self.rxfifo_reset(); } @@ -1304,10 +1406,27 @@ where /// UART Peripheral Instance pub trait Instance: crate::private::Sealed { + /// Returns a reference to the UART register block for the specific + /// instance. + /// + /// # Safety + /// This function returns a reference to the raw hardware registers, so + /// direct interaction with the registers may require careful handling + /// to avoid unintended side effects. fn register_block() -> &'static RegisterBlock; + + /// Returns the UART number associated with this instance (e.g., UART0, + /// UART1, etc.). fn uart_number() -> usize; + + /// Returns the interrupt associated with this UART instance. fn interrupt() -> Interrupt; + /// Disables all TX-related interrupts for this UART instance. + /// + /// This function clears and disables the `transmit FIFO empty` interrupt, + /// `transmit break done`, `transmit break idle done`, and `transmit done` + /// interrupts. fn disable_tx_interrupts() { Self::register_block().int_clr().write(|w| { w.txfifo_empty() @@ -1332,6 +1451,11 @@ pub trait Instance: crate::private::Sealed { }); } + /// Disables all RX-related interrupts for this UART instance. + /// + /// This function clears and disables the `receive FIFO full` interrupt, + /// `receive FIFO overflow`, `receive FIFO timeout`, and `AT command + /// character detection` interrupts. fn disable_rx_interrupts() { Self::register_block().int_clr().write(|w| { w.rxfifo_full() @@ -1357,6 +1481,8 @@ pub trait Instance: crate::private::Sealed { } #[allow(clippy::useless_conversion)] + /// Returns the number of bytes currently in the TX FIFO for this UART + /// instance. fn get_tx_fifo_count() -> u16 { Self::register_block() .status() @@ -1366,6 +1492,8 @@ pub trait Instance: crate::private::Sealed { .into() } + /// Returns the number of bytes currently in the RX FIFO for this UART + /// instance. #[allow(clippy::useless_conversion)] fn get_rx_fifo_count() -> u16 { let fifo_cnt: u16 = Self::register_block() @@ -1406,6 +1534,10 @@ pub trait Instance: crate::private::Sealed { fifo_cnt } + /// Checks if the TX line is idle for this UART instance. + /// + /// Returns `true` if the transmit line is idle, meaning no data is + /// currently being transmitted. fn is_tx_idle() -> bool { #[cfg(esp32)] let idle = Self::register_block().status().read().st_utx_out().bits() == 0x0u8; @@ -1420,6 +1552,10 @@ pub trait Instance: crate::private::Sealed { idle } + /// Checks if the RX line is idle for this UART instance. + /// + /// Returns `true` if the receive line is idle, meaning no data is currently + /// being received. fn is_rx_idle() -> bool { #[cfg(esp32)] let idle = Self::register_block().status().read().st_urx_out().bits() == 0x0u8; @@ -1434,11 +1570,26 @@ pub trait Instance: crate::private::Sealed { idle } + /// Returns the output signal identifier for the TX pin of this UART + /// instance. fn tx_signal() -> OutputSignal; + + /// Returns the input signal identifier for the RX pin of this UART + /// instance. fn rx_signal() -> InputSignal; + + /// Returns the input signal identifier for the CTS (Clear to Send) pin of + /// this UART instance. fn cts_signal() -> InputSignal; + + /// Returns the output signal identifier for the RTS (Request to Send) pin + /// of this UART instance. fn rts_signal() -> OutputSignal; + + /// Enables the clock for this UART peripheral instance. fn enable_peripheral(); + + /// Resets the UART peripheral instance. fn reset_peripheral(); } @@ -1492,7 +1643,6 @@ impl_instance!(UART1, 1, U1TXD, U1RXD, U1CTS, U1RTS, Uart1); #[cfg(uart2)] impl_instance!(UART2, 2, U2TXD, U2RXD, U2CTS, U2RTS, Uart2); -#[cfg(feature = "ufmt")] impl ufmt_write::uWrite for Uart<'_, T, M> where T: Instance, @@ -1511,7 +1661,6 @@ where } } -#[cfg(feature = "ufmt")] impl ufmt_write::uWrite for UartTx<'_, T, M> where T: Instance, @@ -1550,7 +1699,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::serial::Write for Uart<'_, T, M> where T: Instance, @@ -1567,7 +1715,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::serial::Write for UartTx<'_, T, M> where T: Instance, @@ -1584,7 +1731,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::serial::Read for Uart<'_, T, M> where T: Instance, @@ -1597,7 +1743,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::serial::Read for UartRx<'_, T, M> where T: Instance, @@ -1610,22 +1755,18 @@ where } } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::ErrorType for Uart<'_, T, M> { type Error = Error; } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::ErrorType for UartTx<'_, T, M> { type Error = Error; } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::ErrorType for UartRx<'_, T, M> { type Error = Error; } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::Read for Uart<'_, T, M> where T: Instance, @@ -1636,7 +1777,6 @@ where } } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::Read for UartRx<'_, T, M> where T: Instance, @@ -1647,7 +1787,6 @@ where } } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::Write for Uart<'_, T, M> where T: Instance, @@ -1662,7 +1801,6 @@ where } } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::Write for UartTx<'_, T, M> where T: Instance, @@ -1677,22 +1815,18 @@ where } } -#[cfg(feature = "embedded-io")] impl embedded_io::ErrorType for Uart<'_, T, M> { type Error = Error; } -#[cfg(feature = "embedded-io")] impl embedded_io::ErrorType for UartTx<'_, T, M> { type Error = Error; } -#[cfg(feature = "embedded-io")] impl embedded_io::ErrorType for UartRx<'_, T, M> { type Error = Error; } -#[cfg(feature = "embedded-io")] impl embedded_io::Read for Uart<'_, T, M> where T: Instance, @@ -1703,7 +1837,6 @@ where } } -#[cfg(feature = "embedded-io")] impl embedded_io::Read for UartRx<'_, T, M> where T: Instance, @@ -1722,7 +1855,6 @@ where } } -#[cfg(feature = "embedded-io")] impl embedded_io::ReadReady for Uart<'_, T, M> where T: Instance, @@ -1733,7 +1865,6 @@ where } } -#[cfg(feature = "embedded-io")] impl embedded_io::ReadReady for UartRx<'_, T, M> where T: Instance, @@ -1744,7 +1875,6 @@ where } } -#[cfg(feature = "embedded-io")] impl embedded_io::Write for Uart<'_, T, M> where T: Instance, @@ -1759,7 +1889,6 @@ where } } -#[cfg(feature = "embedded-io")] impl embedded_io::Write for UartTx<'_, T, M> where T: Instance, @@ -1782,7 +1911,6 @@ where } } -#[cfg(feature = "async")] mod asynch { use core::task::Poll; @@ -1830,11 +1958,13 @@ mod asynch { /// Upon construction the future enables the passed interrupt and when it /// is dropped it disables the interrupt again. The future returns the event /// that was initially passed, when it resolves. + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct UartRxFuture<'d, T: Instance> { events: EnumSet, phantom: PhantomData<&'d mut T>, registered: bool, } + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct UartTxFuture<'d, T: Instance> { events: EnumSet, phantom: PhantomData<&'d mut T>, @@ -2000,21 +2130,20 @@ mod asynch { { /// Create a new UART instance with configuration options in [`Async`] /// mode. - pub fn new_async_with_config( + pub fn new_async_with_config( uart: impl Peripheral

+ 'd, config: Config, - clocks: &Clocks<'d>, - tx: impl Peripheral

+ 'd, rx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { - crate::into_ref!(tx); crate::into_ref!(rx); + crate::into_ref!(tx); tx.set_to_push_pull_output(Internal); tx.connect_peripheral_to_output(T::tx_signal(), Internal); - rx.set_to_input(Internal); + rx.init_input(false, false, Internal); rx.connect_input_to_peripheral(T::rx_signal(), Internal); - let mut this = Self::new_with_config_inner(uart, config, clocks)?; + let mut this = Self::new_with_config_inner(uart, config)?; this.inner_set_interrupt_handler(match T::uart_number() { #[cfg(uart0)] @@ -2030,23 +2159,21 @@ mod asynch { } /// Create a new UART instance with defaults in [`Async`] mode. - pub fn new_async( + pub fn new_async( uart: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, - tx: impl Peripheral

+ 'd, rx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { - Self::new_async_with_config(uart, Default::default(), clocks, tx, rx) + Self::new_async_with_config(uart, Default::default(), rx, tx) } /// Create a new UART instance with defaults in [`Async`] mode. pub fn new_async_with_default_pins( uart: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, - tx: DefaultTxPin, rx: DefaultRxPin, + tx: DefaultTxPin, ) -> Result { - Self::new_async_with_config(uart, Default::default(), clocks, tx, rx) + Self::new_async_with_config(uart, Default::default(), rx, tx) } } @@ -2054,15 +2181,18 @@ mod asynch { where T: Instance, { - /// See [`UartRx::read_async`] + /// Asynchronously reads data from the UART receive buffer into the + /// provided buffer. pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { self.rx.read_async(buf).await } + /// Asynchronously writes data to the UART transmit buffer. pub async fn write_async(&mut self, words: &[u8]) -> Result { self.tx.write_async(words).await } + /// Asynchronously flushes the UART transmit buffer. pub async fn flush_async(&mut self) -> Result<(), Error> { self.tx.flush_async().await } @@ -2075,10 +2205,9 @@ mod asynch { /// Create a new UART TX instance in [`Async`] mode. pub fn new_async( uart: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, tx: impl Peripheral

+ 'd, ) -> Result { - Self::new_async_with_config(uart, Default::default(), clocks, tx) + Self::new_async_with_config(uart, Default::default(), tx) } /// Create a new UART TX instance with configuration options in @@ -2086,14 +2215,13 @@ mod asynch { pub fn new_async_with_config( uart: impl Peripheral

+ 'd, config: Config, - clocks: &Clocks<'d>, tx: impl Peripheral

+ 'd, ) -> Result { crate::into_ref!(tx); tx.set_to_push_pull_output(Internal); tx.connect_peripheral_to_output(T::tx_signal(), Internal); - let mut uart = Uart::<'d, T, Async>::new_with_config_inner(uart, config, clocks)?; + let mut uart = Uart::<'d, T, Async>::new_with_config_inner(uart, config)?; uart.inner_set_interrupt_handler(match T::uart_number() { #[cfg(uart0)] @@ -2105,10 +2233,16 @@ mod asynch { _ => unreachable!(), }); - let (uart_tx, _) = uart.split(); + let (_, uart_tx) = uart.split(); Ok(uart_tx) } + /// Asynchronously writes data to the UART transmit buffer in chunks. + /// + /// This function sends the contents of the provided buffer `words` over + /// the UART. Data is written in chunks to avoid overflowing the + /// transmit FIFO, and the function waits asynchronously when + /// necessary for space in the buffer to become available. pub async fn write_async(&mut self, words: &[u8]) -> Result { let mut count = 0; let mut offset: usize = 0; @@ -2134,6 +2268,11 @@ mod asynch { Ok(count) } + /// Asynchronously flushes the UART transmit buffer. + /// + /// This function ensures that all pending data in the transmit FIFO has + /// been sent over the UART. If the FIFO contains data, it waits + /// for the transmission to complete before returning. pub async fn flush_async(&mut self) -> Result<(), Error> { let count = T::get_tx_fifo_count(); if count > 0 { @@ -2151,10 +2290,9 @@ mod asynch { /// Create a new UART RX instance in [`Async`] mode. pub fn new_async( uart: impl Peripheral

+ 'd, - clocks: &Clocks<'d>, rx: impl Peripheral

+ 'd, ) -> Result { - Self::new_async_with_config(uart, Default::default(), clocks, rx) + Self::new_async_with_config(uart, Default::default(), rx) } /// Create a new UART RX instance with configuration options in @@ -2162,14 +2300,13 @@ mod asynch { pub fn new_async_with_config( uart: impl Peripheral

+ 'd, config: Config, - clocks: &Clocks<'d>, rx: impl Peripheral

+ 'd, ) -> Result { crate::into_ref!(rx); - rx.set_to_input(Internal); + rx.init_input(false, false, Internal); rx.connect_input_to_peripheral(T::rx_signal(), Internal); - let mut uart = Uart::<'d, T, Async>::new_with_config_inner(uart, config, clocks)?; + let mut uart = Uart::<'d, T, Async>::new_with_config_inner(uart, config)?; uart.inner_set_interrupt_handler(match T::uart_number() { #[cfg(uart0)] @@ -2181,7 +2318,7 @@ mod asynch { _ => unreachable!(), }); - let (_, uart_rx) = uart.split(); + let (uart_rx, _) = uart.split(); Ok(uart_rx) } diff --git a/esp-hal/src/usb_serial_jtag.rs b/esp-hal/src/usb_serial_jtag.rs index 70d6d2f06a3..e2ad90e450b 100644 --- a/esp-hal/src/usb_serial_jtag.rs +++ b/esp-hal/src/usb_serial_jtag.rs @@ -1,6 +1,7 @@ //! USB Serial/JTAG Controller (USB_SERIAL_JTAG) //! //! ## Overview +//! //! The USB Serial/JTAG controller can be used to program the SoC's flash, read //! program output, or attach a debugger to the running program. This is //! possible for any computer with a USB host (hereafter referred to as 'host'), @@ -27,6 +28,7 @@ //! connect to a host computer //! //! ## Usage +//! //! The USB Serial/JTAG driver implements a number of third-party traits, with //! the intention of making the HAL inter-compatible with various device drivers //! from the community. This includes, but is not limited to, the [embedded-hal] @@ -38,25 +40,28 @@ //! with this driver. //! //! ## Examples +//! //! ### Sending and Receiving Data //! ```rust, no_run #![doc = crate::before_snippet!()] //! use esp_hal::usb_serial_jtag::UsbSerialJtag; +//! //! let mut usb_serial = UsbSerialJtag::new(peripherals.USB_DEVICE); //! //! // Write bytes out over the USB Serial/JTAG: -//! usb_serial.write_bytes("Hello, world!".as_bytes()).expect("write error!"); -//! } +//! usb_serial.write_bytes(b"Hello, world!").expect("write error!"); +//! # } //! ``` //! //! ### Splitting the USB Serial/JTAG into TX and RX Components //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::usb_serial_jtag::UsbSerialJtag; +//! use esp_hal::usb_serial_jtag::UsbSerialJtag; +//! //! let mut usb_serial = UsbSerialJtag::new(peripherals.USB_DEVICE); //! // The USB Serial/JTAG can be split into separate Transmit and Receive //! // components: -//! let (mut tx, mut rx) = usb_serial.split(); +//! let (mut rx, mut tx) = usb_serial.split(); //! //! // Each component can be used individually to interact with the USB //! // Serial/JTAG: @@ -87,8 +92,8 @@ type Error = Infallible; /// USB Serial/JTAG (Full-duplex) pub struct UsbSerialJtag<'d, M> { - tx: UsbSerialJtagTx<'d, M>, rx: UsbSerialJtagRx<'d, M>, + tx: UsbSerialJtagTx<'d, M>, } /// USB Serial/JTAG (Transmit) @@ -273,6 +278,8 @@ where M: Mode, { fn new_inner(_usb_device: impl Peripheral

+ 'd) -> Self { + // Do NOT reset the peripheral. Doing so will result in a broken USB JTAG + // connection. PeripheralClockControl::enable(crate::system::Peripheral::UsbDevice); USB_DEVICE::disable_tx_interrupts(); @@ -297,8 +304,8 @@ where } Self { - tx: UsbSerialJtagTx::new_inner(), rx: UsbSerialJtagRx::new_inner(), + tx: UsbSerialJtagTx::new_inner(), } } @@ -310,10 +317,10 @@ where } /// Split the USB Serial JTAG peripheral into a transmitter and receiver, - /// which is particuarly useful when having two tasks correlating to + /// which is particularly useful when having two tasks correlating to /// transmitting and receiving. - pub fn split(self) -> (UsbSerialJtagTx<'d, M>, UsbSerialJtagRx<'d, M>) { - (self.tx, self.rx) + pub fn split(self) -> (UsbSerialJtagRx<'d, M>, UsbSerialJtagTx<'d, M>) { + (self.rx, self.tx) } /// Write data to the serial output in chunks of up to 64 bytes @@ -337,6 +344,7 @@ where self.tx.flush_tx_nb() } + /// Read a single byte but don't block if it isn't ready immediately pub fn read_byte(&mut self) -> nb::Result { self.rx.read_byte() } @@ -417,7 +425,6 @@ where } } -#[cfg(feature = "ufmt")] impl ufmt_write::uWrite for UsbSerialJtag<'_, M> where M: Mode, @@ -435,7 +442,6 @@ where } } -#[cfg(feature = "ufmt")] impl ufmt_write::uWrite for UsbSerialJtagTx<'_, M> where M: Mode, @@ -457,7 +463,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::serial::Read for UsbSerialJtag<'_, M> where M: Mode, @@ -469,7 +474,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::serial::Read for UsbSerialJtagRx<'_, M> where M: Mode, @@ -481,7 +485,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::serial::Write for UsbSerialJtag<'_, M> where M: Mode, @@ -497,7 +500,6 @@ where } } -#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::serial::Write for UsbSerialJtagTx<'_, M> where M: Mode, @@ -513,7 +515,6 @@ where } } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::ErrorType for UsbSerialJtag<'_, M> where M: Mode, @@ -521,7 +522,6 @@ where type Error = Error; } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::ErrorType for UsbSerialJtagTx<'_, M> where M: Mode, @@ -529,7 +529,6 @@ where type Error = Error; } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::ErrorType for UsbSerialJtagRx<'_, M> where M: Mode, @@ -537,7 +536,6 @@ where type Error = Error; } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::Read for UsbSerialJtag<'_, M> where M: Mode, @@ -547,7 +545,6 @@ where } } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::Read for UsbSerialJtagRx<'_, M> where M: Mode, @@ -557,7 +554,6 @@ where } } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::Write for UsbSerialJtag<'_, M> where M: Mode, @@ -571,7 +567,6 @@ where } } -#[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::Write for UsbSerialJtagTx<'_, M> where M: Mode, @@ -585,7 +580,6 @@ where } } -#[cfg(feature = "embedded-io")] impl embedded_io::ErrorType for UsbSerialJtag<'_, M> where M: Mode, @@ -593,7 +587,6 @@ where type Error = Error; } -#[cfg(feature = "embedded-io")] impl embedded_io::ErrorType for UsbSerialJtagTx<'_, M> where M: Mode, @@ -601,7 +594,6 @@ where type Error = Error; } -#[cfg(feature = "embedded-io")] impl embedded_io::ErrorType for UsbSerialJtagRx<'_, M> where M: Mode, @@ -609,7 +601,6 @@ where type Error = Error; } -#[cfg(feature = "embedded-io")] impl embedded_io::Read for UsbSerialJtag<'_, M> where M: Mode, @@ -619,7 +610,6 @@ where } } -#[cfg(feature = "embedded-io")] impl embedded_io::Read for UsbSerialJtagRx<'_, M> where M: Mode, @@ -634,7 +624,6 @@ where } } -#[cfg(feature = "embedded-io")] impl embedded_io::Write for UsbSerialJtag<'_, M> where M: Mode, @@ -648,7 +637,6 @@ where } } -#[cfg(feature = "embedded-io")] impl embedded_io::Write for UsbSerialJtagTx<'_, M> where M: Mode, @@ -664,7 +652,6 @@ where } } -#[cfg(feature = "async")] mod asynch { use core::{marker::PhantomData, task::Poll}; @@ -678,6 +665,7 @@ mod asynch { static WAKER_TX: AtomicWaker = AtomicWaker::new(); static WAKER_RX: AtomicWaker = AtomicWaker::new(); + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct UsbSerialJtagWriteFuture<'d> { phantom: PhantomData<&'d mut USB_DEVICE>, } @@ -720,6 +708,7 @@ mod asynch { } } + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct UsbSerialJtagReadFuture<'d> { phantom: PhantomData<&'d mut USB_DEVICE>, } diff --git a/esp-ieee802154/CHANGELOG.md b/esp-ieee802154/CHANGELOG.md index 48aa0fa47e5..084211a077e 100644 --- a/esp-ieee802154/CHANGELOG.md +++ b/esp-ieee802154/CHANGELOG.md @@ -9,15 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Added additional checks to prevent various array access panics while processing frames -- Added range check to avoid panic when indexing into RX_BUFFER slice - ### Changed ### Fixed ### Removed +## 0.2.0 - 2024-08-29 + +### Added + +- Added additional checks to prevent various array access panics while processing frames (#1923) +- Added range check to avoid panic when indexing into RX_BUFFER slice (#1682) + ## 0.1.0 - 2024-07-15 ### Added diff --git a/esp-ieee802154/Cargo.toml b/esp-ieee802154/Cargo.toml index 8e5b8226dd9..23ac3c74c71 100644 --- a/esp-ieee802154/Cargo.toml +++ b/esp-ieee802154/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "esp-ieee802154" -version = "0.1.0" +version = "0.2.0" edition = "2021" rust-version = "1.76.0" description = "Low-level IEEE 802.15.4 driver for the ESP32-C6 and ESP32-H2" @@ -17,10 +17,10 @@ test = false [dependencies] byte = "0.2.7" -critical-section = "1.1.2" +critical-section = "1.1.3" document-features = "0.2.10" -esp-hal = { version = "0.19.0", path = "../esp-hal" } -esp-wifi-sys = "0.4.0" +esp-hal = { version = "0.20.0", path = "../esp-hal" } +esp-wifi-sys = "0.5.0" heapless = "0.8.0" ieee802154 = "0.6.1" log = "0.4.22" diff --git a/esp-ieee802154/src/compat/mod.rs b/esp-ieee802154/src/compat/mod.rs index 8e16abca2e3..3b6238e1845 100644 --- a/esp-ieee802154/src/compat/mod.rs +++ b/esp-ieee802154/src/compat/mod.rs @@ -1,13 +1,10 @@ -#[cfg(feature = "binary-logs")] -mod str_buf; - #[cfg(feature = "binary-logs")] mod binary_logs { - use core::{ffi::VaListImpl, fmt::Write}; - use log::info; - use super::str_buf::StrBuf; + extern "C" { + fn vsnprintf(dst: *mut u8, _n: u32, format: *const u8, ...) -> i32; + } #[no_mangle] pub unsafe extern "C" fn phy_printf(_format: *const u8, _args: ...) { @@ -29,109 +26,10 @@ mod binary_logs { pub unsafe extern "C" fn syslog(format: *const u8, args: VaListImpl) { let mut buf = [0u8; 512]; vsnprintf(&mut buf as *mut u8, 511, format, args); - let res_str = StrBuf::from(&buf as *const u8); - info!("{}", res_str.as_str_ref()); - } - - pub(crate) unsafe fn vsnprintf( - dst: *mut u8, - _n: u32, - format: *const u8, - mut args: VaListImpl, - ) -> i32 { - let fmt_str_ptr = format; - - let mut res_str = StrBuf::new(); - - let strbuf = StrBuf::from(fmt_str_ptr); - let s = strbuf.as_str_ref(); - - let mut format_char = ' '; - let mut is_long = false; - let mut found = false; - for c in s.chars() { - if !found { - if c == '%' { - found = true; - } - - if !found { - res_str.append_char(c); - } - } else if c.is_numeric() || c == '-' || c == 'l' { - if c == 'l' { - is_long = true; - } - // ignore - } else { - // a format char - format_char = c; - } - - if found && format_char != ' ' { - // have to format an arg - match format_char { - 'd' => { - if is_long { - let v = args.arg::(); - write!(res_str, "{}", v).ok(); - } else { - let v = args.arg::(); - write!(res_str, "{}", v).ok(); - } - } - - 'u' => { - let v = args.arg::(); - write!(res_str, "{}", v).ok(); - } - - 'p' => { - let v = args.arg::(); - write!(res_str, "0x{:x}", v).ok(); - } - - 'X' => { - let v = args.arg::(); - write!(res_str, "{:02x}", (v & 0xff000000) >> 24).ok(); - } - - 'x' => { - let v = args.arg::(); - write!(res_str, "{:02x}", v).ok(); - } - - 's' => { - let v = args.arg::() as *const u8; - let vbuf = StrBuf::from(v); - write!(res_str, "{}", vbuf.as_str_ref()).ok(); - } - - 'c' => { - let v = args.arg::(); - if v != 0 { - write!(res_str, "{}", v as char).ok(); - } - } - - _ => { - write!(res_str, "", format_char).ok(); - } - } - - format_char = ' '; - found = false; - is_long = false; - } - } - let mut idx = 0; - res_str.as_str_ref().chars().for_each(|c| { - *(dst.offset(idx)) = c as u8; - idx += 1; - }); - *(dst.offset(idx)) = 0; - - idx as i32 + let res_str = core::ffi::CStr::from_ptr(core::ptr::addr_of!(buf).cast()) + .to_str() + .unwrap(); + info!("{}", res_str); } } diff --git a/esp-ieee802154/src/compat/str_buf.rs b/esp-ieee802154/src/compat/str_buf.rs deleted file mode 100644 index 42693c77a0e..00000000000 --- a/esp-ieee802154/src/compat/str_buf.rs +++ /dev/null @@ -1,58 +0,0 @@ -use core::fmt::Write; - -pub struct StrBuf { - buffer: [u8; 512], - len: usize, -} - -impl StrBuf { - pub fn new() -> StrBuf { - StrBuf { - buffer: [0u8; 512], - len: 0, - } - } - - pub unsafe fn from(c_str: *const u8) -> StrBuf { - let mut res = StrBuf { - buffer: [0u8; 512], - len: 0, - }; - - let mut idx: usize = 0; - while *(c_str.add(idx)) != 0 { - res.buffer[idx] = *(c_str.add(idx)); - idx += 1; - } - - res.len = idx; - res - } - - pub fn append(&mut self, s: &str) { - let mut idx: usize = self.len; - s.chars().for_each(|c| { - self.buffer[idx] = c as u8; - idx += 1; - }); - self.len = idx; - } - - pub fn append_char(&mut self, c: char) { - let mut idx: usize = self.len; - self.buffer[idx] = c as u8; - idx += 1; - self.len = idx; - } - - pub unsafe fn as_str_ref(&self) -> &str { - core::str::from_utf8_unchecked(&self.buffer[..self.len]) - } -} - -impl Write for StrBuf { - fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { - self.append(s); - Ok(()) - } -} diff --git a/esp-lp-hal/Cargo.toml b/esp-lp-hal/Cargo.toml index 06ab164bdcc..72db1c35d42 100644 --- a/esp-lp-hal/Cargo.toml +++ b/esp-lp-hal/Cargo.toml @@ -7,10 +7,6 @@ description = "HAL for low-power RISC-V coprocessors found in ESP32 devices" repository = "https://github.com/esp-rs/esp-hal" license = "MIT OR Apache-2.0" -[lib] -bench = false -test = false - keywords = [ "embedded", "embedded-hal", @@ -24,6 +20,10 @@ categories = [ "no-std", ] +[lib] +bench = false +test = false + [dependencies] cfg-if = "1.0.0" document-features = "0.2.10" @@ -36,7 +36,7 @@ esp32s2-ulp = { version = "0.3.0", features = ["critical-section"], option esp32s3-ulp = { version = "0.3.0", features = ["critical-section"], optional = true } nb = { version = "1.1.0", optional = true } paste = { version = "1.0.15", optional = true } -procmacros = { version = "0.12.0", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } +procmacros = { version = "0.13.0", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } riscv = { version = "0.11.1", features = ["critical-section-single-hart"] } [dev-dependencies] diff --git a/esp-lp-hal/src/uart.rs b/esp-lp-hal/src/uart.rs index 9e05c1f120b..8ffd2ba0f06 100644 --- a/esp-lp-hal/src/uart.rs +++ b/esp-lp-hal/src/uart.rs @@ -320,6 +320,7 @@ impl embedded_io::Write for LpUart { match self.flush_tx() { Ok(_) => break, Err(nb::Error::WouldBlock) => { /* Wait */ } + #[allow(unreachable_patterns)] Err(nb::Error::Other(e)) => return Err(e), } } diff --git a/esp-metadata/Cargo.toml b/esp-metadata/Cargo.toml index a04eb6ffed1..3499c7d5d99 100644 --- a/esp-metadata/Cargo.toml +++ b/esp-metadata/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "esp-metadata" -version = "0.2.0" +version = "0.3.0" edition = "2021" -rust-version = "1.60.0" +rust-version = "1.70.0" description = "Metadata for Espressif devices" repository = "https://github.com/esp-rs/esp-hal" license = "MIT OR Apache-2.0" [dependencies] anyhow = "1.0.86" -clap = { version = "4.5.4", features = ["derive"] } +clap = { version = "4.5.16", features = ["derive"], optional = true } basic-toml = "0.1.9" -lazy_static = "1.5.0" -serde = { version = "1.0.204", features = ["derive"] } +serde = { version = "1.0.209", features = ["derive"] } strum = { version = "0.26.3", features = ["derive"] } diff --git a/esp-metadata/README.md b/esp-metadata/README.md index dc2b1ae9d0e..6a54fede611 100644 --- a/esp-metadata/README.md +++ b/esp-metadata/README.md @@ -2,7 +2,7 @@ [![Crates.io](https://img.shields.io/crates/v/esp-metadata?labelColor=1C2C2E&color=C96329&logo=Rust&style=flat-square)](https://crates.io/crates/esp-metadata) [![docs.rs](https://img.shields.io/docsrs/esp-metadata?labelColor=1C2C2E&color=C96329&logo=rust&style=flat-square)](https://docs.rs/esp-metadata) -![MSRV](https://img.shields.io/badge/MSRV-1.60-blue?labelColor=1C2C2E&style=flat-square) +![MSRV](https://img.shields.io/badge/MSRV-1.70-blue?labelColor=1C2C2E&style=flat-square) ![Crates.io](https://img.shields.io/crates/l/esp-metadata?labelColor=1C2C2E&style=flat-square) [![Matrix](https://img.shields.io/matrix/esp-rs:matrix.org?label=join%20matrix&labelColor=1C2C2E&color=BEC5C9&logo=matrix&style=flat-square)](https://matrix.to/#/#esp-rs:matrix.org) @@ -14,7 +14,7 @@ Metadata for Espressif devices, intended for use in [build scripts]. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.60 and up. It _might_ +This crate is guaranteed to compile on stable Rust 1.70 and up. It _might_ compile with older versions but that may change in any new patch release. ## License diff --git a/esp-metadata/src/lib.rs b/esp-metadata/src/lib.rs index 7a5b943278d..0f7ad939533 100644 --- a/esp-metadata/src/lib.rs +++ b/esp-metadata/src/lib.rs @@ -1,23 +1,15 @@ //! Metadata for Espressif devices, primarily intended for use in build scripts. +use std::sync::OnceLock; + use anyhow::{bail, Result}; +use strum::IntoEnumIterator; -const ESP32_TOML: &str = include_str!("../devices/esp32.toml"); -const ESP32C2_TOML: &str = include_str!("../devices/esp32c2.toml"); -const ESP32C3_TOML: &str = include_str!("../devices/esp32c3.toml"); -const ESP32C6_TOML: &str = include_str!("../devices/esp32c6.toml"); -const ESP32H2_TOML: &str = include_str!("../devices/esp32h2.toml"); -const ESP32S2_TOML: &str = include_str!("../devices/esp32s2.toml"); -const ESP32S3_TOML: &str = include_str!("../devices/esp32s3.toml"); - -lazy_static::lazy_static! { - static ref ESP32_CFG: Config = basic_toml::from_str(ESP32_TOML).unwrap(); - static ref ESP32C2_CFG: Config = basic_toml::from_str(ESP32C2_TOML).unwrap(); - static ref ESP32C3_CFG: Config = basic_toml::from_str(ESP32C3_TOML).unwrap(); - static ref ESP32C6_CFG: Config = basic_toml::from_str(ESP32C6_TOML).unwrap(); - static ref ESP32H2_CFG: Config = basic_toml::from_str(ESP32H2_TOML).unwrap(); - static ref ESP32S2_CFG: Config = basic_toml::from_str(ESP32S2_TOML).unwrap(); - static ref ESP32S3_CFG: Config = basic_toml::from_str(ESP32S3_TOML).unwrap(); +macro_rules! include_toml { + ($type:ty, $file:expr) => {{ + static LOADED_TOML: OnceLock<$type> = OnceLock::new(); + LOADED_TOML.get_or_init(|| basic_toml::from_str(include_str!($file)).unwrap()) + }}; } /// Supported device architectures. @@ -34,6 +26,7 @@ lazy_static::lazy_static! { strum::Display, strum::EnumIter, strum::EnumString, + strum::AsRefStr, )] #[serde(rename_all = "lowercase")] #[strum(serialize_all = "lowercase")] @@ -58,6 +51,7 @@ pub enum Arch { strum::Display, strum::EnumIter, strum::EnumString, + strum::AsRefStr, )] pub enum Cores { /// Single CPU core @@ -84,8 +78,9 @@ pub enum Cores { strum::Display, strum::EnumIter, strum::EnumString, - clap::ValueEnum, + strum::AsRefStr, )] +#[cfg_attr(feature = "clap", derive(clap::ValueEnum))] #[serde(rename_all = "kebab-case")] #[strum(serialize_all = "kebab-case")] pub enum Chip { @@ -172,15 +167,15 @@ pub struct Config { impl Config { /// The configuration for the specified chip. - pub fn for_chip(chip: &Chip) -> Self { + pub fn for_chip(chip: &Chip) -> &Self { match chip { - Chip::Esp32 => ESP32_CFG.clone(), - Chip::Esp32c2 => ESP32C2_CFG.clone(), - Chip::Esp32c3 => ESP32C3_CFG.clone(), - Chip::Esp32c6 => ESP32C6_CFG.clone(), - Chip::Esp32h2 => ESP32H2_CFG.clone(), - Chip::Esp32s2 => ESP32S2_CFG.clone(), - Chip::Esp32s3 => ESP32S3_CFG.clone(), + Chip::Esp32 => include_toml!(Config, "../devices/esp32.toml"), + Chip::Esp32c2 => include_toml!(Config, "../devices/esp32c2.toml"), + Chip::Esp32c3 => include_toml!(Config, "../devices/esp32c3.toml"), + Chip::Esp32c6 => include_toml!(Config, "../devices/esp32c6.toml"), + Chip::Esp32h2 => include_toml!(Config, "../devices/esp32h2.toml"), + Chip::Esp32s2 => include_toml!(Config, "../devices/esp32s2.toml"), + Chip::Esp32s3 => include_toml!(Config, "../devices/esp32s3.toml"), } } @@ -210,37 +205,42 @@ impl Config { } /// All configuration values for the device. - pub fn all(&self) -> Vec { + pub fn all(&self) -> impl Iterator + '_ { [ - vec![ - self.device.name.clone(), - self.device.arch.to_string(), - self.device.cores.to_string(), - ], - self.device.peripherals.clone(), - self.device.symbols.clone(), + self.device.name.as_str(), + self.device.arch.as_ref(), + self.device.cores.as_ref(), ] - .concat() + .into_iter() + .chain(self.device.peripherals.iter().map(|s| s.as_str())) + .chain(self.device.symbols.iter().map(|s| s.as_str())) } /// Does the configuration contain `item`? - pub fn contains(&self, item: &String) -> bool { - self.all().contains(item) + pub fn contains(&self, item: &str) -> bool { + self.all().any(|i| i == item) } /// Define all symbols for a given configuration. pub fn define_symbols(&self) { + define_all_possible_symbols(); // Define all necessary configuration symbols for the configured device: - println!("cargo:rustc-cfg={}", self.name()); - println!("cargo:rustc-cfg={}", self.arch()); - println!("cargo:rustc-cfg={}", self.cores()); - - for peripheral in self.peripherals() { - println!("cargo:rustc-cfg={peripheral}"); + for symbol in self.all() { + println!("cargo:rustc-cfg={symbol}"); } + } +} - for symbol in self.symbols() { - println!("cargo:rustc-cfg={symbol}"); +/// Defines all possible symbols that _could_ be output from this crate +/// regardless of the chosen configuration. +/// +/// This is required to avoid triggering the unexpected-cfgs lint. +fn define_all_possible_symbols() { + for chip in Chip::iter() { + let config = Config::for_chip(&chip); + for symbol in config.all() { + // https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-check-cfg + println!("cargo:rustc-check-cfg=cfg({})", symbol); } } } diff --git a/esp-println/CHANGELOG.md b/esp-println/CHANGELOG.md index b8768a8befe..1c9a2845174 100644 --- a/esp-println/CHANGELOG.md +++ b/esp-println/CHANGELOG.md @@ -8,7 +8,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added -- made `esp_println::Printer::write_bytes` public (#1812) ### Changed @@ -16,6 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +## 0.11.0 - 2024-08-29 + +### Added + +- Made `esp_println::Printer::write_bytes` public (#1812) + ## 0.10.0 - 2024-07-15 ### Added diff --git a/esp-println/Cargo.toml b/esp-println/Cargo.toml index e24d540ca1d..f476320ef40 100644 --- a/esp-println/Cargo.toml +++ b/esp-println/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "esp-println" -version = "0.10.0" +version = "0.11.0" edition = "2021" rust-version = "1.76.0" description = "Provides `print!` and `println!` implementations various Espressif devices" @@ -14,10 +14,10 @@ default-target = "riscv32imc-unknown-none-elf" features = ["esp32c3"] [dependencies] -critical-section = { version = "1.1.2", optional = true } +critical-section = { version = "1.1.3", optional = true } defmt = { version = "0.3.8", optional = true } log = { version = "0.4.22", optional = true } -portable-atomic = { version = "1.6.0", optional = true, default-features = false } +portable-atomic = { version = "1.7.0", optional = true, default-features = false } [build-dependencies] esp-build = { version = "0.1.0", path = "../esp-build" } diff --git a/esp-println/README.md b/esp-println/README.md index b2d51f7b15a..88f94527c1f 100644 --- a/esp-println/README.md +++ b/esp-println/README.md @@ -19,7 +19,7 @@ logging capabilities for Espressif devices. # Usage ```toml -esp-println = { version = "0.9.1", features = ["esp32c2"] } +esp-println = { version = "0.11.0", features = ["esp32c2"] } ``` or `cargo add esp-println --features esp32c2` diff --git a/esp-riscv-rt/src/lib.rs b/esp-riscv-rt/src/lib.rs index 2283280c96d..a7f344327e3 100644 --- a/esp-riscv-rt/src/lib.rs +++ b/esp-riscv-rt/src/lib.rs @@ -71,44 +71,92 @@ pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! { } /// Registers saved in trap handler -#[allow(missing_docs)] #[derive(Debug, Default, Clone, Copy)] #[repr(C)] pub struct TrapFrame { + /// Return address, stores the address to return to after a function call or + /// interrupt. pub ra: usize, + /// Temporary register t0, used for intermediate values. pub t0: usize, + /// Temporary register t1, used for intermediate values. pub t1: usize, + /// Temporary register t2, used for intermediate values. pub t2: usize, + /// Temporary register t3, used for intermediate values. pub t3: usize, + /// Temporary register t4, used for intermediate values. pub t4: usize, + /// Temporary register t5, used for intermediate values. pub t5: usize, + /// Temporary register t6, used for intermediate values. pub t6: usize, + /// Argument register a0, typically used to pass the first argument to a + /// function. pub a0: usize, + /// Argument register a1, typically used to pass the second argument to a + /// function. pub a1: usize, + /// Argument register a2, typically used to pass the third argument to a + /// function. pub a2: usize, + /// Argument register a3, typically used to pass the fourth argument to a + /// function. pub a3: usize, + /// Argument register a4, typically used to pass the fifth argument to a + /// function. pub a4: usize, + /// Argument register a5, typically used to pass the sixth argument to a + /// function. pub a5: usize, + /// Argument register a6, typically used to pass the seventh argument to a + /// function. pub a6: usize, + /// Argument register a7, typically used to pass the eighth argument to a + /// function. pub a7: usize, + /// Saved register s0, used to hold values across function calls. pub s0: usize, + /// Saved register s1, used to hold values across function calls. pub s1: usize, + /// Saved register s2, used to hold values across function calls. pub s2: usize, + /// Saved register s3, used to hold values across function calls. pub s3: usize, + /// Saved register s4, used to hold values across function calls. pub s4: usize, + /// Saved register s5, used to hold values across function calls. pub s5: usize, + /// Saved register s6, used to hold values across function calls. pub s6: usize, + /// Saved register s7, used to hold values across function calls. pub s7: usize, + /// Saved register s8, used to hold values across function calls. pub s8: usize, + /// Saved register s9, used to hold values across function calls. pub s9: usize, + /// Saved register s10, used to hold values across function calls. pub s10: usize, + /// Saved register s11, used to hold values across function calls. pub s11: usize, + /// Global pointer register, holds the address of the global data area. pub gp: usize, + /// Thread pointer register, holds the address of the thread-local storage + /// area. pub tp: usize, + /// Stack pointer register, holds the address of the top of the stack. pub sp: usize, + /// Program counter, stores the address of the next instruction to be + /// executed. pub pc: usize, + /// Machine status register, holds the current status of the processor, + /// including interrupt enable bits and privilege mode. pub mstatus: usize, + /// Machine cause register, contains the reason for the trap (e.g., + /// exception or interrupt number). pub mcause: usize, + /// Machine trap value register, contains additional information about the + /// trap (e.g., faulting address). pub mtval: usize, } diff --git a/esp-storage/build.rs b/esp-storage/build.rs index ebb63536296..8a7a421f7e6 100644 --- a/esp-storage/build.rs +++ b/esp-storage/build.rs @@ -6,14 +6,14 @@ fn main() -> Result<(), String> { if cfg!(feature = "esp32") { match std::env::var("OPT_LEVEL") { - Ok(level) => { + Ok(level) if std::env::var("CI").is_err() => { if level != "2" && level != "3" { Err(format!("Building esp-storage for ESP32 needs optimization level 2 or 3 - yours is {}. See https://github.com/esp-rs/esp-storage", level)) } else { Ok(()) } } - Err(_err) => Ok(()), + _ => Ok(()), } } else { Ok(()) diff --git a/esp-storage/src/common.rs b/esp-storage/src/common.rs index d6b0f08a9c3..a49e029e8f7 100644 --- a/esp-storage/src/common.rs +++ b/esp-storage/src/common.rs @@ -117,7 +117,7 @@ impl FlashStorage { offset: u32, bytes: &mut [u8], ) -> Result<(), FlashStorageError> { - check_rc(chip_specific::esp_rom_spiflash_read( + check_rc(chip_specific::spiflash_read( offset, bytes.as_ptr() as *mut u32, bytes.len() as u32, @@ -127,7 +127,7 @@ impl FlashStorage { #[inline(always)] fn unlock_once(&mut self) -> Result<(), FlashStorageError> { if !self.unlocked { - if chip_specific::esp_rom_spiflash_unlock() != 0 { + if chip_specific::spiflash_unlock() != 0 { return Err(FlashStorageError::CantUnlock); } self.unlocked = true; @@ -140,7 +140,7 @@ impl FlashStorage { pub(crate) fn internal_erase(&mut self, sector: u32) -> Result<(), FlashStorageError> { self.unlock_once()?; - check_rc(chip_specific::esp_rom_spiflash_erase_sector(sector)) + check_rc(chip_specific::spiflash_erase_sector(sector)) } #[inline(never)] @@ -152,7 +152,7 @@ impl FlashStorage { ) -> Result<(), FlashStorageError> { self.unlock_once()?; - check_rc(chip_specific::esp_rom_spiflash_write( + check_rc(chip_specific::spiflash_write( offset, bytes.as_ptr() as *const u32, bytes.len() as u32, diff --git a/esp-storage/src/esp32.rs b/esp-storage/src/esp32.rs index 352a983ab6b..85956aca3ef 100644 --- a/esp-storage/src/esp32.rs +++ b/esp-storage/src/esp32.rs @@ -1,14 +1,5 @@ use crate::maybe_with_critical_section; -const ESP_ROM_SPIFLASH_READ: u32 = 0x40062ed8; -const ESP_ROM_SPIFLASH_ERASE_SECTOR: u32 = 0x40062ccc; -const SPI_READ_STATUS_HIGH: u32 = 0x40062448; -const SPI_READ_STATUS: u32 = 0x4006226c; -const SPI_WRITE_STATUS: u32 = 0x400622f0; - -const CACHE_FLUSH_ROM: u32 = 0x40009a14; -const CACHE_READ_ENABLE_ROM: u32 = 0x40009a84; - const SPI_BASE_REG: u32 = 0x3ff42000; // SPI peripheral 1, used for SPI flash const SPI0_BASE_REG: u32 = 0x3ff43000; // SPI peripheral 0, inner state machine const SPI_EXT2_REG: u32 = SPI_BASE_REG + 0xF8; @@ -34,23 +25,17 @@ const SPI_WRSR_2B: u32 = 1 << 22; const FLASH_CHIP_ADDR: u32 = 0x3ffae270; const FLASH_DUMMY_LEN_PLUS_ADDR: u32 = 0x3ffae290; -#[inline(always)] -#[link_section = ".rwtext"] -pub(crate) fn cache_flush_rom(cpu_num: u32) { - unsafe { - let cache_flush_rom: unsafe extern "C" fn(u32) = core::mem::transmute(CACHE_FLUSH_ROM); - cache_flush_rom(cpu_num) - } -} - -#[inline(always)] -#[link_section = ".rwtext"] -pub(crate) fn cache_read_enable_rom(cpu_num: u32) { - unsafe { - let cache_read_enable_rom: unsafe extern "C" fn(u32) = - core::mem::transmute(CACHE_READ_ENABLE_ROM); - cache_read_enable_rom(cpu_num) - } +crate::rom_fn! { + fn esp_rom_cache_flush(cpu_num: u32) = 0x40009a14; + fn esp_rom_cache_read_enable(cpu_num: u32) = 0x40009a84; + fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 = 0x40062ed8; + fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 = 0x40062ccc; + fn esp_rom_spi_read_status_high( + flash_chip: *const EspRomSpiflashChipT, + status: *mut u32 + ) -> i32 = 0x40062448; + fn esp_rom_spi_read_status(flash_chip: *const EspRomSpiflashChipT, status: *mut u32) -> i32 = 0x4006226c; + fn esp_rom_spi_write_status(flash_chip: *const EspRomSpiflashChipT, status_value: u32) -> i32 = 0x400622f0; } #[inline(always)] @@ -59,33 +44,19 @@ pub(crate) fn spi_read_status_high( flash_chip: *const EspRomSpiflashChipT, status: &mut u32, ) -> i32 { - unsafe { - let spi_read_status_high: unsafe extern "C" fn( - *const EspRomSpiflashChipT, - *mut u32, - ) -> i32 = core::mem::transmute(SPI_READ_STATUS_HIGH); - spi_read_status_high(flash_chip, status as *mut u32) - } + esp_rom_spi_read_status_high(flash_chip, status as *mut u32) } #[inline(always)] #[link_section = ".rwtext"] pub(crate) fn spi_read_status(flash_chip: *const EspRomSpiflashChipT, status: &mut u32) -> i32 { - unsafe { - let spi_read_status: unsafe extern "C" fn(*const EspRomSpiflashChipT, *mut u32) -> i32 = - core::mem::transmute(SPI_READ_STATUS); - spi_read_status(flash_chip, status as *mut u32) - } + esp_rom_spi_read_status(flash_chip, status as *mut u32) } #[inline(always)] #[link_section = ".rwtext"] pub(crate) fn spi_write_status(flash_chip: *const EspRomSpiflashChipT, status_value: u32) -> i32 { - unsafe { - let spi_write_status: unsafe extern "C" fn(*const EspRomSpiflashChipT, u32) -> i32 = - core::mem::transmute(SPI_WRITE_STATUS); - spi_write_status(flash_chip, status_value) - } + esp_rom_spi_write_status(flash_chip, status_value) } #[inline(always)] @@ -98,10 +69,10 @@ fn begin() { #[inline(always)] #[link_section = ".rwtext"] fn end() { - cache_flush_rom(0); - cache_flush_rom(1); - cache_read_enable_rom(0); - cache_read_enable_rom(1); + esp_rom_cache_flush(0); + esp_rom_cache_flush(1); + esp_rom_cache_read_enable(0); + esp_rom_cache_read_enable(1); } #[derive(Debug)] @@ -117,26 +88,18 @@ pub struct EspRomSpiflashChipT { #[inline(never)] #[link_section = ".rwtext"] -pub(crate) fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { +pub(crate) fn spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { maybe_with_critical_section(|| { spiflash_wait_for_ready(); - unsafe { - let esp_rom_spiflash_read: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_READ); - esp_rom_spiflash_read(src_addr, data, len) - } + esp_rom_spiflash_read(src_addr, data, len) }) } #[inline(never)] #[link_section = ".rwtext"] -pub(crate) fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 { +pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 { maybe_with_critical_section(|| { - let res = unsafe { - let esp_rom_spiflash_erase_sector: unsafe extern "C" fn(u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_ERASE_SECTOR); - esp_rom_spiflash_erase_sector(sector_number) - }; + let res = esp_rom_spiflash_erase_sector(sector_number); spiflash_wait_for_ready(); res }) @@ -154,7 +117,7 @@ fn spi_write_enable() { #[inline(never)] #[link_section = ".rwtext"] -pub(crate) fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { +pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { maybe_with_critical_section(|| { begin(); @@ -250,7 +213,7 @@ fn spiflash_wait_for_ready() { #[inline(never)] #[link_section = ".rwtext"] -pub(crate) fn esp_rom_spiflash_unlock() -> i32 { +pub(crate) fn spiflash_unlock() -> i32 { let flashchip = FLASH_CHIP_ADDR as *const EspRomSpiflashChipT; if unsafe { (*flashchip).device_id } >> 16 & 0xff == 0x9D { panic!("ISSI flash is not supported"); diff --git a/esp-storage/src/esp32c2.rs b/esp-storage/src/esp32c2.rs index c7c071ee253..bb620259106 100644 --- a/esp-storage/src/esp32c2.rs +++ b/esp-storage/src/esp32c2.rs @@ -1,38 +1,24 @@ use crate::maybe_with_critical_section; -const ESP_ROM_SPIFLASH_READ: u32 = 0x4000013c; -const ESP_ROM_SPIFLASH_UNLOCK: u32 = 0x40000140; -const ESP_ROM_SPIFLASH_ERASE_SECTOR: u32 = 0x40000130; -const ESP_ROM_SPIFLASH_WRITE: u32 = 0x40000138; +crate::rom_fn! { + fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 = 0x4000013c; + fn esp_rom_spiflash_unlock() -> i32 = 0x40000140; + fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 = 0x40000130; + fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 = 0x40000138; +} -pub(crate) fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_read: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_READ); - esp_rom_spiflash_read(src_addr, data, len) - }) +pub(crate) fn spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_read(src_addr, data, len)) } -pub(crate) fn esp_rom_spiflash_unlock() -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_unlock: unsafe extern "C" fn() -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_UNLOCK); - esp_rom_spiflash_unlock() - }) +pub(crate) fn spiflash_unlock() -> i32 { + maybe_with_critical_section(esp_rom_spiflash_unlock) } -pub(crate) fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_erase_sector: unsafe extern "C" fn(u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_ERASE_SECTOR); - esp_rom_spiflash_erase_sector(sector_number) - }) +pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_erase_sector(sector_number)) } -pub(crate) fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_write: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_WRITE); - esp_rom_spiflash_write(dest_addr, data, len) - }) +pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_write(dest_addr, data, len)) } diff --git a/esp-storage/src/esp32c3.rs b/esp-storage/src/esp32c3.rs index 73a2d4c8dc1..7dd40900b66 100644 --- a/esp-storage/src/esp32c3.rs +++ b/esp-storage/src/esp32c3.rs @@ -1,38 +1,24 @@ use crate::maybe_with_critical_section; -const ESP_ROM_SPIFLASH_READ: u32 = 0x40000130; -const ESP_ROM_SPIFLASH_UNLOCK: u32 = 0x40000140; -const ESP_ROM_SPIFLASH_ERASE_SECTOR: u32 = 0x40000128; -const ESP_ROM_SPIFLASH_WRITE: u32 = 0x4000012c; +crate::rom_fn! { + fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 = 0x40000130; + fn esp_rom_spiflash_unlock() -> i32 = 0x40000140; + fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 = 0x40000128; + fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 = 0x4000012c; +} -pub(crate) fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_read: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_READ); - esp_rom_spiflash_read(src_addr, data, len) - }) +pub(crate) fn spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_read(src_addr, data, len)) } -pub(crate) fn esp_rom_spiflash_unlock() -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_unlock: unsafe extern "C" fn() -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_UNLOCK); - esp_rom_spiflash_unlock() - }) +pub(crate) fn spiflash_unlock() -> i32 { + maybe_with_critical_section(esp_rom_spiflash_unlock) } -pub(crate) fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_erase_sector: unsafe extern "C" fn(u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_ERASE_SECTOR); - esp_rom_spiflash_erase_sector(sector_number) - }) +pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_erase_sector(sector_number)) } -pub(crate) fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_write: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_WRITE); - esp_rom_spiflash_write(dest_addr, data, len) - }) +pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_write(dest_addr, data, len)) } diff --git a/esp-storage/src/esp32c6.rs b/esp-storage/src/esp32c6.rs index 5255a6370eb..6b85309eeab 100644 --- a/esp-storage/src/esp32c6.rs +++ b/esp-storage/src/esp32c6.rs @@ -1,38 +1,24 @@ use crate::maybe_with_critical_section; -const ESP_ROM_SPIFLASH_READ: u32 = 0x40000150; -const ESP_ROM_SPIFLASH_UNLOCK: u32 = 0x40000154; -const ESP_ROM_SPIFLASH_ERASE_SECTOR: u32 = 0x40000144; -const ESP_ROM_SPIFLASH_WRITE: u32 = 0x4000014c; +crate::rom_fn! { + fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 = 0x40000150; + fn esp_rom_spiflash_unlock() -> i32 = 0x40000154; + fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 = 0x40000144; + fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 = 0x4000014c; +} -pub(crate) fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_read: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_READ); - esp_rom_spiflash_read(src_addr, data, len) - }) +pub(crate) fn spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_read(src_addr, data, len)) } -pub(crate) fn esp_rom_spiflash_unlock() -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_unlock: unsafe extern "C" fn() -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_UNLOCK); - esp_rom_spiflash_unlock() - }) +pub(crate) fn spiflash_unlock() -> i32 { + maybe_with_critical_section(esp_rom_spiflash_unlock) } -pub(crate) fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_erase_sector: unsafe extern "C" fn(u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_ERASE_SECTOR); - esp_rom_spiflash_erase_sector(sector_number) - }) +pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_erase_sector(sector_number)) } -pub(crate) fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_write: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_WRITE); - esp_rom_spiflash_write(dest_addr, data, len) - }) +pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_write(dest_addr, data, len)) } diff --git a/esp-storage/src/esp32h2.rs b/esp-storage/src/esp32h2.rs index b7a1641d2b0..88da9458e42 100644 --- a/esp-storage/src/esp32h2.rs +++ b/esp-storage/src/esp32h2.rs @@ -1,38 +1,24 @@ use crate::maybe_with_critical_section; -const ESP_ROM_SPIFLASH_READ: u32 = 0x4000012c; -const ESP_ROM_SPIFLASH_UNLOCK: u32 = 0x40000130; -const ESP_ROM_SPIFLASH_ERASE_SECTOR: u32 = 0x40000120; -const ESP_ROM_SPIFLASH_WRITE: u32 = 0x40000128; +crate::rom_fn! { + fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 = 0x4000012c; + fn esp_rom_spiflash_unlock() -> i32 = 0x40000130; + fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 = 0x40000120; + fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 = 0x40000128; +} -pub(crate) fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_read: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_READ); - esp_rom_spiflash_read(src_addr, data, len) - }) +pub(crate) fn spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_read(src_addr, data, len)) } -pub(crate) fn esp_rom_spiflash_unlock() -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_unlock: unsafe extern "C" fn() -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_UNLOCK); - esp_rom_spiflash_unlock() - }) +pub(crate) fn spiflash_unlock() -> i32 { + maybe_with_critical_section(esp_rom_spiflash_unlock) } -pub(crate) fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_erase_sector: unsafe extern "C" fn(u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_ERASE_SECTOR); - esp_rom_spiflash_erase_sector(sector_number) - }) +pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_erase_sector(sector_number)) } -pub(crate) fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_write: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_WRITE); - esp_rom_spiflash_write(dest_addr, data, len) - }) +pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_write(dest_addr, data, len)) } diff --git a/esp-storage/src/esp32s2.rs b/esp-storage/src/esp32s2.rs index 2717ae8f26d..19ff74549e4 100644 --- a/esp-storage/src/esp32s2.rs +++ b/esp-storage/src/esp32s2.rs @@ -1,38 +1,24 @@ use crate::maybe_with_critical_section; -const ESP_ROM_SPIFLASH_READ: u32 = 0x4001728c; -const ESP_ROM_SPIFLASH_UNLOCK: u32 = 0x40016e88; -const ESP_ROM_SPIFLASH_ERASE_SECTOR: u32 = 0x4001716c; -const ESP_ROM_SPIFLASH_WRITE: u32 = 0x400171cc; +crate::rom_fn! { + fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 = 0x4001728c; + fn esp_rom_spiflash_unlock() -> i32 = 0x40016e88; + fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 = 0x4001716c; + fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 = 0x400171cc; +} -pub(crate) fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_read: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_READ); - esp_rom_spiflash_read(src_addr, data, len) - }) +pub(crate) fn spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_read(src_addr, data, len)) } -pub(crate) fn esp_rom_spiflash_unlock() -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_unlock: unsafe extern "C" fn() -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_UNLOCK); - esp_rom_spiflash_unlock() - }) +pub(crate) fn spiflash_unlock() -> i32 { + maybe_with_critical_section(esp_rom_spiflash_unlock) } -pub(crate) fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_erase_sector: unsafe extern "C" fn(u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_ERASE_SECTOR); - esp_rom_spiflash_erase_sector(sector_number) - }) +pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_erase_sector(sector_number)) } -pub(crate) fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_write: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_WRITE); - esp_rom_spiflash_write(dest_addr, data, len) - }) +pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_write(dest_addr, data, len)) } diff --git a/esp-storage/src/esp32s3.rs b/esp-storage/src/esp32s3.rs index 37cfbb791f4..3dcb777c46e 100644 --- a/esp-storage/src/esp32s3.rs +++ b/esp-storage/src/esp32s3.rs @@ -1,46 +1,32 @@ use crate::maybe_with_critical_section; -const ESP_ROM_SPIFLASH_READ: u32 = 0x40000a20; -const ESP_ROM_SPIFLASH_UNLOCK: u32 = 0x40000a2c; -const ESP_ROM_SPIFLASH_ERASE_SECTOR: u32 = 0x400009fc; -const ESP_ROM_SPIFLASH_WRITE: u32 = 0x40000a14; +crate::rom_fn! { + fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 = 0x40000a20; + fn esp_rom_spiflash_unlock() -> i32 = 0x40000a2c; + fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 = 0x400009fc; + fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 = 0x40000a14; +} #[inline(always)] #[link_section = ".rwtext"] -pub(crate) fn esp_rom_spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_read: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_READ); - esp_rom_spiflash_read(src_addr, data, len) - }) +pub(crate) fn spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_read(src_addr, data, len)) } #[inline(always)] #[link_section = ".rwtext"] -pub(crate) fn esp_rom_spiflash_unlock() -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_unlock: unsafe extern "C" fn() -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_UNLOCK); - esp_rom_spiflash_unlock() - }) +pub(crate) fn spiflash_unlock() -> i32 { + maybe_with_critical_section(esp_rom_spiflash_unlock) } #[inline(always)] #[link_section = ".rwtext"] -pub(crate) fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_erase_sector: unsafe extern "C" fn(u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_ERASE_SECTOR); - esp_rom_spiflash_erase_sector(sector_number) - }) +pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_erase_sector(sector_number)) } #[inline(always)] #[link_section = ".rwtext"] -pub(crate) fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { - maybe_with_critical_section(|| unsafe { - let esp_rom_spiflash_write: unsafe extern "C" fn(u32, *const u32, u32) -> i32 = - core::mem::transmute(ESP_ROM_SPIFLASH_WRITE); - esp_rom_spiflash_write(dest_addr, data, len) - }) +pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { + maybe_with_critical_section(|| esp_rom_spiflash_write(dest_addr, data, len)) } diff --git a/esp-storage/src/lib.rs b/esp-storage/src/lib.rs index 7247ab8c231..1a18ed1f242 100644 --- a/esp-storage/src/lib.rs +++ b/esp-storage/src/lib.rs @@ -58,3 +58,27 @@ fn maybe_with_critical_section(f: impl FnOnce() -> R) -> R { fn maybe_with_critical_section(f: impl FnOnce() -> R) -> R { f() } + +#[doc(hidden)] +#[macro_export] +macro_rules! rom_fn { + ($(#[$attrs:meta])* fn $name:ident($($arg:tt: $ty:ty),*) $(-> $retval:ty)? = $addr:expr) => { + $(#[$attrs])* + #[allow(unused)] + #[inline(always)] + #[link_section = ".rwtext"] + fn $name($($arg:$ty),*) $(-> $retval)? { + unsafe { + let rom_fn: unsafe extern "C" fn($($arg: $ty),*) $(-> $retval)? = + core::mem::transmute($addr as usize); + rom_fn($($arg),*) + } + } + }; + + ($($(#[$attrs:meta])* fn $name:ident($($arg:tt: $ty:ty),*) $(-> $retval:ty)? = $addr:expr;)+) => { + $( + $crate::rom_fn!(fn $name($($arg: $ty),*) $(-> $retval)? = $addr); + )+ + }; +} diff --git a/esp-storage/src/ll.rs b/esp-storage/src/ll.rs index 67bac35e660..0823ae7e6b3 100644 --- a/esp-storage/src/ll.rs +++ b/esp-storage/src/ll.rs @@ -13,7 +13,7 @@ use crate::chip_specific; /// The `data` expected to points to word-aligned pre-allocated buffer with size /// greater or equals to `len`. pub unsafe fn spiflash_read(src_addr: u32, data: *mut u32, len: u32) -> Result<(), i32> { - match chip_specific::esp_rom_spiflash_read(src_addr, data, len) { + match chip_specific::spiflash_read(src_addr, data, len) { 0 => Ok(()), value => Err(value), } @@ -23,7 +23,7 @@ pub unsafe fn spiflash_read(src_addr: u32, data: *mut u32, len: u32) -> Result<( /// /// # Safety pub unsafe fn spiflash_unlock() -> Result<(), i32> { - match chip_specific::esp_rom_spiflash_unlock() { + match chip_specific::spiflash_unlock() { 0 => Ok(()), value => Err(value), } @@ -35,7 +35,7 @@ pub unsafe fn spiflash_unlock() -> Result<(), i32> { /// /// The `sector_number` * sector_size should not exceeds the size of flash. pub unsafe fn spiflash_erase_sector(sector_number: u32) -> Result<(), i32> { - match chip_specific::esp_rom_spiflash_erase_sector(sector_number) { + match chip_specific::spiflash_erase_sector(sector_number) { 0 => Ok(()), value => Err(value), } @@ -49,7 +49,7 @@ pub unsafe fn spiflash_erase_sector(sector_number: u32) -> Result<(), i32> { /// The `data` expected to points to word-aligned buffer with size greater or /// equals to `len`. pub unsafe fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> Result<(), i32> { - match chip_specific::esp_rom_spiflash_write(dest_addr, data, len) { + match chip_specific::spiflash_write(dest_addr, data, len) { 0 => Ok(()), value => Err(value), } diff --git a/esp-storage/src/stub.rs b/esp-storage/src/stub.rs index 1fa8b839219..60f86ec3eda 100644 --- a/esp-storage/src/stub.rs +++ b/esp-storage/src/stub.rs @@ -57,7 +57,7 @@ fn check( true } -pub(crate) fn esp_rom_spiflash_read(src_addr: u32, data: *mut u32, len: u32) -> i32 { +pub(crate) fn spiflash_read(src_addr: u32, data: *mut u32, len: u32) -> i32 { if check::(src_addr, len, data) { maybe_with_critical_section(|| { let src = unsafe { slice::from_raw_parts_mut(data as *mut u8, len as _) }; @@ -69,14 +69,14 @@ pub(crate) fn esp_rom_spiflash_read(src_addr: u32, data: *mut u32, len: u32) -> } } -pub(crate) fn esp_rom_spiflash_unlock() -> i32 { +pub(crate) fn spiflash_unlock() -> i32 { maybe_with_critical_section(|| { unsafe { FLASH_LOCK = false }; }); SUCCESS_CODE } -pub(crate) fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 { +pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 { if check::<1, NUM_SECTORS, 1>(sector_number, 1, ptr::null()) { maybe_with_critical_section(|| { let dst_addr = sector_number * SECTOR_SIZE; @@ -89,7 +89,7 @@ pub(crate) fn esp_rom_spiflash_erase_sector(sector_number: u32) -> i32 { } } -pub(crate) fn esp_rom_spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { +pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 { if check::(dest_addr, len, data) { maybe_with_critical_section(|| { let dst = unsafe { slice::from_raw_parts(data as *const u8, len as _) }; diff --git a/esp-wifi/CHANGELOG.md b/esp-wifi/CHANGELOG.md index 7cb45eb81de..b85f9612de8 100644 --- a/esp-wifi/CHANGELOG.md +++ b/esp-wifi/CHANGELOG.md @@ -9,16 +9,58 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Implement `embedded_io::{ReadReady, WriteReady}` traits for `WifiStack` (#1882) +- Added `have-strchr` feature to disable including `strchr` (#2096) ### Changed +- esp-wifi now allocates memory from the global allocator provided by `esp-alloc` (#2099) ### Fixed -- Increased NPL event queue size to prevent overflow (#1891) +### Removed + +## 0.9.1 - 2024-09-03 + +### Added + +### Changed + +### Fixed + +- Builds on stable, again (#2067) + +### Removed + +## 0.9.0 - 2024-09-03 + +### Added + +- Added support for WPA2-ENTERPRISE (#2004) + +### Changed + +### Fixed ### Removed +- Removed the `clocks` parameter from `esp_wifi::initialize` (#1999) + +## 0.8.0 - 2024-08-29 + +### Added + +- Implement `embedded_io::{ReadReady, WriteReady}` traits for `WifiStack` (#1882) +- Implement `queue_msg_waiting` on the os_adapter (#1925) +- Added API for promiscuous mode (#1935) +- Implement `bt_hci::transport::Transport` traits for BLE (#1933) + +### Changed + +- Changed `init` to accept timers of multiple types (#1957) + +### Fixed + +- Increased NPL event queue size to prevent overflow (#1891) + ## 0.7.1 - 2024-07-17 ### Changed diff --git a/esp-wifi/Cargo.toml b/esp-wifi/Cargo.toml index 856fc9863f6..8082e54c956 100644 --- a/esp-wifi/Cargo.toml +++ b/esp-wifi/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "esp-wifi" -version = "0.7.1" +version = "0.9.1" edition = "2021" +rust-version = "1.77.0" authors = ["The ESP-RS team"] description = "A WiFi, Bluetooth and ESP-NOW driver for use with Espressif chips and bare-metal Rust" repository = "https://github.com/esp-rs/esp-hal" @@ -12,20 +13,19 @@ categories = ["embedded", "hardware-support", "no-std"] [dependencies] defmt = { version = "0.3.8", optional = true } -esp-hal = { version = "0.19.0", path = "../esp-hal", default-features = false } -esp-hal-embassy = { version = "0.2.0", path = "../esp-hal-embassy", default-features = false, optional = true } +document-features = "0.2.10" +esp-alloc = { version = "0.4.0", path = "../esp-alloc", optional = true } +esp-hal = { version = "0.20.0", path = "../esp-hal", default-features = false } +esp-hal-embassy = { version = "0.3.0", path = "../esp-hal-embassy", default-features = false, optional = true } smoltcp = { version = "0.11.0", default-features = false, features = [ "medium-ethernet", "socket-raw", ], optional = true } -critical-section = "1.1.2" +critical-section = "1.1.3" log = { version = "0.4.22", optional = true } embedded-svc = { version = "0.27.1", default-features = false, features = [ ], optional = true } -enumset = { version = "1.1.3", default-features = false, optional = true } -linked_list_allocator = { version = "0.10.5", default-features = false, features = [ - "const_mut_refs", -] } +enumset = { version = "1.1.5", default-features = false, optional = true } embedded-io = { version = "0.6.1", default-features = false } embedded-io-async = { version = "0.6.1", optional = true } fugit = "0.3.7" @@ -35,14 +35,14 @@ heapless = { version = "0.8.0", default-features = false, features = [ num-derive = { version = "0.4.2" } num-traits = { version = "0.2.19", default-features = false } no-std-net = { version = "0.6.0", optional = true } -esp-wifi-sys = { version = "0.4.0" } +esp-wifi-sys = { version = "0.5.0" } embassy-sync = { version = "0.6.0", optional = true } embassy-futures = { version = "0.1.1", optional = true } embassy-net-driver = { version = "0.2.0", optional = true } toml-cfg = "0.2.0" libm = "0.2.8" cfg-if = "1.0.0" -portable-atomic = { version = "1.6.0", default-features = false } +portable-atomic = { version = "1.7.0", default-features = false } portable_atomic_enum = { version = "0.3.1", features = ["portable-atomic"] } futures-util = { version = "0.3.30", default-features = false, features = [ @@ -51,88 +51,155 @@ futures-util = { version = "0.3.30", default-features = false, features = [ atomic-waker = { version = "1.1.2", default-features = false, features = [ "portable-atomic", ] } +bt-hci = { version = "0.1.0", optional = true } [build-dependencies] -toml-cfg = "0.2.0" -esp-build = { version = "0.1.0", path = "../esp-build" } +toml-cfg = "0.2.0" +esp-build = { version = "0.1.0", path = "../esp-build" } +esp-metadata = { version = "0.3.0", path = "../esp-metadata" } [features] -default = ["log"] +default = ["log", "esp-alloc"] -# chip features +## Use `esp-alloc` for dynamic allocations. +## +## If you opt-out you need to provide implementations for `pub extern "C" fn esp_wifi_free_internal_heap() -> usize` +## and `pub extern "C" fn esp_wifi_allocate_from_internal_ram(size: usize) -> *mut u8` +esp-alloc = ["dep:esp-alloc"] + +# Chip Support Feature Flags +# Target the ESP32-C2. esp32c2 = [ "esp-hal/esp32c2", "esp-hal-embassy?/esp32c2", "esp-wifi-sys/esp32c2", ] +# Target the ESP32-C3. esp32c3 = [ "esp-hal/esp32c3", "esp-hal-embassy?/esp32c3", "esp-wifi-sys/esp32c3", ] +# Target the ESP32-C6. esp32c6 = [ "esp-hal/esp32c6", "esp-hal-embassy?/esp32c6", "esp-wifi-sys/esp32c6", ] +# Target the ESP32-H2. esp32h2 = [ "esp-hal/esp32h2", "esp-hal-embassy?/esp32h2", "esp-wifi-sys/esp32h2", ] +# Target the ESP32. esp32 = [ "esp-hal/esp32", "esp-hal-embassy?/esp32", "esp-wifi-sys/esp32", ] +# Target the ESP32-S2. esp32s2 = [ "esp-hal/esp32s2", "esp-hal-embassy?/esp32s2", "esp-wifi-sys/esp32s2", ] +# Target the ESP32-S3. esp32s3 = [ "esp-hal/esp32s3", "esp-hal-embassy?/esp32s3", "esp-wifi-sys/esp32s3", ] -# async features +## Enable Async support async = [ "dep:embassy-sync", "dep:embassy-futures", "dep:embedded-io-async", "dep:esp-hal-embassy", - "esp-hal/async", + "dep:bt-hci", ] +## Enable `embassy-net` support embassy-net = ["dep:embassy-net-driver", "async"] -# misc features +## Enable WiFi-BLE coexistence support coex = [] + +## Logs the WiFi logs from the driver at log level info (needs a nightly-compiler) wifi-logs = [] + +## Dumps packet info at log level info dump-packets = [] + +## Provide implementations of smoltcp traits smoltcp = ["dep:smoltcp"] + +## Provide utilities for smoltcp initialization. Adds smoltcp dependency utils = ["smoltcp"] + enumset = [] + +## Enable WiFi support wifi = ["dep:enumset", "dep:no-std-net"] + +## Implement the embedded-svc Wifi trait embedded-svc = ["dep:embedded-svc"] + +## Enable BLE support ble = ["esp-hal/bluetooth"] + +## See USB-SERIAL-JTAG below phy-enable-usb = [] + +## Enable minimum modem sleep. Only affects STA mode ps-min-modem = [] + +## Enable maximum modem sleep. Only affects STA mode ps-max-modem = [] + +## Enable esp-now support esp-now = ["wifi"] + +## IPv6 support. Includes utils feature ipv6 = ["wifi", "utils", "smoltcp?/proto-ipv6"] + +## IPv4 support. Includes utils feature ipv4 = ["wifi", "utils", "smoltcp?/proto-ipv4"] + +## TCP socket support. Includes ipv4 feature tcp = ["ipv4", "smoltcp?/socket-tcp"] + +## UDP socket support. Includes ipv4 feature udp = ["ipv4", "smoltcp?/socket-udp"] + +## ICMP socket support. Includes ipv4 feature icmp = ["ipv4", "smoltcp?/socket-icmp"] + +## IGMP (multicast) support. Includes ipv4 featu igmp = ["ipv4", "smoltcp?/proto-igmp"] + +## DNS support. Includes udp feature dns = ["udp", "smoltcp?/proto-dns", "smoltcp?/socket-dns"] + +## DHCPv4 support, both creating sockets and autoconfiguring network settings. Includes utils feature dhcpv4 = ["wifi", "utils", "smoltcp?/proto-dhcpv4", "smoltcp?/socket-dhcpv4"] + +## Convenience to enable "ipv4", "tcp", "udp", "icmp", "igmp", "dns", "dhcpv4" wifi-default = ["ipv4", "tcp", "udp", "icmp", "igmp", "dns", "dhcpv4"] + +## Enable support for `defmt` defmt = ["dep:defmt", "smoltcp?/defmt", "esp-hal/defmt"] + +## Enable support for the `log` crate log = ["dep:log", "esp-hal/log"] +## Enable sniffer mode support +sniffer = ["wifi"] + +# Don't include `strchr` - not shown in docs +have-strchr = [] + [package.metadata.docs.rs] features = [ "esp32c3", diff --git a/esp-wifi/MIGRATING-0.9.md b/esp-wifi/MIGRATING-0.9.md new file mode 100644 index 00000000000..c33374dcf74 --- /dev/null +++ b/esp-wifi/MIGRATING-0.9.md @@ -0,0 +1,62 @@ +# Migration Guide from 0.9.x to vNext + +## Initialization + +You no longer have to set up clocks and pass them to `esp_wifi::initialize`. + +```diff + use esp_hal::{ +- clock::ClockControl, +- peripherals::Peripherals, + prelude::*, +- system::SystemControl, + }; + use esp_wifi::{ + initialize, + // ... + }; + + #[entry] + fn main() -> ! { +- let peripherals = Peripherals::take(); +- let system = SystemControl::new(peripherals.SYSTEM); +- let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); ++ let peripherals = esp_hal::init(esp_hal::Config::default()); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + + let init = initialize( + EspWifiInitFor::Wifi, + timg0.timer0, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, +- &clocks, + ) + .unwrap(); + + // ... + } +``` + +## Memory allocation + +You now need to have a global allocator provided by `esp-alloc` providing allocations from internal memory + +```diff + #![no_std] + #![no_main] + ++extern crate alloc; ++ + use embedded_io::*; ++use esp_alloc as _; + use esp_backtrace as _; + // ... + + #[entry] + fn main() -> ! { ++ esp_alloc::heap_allocator!(72 * 1024); ++ + + // ... +``` diff --git a/esp-wifi/README.md b/esp-wifi/README.md index 04b338e7297..3b19baa4877 100644 --- a/esp-wifi/README.md +++ b/esp-wifi/README.md @@ -28,7 +28,7 @@ Minimum supported Rust compiler version: 1.72.0.0 ### Importing -Ensure that the right features are enabled for your chip. See [Examples] for more examples. +Ensure that the right features are enabled for your chip. See [Examples](https://github.com/esp-rs/esp-hal/tree/main/examples#examples) for more examples. ```toml [dependencies.esp-wifi] @@ -71,38 +71,6 @@ When using USB-SERIAL-JTAG (for example by selecting `jtag-serial` in [`esp-prin Don't use this feature if you are _not_ using USB-SERIAL-JTAG as it might reduce WiFi performance. -## Features - -| Feature | Meaning | -| -------------- | ---------------------------------------------------------------------------------------------------- | -| wifi-logs | logs the WiFi logs from the driver at log level `info` (needs a nightly-compiler) | -| wifi-default | A convenience feature to enable some reasonable defaults for wifi use. | -| dump-packets | dumps packet info at log level `info` | -| smoltcp | Provide implementations of `smoltcp` traits | -| utils | Provide utilities for smoltcp initialization. Adds `smoltcp` dependency | -| ble | Enable BLE support | -| wifi | Enable WiFi support | -| esp-now | Enable [esp-now](https://www.espressif.com/en/solutions/low-power-solutions/esp-now) support | -| coex | Enable WiFi-BLE coexistence support | -| ipv4 | IPv4 support. Includes `utils` feature | -| ipv6 | IPv6 support. Includes `utils` feature | -| tcp | TCP socket support. Includes `ipv4` feature | -| udp | UDP socket support. Includes `ipv4` feature | -| igmp | IGMP (multicast) support. Includes `ipv4` feature | -| dns | DNS support. Includes `udp` feature | -| dhcpv4 | DHCPv4 support, both creating sockets and autoconfiguring network settings. Includes `utils` feature | -| phy-enable-usb | See [USB-SERIAL-JTAG](#usb-serial-jtag) above | -| ps-min-modem | Enable minimum modem sleep. Only affects STA mode | -| ps-max-modem | Enable maximum modem sleep. Only affects STA mode | -| log | Route log output to the `log` crate | -| defmt | Add `defmt::Format` implementation and output logs via `defmt` | -| embedded-svc | Implement the embedded-svc Wifi trait | - -Note that not all features are available on every MCU. For example, `ble` (and thus, `coex`) is not available on ESP32-S2. - -When using the `dump-packets` feature you can use the extcap in `extras/esp-wifishark` to analyze the frames in Wireshark. -For more information see [extras/esp-wifishark/README.md](../extras/esp-wifishark/README.md) - ## Tuning The defaults used by `esp-wifi` and the examples are rather conservative. It is possible to change a few of the important settings. diff --git a/esp-wifi/build.rs b/esp-wifi/build.rs index e389bc7500c..14f089947e2 100644 --- a/esp-wifi/build.rs +++ b/esp-wifi/build.rs @@ -1,11 +1,41 @@ +use std::{error::Error, str::FromStr}; + use esp_build::assert_unique_used_features; +use esp_metadata::{Chip, Config}; -fn main() -> Result<(), String> { +fn main() -> Result<(), Box> { // Ensure that only a single chip is specified: assert_unique_used_features!( "esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32s2", "esp32s3" ); + // NOTE: update when adding new device support! + // Determine the name of the configured device: + let device_name = if cfg!(feature = "esp32") { + "esp32" + } else if cfg!(feature = "esp32c2") { + "esp32c2" + } else if cfg!(feature = "esp32c3") { + "esp32c3" + } else if cfg!(feature = "esp32c6") { + "esp32c6" + } else if cfg!(feature = "esp32h2") { + "esp32h2" + } else if cfg!(feature = "esp32s2") { + "esp32s2" + } else if cfg!(feature = "esp32s3") { + "esp32s3" + } else { + unreachable!() // We've confirmed exactly one known device was selected + }; + + // Load the configuration file for the configured device: + let chip = Chip::from_str(device_name)?; + let config = Config::for_chip(&chip); + + // Define all necessary configuration symbols for the configured device: + config.define_symbols(); + #[cfg(all(feature = "ble", feature = "esp32s2"))] { panic!( @@ -38,39 +68,17 @@ fn main() -> Result<(), String> { "# ); } - match std::env::var("OPT_LEVEL") { - Ok(level) => { - if level != "2" && level != "3" { - let message = format!( - "esp-wifi should be built with optimization level 2 or 3 - yours is {level}. - See https://github.com/esp-rs/esp-wifi", - ); - print_warning(message); - } + if let Ok(level) = std::env::var("OPT_LEVEL") { + if level != "2" && level != "3" { + let message = format!( + "esp-wifi should be built with optimization level 2 or 3 - yours is {level}. + See https://github.com/esp-rs/esp-wifi", + ); + print_warning(message); } - Err(_err) => (), } - #[cfg(feature = "esp32")] - println!("cargo:rustc-cfg=esp32"); - - #[cfg(feature = "esp32c2")] - println!("cargo:rustc-cfg=esp32c2"); - - #[cfg(feature = "esp32c3")] - println!("cargo:rustc-cfg=esp32c3"); - - #[cfg(feature = "esp32c6")] - println!("cargo:rustc-cfg=esp32c6"); - - #[cfg(feature = "esp32h2")] - println!("cargo:rustc-cfg=esp32h2"); - - #[cfg(feature = "esp32s2")] - println!("cargo:rustc-cfg=esp32s2"); - - #[cfg(feature = "esp32s3")] - println!("cargo:rustc-cfg=esp32s3"); + println!("cargo:rustc-cfg={}", device_name); #[cfg(feature = "coex")] { diff --git a/esp-wifi/src/ble/btdm.rs b/esp-wifi/src/ble/btdm.rs index 2c613144f68..88636979b43 100644 --- a/esp-wifi/src/ble/btdm.rs +++ b/esp-wifi/src/ble/btdm.rs @@ -10,7 +10,7 @@ use crate::{ HciOutCollector, HCI_OUT_COLLECTOR, }, - compat::{common::str_from_c, queue::SimpleQueue, task_runner::spawn_task}, + compat::{common::str_from_c, queue::SimpleQueue}, hal::macros::ram, memory_fence::memory_fence, timer::yield_task, @@ -305,21 +305,15 @@ unsafe extern "C" fn task_create( core_id ); - *(handle as *mut usize) = 0; // we will run it in task 0 + let task_func = core::mem::transmute::< + *mut crate::binary::c_types::c_void, + extern "C" fn(*mut esp_wifi_sys::c_types::c_void), + >(func); - if spawn_task( - func, - name as *const i8, - stack_depth, - param, - prio, - handle, - core_id, - ) { - 1 - } else { - 0 - } + let task = crate::preempt::arch_specific::task_create(task_func, param, stack_depth as usize); + *(handle as *mut usize) = task as usize; + + 1 } unsafe extern "C" fn task_delete(_task: *const ()) { diff --git a/esp-wifi/src/ble/controller/mod.rs b/esp-wifi/src/ble/controller/mod.rs index 69d06e7df2b..45eb991e31b 100644 --- a/esp-wifi/src/ble/controller/mod.rs +++ b/esp-wifi/src/ble/controller/mod.rs @@ -83,6 +83,13 @@ impl Write for BleConnector<'_> { pub mod asynch { use core::task::Poll; + use bt_hci::{ + transport::{Transport, WithIndicator}, + ControllerToHostPacket, + FromHciBytes, + HostToControllerPacket, + WriteHci, + }; use embassy_sync::waitqueue::AtomicWaker; use embedded_io::ErrorType; @@ -158,6 +165,7 @@ pub mod asynch { } } + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct HciReadyEventFuture; impl core::future::Future for HciReadyEventFuture { @@ -176,4 +184,64 @@ pub mod asynch { } } } + + fn parse_hci(data: &[u8]) -> Result>, BleConnectorError> { + match ControllerToHostPacket::from_hci_bytes_complete(data) { + Ok(p) => Ok(Some(p)), + Err(e) => { + if e == bt_hci::FromHciBytesError::InvalidSize { + use bt_hci::{event::EventPacketHeader, PacketKind}; + + // Some controllers emit a suprious command complete event at startup. + let (kind, data) = + PacketKind::from_hci_bytes(data).map_err(|_| BleConnectorError::Unknown)?; + if kind == PacketKind::Event { + let (header, _) = EventPacketHeader::from_hci_bytes(data) + .map_err(|_| BleConnectorError::Unknown)?; + const COMMAND_COMPLETE: u8 = 0x0E; + if header.code == COMMAND_COMPLETE && header.params_len < 4 { + return Ok(None); + } + } + } + warn!("[hci] error parsing packet: {:?}", e); + Err(BleConnectorError::Unknown) + } + } + } + + impl<'d> Transport for BleConnector<'d> { + /// Read a complete HCI packet into the rx buffer + async fn read<'a>( + &self, + rx: &'a mut [u8], + ) -> Result, Self::Error> { + loop { + if !have_hci_read_data() { + HciReadyEventFuture.await; + } + + // Workaround for borrow checker. + // Safety: we only return a reference to x once, if parsing is successful. + let rx = + unsafe { &mut *core::ptr::slice_from_raw_parts_mut(rx.as_mut_ptr(), rx.len()) }; + + let len = crate::ble::read_next(rx); + if let Some(packet) = parse_hci(&rx[..len])? { + return Ok(packet); + } + } + } + + /// Write a complete HCI packet from the tx buffer + async fn write(&self, val: &T) -> Result<(), Self::Error> { + let mut buf: [u8; 259] = [0; 259]; + let w = WithIndicator::new(val); + let len = w.size(); + w.write_hci(&mut buf[..]) + .map_err(|_| BleConnectorError::Unknown)?; + send_hci(&buf[..len]); + Ok(()) + } + } } diff --git a/esp-wifi/src/ble/npl.rs b/esp-wifi/src/ble/npl.rs index 4f6cd2fc34f..fe0cc5fbf40 100644 --- a/esp-wifi/src/ble/npl.rs +++ b/esp-wifi/src/ble/npl.rs @@ -12,7 +12,7 @@ use crate::{ include::*, }, compat, - compat::{common::str_from_c, queue::SimpleQueue, task_runner::spawn_task}, + compat::{common::str_from_c, queue::SimpleQueue}, timer::yield_task, }; @@ -294,10 +294,10 @@ pub struct ext_funcs_t { hal_uart_init: Option i32>, task_create: Option< unsafe extern "C" fn( - *const c_void, + *mut c_void, *const c_char, u32, - *const c_void, + *mut c_void, u32, *const c_void, u32, @@ -352,10 +352,10 @@ unsafe extern "C" fn os_random() -> u32 { } unsafe extern "C" fn task_create( - task_func: *const c_void, + task_func: *mut c_void, name: *const c_char, stack_depth: u32, - param: *const c_void, + param: *mut c_void, prio: u32, task_handle: *const c_void, core_id: u32, @@ -374,19 +374,15 @@ unsafe extern "C" fn task_create( *(task_handle as *mut usize) = 0; // we will run it in task 0 - if spawn_task( - task_func as *mut c_void, - name as *const c_char, - stack_depth, - param as *mut c_void, - prio, - task_handle as *mut c_void, - core_id, - ) { - 1 - } else { - 0 - } + let task_func = core::mem::transmute::< + *mut crate::binary::c_types::c_void, + extern "C" fn(*mut esp_wifi_sys::c_types::c_void), + >(task_func); + + let task = crate::preempt::arch_specific::task_create(task_func, param, stack_depth as usize); + *(task_handle as *mut usize) = task as usize; + + 1 } unsafe extern "C" fn task_delete(_: *const c_void) { diff --git a/esp-wifi/src/common_adapter/mod.rs b/esp-wifi/src/common_adapter/mod.rs index 50e7ce548af..5e4c708ab49 100644 --- a/esp-wifi/src/common_adapter/mod.rs +++ b/esp-wifi/src/common_adapter/mod.rs @@ -1,6 +1,6 @@ use core::ptr::addr_of; -use esp_wifi_sys::include::timespec; +use esp_wifi_sys::include::timeval; use hal::{macros::ram, rng::Rng}; use crate::{ @@ -219,48 +219,8 @@ pub unsafe extern "C" fn puts(s: *const u8) { info!("{}", cstr); } -#[cfg(any(feature = "wifi-logs", nightly))] -#[no_mangle] -pub unsafe extern "C" fn sprintf(dst: *mut u8, format: *const u8, args: ...) -> i32 { - let str = str_from_c(format); - trace!("sprintf format: {}", str); - - let len = crate::compat::syslog::vsnprintf(dst, 512, format, args); - - let s = str_from_c(dst); - trace!("sprintf result: {}", s); - - len -} - -#[cfg(all(not(feature = "wifi-logs"), not(nightly)))] -#[no_mangle] -pub unsafe extern "C" fn sprintf(dst: *mut u8, format: *const u8, _args: *const ()) -> i32 { - let str = str_from_c(format); - - let res = match str { - "ESP_%02X%02X%02X" => "ESP_0000", - "%d,%s,%s,%s" => "????", - "unknown id:%d" => "unknown id:??", - _ => { - warn!("Unexpected call to `sprintf`: {}", str); - "???" - } - }; - - dst.copy_from_nonoverlapping(res.as_ptr(), res.len()); - dst.add(res.len()).write_volatile(0); - - res.len() as i32 -} - #[cfg(feature = "wifi-logs")] mod log { - #[no_mangle] - pub unsafe extern "C" fn printf(s: *const u8, args: ...) { - crate::compat::syslog::syslog(0, s, args); - } - #[no_mangle] pub unsafe extern "C" fn rtc_printf(s: *const u8, args: ...) { crate::compat::syslog::syslog(0, s, args); @@ -289,9 +249,6 @@ mod log { #[cfg(not(feature = "wifi-logs"))] mod log { - #[no_mangle] - pub unsafe extern "C" fn printf(_s: *const u8, _args: *const ()) {} - #[no_mangle] pub unsafe extern "C" fn rtc_printf(_s: *const u8, _args: *const ()) {} @@ -311,6 +268,7 @@ mod log { // #define ESP_EVENT_DEFINE_BASE(id) esp_event_base_t id = #id static mut EVT: i8 = 0; #[no_mangle] +#[allow(unused_unsafe)] static mut WIFI_EVENT: esp_event_base_t = unsafe { addr_of!(EVT) }; // stuff needed by wpa-supplicant @@ -380,12 +338,12 @@ pub unsafe extern "C" fn ets_timer_arm_us( } #[no_mangle] -pub unsafe extern "C" fn gettimeofday(tv: *mut timespec, _tz: *mut ()) -> i32 { +pub unsafe extern "C" fn gettimeofday(tv: *mut timeval, _tz: *mut ()) -> i32 { if !tv.is_null() { unsafe { let microseconds = esp_timer_get_time(); - (*tv).tv_sec = (microseconds / 1_000_000) as i32; - (*tv).tv_nsec = (microseconds % 1_000_000) as i32 * 1000; + (*tv).tv_sec = (microseconds / 1_000_000) as u64; + (*tv).tv_usec = (microseconds % 1_000_000) as u32; } } diff --git a/esp-wifi/src/compat/common.rs b/esp-wifi/src/compat/common.rs index 5fa4a8f4f06..8ad1df73c1a 100644 --- a/esp-wifi/src/compat/common.rs +++ b/esp-wifi/src/compat/common.rs @@ -2,10 +2,13 @@ use core::{ fmt::Write, + mem::size_of_val, ptr::{addr_of, addr_of_mut}, }; -use super::queue::SimpleQueue; +use esp_wifi_sys::include::malloc; + +use super::malloc::free; use crate::{ binary::{ c_types::{c_int, c_void}, @@ -16,13 +19,6 @@ use crate::{ timer::yield_task, }; -static mut CURR_SEM: [Option; 20] = [ - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, -]; - -static mut PER_THREAD_SEM: [Option<*mut c_void>; 4] = [None; 4]; - #[derive(Clone, Copy, Debug)] struct Mutex { locking_pid: usize, @@ -37,8 +33,66 @@ static mut MUTEXES: [Mutex; 10] = [Mutex { }; 10]; static mut MUTEX_IDX_CURRENT: usize = 0; -static mut FAKE_WIFI_QUEUE: *const SimpleQueue<[u8; 8], 200> = unsafe { addr_of!(REAL_WIFI_QUEUE) }; -static mut REAL_WIFI_QUEUE: SimpleQueue<[u8; 8], 200> = SimpleQueue::new(); // first there is a ptr to the real queue - driver checks it's not null +/// A naive and pretty much unsafe queue to back the queues used in drivers and +/// supplicant code +struct RawQueue { + count: usize, + item_size: usize, + current_read: usize, + current_write: usize, + storage: *mut u8, +} + +impl RawQueue { + fn new(count: usize, item_size: usize) -> Self { + let storage = unsafe { malloc((count * item_size) as u32) as *mut u8 }; + Self { + count, + item_size, + current_read: 0, + current_write: 0, + storage, + } + } + + fn free_storage(&mut self) { + unsafe { + free(self.storage); + } + self.storage = core::ptr::null_mut(); + } + + fn enqueue(&mut self, item: *mut c_void) -> i32 { + if (self.current_write - self.current_read) % self.count < self.count { + unsafe { + let p = self.storage.byte_add(self.item_size * self.current_write); + p.copy_from(item as *mut u8, self.item_size); + self.current_write = (self.current_write + 1) % self.count; + } + + 1 + } else { + 0 + } + } + + fn try_dequeue(&mut self, item: *mut c_void) -> bool { + if (self.current_write - self.current_read) % self.count > 0 { + unsafe { + let p = self.storage.byte_add(self.item_size * self.current_read) as *const c_void; + item.copy_from(p, self.item_size); + self.current_read = (self.current_read + 1) % self.count; + } + true + } else { + false + } + } + + fn count(&self) -> usize { + (self.current_write - self.current_read) % self.count + } +} pub unsafe fn str_from_c<'a>(s: *const u8) -> &'a str { let c_str = core::ffi::CStr::from_ptr(s.cast()); @@ -60,33 +114,31 @@ unsafe extern "C" fn strnlen(chars: *const u8, maxlen: usize) -> usize { pub fn sem_create(max: u32, init: u32) -> *mut c_void { critical_section::with(|_| unsafe { - let mut res = 0xffff; - memory_fence(); - for (i, sem) in CURR_SEM.iter().enumerate() { - memory_fence(); - if sem.is_none() { - res = i; - break; - } - } - - trace!("sem created res = {} (+1)", res); + let ptr = malloc(4) as *mut u32; + ptr.write_volatile(init); - if res != 0xffff { - memory_fence(); - CURR_SEM[res] = Some(init); - (res + 1) as *mut c_void - } else { - core::ptr::null_mut() - } + trace!("sem created res = {:?}", ptr); + ptr.cast() }) } pub fn sem_delete(semphr: *mut c_void) { trace!(">>> sem delete"); + + // TODO remove this once fixed in esp_supplicant AND we updated to the fixed - + // JIRA: WIFI-6676 + unsafe { + if semphr as usize > addr_of!(MUTEXES) as usize + && semphr as usize + <= unsafe { addr_of!(MUTEXES).byte_add(size_of_val(&*addr_of!(MUTEXES))) } as usize + { + warn!("trying to remove a mutex via sem_delete"); + return; + } + } + critical_section::with(|_| unsafe { - CURR_SEM[semphr as usize - 1] = None; - memory_fence(); + free(semphr.cast()); }) } @@ -97,18 +149,15 @@ pub fn sem_take(semphr: *mut c_void, tick: u32) -> i32 { let timeout = tick as u64; let start = crate::timer::get_systimer_count(); - let sem_idx = semphr as usize - 1; + let sem = semphr as *mut u32; 'outer: loop { let res = critical_section::with(|_| unsafe { memory_fence(); - if let Some(cnt) = CURR_SEM[sem_idx] { - if cnt > 0 { - CURR_SEM[sem_idx] = Some(cnt - 1); - 1 - } else { - 0 - } + let cnt = *sem; + if cnt > 0 { + *sem = cnt - 1; + 1 } else { 0 } @@ -132,33 +181,18 @@ pub fn sem_take(semphr: *mut c_void, tick: u32) -> i32 { pub fn sem_give(semphr: *mut c_void) -> i32 { trace!("semphr_give {:?}", semphr); - let sem_idx = semphr as usize - 1; + let sem = semphr as *mut u32; critical_section::with(|_| unsafe { - if let Some(cnt) = CURR_SEM[sem_idx] { - CURR_SEM[sem_idx] = Some(cnt + 1); - memory_fence(); - 1 - } else { - 0 - } + let cnt = *sem; + *sem = cnt + 1; + 1 }) } pub fn thread_sem_get() -> *mut c_void { trace!("wifi_thread_semphr_get"); - critical_section::with(|_| unsafe { - let tid = current_task(); - if let Some(sem) = PER_THREAD_SEM[tid] { - trace!("wifi_thread_semphr_get - return for {} {:?}", tid, sem); - sem - } else { - let sem = sem_create(1, 0); - trace!("wifi_thread_semphr_get - create for {} {:?}", tid, sem); - PER_THREAD_SEM[tid] = Some(sem); - sem - } - }) + unsafe { &mut ((*current_task()).thread_semaphore) as *mut _ as *mut c_void } } pub fn create_recursive_mutex() -> *mut c_void { @@ -177,7 +211,7 @@ pub fn lock_mutex(mutex: *mut c_void) -> i32 { trace!("mutex_lock ptr = {:?}", mutex); let ptr = mutex as *mut Mutex; - let current_task = current_task(); + let current_task = current_task() as usize; loop { let mutex_locked = critical_section::with(|_| unsafe { @@ -217,21 +251,28 @@ pub fn unlock_mutex(mutex: *mut c_void) -> i32 { }) } -pub fn create_wifi_queue(queue_len: c_int, item_size: c_int) -> *mut c_void { - trace!( - "wifi_create_queue len={} size={} ptr={:?} real-queue {:?} - not checked", - queue_len, - item_size, - unsafe { addr_of_mut!(FAKE_WIFI_QUEUE) }, - unsafe { addr_of_mut!(REAL_WIFI_QUEUE) }, - ); +pub fn create_queue(queue_len: c_int, item_size: c_int) -> *mut c_void { + trace!("wifi_create_queue len={} size={}", queue_len, item_size,); - if item_size > 8 { - // TODO: don't assume - panic!("don't expecting the wifi queue to hold items larger than 8"); + let queue = RawQueue::new(queue_len as usize, item_size as usize); + let ptr = unsafe { malloc(size_of_val(&queue) as u32) as *mut RawQueue }; + unsafe { + ptr.write(queue); } - unsafe { addr_of_mut!(FAKE_WIFI_QUEUE).cast() } + trace!("created queue @{:?}", ptr); + + ptr.cast() +} + +pub fn delete_queue(queue: *mut c_void) { + trace!("delete_queue {:?}", queue); + + let queue: *mut RawQueue = queue.cast(); + unsafe { + (*queue).free_storage(); + free(queue.cast()); + } } pub fn send_queued(queue: *mut c_void, item: *mut c_void, block_time_tick: u32) -> i32 { @@ -242,30 +283,8 @@ pub fn send_queued(queue: *mut c_void, item: *mut c_void, block_time_tick: u32) block_time_tick ); - // handle the WIFI_QUEUE - unsafe { - if queue != addr_of_mut!(REAL_WIFI_QUEUE).cast() { - warn!("Posting message to an unknown queue"); - return 0; - } - } - - let message = unsafe { - // SAFETY: we checked that our queue is used and it stores with 8 byte items - core::slice::from_raw_parts(item.cast::(), 8) - }; - let mut data: [u8; 8] = unwrap!(message.try_into()); - trace!("queue posting {:?}", data); - - critical_section::with(|_| { - if unsafe { REAL_WIFI_QUEUE.enqueue(data).is_ok() } { - memory_fence(); - 1 - } else { - warn!("queue_send failed"); - 0 - } - }) + let queue: *mut RawQueue = queue.cast(); + critical_section::with(|_| unsafe { (*queue).enqueue(item) }) } pub fn receive_queued(queue: *mut c_void, item: *mut c_void, block_time_tick: u32) -> i32 { @@ -280,23 +299,11 @@ pub fn receive_queued(queue: *mut c_void, item: *mut c_void, block_time_tick: u3 let timeout = block_time_tick as u64; let start = crate::timer::get_systimer_count(); - // handle the WIFI_QUEUE - if queue != unsafe { addr_of_mut!(REAL_WIFI_QUEUE).cast() } { - warn!("Posting message to an unknown queue"); - return 0; - } + let queue: *mut RawQueue = queue.cast(); loop { - let message = critical_section::with(|_| unsafe { REAL_WIFI_QUEUE.dequeue() }); - - if let Some(message) = message { - let out_message = unsafe { - // SAFETY: we checked that our queue is used and it stores with 8 byte items - core::slice::from_raw_parts_mut(item.cast::(), 8) - }; - out_message.copy_from_slice(&message); - trace!("received {:?}", message); - + if critical_section::with(|_| unsafe { (*queue).try_dequeue(item) }) { + trace!("received"); return 1; } @@ -309,6 +316,13 @@ pub fn receive_queued(queue: *mut c_void, item: *mut c_void, block_time_tick: u3 } } +pub fn number_of_messages_in_queue(queue: *const c_void) -> u32 { + trace!("queue_msg_waiting {:?}", queue); + + let queue: *const RawQueue = queue.cast(); + critical_section::with(|_| unsafe { (*queue).count() as u32 }) +} + /// Implementation of sleep() from newlib in esp-idf. /// components/newlib/time.c #[no_mangle] @@ -335,6 +349,6 @@ unsafe extern "C" fn usleep(us: u32) -> crate::binary::c_types::c_int { #[no_mangle] unsafe extern "C" fn putchar(c: i32) -> crate::binary::c_types::c_int { - info!("putchar {}", c as u8 as char); + trace!("putchar {}", c as u8 as char); c } diff --git a/esp-wifi/src/compat/malloc.rs b/esp-wifi/src/compat/malloc.rs index 7fb31cda7fe..be4eb3ccc2a 100644 --- a/esp-wifi/src/compat/malloc.rs +++ b/esp-wifi/src/compat/malloc.rs @@ -1,19 +1,16 @@ use core::alloc::Layout; -use crate::HEAP; - +#[no_mangle] pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 { trace!("alloc {}", size); let total_size = size + 4; - let layout = Layout::from_size_align_unchecked(total_size, 4); - let ptr = critical_section::with(|cs| { - HEAP.borrow_ref_mut(cs) - .allocate_first_fit(layout) - .ok() - .map_or(core::ptr::null_mut(), |allocation| allocation.as_ptr()) - }); + extern "C" { + fn esp_wifi_allocate_from_internal_ram(size: usize) -> *mut u8; + } + + let ptr = unsafe { esp_wifi_allocate_from_internal_ram(total_size) }; if ptr.is_null() { warn!("Unable to allocate {} bytes", size); @@ -24,6 +21,7 @@ pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 { ptr.offset(4) } +#[no_mangle] pub unsafe extern "C" fn free(ptr: *mut u8) { trace!("free {:?}", ptr); @@ -35,10 +33,7 @@ pub unsafe extern "C" fn free(ptr: *mut u8) { let total_size = *(ptr as *const usize); let layout = Layout::from_size_align_unchecked(total_size, 4); - critical_section::with(|cs| { - HEAP.borrow_ref_mut(cs) - .deallocate(core::ptr::NonNull::new_unchecked(ptr), layout) - }); + alloc::alloc::dealloc(ptr, layout); } #[no_mangle] @@ -56,3 +51,44 @@ pub unsafe extern "C" fn calloc(number: u32, size: usize) -> *mut u8 { ptr } + +#[no_mangle] +unsafe extern "C" fn realloc(ptr: *mut u8, new_size: usize) -> *mut u8 { + trace!("realloc {:?} {}", ptr, new_size); + + extern "C" { + fn memcpy(d: *mut u8, s: *const u8, l: usize); + } + + unsafe { + let p = malloc(new_size); + if !p.is_null() && !ptr.is_null() { + let len = usize::min( + (ptr as *const u32).sub(1).read_volatile() as usize, + new_size, + ); + memcpy(p, ptr, len); + free(ptr); + } + p + } +} + +#[cfg(feature = "esp-alloc")] +#[doc(hidden)] +#[no_mangle] +pub extern "C" fn esp_wifi_free_internal_heap() -> usize { + esp_alloc::HEAP.free_caps(esp_alloc::MemoryCapability::Internal.into()) +} + +#[cfg(feature = "esp-alloc")] +#[doc(hidden)] +#[no_mangle] +pub extern "C" fn esp_wifi_allocate_from_internal_ram(size: usize) -> *mut u8 { + unsafe { + esp_alloc::HEAP.alloc_caps( + esp_alloc::MemoryCapability::Internal.into(), + core::alloc::Layout::from_size_align_unchecked(size, 4), + ) + } +} diff --git a/esp-wifi/src/compat/misc.rs b/esp-wifi/src/compat/misc.rs new file mode 100644 index 00000000000..d0b44af3b9b --- /dev/null +++ b/esp-wifi/src/compat/misc.rs @@ -0,0 +1,211 @@ +use crate::compat::malloc::*; + +#[no_mangle] +unsafe extern "C" fn fwrite(ptr: *const (), size: usize, count: usize, stream: *const ()) -> usize { + todo!("fwrite {:?} {} {} {:?}", ptr, size, count, stream) +} + +#[no_mangle] +unsafe extern "C" fn fopen(filename: *const u8, mode: *const u8) -> *const () { + todo!("fopen {:?} {:?}", filename, mode) +} + +#[no_mangle] +unsafe extern "C" fn fgets(str: *const u8, count: u32, file: *const ()) -> *const u8 { + todo!("fgets {:?} {} {:?}", str, count, file) +} + +#[no_mangle] +unsafe extern "C" fn fclose(stream: *const ()) -> i32 { + todo!("fclose {:?}", stream); +} + +#[no_mangle] +unsafe extern "C" fn strcat(destination: *mut u8, source: *const u8) -> *const u8 { + trace!("strcat {:?} {:?}", destination, source); + + let dst: *mut u8 = strchr(destination.cast(), 0) as *mut u8; + let len = strchr(source.cast(), 0) as usize - source as usize; + core::ptr::copy(source, dst, len); + destination +} + +#[no_mangle] +unsafe extern "C" fn strcmp(str1: *const i8, str2: *const i8) -> i32 { + trace!("strcmp {:?} {:?}", str1, str2); + + let s1 = core::ffi::CStr::from_ptr(str1).to_str().unwrap(); + let s2 = core::ffi::CStr::from_ptr(str2).to_str().unwrap(); + + let x = s1.cmp(s2); + + match x { + core::cmp::Ordering::Less => -1, + core::cmp::Ordering::Equal => 0, + core::cmp::Ordering::Greater => 1, + } +} + +#[cfg(feature = "have-strchr")] +extern "C" { + fn strchr(str: *const i8, c: i32) -> *const i8; +} + +#[cfg(not(feature = "have-strchr"))] +#[no_mangle] +unsafe extern "C" fn strchr(str: *const i8, c: i32) -> *const i8 { + trace!("strchr {:?} {}", str, c); + + unsafe { + let mut p = str; + loop { + if *p == c as i8 { + return p; + } + + if *p == 0 { + return core::ptr::null(); + } + + p = p.add(1); + } + } +} + +#[no_mangle] +unsafe extern "C" fn strlcpy(dst: *mut u8, src: *const u8, size: usize) -> usize { + trace!("strlcpy {:?} {:?} {}", dst, src, size); + + let mut dst = dst; + let mut src = src; + let mut cnt = 0; + loop { + dst.write_volatile(0); + + let c = src.read_volatile(); + + if c == 0 || cnt >= size { + break; + } + + dst.write_volatile(c); + dst = dst.add(1); + src = src.add(1); + cnt += 1; + } + + cnt +} + +#[no_mangle] +unsafe extern "C" fn strstr(str1: *const i8, str2: *const i8) -> *const i8 { + trace!("strstr {:?} {:?}", str1, str2); + + let s1 = core::ffi::CStr::from_ptr(str1).to_str().unwrap(); + let s2 = core::ffi::CStr::from_ptr(str2).to_str().unwrap(); + + let idx = s1.find(s2); + + match idx { + Some(offset) => str1.add(offset), + None => core::ptr::null(), + } +} + +#[no_mangle] +unsafe extern "C" fn strcasecmp(str1: *const u8, str2: *const u8) -> i32 { + trace!("strcasecmp {:?} {:?}", str1, str2); + + let mut str1 = str1; + let mut str2 = str2; + + let mut c1 = *str1 as char; + let mut c2 = *str2 as char; + + while c1 != '\0' && c2 != '\0' { + c1 = c1.to_ascii_lowercase(); + c2 = c2.to_ascii_lowercase(); + + if c1 != c2 { + return c1 as i32 - c2 as i32; + } + + str1 = str1.add(1); + str2 = str2.add(1); + + c1 = *str1 as char; + c2 = *str2 as char; + } + + c1 as i32 - c2 as i32 +} + +#[no_mangle] +unsafe extern "C" fn strdup(str: *const i8) -> *const u8 { + trace!("strdup {:?}", str); + + unsafe { + let s = core::ffi::CStr::from_ptr(str); + let s = s.to_str().unwrap(); + + let p = malloc(s.len() + 1); + core::ptr::copy_nonoverlapping(str, p as *mut i8, s.len() + 1); + p as *const u8 + } +} + +#[no_mangle] +unsafe extern "C" fn atoi(str: *const i8) -> i32 { + trace!("atoi {:?}", str); + + unsafe { + let s = core::ffi::CStr::from_ptr(str); + let r = s.to_str().unwrap().parse().unwrap(); + r + } +} + +#[derive(Debug, Copy, Clone)] +#[repr(C)] +struct Tm { + tm_sec: u32, // seconds after the minute - [0, 60] including leap second + tm_min: u32, // minutes after the hour - [0, 59] + tm_hour: u32, // hours since midnight - [0, 23] + tm_mday: u32, // day of the month - [1, 31] + tm_mon: u32, // months since January - [0, 11] + tm_year: u32, // years since 1900 + tm_wday: u32, // days since Sunday - [0, 6] + tm_yday: u32, // days since January 1 - [0, 365] + tm_isdst: u32, // daylight savings time flag +} + +#[no_mangle] +unsafe extern "C" fn mktime(time: *const Tm) -> i64 { + trace!("mktime {:?}", time); + let time = *time; + + // Simplified implementation, ignoring time zones, leap seconds, and other + // complexities + let mut days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + let is_leap_year = |year: u32| year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + + let mut days = 0; + let year = time.tm_year + 1900; + for y in 1970..year { + days += if is_leap_year(y) { 366 } else { 365 }; + } + + if is_leap_year(year) { + days_in_month[1] = 29; + } + + for m in 0..time.tm_mon { + days += days_in_month[m as usize]; + } + days += time.tm_mday - 1; + + let seconds = days * 24 * 60 * 60 + time.tm_hour * 60 * 60 + time.tm_min * 60 + time.tm_sec; + + seconds as i64 +} diff --git a/esp-wifi/src/compat/mod.rs b/esp-wifi/src/compat/mod.rs index 2a579922d0f..b694b5ebceb 100644 --- a/esp-wifi/src/compat/mod.rs +++ b/esp-wifi/src/compat/mod.rs @@ -1,10 +1,30 @@ pub mod common; pub mod malloc; +pub mod misc; #[cfg(any(feature = "wifi-logs", nightly))] pub mod syslog; -pub mod task_runner; pub mod timer_compat; pub mod queue { pub use heapless::spsc::Queue as SimpleQueue; } + +#[no_mangle] +unsafe extern "C" fn _putchar(c: u8) { + static mut BUFFER: [u8; 256] = [0u8; 256]; + static mut IDX: usize = 0; + + if c == 0 || c == b'\n' || IDX == BUFFER.len() - 1 { + if c != 0 { + BUFFER[IDX] = c; + } else { + IDX = IDX.saturating_sub(1); + } + + info!("{}", core::str::from_utf8_unchecked(&BUFFER[..IDX])); + IDX = 0; + } else { + BUFFER[IDX] = c; + IDX += 1; + } +} diff --git a/esp-wifi/src/compat/syslog.rs b/esp-wifi/src/compat/syslog.rs index e49cd92340b..ef44bd8468e 100644 --- a/esp-wifi/src/compat/syslog.rs +++ b/esp-wifi/src/compat/syslog.rs @@ -1,72 +1,4 @@ -use core::{ffi::VaListImpl, fmt::Write}; - -use super::common::str_from_c; - -pub struct StrWriter { - dst: *mut u8, - capacity: usize, - len: usize, -} - -impl StrWriter { - pub fn new(dst: *mut u8, capacity: usize) -> Self { - Self { - dst, - capacity, - len: 0, - } - } - - pub fn len(&self) -> usize { - self.len - } - - fn space(&self) -> usize { - self.capacity - self.len - } - - fn write(&mut self, byte: u8) { - unsafe { - self.dst.write(byte); - self.dst = self.dst.add(1); - } - } - - pub fn append_char(&mut self, c: char) { - let mut buf = [0u8; 4]; - let char = c.encode_utf8(&mut buf); - self.append(char); - } - - pub fn append(&mut self, s: &str) { - // Write as many bytes as possible. We're writing a c string which means we - // don't have to deal with utf8 character boundaries, so this should be - // fine. - let len = s.len().min(self.space()); - for byte in &s.as_bytes()[..len] { - self.write(*byte); - } - - // vsnprintf's semantics: it counts unwritten bytes, too - self.len += s.len(); - } - - pub fn append_byte(&mut self, b: u8) { - if self.space() >= 1 { - self.write(b); - } - - // vsnprintf's semantics: it counts unwritten bytes, too - self.len += 1; - } -} - -impl Write for StrWriter { - fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { - self.append(s); - Ok(()) - } -} +use core::ffi::VaListImpl; #[allow(unused)] pub unsafe extern "C" fn syslog(_priority: u32, _format: *const u8, _args: VaListImpl) { @@ -86,110 +18,3 @@ pub unsafe extern "C" fn syslog(_priority: u32, _format: *const u8, _args: VaLis } } } - -/// Returns the number of character that would have been written if the buffer -/// was big enough. -pub(crate) unsafe fn vsnprintf( - dst: *mut u8, - capacity: u32, - format: *const u8, - mut args: VaListImpl, -) -> i32 { - let mut res_str = StrWriter::new(dst, capacity as usize - 1); - - let s = str_from_c(format); - - let mut format_char = ' '; - let mut is_long = 0; - let mut found = false; - for c in s.chars() { - if !found { - if c == '%' { - found = true; - } - - if !found { - res_str.append_char(c); - } - } else if c.is_numeric() || c == '-' || c == 'l' { - if c == 'l' { - is_long += 1; - } - // ignore - } else { - // a format char - format_char = c; - } - - if found && format_char != ' ' { - // have to format an arg - match format_char { - 'd' => { - if is_long < 2 { - let v = args.arg::(); - write!(res_str, "{}", v).ok(); - } else { - let v = args.arg::(); - write!(res_str, "{}", v).ok(); - } - } - - 'u' => { - if is_long < 2 { - let v = args.arg::(); - write!(res_str, "{}", v).ok(); - } else { - let v = args.arg::(); - write!(res_str, "{}", v).ok(); - } - } - - 'p' => { - let v = args.arg::(); - write!(res_str, "0x{:x}", v).ok(); - } - - 'X' => { - let v = args.arg::(); - write!(res_str, "{:02X}", v).ok(); - } - - 'x' => { - let v = args.arg::(); - write!(res_str, "{:02x}", v).ok(); - } - - 's' => { - let v = args.arg::<*const u8>(); - let vbuf = str_from_c(v); - res_str.append(vbuf); - } - - 'c' => { - let v = args.arg::(); - if v != 0 { - res_str.append_byte(v); - } - } - - '%' => { - res_str.append_char('%'); - } - - _ => { - write!(res_str, "", format_char).ok(); - } - } - - format_char = ' '; - found = false; - is_long = 0; - } - } - - let chars_written = res_str.len(); - let terminating_at = chars_written.min(capacity as usize - 1); - dst.add(terminating_at).write(0); - - chars_written as i32 -} diff --git a/esp-wifi/src/compat/task_runner.rs b/esp-wifi/src/compat/task_runner.rs deleted file mode 100644 index 4186dc443c8..00000000000 --- a/esp-wifi/src/compat/task_runner.rs +++ /dev/null @@ -1,56 +0,0 @@ -use super::queue::SimpleQueue; -use crate::{binary::c_types, compat::common::str_from_c, timer::yield_task}; - -type TaskFunc = (extern "C" fn(*mut c_types::c_void), *mut c_types::c_void); - -static mut TASK_SPAWN_QUEUE: SimpleQueue = SimpleQueue::new(); - -pub fn spawn_task( - task_func: *mut c_types::c_void, - name: *const c_types::c_char, - _stack_depth: u32, - param: *mut c_types::c_void, - prio: u32, - _task_handle: *mut c_types::c_void, - _core_id: u32, -) -> bool { - debug!( - "spawning task {}: {:?} param {:?} prio {}", - unsafe { str_from_c(name.cast()) }, - task_func, - param, - prio - ); - - // TODO: allocate a stack and insert into the task queue - - critical_section::with(|_| unsafe { - if TASK_SPAWN_QUEUE - .enqueue(( - core::mem::transmute::<*mut c_types::c_void, extern "C" fn(*mut c_types::c_void)>( - task_func, - ), - param, - )) - .is_ok() - { - true - } else { - warn!("task spawn queue full"); - false - } - }) -} - -/// This function runs a single C task started by the wifi stack. -pub(crate) extern "C" fn run_c_task() { - loop { - // Take a task and run it. - if let Some((f, p)) = critical_section::with(|_| unsafe { TASK_SPAWN_QUEUE.dequeue() }) { - debug!("task started: {:?} {:?}", f, p); - f(p); - debug!("task finished: {:?} {:?}", f, p); - } - yield_task(); - } -} diff --git a/esp-wifi/src/esp_now/mod.rs b/esp-wifi/src/esp_now/mod.rs index a0e2e7d4931..44fa7d96b91 100644 --- a/esp-wifi/src/esp_now/mod.rs +++ b/esp-wifi/src/esp_now/mod.rs @@ -19,7 +19,7 @@ use crate::{ binary::include::*, compat::queue::SimpleQueue, hal::peripheral::{Peripheral, PeripheralRef}, - wifi::Protocol, + wifi::{Protocol, RxControlInfo}, EspWifiInitialization, }; @@ -179,57 +179,6 @@ pub struct PeerInfo { // we always use STA for now } -#[cfg(not(any(esp32c6)))] -#[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct RxControlInfo { - pub rssi: i32, - pub rate: u32, - pub sig_mode: u32, - pub mcs: u32, - pub cwb: u32, - pub smoothing: u32, - pub not_sounding: u32, - pub aggregation: u32, - pub stbc: u32, - pub fec_coding: u32, - pub sgi: u32, - pub ampdu_cnt: u32, - pub channel: u32, - pub secondary_channel: u32, - pub timestamp: u32, - pub noise_floor: i32, - pub ant: u32, - pub sig_len: u32, - pub rx_state: u32, -} - -#[cfg(esp32c6)] -#[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct RxControlInfo { - pub rssi: i32, - pub rate: u32, - pub sig_len: u32, - pub rx_state: u32, - pub dump_len: u32, - pub he_sigb_len: u32, - pub cur_single_mpdu: u32, - pub cur_bb_format: u32, - pub rx_channel_estimate_info_vld: u32, - pub rx_channel_estimate_len: u32, - pub second: u32, - pub channel: u32, - pub data_rssi: i32, - pub noise_floor: u32, - pub is_group: u32, - pub rxend_state: u32, - pub rxmatch3: u32, - pub rxmatch2: u32, - pub rxmatch1: u32, - pub rxmatch0: u32, -} - #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ReceiveInfo { @@ -489,6 +438,8 @@ impl<'d> EspNowSender<'d> { } } +#[allow(unknown_lints)] +#[allow(clippy::too_long_first_doc_paragraph)] /// This struct is returned by a sync esp now send. Invoking `wait` method of /// this struct will block current task until the callback function of esp now /// send is called and return the status of previous sending. @@ -586,6 +537,8 @@ impl<'d> Drop for EspNowRc<'d> { } } +#[allow(unknown_lints)] +#[allow(clippy::too_long_first_doc_paragraph)] /// ESP-NOW is a kind of connectionless Wi-Fi communication protocol that is /// defined by Espressif. In ESP-NOW, application data is encapsulated in a /// vendor-specific action frame and then transmitted from one Wi-Fi device to @@ -813,52 +766,7 @@ unsafe extern "C" fn rcv_cb( ]; let rx_cntl = (*esp_now_info).rx_ctrl; - #[cfg(not(any(esp32c6)))] - let rx_control = RxControlInfo { - rssi: (*rx_cntl).rssi(), - rate: (*rx_cntl).rate(), - sig_mode: (*rx_cntl).sig_mode(), - mcs: (*rx_cntl).mcs(), - cwb: (*rx_cntl).cwb(), - smoothing: (*rx_cntl).smoothing(), - not_sounding: (*rx_cntl).not_sounding(), - aggregation: (*rx_cntl).aggregation(), - stbc: (*rx_cntl).stbc(), - fec_coding: (*rx_cntl).fec_coding(), - sgi: (*rx_cntl).sgi(), - ampdu_cnt: (*rx_cntl).ampdu_cnt(), - channel: (*rx_cntl).channel(), - secondary_channel: (*rx_cntl).secondary_channel(), - timestamp: (*rx_cntl).timestamp(), - noise_floor: (*rx_cntl).noise_floor(), - ant: (*rx_cntl).ant(), - sig_len: (*rx_cntl).sig_len(), - rx_state: (*rx_cntl).rx_state(), - }; - - #[cfg(esp32c6)] - let rx_control = RxControlInfo { - rssi: (*rx_cntl).rssi(), - rate: (*rx_cntl).rate(), - sig_len: (*rx_cntl).sig_len(), - rx_state: (*rx_cntl).rx_state(), - dump_len: (*rx_cntl).dump_len(), - he_sigb_len: (*rx_cntl).he_sigb_len(), - cur_single_mpdu: (*rx_cntl).cur_single_mpdu(), - cur_bb_format: (*rx_cntl).cur_bb_format(), - rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(), - rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(), - second: (*rx_cntl).second(), - channel: (*rx_cntl).channel(), - data_rssi: (*rx_cntl).data_rssi(), - noise_floor: (*rx_cntl).noise_floor(), - is_group: (*rx_cntl).is_group(), - rxend_state: (*rx_cntl).rxend_state(), - rxmatch3: (*rx_cntl).rxmatch3(), - rxmatch2: (*rx_cntl).rxmatch2(), - rxmatch1: (*rx_cntl).rxmatch1(), - rxmatch0: (*rx_cntl).rxmatch0(), - }; + let rx_control = RxControlInfo::from_raw(rx_cntl); let info = ReceiveInfo { src_address: src, @@ -928,14 +836,12 @@ mod asynch { /// This function takes mutable reference to self because the /// implementation of `ReceiveFuture` is not logically thread /// safe. - #[must_use] pub fn receive_async(&mut self) -> ReceiveFuture<'_> { self.receiver.receive_async() } /// The returned future must not be dropped before it's ready to avoid /// getting wrong status for sendings. - #[must_use] pub fn send_async<'s, 'r>( &'s mut self, dst_addr: &'r [u8; 6], @@ -945,6 +851,7 @@ mod asynch { } } + #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct SendFuture<'s, 'r> { _sender: PhantomData<&'s mut EspNowSender<'s>>, addr: &'r [u8; 6], @@ -982,6 +889,7 @@ mod asynch { /// It's not logically safe to poll multiple instances of `ReceiveFuture` /// simultaneously since the callback can only wake one future, leaving /// the rest of them unwakable. + #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct ReceiveFuture<'r>(PhantomData<&'r mut EspNowReceiver<'r>>); impl<'r> core::future::Future for ReceiveFuture<'r> { diff --git a/esp-wifi/src/lib.rs b/esp-wifi/src/lib.rs index 6accfc2b246..66a495e850c 100644 --- a/esp-wifi/src/lib.rs +++ b/esp-wifi/src/lib.rs @@ -1,25 +1,39 @@ +//! # Features flags +//! +//! Note that not all features are available on every MCU. For example, `ble` +//! (and thus, `coex`) is not available on ESP32-S2. +//! +//! When using the `dump-packets` feature you can use the extcap in +//! `extras/esp-wifishark` to analyze the frames in Wireshark. +//! For more information see +//! [extras/esp-wifishark/README.md](../extras/esp-wifishark/README.md) +#![doc = document_features::document_features!(feature_label = r#"{feature}"#)] +#![doc = include_str!("../README.md")] +#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")] #![no_std] #![cfg_attr(target_arch = "xtensa", feature(asm_experimental_arch))] #![cfg_attr(any(feature = "wifi-logs", nightly), feature(c_variadic))] -#![doc = include_str!("../README.md")] -#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")] #![allow(rustdoc::bare_urls)] // allow until num-derive doesn't generate this warning anymore (unknown_lints because Xtensa // toolchain doesn't know about that lint, yet) #![allow(unknown_lints)] #![allow(non_local_definitions)] +extern crate alloc; + // MUST be the first module mod fmt; -use core::{cell::RefCell, mem::MaybeUninit, ptr::addr_of_mut}; - use common_adapter::{chip_specific::phy_mem_init, init_radio_clock_control, RADIO_CLOCKS}; -use critical_section::Mutex; use esp_hal as hal; +#[cfg(not(feature = "esp32"))] +use esp_hal::timer::systimer::Alarm; use fugit::MegahertzU32; -use hal::{clock::Clocks, system::RadioClockController}; -use linked_list_allocator::Heap; +use hal::{ + clock::Clocks, + system::RadioClockController, + timer::{timg::Timer as TimgTimer, ErasedTimer, PeriodicTimer}, +}; #[cfg(feature = "wifi")] use wifi::WifiError; @@ -100,8 +114,6 @@ struct Config { country_code_operating_class: u8, #[default(1492)] mtu: usize, - #[default(65536)] - heap_size: usize, #[default(DEFAULT_TICK_RATE_HZ)] tick_rate_hz: u32, #[default(3)] @@ -128,21 +140,7 @@ const _: () = { core::assert!(CONFIG.rx_ba_win < (CONFIG.static_rx_buf_num * 2), "WiFi configuration check: rx_ba_win should not be larger than double of the static_rx_buf_num!"); }; -const HEAP_SIZE: usize = crate::CONFIG.heap_size; - -#[cfg_attr(esp32, link_section = ".dram2_uninit")] -static mut HEAP_DATA: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - -pub(crate) static HEAP: Mutex> = Mutex::new(RefCell::new(Heap::empty())); - -fn init_heap() { - critical_section::with(|cs| { - HEAP.borrow_ref_mut(cs) - .init_from_slice(unsafe { &mut *addr_of_mut!(HEAP_DATA) as &mut [MaybeUninit] }) - }); -} - -pub(crate) type EspWifiTimer = crate::timer::TimeBase; +type TimeBase = PeriodicTimer<'static, ErasedTimer>; #[derive(Debug, PartialEq, PartialOrd)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -215,16 +213,91 @@ impl EspWifiInitFor { } } -/// Initialize for using WiFi and or BLE +/// A trait to allow better UX for initializing esp-wifi. +/// +/// This trait is meant to be used only for the `init` function. +/// Calling `timers()` multiple times may panic. +pub trait EspWifiTimerSource { + /// Returns the timer source. + fn timer(self) -> TimeBase; +} + +/// Helper trait to reduce boilerplate. +/// +/// We can't blanket-implement for `Into` because of possible +/// conflicting implementations. +trait IntoErasedTimer: Into {} + +impl IntoErasedTimer for TimgTimer +where + DM: esp_hal::Mode, + Self: Into, +{ +} + +#[cfg(not(feature = "esp32"))] +impl IntoErasedTimer for Alarm<'_, T, DM, COMP, UNIT> +where + DM: esp_hal::Mode, + Self: Into, +{ +} + +impl IntoErasedTimer for ErasedTimer {} + +impl EspWifiTimerSource for T +where + T: IntoErasedTimer, +{ + fn timer(self) -> TimeBase { + TimeBase::new(self.into()).timer() + } +} + +impl EspWifiTimerSource for TimeBase { + fn timer(self) -> TimeBase { + self + } +} + +/// Initialize for using WiFi and or BLE. +/// +/// # The `timer` argument +/// +/// The `timer` argument is a timer source that is used by the WiFi driver to +/// schedule internal tasks. The timer source can be any of the following: +/// +/// - A timg `Timer` instance +/// - A systimer `Alarm` instance +/// - An `ErasedTimer` instance +/// - A `OneShotTimer` instance +/// +/// # Examples +/// +/// ```rust, no_run +#[doc = esp_hal::before_snippet!()] +/// use esp_hal::{rng::Rng, timg::TimerGroup}; +/// use esp_wifi::EspWifiInitFor; +/// +/// let timg0 = TimerGroup::new(peripherals.TIMG0); +/// let init = esp_wifi::initialize( +/// EspWifiInitFor::Wifi, +/// timg0.timer0, +/// Rng::new(peripherals.RNG), +/// peripherals.RADIO_CLK, +/// ) +/// .unwrap(); +/// # } +/// ``` pub fn initialize( init_for: EspWifiInitFor, - timer: EspWifiTimer, + timer: impl EspWifiTimerSource, rng: hal::rng::Rng, radio_clocks: hal::peripherals::RADIO_CLK, - clocks: &Clocks, ) -> Result { // A minimum clock of 80MHz is required to operate WiFi module. const MIN_CLOCK: u32 = 80; + let clocks = Clocks::get(); if clocks.cpu_clock < MegahertzU32::MHz(MIN_CLOCK) { return Err(InitializationError::WrongClockConfig); } @@ -233,12 +306,11 @@ pub fn initialize( crate::common_adapter::chip_specific::enable_wifi_power_domain(); - init_heap(); phy_mem_init(); init_radio_clock_control(radio_clocks); init_rng(rng); init_tasks(); - setup_timer_isr(timer)?; + setup_timer_isr(timer.timer())?; wifi_set_log_verbose(); init_clocks(); diff --git a/esp-wifi/src/preempt/mod.rs b/esp-wifi/src/preempt/mod.rs index be2d843e259..27f3fa7e7b5 100644 --- a/esp-wifi/src/preempt/mod.rs +++ b/esp-wifi/src/preempt/mod.rs @@ -1,49 +1,112 @@ -macro_rules! sum { - ($h:expr) => ($h); - ($h:expr, $($t:expr),*) => - ($h + sum!($($t),*)); -} +#[cfg_attr(target_arch = "riscv32", path = "preempt_riscv.rs")] +#[cfg_attr(target_arch = "xtensa", path = "preempt_xtensa.rs")] +pub mod arch_specific; + +use core::{cell::RefCell, mem::size_of}; + +use arch_specific::*; +use critical_section::Mutex; +use esp_wifi_sys::include::malloc; + +use crate::{compat::malloc::free, hal::trapframe::TrapFrame, memory_fence::memory_fence}; + +static mut CTX_NOW: Mutex> = Mutex::new(RefCell::new(core::ptr::null_mut())); -macro_rules! task_stack { - ($($task_stack_size:literal),+) => { - const TASK_COUNT: usize = [$($task_stack_size),+].len(); - const TASK_STACK_SIZE: [usize; TASK_COUNT] = [$($task_stack_size),+]; - const TOTAL_STACK_SIZE: usize = sum!($($task_stack_size),+); - const MAX_TASK: usize = TASK_COUNT + 1; // +1 for the user program +static mut SCHEDULED_TASK_TO_DELETE: *mut Context = core::ptr::null_mut(); - static mut TASK_STACK: [u8; TOTAL_STACK_SIZE] = [0u8; TOTAL_STACK_SIZE]; - }; +pub fn allocate_main_task() -> *mut Context { + critical_section::with(|cs| unsafe { + let mut ctx_now = CTX_NOW.borrow_ref_mut(cs); + if !(*ctx_now).is_null() { + panic!("Tried to allocate main task multiple times"); + } + + let ptr = malloc(size_of::() as u32) as *mut Context; + core::ptr::write_bytes(ptr, 0, 1); + (*ptr).next = ptr; + *ctx_now = ptr; + ptr + }) } -static mut TASK_TOP: usize = 1; -static mut CTX_NOW: usize = 0; +fn allocate_task() -> *mut Context { + critical_section::with(|cs| unsafe { + let mut ctx_now = CTX_NOW.borrow_ref_mut(cs); + if (*ctx_now).is_null() { + panic!("Called `allocate_task` before allocating main task"); + } -fn allocate_task() -> usize { - unsafe { - let i = TASK_TOP - 1; - CTX_NOW = TASK_TOP; - TASK_TOP += 1; - i - } + let ptr = malloc(size_of::() as u32) as *mut Context; + core::ptr::write_bytes(ptr, 0, 1); + (*ptr).next = (**ctx_now).next; + (**ctx_now).next = ptr; + ptr + }) } fn next_task() { + critical_section::with(|cs| unsafe { + let mut ctx_now = CTX_NOW.borrow_ref_mut(cs); + *ctx_now = (**ctx_now).next; + }); +} + +/// Delete the given task. +/// +/// This will also free the memory (stack and context) allocated for it. +fn delete_task(task: *mut Context) { + critical_section::with(|cs| unsafe { + let mut ptr = *CTX_NOW.borrow_ref_mut(cs); + let initial = ptr; + loop { + if (*ptr).next == task { + (*ptr).next = (*((*ptr).next)).next; + + free((*task).allocated_stack as *mut u8); + free(task as *mut u8); + break; + } + + ptr = (*ptr).next; + + if ptr == initial { + break; + } + } + + memory_fence(); + }); +} + +pub fn current_task() -> *mut Context { + critical_section::with(|cs| unsafe { *CTX_NOW.borrow_ref(cs) }) +} + +#[cfg(feature = "wifi")] +pub fn schedule_task_deletion(task: *mut Context) { + use crate::timer::yield_task; + unsafe { - CTX_NOW = (CTX_NOW + 1) % TASK_TOP; + SCHEDULED_TASK_TO_DELETE = task; } -} -pub fn current_task() -> usize { - unsafe { CTX_NOW } + if task == current_task() { + loop { + yield_task(); + } + } } -#[cfg(coex)] -task_stack!(8192, 8192, 8192); +pub fn task_switch(trap_frame: &mut TrapFrame) { + save_task_context(current_task(), trap_frame); -#[cfg(not(coex))] -task_stack!(8192, 8192); + unsafe { + if !SCHEDULED_TASK_TO_DELETE.is_null() { + delete_task(SCHEDULED_TASK_TO_DELETE); + SCHEDULED_TASK_TO_DELETE = core::ptr::null_mut(); + } + } -#[cfg_attr(target_arch = "riscv32", path = "preempt_riscv.rs")] -#[cfg_attr(target_arch = "xtensa", path = "preempt_xtensa.rs")] -#[allow(clippy::module_inception)] -pub mod preempt; + next_task(); + restore_task_context(current_task(), trap_frame); +} diff --git a/esp-wifi/src/preempt/preempt_riscv.rs b/esp-wifi/src/preempt/preempt_riscv.rs index 9b4a87aae42..a729fb0bf0a 100644 --- a/esp-wifi/src/preempt/preempt_riscv.rs +++ b/esp-wifi/src/preempt/preempt_riscv.rs @@ -1,165 +1,109 @@ -use core::ptr::addr_of; +use esp_wifi_sys::c_types; use super::*; use crate::hal::interrupt::TrapFrame; -#[derive(Debug, Default, Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub struct Context { trap_frame: TrapFrame, - _running: bool, + pub thread_semaphore: u32, + pub next: *mut Context, + pub allocated_stack: *const u8, } -static mut CTX_TASKS: [Context; MAX_TASK] = [Context { - trap_frame: TrapFrame { - ra: 0, - t0: 0, - t1: 0, - t2: 0, - t3: 0, - t4: 0, - t5: 0, - t6: 0, - a0: 0, - a1: 0, - a2: 0, - a3: 0, - a4: 0, - a5: 0, - a6: 0, - a7: 0, - s0: 0, - s1: 0, - s2: 0, - s3: 0, - s4: 0, - s5: 0, - s6: 0, - s7: 0, - s8: 0, - s9: 0, - s10: 0, - s11: 0, - gp: 0, - tp: 0, - sp: 0, - pc: 0, - mstatus: 0, - mcause: 0, - mtval: 0, - }, - _running: false, -}; MAX_TASK]; - -pub fn task_create(task: extern "C" fn()) { +pub fn task_create( + task: extern "C" fn(*mut c_types::c_void), + param: *mut c_types::c_void, + task_stack_size: usize, +) -> *mut Context { unsafe { - let i = allocate_task(); + let ctx = allocate_task(); - CTX_TASKS[i].trap_frame.pc = task as usize; + (*ctx).trap_frame.pc = task as usize; + (*ctx).trap_frame.a0 = param as usize; - let task_stack_size = TASK_STACK_SIZE[i]; + let stack = malloc(task_stack_size as u32); + (*ctx).allocated_stack = stack.cast(); // stack must be aligned by 16 - let task_stack_ptr = - addr_of!(TASK_STACK) as usize + (task_stack_size * i) + task_stack_size - 4; + let task_stack_ptr = stack as usize + task_stack_size; let stack_ptr = task_stack_ptr - (task_stack_ptr % 0x10); - CTX_TASKS[i].trap_frame.sp = stack_ptr; + (*ctx).trap_frame.sp = stack_ptr; + + ctx } } -pub fn restore_task_context(id: usize, trap_frame: &mut TrapFrame) -> usize { +pub fn restore_task_context(ctx: *mut Context, trap_frame: &mut TrapFrame) { unsafe { - trap_frame.ra = CTX_TASKS[id].trap_frame.ra; - trap_frame.sp = CTX_TASKS[id].trap_frame.sp; - trap_frame.a0 = CTX_TASKS[id].trap_frame.a0; - trap_frame.a1 = CTX_TASKS[id].trap_frame.a1; - trap_frame.a2 = CTX_TASKS[id].trap_frame.a2; - trap_frame.a3 = CTX_TASKS[id].trap_frame.a3; - trap_frame.a4 = CTX_TASKS[id].trap_frame.a4; - trap_frame.a5 = CTX_TASKS[id].trap_frame.a5; - trap_frame.a6 = CTX_TASKS[id].trap_frame.a6; - trap_frame.a7 = CTX_TASKS[id].trap_frame.a7; - trap_frame.t0 = CTX_TASKS[id].trap_frame.t0; - trap_frame.t1 = CTX_TASKS[id].trap_frame.t1; - trap_frame.t2 = CTX_TASKS[id].trap_frame.t2; - trap_frame.t3 = CTX_TASKS[id].trap_frame.t3; - trap_frame.t4 = CTX_TASKS[id].trap_frame.t4; - trap_frame.t5 = CTX_TASKS[id].trap_frame.t5; - trap_frame.t6 = CTX_TASKS[id].trap_frame.t6; - trap_frame.s0 = CTX_TASKS[id].trap_frame.s0; - trap_frame.s1 = CTX_TASKS[id].trap_frame.s1; - trap_frame.s2 = CTX_TASKS[id].trap_frame.s2; - trap_frame.s3 = CTX_TASKS[id].trap_frame.s3; - trap_frame.s4 = CTX_TASKS[id].trap_frame.s4; - trap_frame.s5 = CTX_TASKS[id].trap_frame.s5; - trap_frame.s6 = CTX_TASKS[id].trap_frame.s6; - trap_frame.s7 = CTX_TASKS[id].trap_frame.s7; - trap_frame.s8 = CTX_TASKS[id].trap_frame.s8; - trap_frame.s9 = CTX_TASKS[id].trap_frame.s9; - trap_frame.s10 = CTX_TASKS[id].trap_frame.s10; - trap_frame.s11 = CTX_TASKS[id].trap_frame.s11; - trap_frame.gp = CTX_TASKS[id].trap_frame.gp; - trap_frame.tp = CTX_TASKS[id].trap_frame.tp; - - CTX_TASKS[id].trap_frame.pc + trap_frame.ra = (*ctx).trap_frame.ra; + trap_frame.sp = (*ctx).trap_frame.sp; + trap_frame.a0 = (*ctx).trap_frame.a0; + trap_frame.a1 = (*ctx).trap_frame.a1; + trap_frame.a2 = (*ctx).trap_frame.a2; + trap_frame.a3 = (*ctx).trap_frame.a3; + trap_frame.a4 = (*ctx).trap_frame.a4; + trap_frame.a5 = (*ctx).trap_frame.a5; + trap_frame.a6 = (*ctx).trap_frame.a6; + trap_frame.a7 = (*ctx).trap_frame.a7; + trap_frame.t0 = (*ctx).trap_frame.t0; + trap_frame.t1 = (*ctx).trap_frame.t1; + trap_frame.t2 = (*ctx).trap_frame.t2; + trap_frame.t3 = (*ctx).trap_frame.t3; + trap_frame.t4 = (*ctx).trap_frame.t4; + trap_frame.t5 = (*ctx).trap_frame.t5; + trap_frame.t6 = (*ctx).trap_frame.t6; + trap_frame.s0 = (*ctx).trap_frame.s0; + trap_frame.s1 = (*ctx).trap_frame.s1; + trap_frame.s2 = (*ctx).trap_frame.s2; + trap_frame.s3 = (*ctx).trap_frame.s3; + trap_frame.s4 = (*ctx).trap_frame.s4; + trap_frame.s5 = (*ctx).trap_frame.s5; + trap_frame.s6 = (*ctx).trap_frame.s6; + trap_frame.s7 = (*ctx).trap_frame.s7; + trap_frame.s8 = (*ctx).trap_frame.s8; + trap_frame.s9 = (*ctx).trap_frame.s9; + trap_frame.s10 = (*ctx).trap_frame.s10; + trap_frame.s11 = (*ctx).trap_frame.s11; + trap_frame.gp = (*ctx).trap_frame.gp; + trap_frame.tp = (*ctx).trap_frame.tp; + trap_frame.pc = (*ctx).trap_frame.pc; } } -pub fn save_task_context(id: usize, pc: usize, trap_frame: &TrapFrame) { +pub fn save_task_context(ctx: *mut Context, trap_frame: &TrapFrame) { unsafe { - CTX_TASKS[id].trap_frame.ra = trap_frame.ra; - CTX_TASKS[id].trap_frame.sp = trap_frame.sp; - CTX_TASKS[id].trap_frame.a0 = trap_frame.a0; - CTX_TASKS[id].trap_frame.a1 = trap_frame.a1; - CTX_TASKS[id].trap_frame.a2 = trap_frame.a2; - CTX_TASKS[id].trap_frame.a3 = trap_frame.a3; - CTX_TASKS[id].trap_frame.a4 = trap_frame.a4; - CTX_TASKS[id].trap_frame.a5 = trap_frame.a5; - CTX_TASKS[id].trap_frame.a6 = trap_frame.a6; - CTX_TASKS[id].trap_frame.a7 = trap_frame.a7; - CTX_TASKS[id].trap_frame.t0 = trap_frame.t0; - CTX_TASKS[id].trap_frame.t1 = trap_frame.t1; - CTX_TASKS[id].trap_frame.t2 = trap_frame.t2; - CTX_TASKS[id].trap_frame.t3 = trap_frame.t3; - CTX_TASKS[id].trap_frame.t4 = trap_frame.t4; - CTX_TASKS[id].trap_frame.t5 = trap_frame.t5; - CTX_TASKS[id].trap_frame.t6 = trap_frame.t6; - CTX_TASKS[id].trap_frame.s0 = trap_frame.s0; - CTX_TASKS[id].trap_frame.s1 = trap_frame.s1; - CTX_TASKS[id].trap_frame.s2 = trap_frame.s2; - CTX_TASKS[id].trap_frame.s3 = trap_frame.s3; - CTX_TASKS[id].trap_frame.s4 = trap_frame.s4; - CTX_TASKS[id].trap_frame.s5 = trap_frame.s5; - CTX_TASKS[id].trap_frame.s6 = trap_frame.s6; - CTX_TASKS[id].trap_frame.s7 = trap_frame.s7; - CTX_TASKS[id].trap_frame.s8 = trap_frame.s8; - CTX_TASKS[id].trap_frame.s9 = trap_frame.s9; - CTX_TASKS[id].trap_frame.s10 = trap_frame.s10; - CTX_TASKS[id].trap_frame.s11 = trap_frame.s11; - CTX_TASKS[id].trap_frame.gp = trap_frame.gp; - CTX_TASKS[id].trap_frame.tp = trap_frame.tp; - - CTX_TASKS[id].trap_frame.pc = pc; + (*ctx).trap_frame.ra = trap_frame.ra; + (*ctx).trap_frame.sp = trap_frame.sp; + (*ctx).trap_frame.a0 = trap_frame.a0; + (*ctx).trap_frame.a1 = trap_frame.a1; + (*ctx).trap_frame.a2 = trap_frame.a2; + (*ctx).trap_frame.a3 = trap_frame.a3; + (*ctx).trap_frame.a4 = trap_frame.a4; + (*ctx).trap_frame.a5 = trap_frame.a5; + (*ctx).trap_frame.a6 = trap_frame.a6; + (*ctx).trap_frame.a7 = trap_frame.a7; + (*ctx).trap_frame.t0 = trap_frame.t0; + (*ctx).trap_frame.t1 = trap_frame.t1; + (*ctx).trap_frame.t2 = trap_frame.t2; + (*ctx).trap_frame.t3 = trap_frame.t3; + (*ctx).trap_frame.t4 = trap_frame.t4; + (*ctx).trap_frame.t5 = trap_frame.t5; + (*ctx).trap_frame.t6 = trap_frame.t6; + (*ctx).trap_frame.s0 = trap_frame.s0; + (*ctx).trap_frame.s1 = trap_frame.s1; + (*ctx).trap_frame.s2 = trap_frame.s2; + (*ctx).trap_frame.s3 = trap_frame.s3; + (*ctx).trap_frame.s4 = trap_frame.s4; + (*ctx).trap_frame.s5 = trap_frame.s5; + (*ctx).trap_frame.s6 = trap_frame.s6; + (*ctx).trap_frame.s7 = trap_frame.s7; + (*ctx).trap_frame.s8 = trap_frame.s8; + (*ctx).trap_frame.s9 = trap_frame.s9; + (*ctx).trap_frame.s10 = trap_frame.s10; + (*ctx).trap_frame.s11 = trap_frame.s11; + (*ctx).trap_frame.gp = trap_frame.gp; + (*ctx).trap_frame.tp = trap_frame.tp; + (*ctx).trap_frame.pc = trap_frame.pc; } } - -pub fn task_switch(trap_frame: &mut TrapFrame) { - let old_mepc = trap_frame.pc; - - save_task_context(current_task(), old_mepc, trap_frame); - - next_task(); - - let new_pc = restore_task_context(current_task(), trap_frame); - trap_frame.pc = new_pc; - - // debug aid! remove when not needed anymore!!!!! - // static mut CNT: u32 = 0; - // if CTX_NOW == 0 { - // if CNT < 5_000 { - // CNT += 1; - // } else { - // CNT = 0; - // info!("@@@ Task {} PC = {:x} {:?}", CTX_NOW, new_pc, trap_frame); - // } - // } -} diff --git a/esp-wifi/src/preempt/preempt_xtensa.rs b/esp-wifi/src/preempt/preempt_xtensa.rs index e6da2166287..eeb43f59be0 100644 --- a/esp-wifi/src/preempt/preempt_xtensa.rs +++ b/esp-wifi/src/preempt/preempt_xtensa.rs @@ -1,123 +1,57 @@ -use core::ptr::addr_of; +use esp_wifi_sys::c_types; use super::*; use crate::hal::trapframe::TrapFrame; #[derive(Debug, Clone, Copy)] -pub struct TaskContext { +pub struct Context { trap_frame: TrapFrame, + pub thread_semaphore: u32, + pub next: *mut Context, + pub allocated_stack: *const u8, } -static mut CTX_TASKS: [TaskContext; MAX_TASK] = [TaskContext { - trap_frame: TrapFrame { - PC: 0, - PS: 0, - A0: 0, - A1: 0, - A2: 0, - A3: 0, - A4: 0, - A5: 0, - A6: 0, - A7: 0, - A8: 0, - A9: 0, - A10: 0, - A11: 0, - A12: 0, - A13: 0, - A14: 0, - A15: 0, - SAR: 0, - EXCCAUSE: 0, - EXCVADDR: 0, - LBEG: 0, - LEND: 0, - LCOUNT: 0, - THREADPTR: 0, - SCOMPARE1: 0, - BR: 0, - ACCLO: 0, - ACCHI: 0, - M0: 0, - M1: 0, - M2: 0, - M3: 0, - F64R_LO: 0, - F64R_HI: 0, - F64S: 0, - FCR: 0, - FSR: 0, - F0: 0, - F1: 0, - F2: 0, - F3: 0, - F4: 0, - F5: 0, - F6: 0, - F7: 0, - F8: 0, - F9: 0, - F10: 0, - F11: 0, - F12: 0, - F13: 0, - F14: 0, - F15: 0, - }, -}; MAX_TASK]; - -pub fn task_create(task: extern "C" fn()) { +pub fn task_create( + task: extern "C" fn(*mut c_types::c_void), + param: *mut c_types::c_void, + task_stack_size: usize, +) -> *mut Context { + trace!("task_create {:?} {:?} {}", task, param, task_stack_size); unsafe { - let i = allocate_task(); + let ctx = allocate_task(); - CTX_TASKS[i].trap_frame.PC = task as usize as u32; + (*ctx).trap_frame.PC = task as usize as u32; + (*ctx).trap_frame.A6 = param as usize as u32; - let task_stack_size = TASK_STACK_SIZE[i]; + let stack = malloc(task_stack_size as u32); + (*ctx).allocated_stack = stack.cast(); // stack must be aligned by 16 - let task_stack_ptr = - (addr_of!(TASK_STACK) as *const _ as usize + (task_stack_size * i) + task_stack_size - - 4) as u32; + let task_stack_ptr = stack as usize + task_stack_size; let stack_ptr = task_stack_ptr - (task_stack_ptr % 0x10); - CTX_TASKS[i].trap_frame.A1 = stack_ptr; + (*ctx).trap_frame.A1 = stack_ptr as u32; - CTX_TASKS[i].trap_frame.PS = 0x00040000 | (1 & 3) << 16; // For windowed ABI set WOE and CALLINC (pretend task was 'call4'd). + (*ctx).trap_frame.PS = 0x00040000 | (1 & 3) << 16; // For windowed ABI set WOE and CALLINC (pretend task was 'call4'd). - CTX_TASKS[i].trap_frame.A0 = 0; + (*ctx).trap_frame.A0 = 0; *((task_stack_ptr - 4) as *mut u32) = 0; *((task_stack_ptr - 8) as *mut u32) = 0; - *((task_stack_ptr - 12) as *mut u32) = stack_ptr; + *((task_stack_ptr - 12) as *mut u32) = stack_ptr as u32; *((task_stack_ptr - 16) as *mut u32) = 0; + + ctx } } -fn restore_task_context(id: usize, trap_frame: &mut TrapFrame) { +pub fn restore_task_context(ctx: *mut Context, trap_frame: &mut TrapFrame) { unsafe { - *trap_frame = CTX_TASKS[id].trap_frame; + *trap_frame = (*ctx).trap_frame; } } -fn save_task_context(id: usize, trap_frame: &TrapFrame) { +pub fn save_task_context(ctx: *mut Context, trap_frame: &TrapFrame) { unsafe { - CTX_TASKS[id].trap_frame = *trap_frame; + (*ctx).trap_frame = *trap_frame; } } - -pub fn task_switch(trap_frame: &mut TrapFrame) { - save_task_context(current_task(), trap_frame); - next_task(); - restore_task_context(current_task(), trap_frame); - - // debug aid! remove when not needed anymore!!!!! - // static mut CNT: u32 = 0; - // if CTX_NOW == 0 { - // if CNT < 2_000 { - // CNT += 1; - // } else { - // CNT = 0; - // info!("@@@ Task {} {:?} ", 1, CTX_TASKS[1].trap_frame.PC); - // } - // } -} diff --git a/esp-wifi/src/tasks.rs b/esp-wifi/src/tasks.rs index 73fc31d4581..51eeb5455b7 100644 --- a/esp-wifi/src/tasks.rs +++ b/esp-wifi/src/tasks.rs @@ -1,20 +1,19 @@ use crate::{ - compat::{self, queue::SimpleQueue, timer_compat::TIMERS}, + compat::{queue::SimpleQueue, timer_compat::TIMERS}, memory_fence::memory_fence, - preempt::preempt::task_create, + preempt::arch_specific::task_create, timer::{get_systimer_count, yield_task}, }; pub fn init_tasks() { - task_create(compat::task_runner::run_c_task); - task_create(timer_task); + // allocate the main task + crate::preempt::allocate_main_task(); - // if coex then we know we have ble + wifi - #[cfg(coex)] - task_create(compat::task_runner::run_c_task); + // schedule the timer task + task_create(timer_task, core::ptr::null_mut(), 8192); } -pub extern "C" fn timer_task() { +pub extern "C" fn timer_task(_param: *mut esp_wifi_sys::c_types::c_void) { loop { let mut to_run = SimpleQueue::<_, 20>::new(); diff --git a/esp-wifi/src/timer/mod.rs b/esp-wifi/src/timer/mod.rs index 37a865d8637..a8240425394 100644 --- a/esp-wifi/src/timer/mod.rs +++ b/esp-wifi/src/timer/mod.rs @@ -14,6 +14,8 @@ mod arch_specific; pub use arch_specific::*; pub use chip_specific::*; +use crate::TimeBase; + pub fn setup_timer_isr(timebase: TimeBase) -> Result<(), esp_hal::timer::Error> { setup_radio_isr(); diff --git a/esp-wifi/src/timer/riscv.rs b/esp-wifi/src/timer/riscv.rs index 0b7778b62ba..7e3b0bc0d14 100644 --- a/esp-wifi/src/timer/riscv.rs +++ b/esp-wifi/src/timer/riscv.rs @@ -1,10 +1,7 @@ use core::cell::RefCell; use critical_section::Mutex; -use esp_hal::{ - interrupt::InterruptHandler, - timer::{ErasedTimer, PeriodicTimer}, -}; +use esp_hal::interrupt::InterruptHandler; #[cfg(any(feature = "esp32c6", feature = "esp32h2"))] use peripherals::INTPRI as SystemPeripheral; #[cfg(not(any(feature = "esp32c6", feature = "esp32h2")))] @@ -16,11 +13,11 @@ use crate::{ peripherals::{self, Interrupt}, riscv, }, - preempt::preempt::task_switch, + preempt::task_switch, + TimeBase, }; /// The timer responsible for time slicing. -pub type TimeBase = PeriodicTimer<'static, ErasedTimer>; static ALARM0: Mutex>> = Mutex::new(RefCell::new(None)); const TIMESLICE_FREQUENCY: fugit::HertzU64 = fugit::HertzU64::from_raw(crate::CONFIG.tick_rate_hz as u64); @@ -92,7 +89,7 @@ pub fn yield_task() { /// Current systimer count value /// A tick is 1 / 1_000_000 seconds pub fn get_systimer_count() -> u64 { - esp_hal::time::current_time().ticks() + esp_hal::time::now().ticks() } // TODO: use an Instance type instead... diff --git a/esp-wifi/src/timer/xtensa.rs b/esp-wifi/src/timer/xtensa.rs index e4044d9abc5..abe045f1f1e 100644 --- a/esp-wifi/src/timer/xtensa.rs +++ b/esp-wifi/src/timer/xtensa.rs @@ -1,18 +1,15 @@ use core::cell::RefCell; use critical_section::Mutex; -use esp_hal::{ - interrupt::InterruptHandler, - timer::{ErasedTimer, PeriodicTimer}, -}; +use esp_hal::interrupt::InterruptHandler; use crate::{ hal::{interrupt, trapframe::TrapFrame, xtensa_lx, xtensa_lx_rt}, - preempt::preempt::task_switch, + preempt::task_switch, + TimeBase, }; /// The timer responsible for time slicing. -pub type TimeBase = PeriodicTimer<'static, ErasedTimer>; static TIMER1: Mutex>> = Mutex::new(RefCell::new(None)); const TIMESLICE_FREQUENCY: fugit::HertzU64 = fugit::HertzU64::from_raw(crate::CONFIG.tick_rate_hz as u64); @@ -23,7 +20,7 @@ pub const TICKS_PER_SECOND: u64 = 1_000_000; /// This function must not be called in a critical section. Doing so may return /// an incorrect value. pub fn get_systimer_count() -> u64 { - esp_hal::time::current_time().ticks() + esp_hal::time::now().ticks() } pub fn setup_timer(mut timer1: TimeBase) -> Result<(), esp_hal::timer::Error> { diff --git a/esp-wifi/src/wifi/mod.rs b/esp-wifi/src/wifi/mod.rs index f93dbc2e94d..f049cedbe5e 100644 --- a/esp-wifi/src/wifi/mod.rs +++ b/esp-wifi/src/wifi/mod.rs @@ -6,8 +6,7 @@ pub(crate) mod state; use core::{ cell::{RefCell, RefMut}, fmt::Debug, - mem, - mem::MaybeUninit, + mem::{self, MaybeUninit}, ptr::addr_of, time::Duration, }; @@ -15,16 +14,45 @@ use core::{ use critical_section::{CriticalSection, Mutex}; use enumset::{EnumSet, EnumSetType}; use esp_wifi_sys::include::{ + esp_eap_client_clear_ca_cert, + esp_eap_client_clear_certificate_and_key, + esp_eap_client_clear_identity, + esp_eap_client_clear_new_password, + esp_eap_client_clear_password, + esp_eap_client_clear_username, + esp_eap_client_set_ca_cert, + esp_eap_client_set_certificate_and_key, + esp_eap_client_set_disable_time_check, + esp_eap_client_set_fast_params, + esp_eap_client_set_identity, + esp_eap_client_set_new_password, + esp_eap_client_set_pac_file, + esp_eap_client_set_password, + esp_eap_client_set_ttls_phase2_method, + esp_eap_client_set_username, + esp_eap_fast_config, + esp_wifi_sta_enterprise_enable, + wifi_pkt_rx_ctrl_t, WIFI_PROTOCOL_11AX, WIFI_PROTOCOL_11B, WIFI_PROTOCOL_11G, WIFI_PROTOCOL_11N, WIFI_PROTOCOL_LR, }; +#[cfg(feature = "sniffer")] +use esp_wifi_sys::include::{ + esp_wifi_80211_tx, + esp_wifi_set_promiscuous, + esp_wifi_set_promiscuous_rx_cb, + wifi_promiscuous_pkt_t, + wifi_promiscuous_pkt_type_t, +}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; #[doc(hidden)] pub(crate) use os_adapter::*; +#[cfg(feature = "sniffer")] +use portable_atomic::AtomicBool; use portable_atomic::{AtomicUsize, Ordering}; #[cfg(feature = "smoltcp")] use smoltcp::phy::{Device, DeviceCapabilities, RxToken, TxToken}; @@ -228,6 +256,114 @@ impl Default for ClientConfiguration { } } +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))] +pub struct EapFastConfig { + pub fast_provisioning: u8, + pub fast_max_pac_list_len: u8, + pub fast_pac_format_binary: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))] +pub enum TtlsPhase2Method { + Eap, + Mschapv2, + Mschap, + Pap, + Chap, +} + +impl TtlsPhase2Method { + fn to_raw(&self) -> u32 { + match self { + TtlsPhase2Method::Eap => { + esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_EAP + } + TtlsPhase2Method::Mschapv2 => { + esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAPV2 + } + TtlsPhase2Method::Mschap => { + esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAP + } + TtlsPhase2Method::Pap => { + esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_PAP + } + TtlsPhase2Method::Chap => { + esp_wifi_sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_CHAP + } + } + } +} + +#[derive(Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))] +pub struct EapClientConfiguration { + pub ssid: heapless::String<32>, + pub bssid: Option<[u8; 6]>, + // pub protocol: Protocol, + pub auth_method: AuthMethod, + pub identity: Option>, + pub username: Option>, + pub password: Option>, + pub new_password: Option>, + pub eap_fast_config: Option, + pub pac_file: Option<&'static [u8]>, + pub time_check: bool, + pub ca_cert: Option<&'static [u8]>, + #[allow(clippy::type_complexity)] + pub certificate_and_key: Option<(&'static [u8], &'static [u8], Option<&'static [u8]>)>, + pub ttls_phase2_method: Option, + + pub channel: Option, +} + +impl Debug for EapClientConfiguration { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("EapClientConfiguration") + .field("ssid", &self.ssid) + .field("bssid", &self.bssid) + .field("auth_method", &self.auth_method) + .field("channel", &self.channel) + .field("identity", &self.identity) + .field("username", &self.username) + .field("eap_fast_config", &self.eap_fast_config) + .field("time_check", &self.time_check) + .field("pac_file set", &self.pac_file.is_some()) + .field("ca_cert set", &self.ca_cert.is_some()) + .field( + "certificate_and_key set", + &self.certificate_and_key.is_some(), + ) + .field("ttls_phase2_method", &self.ttls_phase2_method) + .finish() + } +} + +impl Default for EapClientConfiguration { + fn default() -> Self { + EapClientConfiguration { + ssid: heapless::String::new(), + bssid: None, + auth_method: AuthMethod::WPA2Enterprise, + identity: None, + username: None, + password: None, + channel: None, + eap_fast_config: None, + time_check: false, + new_password: None, + pac_file: None, + ca_cert: None, + certificate_and_key: None, + ttls_phase2_method: None, + } + } +} + #[derive(EnumSetType, Debug, PartialOrd)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Capability { @@ -239,12 +375,14 @@ pub enum Capability { #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Default)] +#[allow(clippy::large_enum_variant)] pub enum Configuration { #[default] None, Client(ClientConfiguration), AccessPoint(AccessPointConfiguration), Mixed(ClientConfiguration, AccessPointConfiguration), + EapClient(EapClientConfiguration), } impl Configuration { @@ -628,6 +766,7 @@ impl TryFrom<&Configuration> for WifiMode { Configuration::AccessPoint(_) => Self::Ap, Configuration::Client(_) => Self::Sta, Configuration::Mixed(_, _) => Self::ApSta, + Configuration::EapClient(_) => Self::Sta, }; Ok(mode) @@ -680,6 +819,7 @@ pub enum WifiError { InternalError(InternalWifiError), Disconnected, UnknownWifiMode, + Unsupported, } /// Events generated by the WiFi driver @@ -1770,10 +1910,206 @@ fn convert_ap_info(record: &include::wifi_ap_record_t) -> AccessPointInfo { } } +#[cfg(not(any(esp32c6)))] +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct RxControlInfo { + pub rssi: i32, + pub rate: u32, + pub sig_mode: u32, + pub mcs: u32, + pub cwb: u32, + pub smoothing: u32, + pub not_sounding: u32, + pub aggregation: u32, + pub stbc: u32, + pub fec_coding: u32, + pub sgi: u32, + pub ampdu_cnt: u32, + pub channel: u32, + pub secondary_channel: u32, + pub timestamp: u32, + pub noise_floor: i32, + pub ant: u32, + pub sig_len: u32, + pub rx_state: u32, +} + +#[cfg(esp32c6)] +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct RxControlInfo { + pub rssi: i32, + pub rate: u32, + pub sig_len: u32, + pub rx_state: u32, + pub dump_len: u32, + pub he_sigb_len: u32, + pub cur_single_mpdu: u32, + pub cur_bb_format: u32, + pub rx_channel_estimate_info_vld: u32, + pub rx_channel_estimate_len: u32, + pub second: u32, + pub channel: u32, + pub noise_floor: i32, + pub is_group: u32, + pub rxend_state: u32, + pub rxmatch3: u32, + pub rxmatch2: u32, + pub rxmatch1: u32, + pub rxmatch0: u32, +} +impl RxControlInfo { + /// Create an instance from a raw pointer to [wifi_pkt_rx_ctrl_t]. + /// + /// # Safety + /// When calling this, you must ensure, that `rx_cntl` points to a valid + /// instance of [wifi_pkt_rx_ctrl_t]. + pub unsafe fn from_raw(rx_cntl: *const wifi_pkt_rx_ctrl_t) -> Self { + #[cfg(not(esp32c6))] + let rx_control_info = RxControlInfo { + rssi: (*rx_cntl).rssi(), + rate: (*rx_cntl).rate(), + sig_mode: (*rx_cntl).sig_mode(), + mcs: (*rx_cntl).mcs(), + cwb: (*rx_cntl).cwb(), + smoothing: (*rx_cntl).smoothing(), + not_sounding: (*rx_cntl).not_sounding(), + aggregation: (*rx_cntl).aggregation(), + stbc: (*rx_cntl).stbc(), + fec_coding: (*rx_cntl).fec_coding(), + sgi: (*rx_cntl).sgi(), + ampdu_cnt: (*rx_cntl).ampdu_cnt(), + channel: (*rx_cntl).channel(), + secondary_channel: (*rx_cntl).secondary_channel(), + timestamp: (*rx_cntl).timestamp(), + noise_floor: (*rx_cntl).noise_floor(), + ant: (*rx_cntl).ant(), + sig_len: (*rx_cntl).sig_len(), + rx_state: (*rx_cntl).rx_state(), + }; + #[cfg(esp32c6)] + let rx_control_info = RxControlInfo { + rssi: (*rx_cntl).rssi(), + rate: (*rx_cntl).rate(), + sig_len: (*rx_cntl).sig_len(), + rx_state: (*rx_cntl).rx_state(), + dump_len: (*rx_cntl).dump_len(), + he_sigb_len: (*rx_cntl).he_sigb_len(), + cur_single_mpdu: (*rx_cntl).cur_single_mpdu(), + cur_bb_format: (*rx_cntl).cur_bb_format(), + rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(), + rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(), + second: (*rx_cntl).second(), + channel: (*rx_cntl).channel(), + noise_floor: (*rx_cntl).noise_floor(), + is_group: (*rx_cntl).is_group(), + rxend_state: (*rx_cntl).rxend_state(), + rxmatch3: (*rx_cntl).rxmatch3(), + rxmatch2: (*rx_cntl).rxmatch2(), + rxmatch1: (*rx_cntl).rxmatch1(), + rxmatch0: (*rx_cntl).rxmatch0(), + }; + rx_control_info + } +} +#[cfg(feature = "sniffer")] +pub struct PromiscuousPkt<'a> { + pub rx_cntl: RxControlInfo, + pub frame_type: wifi_promiscuous_pkt_type_t, + pub len: usize, + pub data: &'a [u8], +} +#[cfg(feature = "sniffer")] +impl PromiscuousPkt<'_> { + /// # Safety + /// When calling this, you have to ensure, that `buf` points to a valid + /// [wifi_promiscuous_pkt_t]. + pub(crate) unsafe fn from_raw( + buf: *const wifi_promiscuous_pkt_t, + frame_type: wifi_promiscuous_pkt_type_t, + ) -> Self { + let rx_cntl = RxControlInfo::from_raw(&(*buf).rx_ctrl); + let len = rx_cntl.sig_len as usize; + PromiscuousPkt { + rx_cntl, + frame_type, + len, + data: core::slice::from_raw_parts( + (buf as *const u8).add(size_of::()), + len, + ), + } + } +} + +#[cfg(feature = "sniffer")] +static SNIFFER_CB: Mutex>> = Mutex::new(RefCell::new(None)); + +#[cfg(feature = "sniffer")] +unsafe extern "C" fn promiscuous_rx_cb(buf: *mut core::ffi::c_void, frame_type: u32) { + critical_section::with(|cs| { + let Some(sniffer_callback) = *SNIFFER_CB.borrow_ref(cs) else { + return; + }; + let promiscuous_pkt = PromiscuousPkt::from_raw(buf as *const _, frame_type); + sniffer_callback(promiscuous_pkt); + }); +} + +#[cfg(feature = "sniffer")] +/// A wifi sniffer. +pub struct Sniffer { + promiscuous_mode_enabled: AtomicBool, +} +#[cfg(feature = "sniffer")] +impl Sniffer { + pub(crate) fn new() -> Self { + // This shouldn't fail, since the way this is created, means that wifi will + // always be initialized. + esp_wifi_result!(unsafe { esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb)) }) + .unwrap(); + Self { + promiscuous_mode_enabled: AtomicBool::new(false), + } + } + /// Set promiscuous mode enabled or disabled. + pub fn set_promiscuous_mode(&self, enabled: bool) -> Result<(), WifiError> { + esp_wifi_result!(unsafe { esp_wifi_set_promiscuous(enabled) })?; + self.promiscuous_mode_enabled + .store(enabled, Ordering::Relaxed); + Ok(()) + } + /// Transmit a raw frame. + pub fn send_raw_frame( + &mut self, + use_sta_interface: bool, + buffer: &[u8], + use_internal_seq_num: bool, + ) -> Result<(), WifiError> { + esp_wifi_result!(unsafe { + esp_wifi_80211_tx( + if use_sta_interface { 0 } else { 1 } as wifi_interface_t, + buffer.as_ptr() as *const _, + buffer.len() as i32, + use_internal_seq_num, + ) + }) + } + /// Set the callback for receiving a packet. + pub fn set_receive_cb(&mut self, cb: fn(PromiscuousPkt)) { + critical_section::with(|cs| { + *SNIFFER_CB.borrow_ref_mut(cs) = Some(cb); + }); + } +} + /// A wifi controller pub struct WifiController<'d> { _device: PeripheralRef<'d, crate::hal::peripherals::WIFI>, config: Configuration, + #[cfg(feature = "sniffer")] + sniffer_taken: AtomicBool, } impl<'d> WifiController<'d> { @@ -1792,6 +2128,8 @@ impl<'d> WifiController<'d> { let mut this = Self { _device, config: Default::default(), + #[cfg(feature = "sniffer")] + sniffer_taken: AtomicBool::new(false), }; let mode = WifiMode::try_from(&config)?; @@ -1801,6 +2139,18 @@ impl<'d> WifiController<'d> { this.set_configuration(&config)?; Ok(this) } + #[cfg(feature = "sniffer")] + pub fn take_sniffer(&self) -> Option { + if self + .sniffer_taken + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + == Ok(false) + { + Some(Sniffer::new()) + } else { + None + } + } /// Set the wifi protocol. /// @@ -2134,6 +2484,140 @@ fn apply_sta_config(config: &ClientConfiguration) -> Result<(), WifiError> { } } +fn apply_sta_eap_config(config: &EapClientConfiguration) -> Result<(), WifiError> { + let mut cfg = wifi_config_t { + sta: wifi_sta_config_t { + ssid: [0; 32], + password: [0; 64], + scan_method: crate::CONFIG.scan_method, + bssid_set: config.bssid.is_some(), + bssid: config.bssid.unwrap_or_default(), + channel: config.channel.unwrap_or(0), + listen_interval: crate::CONFIG.listen_interval, + sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL, + threshold: wifi_scan_threshold_t { + rssi: -99, + authmode: config.auth_method.to_raw(), + }, + pmf_cfg: wifi_pmf_config_t { + capable: true, + required: false, + }, + sae_pwe_h2e: 3, + _bitfield_align_1: [0; 0], + _bitfield_1: __BindgenBitfieldUnit::new([0; 4]), + failure_retry_cnt: crate::CONFIG.failure_retry_cnt, + _bitfield_align_2: [0; 0], + _bitfield_2: __BindgenBitfieldUnit::new([0; 4]), + sae_pk_mode: 0, // ?? + sae_h2e_identifier: [0; 32], + }, + }; + + unsafe { + cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes()); + esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))?; + + if let Some(identity) = &config.identity { + esp_wifi_result!(esp_eap_client_set_identity( + identity.as_str().as_ptr(), + identity.len() as i32 + ))?; + } else { + esp_eap_client_clear_identity(); + } + + if let Some(username) = &config.username { + esp_wifi_result!(esp_eap_client_set_username( + username.as_str().as_ptr(), + username.len() as i32 + ))?; + } else { + esp_eap_client_clear_username(); + } + + if let Some(password) = &config.password { + esp_wifi_result!(esp_eap_client_set_password( + password.as_str().as_ptr(), + password.len() as i32 + ))?; + } else { + esp_eap_client_clear_password(); + } + + if let Some(new_password) = &config.new_password { + esp_wifi_result!(esp_eap_client_set_new_password( + new_password.as_str().as_ptr(), + new_password.len() as i32 + ))?; + } else { + esp_eap_client_clear_new_password(); + } + + if let Some(pac_file) = &config.pac_file { + esp_wifi_result!(esp_eap_client_set_pac_file( + pac_file.as_ptr(), + pac_file.len() as i32 + ))?; + } + + if let Some(phase2_method) = &config.ttls_phase2_method { + esp_wifi_result!(esp_eap_client_set_ttls_phase2_method( + phase2_method.to_raw() + ))?; + } + + if let Some(ca_cert) = config.ca_cert { + esp_wifi_result!(esp_eap_client_set_ca_cert( + ca_cert.as_ptr(), + ca_cert.len() as i32 + ))?; + } else { + esp_eap_client_clear_ca_cert(); + } + + if let Some((cert, key, password)) = config.certificate_and_key { + let (pwd, pwd_len) = if let Some(pwd) = password { + (pwd.as_ptr(), pwd.len() as i32) + } else { + (core::ptr::null(), 0) + }; + + esp_wifi_result!(esp_eap_client_set_certificate_and_key( + cert.as_ptr(), + cert.len() as i32, + key.as_ptr(), + key.len() as i32, + pwd, + pwd_len, + ))?; + } else { + esp_eap_client_clear_certificate_and_key(); + } + + if let Some(cfg) = &config.eap_fast_config { + let params = esp_eap_fast_config { + fast_provisioning: cfg.fast_provisioning as i32, + fast_max_pac_list_len: cfg.fast_max_pac_list_len as i32, + fast_pac_format_binary: cfg.fast_pac_format_binary, + }; + esp_wifi_result!(esp_eap_client_set_fast_params(params))?; + } + + esp_wifi_result!(esp_eap_client_set_disable_time_check(!&config.time_check))?; + + // esp_eap_client_set_suiteb_192bit_certification unsupported because we build + // without MBEDTLS + + // esp_eap_client_use_default_cert_bundle unsupported because we build without + // MBEDTLS + + esp_wifi_result!(esp_wifi_sta_enterprise_enable())?; + + Ok(()) + } +} + impl WifiController<'_> { /// Get the supported capabilities of the controller. pub fn get_capabilities(&self) -> Result, WifiError> { @@ -2144,6 +2628,7 @@ impl WifiController<'_> { Configuration::Mixed(_, _) => { Capability::Client | Capability::AccessPoint | Capability::Mixed } + Configuration::EapClient(_) => enumset::enum_set! { Capability::Client }, }; Ok(caps) @@ -2155,40 +2640,35 @@ impl WifiController<'_> { } /// Set the configuration, you need to use Wifi::connect() for connecting to - /// an AP + /// an AP. + /// + /// This will replace any previously set configuration pub fn set_configuration(&mut self, conf: &Configuration) -> Result<(), WifiError> { - match self.config { - Configuration::None => self.config = conf.clone(), // initial config - Configuration::Client(ref mut client) => { - if let Configuration::Client(conf) = conf { - *client = conf.clone(); - } else { - return Err(WifiError::InternalError( - InternalWifiError::EspErrInvalidArg, - )); - } + let wifi_mode = WifiMode::current().unwrap_or(WifiMode::Sta); + let sta_enabled = wifi_mode.is_sta(); + let ap_enabled = wifi_mode.is_ap(); + + match conf { + Configuration::Client(_) if !sta_enabled => { + return Err(WifiError::InternalError( + InternalWifiError::EspErrInvalidArg, + )) } - Configuration::AccessPoint(ref mut ap) => { - if let Configuration::AccessPoint(conf) = conf { - *ap = conf.clone(); - } else { - return Err(WifiError::InternalError( - InternalWifiError::EspErrInvalidArg, - )); - } + Configuration::AccessPoint(_) if !ap_enabled => { + return Err(WifiError::InternalError( + InternalWifiError::EspErrInvalidArg, + )) } - Configuration::Mixed(ref mut client, ref mut ap) => match conf { - Configuration::None => { - return Err(WifiError::InternalError( - InternalWifiError::EspErrInvalidArg, - )); - } - Configuration::Mixed(_, _) => self.config = conf.clone(), - Configuration::Client(conf) => *client = conf.clone(), - Configuration::AccessPoint(conf) => *ap = conf.clone(), - }, + Configuration::EapClient(_) if !sta_enabled => { + return Err(WifiError::InternalError( + InternalWifiError::EspErrInvalidArg, + )) + } + _ => (), } + self.config = conf.clone(); + match conf { Configuration::None => { return Err(WifiError::InternalError( @@ -2201,6 +2681,7 @@ impl WifiController<'_> { apply_ap_config(ap_config)?; apply_sta_config(sta_config)?; } + Configuration::EapClient(config) => apply_sta_eap_config(config)?, }; Ok(()) @@ -2607,6 +3088,7 @@ mod asynch { } } + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct WifiEventFuture { event: WifiEvent, } @@ -2633,6 +3115,7 @@ mod asynch { } } + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct MultiWifiEventFuture { event: EnumSet, } @@ -2813,6 +3296,7 @@ mod embedded_svc_compat { max_connections: ap.max_connections, }, ), + Configuration::EapClient(_) => panic!("EAP not supported in embedded-svc"), } } } diff --git a/esp-wifi/src/wifi/os_adapter.rs b/esp-wifi/src/wifi/os_adapter.rs index 805aa64f35c..b45a2276d03 100644 --- a/esp-wifi/src/wifi/os_adapter.rs +++ b/esp-wifi/src/wifi/os_adapter.rs @@ -17,9 +17,11 @@ use crate::{ common_adapter::RADIO_CLOCKS, compat::{ common::{ + create_queue, create_recursive_mutex, - create_wifi_queue, + delete_queue, lock_mutex, + number_of_messages_in_queue, receive_queued, send_queued, str_from_c, @@ -27,13 +29,14 @@ use crate::{ unlock_mutex, }, malloc::calloc, - task_runner::spawn_task, }, hal::system::{RadioClockController, RadioPeripherals}, memory_fence::memory_fence, timer::yield_task, }; +static mut QUEUE_HANDLE: *mut crate::binary::c_types::c_void = core::ptr::null_mut(); + // useful for waiting for events - clear and wait for the event bit to be set // again pub(crate) static WIFI_EVENTS: Mutex>> = @@ -376,10 +379,19 @@ pub unsafe extern "C" fn mutex_unlock(mutex: *mut crate::binary::c_types::c_void /// /// ************************************************************************* pub unsafe extern "C" fn queue_create( - _queue_len: u32, - _item_size: u32, + queue_len: u32, + item_size: u32, ) -> *mut crate::binary::c_types::c_void { - todo!("queue_create") + // TODO remove this once fixed in esp_supplicant AND we updated to the fixed + // version - JIRA: WIFI-6676 + let (queue_len, item_size) = if queue_len != 3 && item_size != 4 { + (queue_len, item_size) + } else { + warn!("Fixing queue item_size"); + (3, 8) + }; + + create_queue(queue_len as i32, item_size as i32) } /// ************************************************************************** @@ -395,8 +407,8 @@ pub unsafe extern "C" fn queue_create( /// None /// /// ************************************************************************* -pub unsafe extern "C" fn queue_delete(_queue: *mut crate::binary::c_types::c_void) { - todo!("queue_delete") +pub unsafe extern "C" fn queue_delete(queue: *mut crate::binary::c_types::c_void) { + delete_queue(queue); } /// ************************************************************************** @@ -530,8 +542,8 @@ pub unsafe extern "C" fn queue_recv( /// Message number /// /// ************************************************************************* -pub unsafe extern "C" fn queue_msg_waiting(_queue: *mut crate::binary::c_types::c_void) -> u32 { - todo!("queue_msg_waiting") +pub unsafe extern "C" fn queue_msg_waiting(queue: *mut crate::binary::c_types::c_void) -> u32 { + number_of_messages_in_queue(queue) } /// ************************************************************************** @@ -641,21 +653,15 @@ pub unsafe extern "C" fn task_create_pinned_to_core( core_id ); - *(task_handle as *mut usize) = 0; // we will run it in task 0 + let task_func = core::mem::transmute::< + *mut crate::binary::c_types::c_void, + extern "C" fn(*mut esp_wifi_sys::c_types::c_void), + >(task_func); - if spawn_task( - task_func, - name, - stack_depth, - param, - prio, - task_handle, - core_id, - ) { - 1 - } else { - 0 - } + let task = crate::preempt::arch_specific::task_create(task_func, param, stack_depth as usize); + *(task_handle as *mut usize) = task as usize; + + 1 } /// ************************************************************************** @@ -678,14 +684,14 @@ pub unsafe extern "C" fn task_create_pinned_to_core( /// /// ************************************************************************* pub unsafe extern "C" fn task_create( - _task_func: *mut crate::binary::c_types::c_void, - _name: *const crate::binary::c_types::c_char, - _stack_depth: u32, - _param: *mut crate::binary::c_types::c_void, - _prio: u32, - _task_handle: *mut crate::binary::c_types::c_void, + task_func: *mut crate::binary::c_types::c_void, + name: *const crate::binary::c_types::c_char, + stack_depth: u32, + param: *mut crate::binary::c_types::c_void, + prio: u32, + task_handle: *mut crate::binary::c_types::c_void, ) -> i32 { - todo!("task_create"); + task_create_pinned_to_core(task_func, name, stack_depth, param, prio, task_handle, 0) } /// ************************************************************************** @@ -702,8 +708,15 @@ pub unsafe extern "C" fn task_create( /// None /// /// ************************************************************************* -pub unsafe extern "C" fn task_delete(_task_handle: *mut crate::binary::c_types::c_void) { - todo!("task_delete") +pub unsafe extern "C" fn task_delete(task_handle: *mut crate::binary::c_types::c_void) { + trace!("task delete called for {:?}", task_handle); + + let task = if task_handle.is_null() { + crate::preempt::current_task() + } else { + task_handle as *mut _ + }; + crate::preempt::schedule_task_deletion(task); } /// ************************************************************************** @@ -796,7 +809,6 @@ pub unsafe extern "C" fn task_get_max_priority() -> i32 { /// Memory pointer /// /// ************************************************************************* -#[no_mangle] pub unsafe extern "C" fn malloc(size: usize) -> *mut crate::binary::c_types::c_void { crate::compat::malloc::malloc(size).cast() } @@ -814,7 +826,6 @@ pub unsafe extern "C" fn malloc(size: usize) -> *mut crate::binary::c_types::c_v /// No /// /// ************************************************************************* -#[no_mangle] pub unsafe extern "C" fn free(p: *mut crate::binary::c_types::c_void) { crate::compat::malloc::free(p.cast()); } @@ -894,7 +905,11 @@ pub unsafe extern "C" fn event_post( /// /// ************************************************************************* pub unsafe extern "C" fn get_free_heap_size() -> u32 { - critical_section::with(|cs| crate::HEAP.borrow_ref(cs).free() as u32) + extern "C" { + fn esp_wifi_free_internal_heap() -> usize; + } + + esp_wifi_free_internal_heap() as u32 } /// ************************************************************************** @@ -1651,7 +1666,10 @@ pub unsafe extern "C" fn wifi_create_queue( queue_len: crate::binary::c_types::c_int, item_size: crate::binary::c_types::c_int, ) -> *mut crate::binary::c_types::c_void { - create_wifi_queue(queue_len, item_size) + let queue = create_queue(queue_len, item_size); + QUEUE_HANDLE = queue; + + addr_of_mut!(QUEUE_HANDLE).cast() } /// ************************************************************************** @@ -1668,10 +1686,12 @@ pub unsafe extern "C" fn wifi_create_queue( /// /// ************************************************************************* pub unsafe extern "C" fn wifi_delete_queue(queue: *mut crate::binary::c_types::c_void) { - trace!( - "wifi_delete_queue {:?} - not implemented - doing nothing", - queue - ); + trace!("wifi_delete_queue {:?}", queue); + if queue == addr_of_mut!(QUEUE_HANDLE).cast() { + delete_queue(QUEUE_HANDLE); + } else { + warn!("unknown queue when trying to delete WIFI queue"); + } } /// ************************************************************************** diff --git a/esp-wifi/src/wifi/os_adapter_esp32.rs b/esp-wifi/src/wifi/os_adapter_esp32.rs index 6ef40ee33d1..f91e0b515a8 100644 --- a/esp-wifi/src/wifi/os_adapter_esp32.rs +++ b/esp-wifi/src/wifi/os_adapter_esp32.rs @@ -4,12 +4,7 @@ use crate::hal::{interrupt, peripherals}; -const DR_REG_DPORT_BASE: u32 = 0x3ff00000; -const DPORT_WIFI_CLK_EN_REG: u32 = DR_REG_DPORT_BASE + 0x0CC; -const DPORT_WIFI_CLK_WIFI_EN: u32 = 0x00000406; -const DPORT_WIFI_CLK_WIFI_EN_V: u32 = 0x406; -const DPORT_WIFI_CLK_WIFI_EN_S: u32 = 0; -const DPORT_WIFI_CLK_WIFI_EN_M: u32 = (DPORT_WIFI_CLK_WIFI_EN_V) << (DPORT_WIFI_CLK_WIFI_EN_S); +const DPORT_WIFI_CLK_WIFI_EN_M: u32 = 0x406; pub(crate) fn chip_ints_on(mask: u32) { unsafe { crate::hal::xtensa_lx::interrupt::enable_mask(mask) }; @@ -56,15 +51,21 @@ pub(crate) unsafe extern "C" fn set_intr( } pub(crate) unsafe extern "C" fn wifi_clock_enable() { - let ptr = DPORT_WIFI_CLK_EN_REG as *mut u32; - let old = ptr.read_volatile(); - ptr.write_volatile(old | DPORT_WIFI_CLK_WIFI_EN_M); + let dport = &*crate::hal::peripherals::SYSTEM::ptr(); + dport.wifi_clk_en().modify(|r, w| { + let old = r.bits(); + let new_bits = old | DPORT_WIFI_CLK_WIFI_EN_M; + w.bits(new_bits) + }); } pub(crate) unsafe extern "C" fn wifi_clock_disable() { - let ptr = DPORT_WIFI_CLK_EN_REG as *mut u32; - let old = ptr.read_volatile(); - ptr.write_volatile(old & !DPORT_WIFI_CLK_WIFI_EN_M); + let dport = &*crate::hal::peripherals::SYSTEM::ptr(); + dport.wifi_clk_en().modify(|r, w| { + let old = r.bits(); + let new_bits = old & !DPORT_WIFI_CLK_WIFI_EN_M; + w.bits(new_bits) + }); } /// ************************************************************************** diff --git a/esp-wifi/tuning.md b/esp-wifi/tuning.md index c73625e1877..ec80d27e408 100644 --- a/esp-wifi/tuning.md +++ b/esp-wifi/tuning.md @@ -44,7 +44,6 @@ You can set the following settings |country_code|Country code. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-country-code)| |country_code_operating_class|If not 0: Operating Class table number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-country-code)| |mtu|MTU, see [documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_transmission_unit)| -|heap_size|Size of the WiFi/BLE heap in bytes| |tick_rate_hz|Tick rate of the internal task scheduler in hertz.| |listen_interval|Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval. For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen to beacon is 300 ms| |beacon_timeout|For Station, If the station does not receive a beacon frame from the connected SoftAP during the inactive time, disconnect from SoftAP. Default 6s. Range 6-30| diff --git a/examples/Cargo.toml b/examples/Cargo.toml index e3bb27e0060..c7c22dd935e 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -9,17 +9,18 @@ publish = false aes = "0.8.4" aligned = { version = "0.4.2", optional = true } bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "a5148d8ae679e021b78f53fd33afb8bb35d0b62e", features = [ "macros", "async"] } +bt-hci = "0.1.0" cfg-if = "1.0.0" critical-section = "1.1.2" crypto-bigint = { version = "0.5.5", default-features = false } elliptic-curve = { version = "0.13.8", default-features = false, features = ["sec1"] } -embassy-executor = { version = "0.5.0", features = ["task-arena-size-40960"] } +embassy-executor = { version = "0.6.0", features = ["task-arena-size-12288"] } embassy-futures = "0.1.1" embassy-net = { version = "0.4.0", features = [ "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-sync = "0.6.0" embassy-time = "0.3.1" embassy-time-driver = { version = "0.1.0", optional = true } -embassy-usb = { version = "0.2.0", default-features = false, optional = true } +embassy-usb = { version = "0.2.0", default-features = false } embedded-can = "0.4.1" embedded-graphics = "0.8.1" embedded-hal = "1.0.0" @@ -42,6 +43,7 @@ fugit = "0.3.7" heapless = "0.8.0" hex-literal = "0.4.1" hmac = { version = "0.12.1", default-features = false } +ieee80211 = { version = "0.4.0", default-features = false } ieee802154 = "0.6.1" lis3dh-async = "0.9.3" log = "0.4.22" @@ -54,6 +56,7 @@ smart-leds = "0.4.0" smoltcp = { version = "0.11.0", default-features = false, features = [ "medium-ethernet", "socket-raw"] } ssd1306 = "0.8.4" static_cell = { version = "2.1.0", features = ["nightly"] } +trouble-host = { git = "https://github.com/embassy-rs/trouble", package = "trouble-host", rev = "4f1114ce58e96fe54f5ed7e726f66e1ad8d9ce54", features = [ "log", "gatt" ] } usb-device = "0.3.2" usbd-serial = "0.2.2" @@ -68,13 +71,7 @@ esp32s3 = ["esp-hal/esp32s3", "esp-backtrace/esp32s3", "esp-hal-embassy?/esp32s3 esp-wifi = ["dep:esp-wifi"] -async = ["esp-hal/async", "embassy-usb"] - -embedded-hal-02 = ["esp-hal/embedded-hal-02"] -embedded-hal = ["esp-hal/embedded-hal"] - -embassy = ["dep:esp-hal-embassy"] - +embassy = ["dep:esp-hal-embassy"] embassy-generic-timers = ["embassy-time/generic-queue-8"] opsram-2m = ["esp-hal/opsram-2m"] diff --git a/examples/build.rs b/examples/build.rs index ffde503da6d..77d3bb7d796 100644 --- a/examples/build.rs +++ b/examples/build.rs @@ -8,4 +8,12 @@ fn main() { if cfg!(feature = "esp-wifi") { println!("cargo::rustc-link-arg=-Trom_functions.x"); } + + // Allow building examples in CI in debug mode + println!("cargo:rustc-check-cfg=cfg(is_not_release)"); + println!("cargo:rerun-if-env-changed=CI"); + #[cfg(debug_assertions)] + if std::env::var("CI").is_err() { + println!("cargo::rustc-cfg=is_not_release"); + } } diff --git a/examples/src/bin/adc.rs b/examples/src/bin/adc.rs index 00d528c17ba..08fe4b3786a 100644 --- a/examples/src/bin/adc.rs +++ b/examples/src/bin/adc.rs @@ -19,20 +19,15 @@ use esp_backtrace as _; use esp_hal::{ analog::adc::{Adc, AdcConfig, Attenuation}, - clock::ClockControl, delay::Delay, gpio::Io, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); cfg_if::cfg_if! { @@ -50,7 +45,7 @@ fn main() -> ! { let mut adc1_pin = adc1_config.enable_pin(analog_pin, Attenuation::Attenuation11dB); let mut adc1 = Adc::new(peripherals.ADC1, adc1_config); - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { let pin_value: u16 = nb::block!(adc1.read_oneshot(&mut adc1_pin)).unwrap(); diff --git a/examples/src/bin/adc_cal.rs b/examples/src/bin/adc_cal.rs index 8b8689e1128..a025a6ba52f 100644 --- a/examples/src/bin/adc_cal.rs +++ b/examples/src/bin/adc_cal.rs @@ -15,20 +15,15 @@ use esp_backtrace as _; use esp_hal::{ analog::adc::{Adc, AdcConfig, Attenuation}, - clock::ClockControl, delay::Delay, gpio::Io, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); cfg_if::cfg_if! { @@ -54,7 +49,7 @@ fn main() -> ! { adc1_config.enable_pin_with_cal::<_, AdcCal>(analog_pin, Attenuation::Attenuation11dB); let mut adc1 = Adc::new(peripherals.ADC1, adc1_config); - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { let pin_mv = nb::block!(adc1.read_oneshot(&mut adc1_pin)).unwrap(); diff --git a/examples/src/bin/advanced_serial.rs b/examples/src/bin/advanced_serial.rs index 12b4d455151..b0c4e8d90b3 100644 --- a/examples/src/bin/advanced_serial.rs +++ b/examples/src/bin/advanced_serial.rs @@ -13,29 +13,19 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - delay::Delay, - gpio::Io, - peripherals::Peripherals, - prelude::*, - system::SystemControl, - uart::Uart, -}; +use esp_hal::{delay::Delay, gpio::Io, prelude::*, uart::Uart}; use esp_println::println; use nb::block; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let mut serial1 = Uart::new(peripherals.UART1, &clocks, io.pins.gpio4, io.pins.gpio5).unwrap(); + let mut serial1 = Uart::new(peripherals.UART1, io.pins.gpio4, io.pins.gpio5).unwrap(); - let delay = Delay::new(&clocks); + let delay = Delay::new(); println!("Start"); loop { diff --git a/examples/src/bin/blinky.rs b/examples/src/bin/blinky.rs index 4509656832f..f863b8b19b8 100644 --- a/examples/src/bin/blinky.rs +++ b/examples/src/bin/blinky.rs @@ -10,25 +10,20 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, gpio::{Io, Level, Output}, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); // Set GPIO0 as an output, and set its state high initially. let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let mut led = Output::new(io.pins.gpio0, Level::High); - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { led.toggle(); diff --git a/examples/src/bin/blinky_erased_pins.rs b/examples/src/bin/blinky_erased_pins.rs index d5c6519e065..93d23e56f97 100644 --- a/examples/src/bin/blinky_erased_pins.rs +++ b/examples/src/bin/blinky_erased_pins.rs @@ -13,38 +13,33 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, - gpio::{AnyInput, AnyOutput, Io, Level, Pull}, - peripherals::Peripherals, + gpio::{ErasedPin, Input, Io, Level, Output, Pin, Pull}, prelude::*, - system::SystemControl, }; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); // Set LED GPIOs as an output: - let led1 = AnyOutput::new(io.pins.gpio2, Level::Low); - let led2 = AnyOutput::new(io.pins.gpio4, Level::Low); - let led3 = AnyOutput::new(io.pins.gpio5, Level::Low); + let led1 = Output::new(io.pins.gpio2.degrade(), Level::Low); + let led2 = Output::new(io.pins.gpio4.degrade(), Level::Low); + let led3 = Output::new(io.pins.gpio5.degrade(), Level::Low); // Use boot button as an input: #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] - let button = io.pins.gpio0; + let button = io.pins.gpio0.degrade(); #[cfg(not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")))] - let button = io.pins.gpio9; + let button = io.pins.gpio9.degrade(); - let button = AnyInput::new(button, Pull::Up); + let button = Input::new(button, Pull::Up); let mut pins = [led1, led2, led3]; - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { toggle_pins(&mut pins, &button); @@ -52,7 +47,7 @@ fn main() -> ! { } } -fn toggle_pins(leds: &mut [AnyOutput], button: &AnyInput) { +fn toggle_pins(leds: &mut [Output], button: &Input) { for pin in leds.iter_mut() { pin.toggle(); } diff --git a/examples/src/bin/dac.rs b/examples/src/bin/dac.rs index 7ad9c00226a..d83740d97da 100644 --- a/examples/src/bin/dac.rs +++ b/examples/src/bin/dac.rs @@ -19,21 +19,11 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - analog::dac::Dac, - clock::ClockControl, - delay::Delay, - gpio::Io, - peripherals::Peripherals, - prelude::*, - system::SystemControl, -}; +use esp_hal::{analog::dac::Dac, delay::Delay, gpio::Io, prelude::*}; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -51,7 +41,7 @@ fn main() -> ! { let mut dac1 = Dac::new(peripherals.DAC1, dac1_pin); let mut dac2 = Dac::new(peripherals.DAC2, dac2_pin); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut voltage_dac1: u8 = 200; let mut voltage_dac2: u8 = 255; diff --git a/examples/src/bin/debug_assist.rs b/examples/src/bin/debug_assist.rs index 8b7e09772ca..639037f5cfe 100644 --- a/examples/src/bin/debug_assist.rs +++ b/examples/src/bin/debug_assist.rs @@ -11,22 +11,14 @@ use core::cell::RefCell; use critical_section::Mutex; use esp_backtrace as _; -use esp_hal::{ - assist_debug::DebugAssist, - clock::ClockControl, - peripherals::Peripherals, - prelude::*, - system::SystemControl, -}; +use esp_hal::{assist_debug::DebugAssist, prelude::*}; use esp_println::println; static DA: Mutex>> = Mutex::new(RefCell::new(None)); #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let mut da = DebugAssist::new(peripherals.ASSIST_DEBUG); da.set_interrupt_handler(interrupt_handler); @@ -42,7 +34,9 @@ fn main() -> ! { static mut _stack_end: u32; } + #[allow(unused_unsafe)] let stack_top = unsafe { addr_of_mut!(_stack_start) } as *mut _ as u32; + #[allow(unused_unsafe)] let stack_bottom = unsafe { addr_of_mut!(_stack_end) } as *mut _ as u32; let size = 4096; diff --git a/examples/src/bin/dma_extmem2mem.rs b/examples/src/bin/dma_extmem2mem.rs index 63360a776cf..1fb0081f631 100644 --- a/examples/src/bin/dma_extmem2mem.rs +++ b/examples/src/bin/dma_extmem2mem.rs @@ -7,15 +7,13 @@ #![no_main] use aligned::{Aligned, A64}; +use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, dma::{Dma, DmaPriority, Mem2Mem}, dma_descriptors_chunk_size, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; use log::{error, info}; extern crate alloc; @@ -44,9 +42,6 @@ macro_rules! dma_alloc_buffer { }}; } -#[global_allocator] -static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); - fn init_heap(psram: impl esp_hal::peripheral::Peripheral

) { esp_hal::psram::init_psram(psram); info!( @@ -54,10 +49,11 @@ fn init_heap(psram: impl esp_hal::peripheral::Peripheral

! { esp_println::logger::init_logger(log::LevelFilter::Info); - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); + init_heap(peripherals.PSRAM); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut extram_buffer: &mut [u8] = dma_alloc_buffer!(DATA_SIZE, 64); let mut intram_buffer = dma_buffer_aligned!(DATA_SIZE, A64); - let (tx_descriptors, rx_descriptors) = dma_descriptors_chunk_size!(DATA_SIZE, CHUNK_SIZE); + let (rx_descriptors, tx_descriptors) = dma_descriptors_chunk_size!(DATA_SIZE, CHUNK_SIZE); let dma = Dma::new(peripherals.DMA); let channel = dma.channel0.configure(false, DmaPriority::Priority0); @@ -82,8 +77,8 @@ fn main() -> ! { let mut mem2mem = Mem2Mem::new_with_chunk_size( channel, dma_peripheral, - tx_descriptors, rx_descriptors, + tx_descriptors, CHUNK_SIZE, ) .unwrap(); @@ -94,7 +89,7 @@ fn main() -> ! { } info!(" ext2int: Starting transfer of {} bytes", DATA_SIZE); - match mem2mem.start_transfer(&extram_buffer, &mut intram_buffer) { + match mem2mem.start_transfer(&mut intram_buffer, &extram_buffer) { Ok(dma_wait) => { info!("Transfer started"); dma_wait.wait().unwrap(); @@ -126,7 +121,7 @@ fn main() -> ! { } info!(" int2ext: Starting transfer of {} bytes", DATA_SIZE); - match mem2mem.start_transfer(&intram_buffer, &mut extram_buffer) { + match mem2mem.start_transfer(&mut extram_buffer, &intram_buffer) { Ok(dma_wait) => { info!("Transfer started"); dma_wait.wait().unwrap(); diff --git a/examples/src/bin/dma_mem2mem.rs b/examples/src/bin/dma_mem2mem.rs index 6374d03c7bb..795e158d290 100644 --- a/examples/src/bin/dma_mem2mem.rs +++ b/examples/src/bin/dma_mem2mem.rs @@ -8,13 +8,10 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, dma::{Dma, DmaPriority, Mem2Mem}, dma_buffers, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; use log::{error, info}; @@ -24,12 +21,11 @@ const DATA_SIZE: usize = 1024 * 10; fn main() -> ! { esp_println::logger::init_logger(log::LevelFilter::Info); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - let delay = Delay::new(&clocks); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let (tx_buffer, tx_descriptors, mut rx_buffer, rx_descriptors) = dma_buffers!(DATA_SIZE); + let delay = Delay::new(); + + let (mut rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(DATA_SIZE); let dma = Dma::new(peripherals.DMA); let channel = dma.channel0.configure(false, DmaPriority::Priority0); @@ -39,14 +35,14 @@ fn main() -> ! { let dma_peripheral = peripherals.MEM2MEM1; let mut mem2mem = - Mem2Mem::new(channel, dma_peripheral, tx_descriptors, rx_descriptors).unwrap(); + Mem2Mem::new(channel, dma_peripheral, rx_descriptors, tx_descriptors).unwrap(); for i in 0..core::mem::size_of_val(tx_buffer) { tx_buffer[i] = (i % 256) as u8; } info!("Starting transfer of {} bytes", DATA_SIZE); - let result = mem2mem.start_transfer(&tx_buffer, &mut rx_buffer); + let result = mem2mem.start_transfer(&mut rx_buffer, tx_buffer); match result { Ok(dma_wait) => { info!("Transfer started"); diff --git a/examples/src/bin/embassy_hello_world.rs b/examples/src/bin/embassy_hello_world.rs index 786435e4b4b..d4087d59b2c 100644 --- a/examples/src/bin/embassy_hello_world.rs +++ b/examples/src/bin/embassy_hello_world.rs @@ -12,22 +12,7 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, -}; - -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} +use esp_hal::timer::timg::TimerGroup; #[embassy_executor::task] async fn run() { @@ -40,17 +25,12 @@ async fn run() { #[esp_hal_embassy::main] async fn main(spawner: Spawner) { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init(esp_hal::Config::default()); esp_println::println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); spawner.spawn(run()).ok(); diff --git a/examples/src/bin/embassy_i2c.rs b/examples/src/bin/embassy_i2c.rs index 5585a737131..f9352e1fda5 100644 --- a/examples/src/bin/embassy_i2c.rs +++ b/examples/src/bin/embassy_i2c.rs @@ -11,7 +11,7 @@ //! - SCL => GPIO5 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -19,48 +19,19 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - gpio::Io, - i2c::I2C, - peripherals::Peripherals, - prelude::*, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, -}; +use esp_hal::{gpio::Io, i2c::I2C, prelude::*, timer::timg::TimerGroup}; use lis3dh_async::{Lis3dh, Range, SlaveAddr}; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let i2c0 = I2C::new_async( - peripherals.I2C0, - io.pins.gpio4, - io.pins.gpio5, - 400.kHz(), - &clocks, - ); + let i2c0 = I2C::new_async(peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, 400.kHz()); let mut lis3dh = Lis3dh::new_i2c(i2c0, SlaveAddr::Alternate).await.unwrap(); lis3dh.set_range(Range::G8).await.unwrap(); diff --git a/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs b/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs index 48ee77717f6..f1e5299f171 100644 --- a/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs +++ b/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs @@ -1,4 +1,4 @@ -//! Embassy "async" vesrion of ead calibration data from BMP180 sensor +//! Embassy "async" version of ead calibration data from BMP180 sensor //! //! This example dumps the calibration data from a BMP180 sensor by reading by reading //! with the direct I2C API and the embedded-hal-async I2C API. @@ -12,7 +12,7 @@ //! //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -20,47 +20,18 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - gpio::Io, - i2c::I2C, - peripherals::Peripherals, - prelude::*, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, -}; - -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} +use esp_hal::{gpio::Io, i2c::I2C, prelude::*, timer::timg::TimerGroup}; #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let mut i2c = I2C::new_async( - peripherals.I2C0, - io.pins.gpio4, - io.pins.gpio5, - 400.kHz(), - &clocks, - ); + let mut i2c = I2C::new_async(peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, 400.kHz()); loop { let mut data = [0u8; 22]; diff --git a/examples/src/bin/embassy_i2s_read.rs b/examples/src/bin/embassy_i2s_read.rs index f64b6bbb476..7a644cf5c5d 100644 --- a/examples/src/bin/embassy_i2s_read.rs +++ b/examples/src/bin/embassy_i2s_read.rs @@ -1,4 +1,4 @@ -//! This shows how to continously receive data via I2S. +//! This shows how to continuously receive data via I2S. //! //! Without an additional I2S source device you can connect 3V3 or GND to DIN //! to read 0 or 0xFF or connect DIN to WS to read two different values. @@ -12,7 +12,7 @@ //! - DIN => GPIO5 //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -20,40 +20,22 @@ use embassy_executor::Spawner; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, dma::{Dma, DmaPriority}, dma_buffers, gpio::Io, i2s::{asynch::*, DataFormat, I2s, Standard}, - peripherals::Peripherals, prelude::*, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + timer::timg::TimerGroup, }; use esp_println::println; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -63,7 +45,7 @@ async fn main(_spawner: Spawner) { #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] let dma_channel = dma.channel0; - let (_, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(0, 4092 * 4); + let (rx_buffer, rx_descriptors, _, tx_descriptors) = dma_buffers!(0, 4092 * 4); let i2s = I2s::new( peripherals.I2S0, @@ -71,9 +53,8 @@ async fn main(_spawner: Spawner) { DataFormat::Data16Channel16, 44100u32.Hz(), dma_channel.configure_for_async(false, DmaPriority::Priority0), - tx_descriptors, rx_descriptors, - &clocks, + tx_descriptors, ); #[cfg(not(feature = "esp32"))] diff --git a/examples/src/bin/embassy_i2s_sound.rs b/examples/src/bin/embassy_i2s_sound.rs index 35ba3c6d7af..1425a4a431e 100644 --- a/examples/src/bin/embassy_i2s_sound.rs +++ b/examples/src/bin/embassy_i2s_sound.rs @@ -1,4 +1,4 @@ -//! This shows how to transmit data continously via I2S. +//! This shows how to transmit data continuously via I2S. //! //! Without an additional I2S sink device you can inspect the BCLK, WS //! and DOUT with a logic analyzer. @@ -26,7 +26,7 @@ //! | XSMT | +3V3 | //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -34,15 +34,12 @@ use embassy_executor::Spawner; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, dma::{Dma, DmaPriority}, dma_buffers, gpio::Io, i2s::{asynch::*, DataFormat, I2s, Standard}, - peripherals::Peripherals, prelude::*, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + timer::timg::TimerGroup, }; use esp_println::println; @@ -54,28 +51,13 @@ const SINE: [i16; 64] = [ -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, ]; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -85,7 +67,7 @@ async fn main(_spawner: Spawner) { #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] let dma_channel = dma.channel0; - let (tx_buffer, tx_descriptors, _, rx_descriptors) = dma_buffers!(32000, 0); + let (_, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000, 0); let i2s = I2s::new( peripherals.I2S0, @@ -93,9 +75,8 @@ async fn main(_spawner: Spawner) { DataFormat::Data16Channel16, 44100u32.Hz(), dma_channel.configure_for_async(false, DmaPriority::Priority0), - tx_descriptors, rx_descriptors, - &clocks, + tx_descriptors, ); let i2s_tx = i2s diff --git a/examples/src/bin/embassy_multicore.rs b/examples/src/bin/embassy_multicore.rs index ca0638b2808..7d871f2b006 100644 --- a/examples/src/bin/embassy_multicore.rs +++ b/examples/src/bin/embassy_multicore.rs @@ -19,13 +19,10 @@ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal} use embassy_time::{Duration, Ticker}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, cpu_control::{CpuControl, Stack}, get_core, - gpio::{AnyOutput, Io, Level}, - peripherals::Peripherals, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + gpio::{ErasedPin, Io, Level, Output, Pin}, + timer::{timg::TimerGroup, ErasedTimer}, }; use esp_hal_embassy::Executor; use esp_println::println; @@ -33,21 +30,11 @@ use static_cell::StaticCell; static mut APP_CORE_STACK: Stack<8192> = Stack::new(); -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - /// Waits for a message that contains a duration, then flashes a led for that /// duration of time. #[embassy_executor::task] async fn control_led( - mut led: AnyOutput<'static>, + mut led: Output<'static, ErasedPin>, control: &'static Signal, ) { println!("Starting control_led() on core {}", get_core() as usize); @@ -64,25 +51,21 @@ async fn control_led( #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let timg0 = TimerGroup::new(peripherals.TIMG0); let timer0: ErasedTimer = timg0.timer0.into(); let timer1: ErasedTimer = timg0.timer1.into(); - let timers = [OneShotTimer::new(timer0), OneShotTimer::new(timer1)]; - let timers = mk_static!([OneShotTimer; 2], timers); - esp_hal_embassy::init(&clocks, timers); + esp_hal_embassy::init([timer0, timer1]); let mut cpu_control = CpuControl::new(peripherals.CPU_CTRL); static LED_CTRL: StaticCell> = StaticCell::new(); let led_ctrl_signal = &*LED_CTRL.init(Signal::new()); - let led = AnyOutput::new(io.pins.gpio0, Level::Low); + let led = Output::new(io.pins.gpio0.degrade(), Level::Low); let _guard = cpu_control .start_app_core(unsafe { &mut *addr_of_mut!(APP_CORE_STACK) }, move || { diff --git a/examples/src/bin/embassy_multicore_interrupt.rs b/examples/src/bin/embassy_multicore_interrupt.rs index 648747dd37f..0c69a468f30 100644 --- a/examples/src/bin/embassy_multicore_interrupt.rs +++ b/examples/src/bin/embassy_multicore_interrupt.rs @@ -18,15 +18,12 @@ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal} use embassy_time::{Duration, Ticker}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, cpu_control::{CpuControl, Stack}, get_core, - gpio::{AnyOutput, Io, Level}, - interrupt::Priority, - peripherals::Peripherals, + gpio::{ErasedPin, Io, Level, Output, Pin}, + interrupt::{software::SoftwareInterruptControl, Priority}, prelude::*, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + timer::{timg::TimerGroup, ErasedTimer}, }; use esp_hal_embassy::InterruptExecutor; use esp_println::println; @@ -34,21 +31,11 @@ use static_cell::StaticCell; static mut APP_CORE_STACK: Stack<8192> = Stack::new(); -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - /// Waits for a message that contains a duration, then flashes a led for that /// duration of time. #[embassy_executor::task] async fn control_led( - mut led: AnyOutput<'static>, + mut led: Output<'static, ErasedPin>, control: &'static Signal, ) { println!("Starting control_led() on core {}", get_core() as usize); @@ -84,29 +71,26 @@ async fn enable_disable_led(control: &'static Signal ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let timg0 = TimerGroup::new(peripherals.TIMG0); let timer0: ErasedTimer = timg0.timer0.into(); let timer1: ErasedTimer = timg0.timer1.into(); - let timers = [OneShotTimer::new(timer0), OneShotTimer::new(timer1)]; - let timers = mk_static!([OneShotTimer; 2], timers); - esp_hal_embassy::init(&clocks, timers); + esp_hal_embassy::init([timer0, timer1]); let mut cpu_control = CpuControl::new(peripherals.CPU_CTRL); static LED_CTRL: StaticCell> = StaticCell::new(); let led_ctrl_signal = &*LED_CTRL.init(Signal::new()); - let led = AnyOutput::new(io.pins.gpio0, Level::Low); + let led = Output::new(io.pins.gpio0.degrade(), Level::Low); static EXECUTOR_CORE_1: StaticCell> = StaticCell::new(); - let executor_core1 = - InterruptExecutor::new(system.software_interrupt_control.software_interrupt1); + let executor_core1 = InterruptExecutor::new(sw_ints.software_interrupt1); let executor_core1 = EXECUTOR_CORE_1.init(executor_core1); let _guard = cpu_control @@ -121,8 +105,7 @@ fn main() -> ! { .unwrap(); static EXECUTOR_CORE_0: StaticCell> = StaticCell::new(); - let executor_core0 = - InterruptExecutor::new(system.software_interrupt_control.software_interrupt0); + let executor_core0 = InterruptExecutor::new(sw_ints.software_interrupt0); let executor_core0 = EXECUTOR_CORE_0.init(executor_core0); let spawner = executor_core0.start(Priority::Priority1); diff --git a/examples/src/bin/embassy_multiprio.rs b/examples/src/bin/embassy_multiprio.rs index 36386c0d4da..f664277b019 100644 --- a/examples/src/bin/embassy_multiprio.rs +++ b/examples/src/bin/embassy_multiprio.rs @@ -24,26 +24,13 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Ticker, Timer}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, - interrupt::Priority, - peripherals::Peripherals, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + interrupt::{software::SoftwareInterruptControl, Priority}, + timer::{timg::TimerGroup, ErasedTimer}, }; use esp_hal_embassy::InterruptExecutor; use esp_println::println; use static_cell::StaticCell; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - /// Periodically print something. #[embassy_executor::task] async fn high_prio() { @@ -83,33 +70,29 @@ async fn low_prio_async() { async fn main(low_prio_spawner: Spawner) { esp_println::logger::init_logger_from_env(); println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); + + let timg0 = TimerGroup::new(peripherals.TIMG0); let timer0: ErasedTimer = timg0.timer0.into(); - let timer0 = OneShotTimer::new(timer0); - - #[cfg(not(feature = "esp32c2"))] - let timer1 = { - let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks); - let timer0: ErasedTimer = timg1.timer0.into(); - OneShotTimer::new(timer0) - }; - #[cfg(feature = "esp32c2")] - let timer1 = { - let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - OneShotTimer::new(alarm0) - }; - - let timers = [timer0, timer1]; - let timers = mk_static!([OneShotTimer; 2], timers); - esp_hal_embassy::init(&clocks, timers); + + cfg_if::cfg_if! { + if #[cfg(feature = "esp32c2")] { + use esp_hal::timer::systimer::{SystemTimer, Target}; + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + let timer1: ErasedTimer = systimer.alarm0.into(); + } else { + let timg1 = TimerGroup::new(peripherals.TIMG1); + let timer1: ErasedTimer = timg1.timer0.into(); + } + } + + esp_hal_embassy::init([timer0, timer1]); static EXECUTOR: StaticCell> = StaticCell::new(); - let executor = InterruptExecutor::new(system.software_interrupt_control.software_interrupt2); + let executor = InterruptExecutor::new(sw_ints.software_interrupt2); let executor = EXECUTOR.init(executor); let spawner = executor.start(Priority::Priority3); diff --git a/examples/src/bin/embassy_parl_io_rx.rs b/examples/src/bin/embassy_parl_io_rx.rs index ef74d3a7e7e..687502baeb3 100644 --- a/examples/src/bin/embassy_parl_io_rx.rs +++ b/examples/src/bin/embassy_parl_io_rx.rs @@ -5,7 +5,7 @@ //! - Data pins => GPIO1, GPIO2, GPIO3, and GPIO4. //% CHIPS: esp32c6 esp32h2 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -14,44 +14,26 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, dma::{Dma, DmaPriority}, dma_buffers, gpio::Io, parl_io::{no_clk_pin, BitPackOrder, ParlIoRxOnly, RxFourBits}, - peripherals::Peripherals, prelude::*, - system::SystemControl, - timer::{systimer::SystemTimer, ErasedTimer, OneShotTimer}, + timer::systimer::{SystemTimer, Target}, }; use esp_println::println; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { esp_println::println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let systimer = SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init(systimer.alarm0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let (_, _, rx_buffer, rx_descriptors) = dma_buffers!(0, 32000); + let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(0, 32000); let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; @@ -63,7 +45,6 @@ async fn main(_spawner: Spawner) { dma_channel.configure_for_async(false, DmaPriority::Priority0), rx_descriptors, 1.MHz(), - &clocks, ) .unwrap(); diff --git a/examples/src/bin/embassy_parl_io_tx.rs b/examples/src/bin/embassy_parl_io_tx.rs index 7e7a6165e0e..7b16470ba88 100644 --- a/examples/src/bin/embassy_parl_io_tx.rs +++ b/examples/src/bin/embassy_parl_io_tx.rs @@ -9,7 +9,7 @@ //! You can use a logic analyzer to see how the pins are used. //% CHIPS: esp32c6 esp32h2 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -18,7 +18,6 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, dma::{Dma, DmaPriority}, dma_buffers, gpio::Io, @@ -30,39 +29,22 @@ use esp_hal::{ TxFourBits, TxPinConfigWithValidPin, }, - peripherals::Peripherals, prelude::*, - system::SystemControl, - timer::{systimer::SystemTimer, ErasedTimer, OneShotTimer}, + timer::systimer::{SystemTimer, Target}, }; use esp_println::println; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { esp_println::println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let systimer = SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init(systimer.alarm0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let (tx_buffer, tx_descriptors, _, _) = dma_buffers!(32000, 0); + let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(32000, 0); let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; @@ -76,7 +58,6 @@ async fn main(_spawner: Spawner) { dma_channel.configure_for_async(false, DmaPriority::Priority0), tx_descriptors, 1.MHz(), - &clocks, ) .unwrap(); diff --git a/examples/src/bin/embassy_rmt_rx.rs b/examples/src/bin/embassy_rmt_rx.rs index cf174a7c205..0befacef8ff 100644 --- a/examples/src/bin/embassy_rmt_rx.rs +++ b/examples/src/bin/embassy_rmt_rx.rs @@ -4,7 +4,7 @@ //! - Connect GPIO4 and GPIO5 //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -13,33 +13,20 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, - gpio::{Gpio5, Io, Level, Output}, - peripherals::Peripherals, + gpio::{Io, Level, Output}, prelude::*, rmt::{asynch::RxChannelAsync, PulseCode, Rmt, RxChannelConfig, RxChannelCreatorAsync}, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + timer::timg::TimerGroup, }; use esp_println::{print, println}; const WIDTH: usize = 80; -#[cfg(debug_assertions)] +#[cfg(is_not_release)] compile_error!("Run this example in release mode"); -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[embassy_executor::task] -async fn signal_task(mut pin: Output<'static, Gpio5>) { +async fn signal_task(mut pin: Output<'static>) { loop { for _ in 0..10 { pin.toggle(); @@ -52,15 +39,10 @@ async fn signal_task(mut pin: Output<'static, Gpio5>) { #[esp_hal_embassy::main] async fn main(spawner: Spawner) { println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -72,7 +54,7 @@ async fn main(spawner: Spawner) { } }; - let rmt = Rmt::new_async(peripherals.RMT, freq, &clocks).unwrap(); + let rmt = Rmt::new_async(peripherals.RMT, freq).unwrap(); let rx_config = RxChannelConfig { clk_divider: 255, idle_threshold: 10000, diff --git a/examples/src/bin/embassy_rmt_tx.rs b/examples/src/bin/embassy_rmt_tx.rs index 45244988e3e..04ddc45d5cf 100644 --- a/examples/src/bin/embassy_rmt_tx.rs +++ b/examples/src/bin/embassy_rmt_tx.rs @@ -6,7 +6,7 @@ //! - generated pulses => GPIO4 //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -15,38 +15,20 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, - peripherals::Peripherals, prelude::*, rmt::{asynch::TxChannelAsync, PulseCode, Rmt, TxChannelConfig, TxChannelCreatorAsync}, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + timer::timg::TimerGroup, }; use esp_println::println; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -58,7 +40,7 @@ async fn main(_spawner: Spawner) { } }; - let rmt = Rmt::new_async(peripherals.RMT, freq, &clocks).unwrap(); + let rmt = Rmt::new_async(peripherals.RMT, freq).unwrap(); let mut channel = rmt .channel0 diff --git a/examples/src/bin/embassy_serial.rs b/examples/src/bin/embassy_serial.rs index 223138040e2..fff7c07d451 100644 --- a/examples/src/bin/embassy_serial.rs +++ b/examples/src/bin/embassy_serial.rs @@ -1,10 +1,10 @@ //! embassy serial //! //! This is an example of running the embassy executor and asynchronously -//! writing to and reading from uart +//! writing to and reading from UART. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -13,11 +13,9 @@ use embassy_executor::Spawner; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, signal::Signal}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, - peripherals::{Peripherals, UART0}, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + peripherals::UART0, + timer::timg::TimerGroup, uart::{ config::{AtCmdConfig, Config}, Uart, @@ -33,16 +31,6 @@ const READ_BUF_SIZE: usize = 64; // EOT (CTRL-D) const AT_CMD: u8 = 0x04; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[embassy_executor::task] async fn writer( mut tx: UartTx<'static, UART0, Async>, @@ -90,41 +78,36 @@ async fn reader( #[esp_hal_embassy::main] async fn main(spawner: Spawner) { esp_println::println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); // Default pins for Uart/Serial communication - #[cfg(feature = "esp32")] - let (tx_pin, rx_pin) = (io.pins.gpio1, io.pins.gpio3); - #[cfg(feature = "esp32c2")] - let (tx_pin, rx_pin) = (io.pins.gpio20, io.pins.gpio19); - #[cfg(feature = "esp32c3")] - let (tx_pin, rx_pin) = (io.pins.gpio21, io.pins.gpio20); - #[cfg(feature = "esp32c6")] - let (tx_pin, rx_pin) = (io.pins.gpio16, io.pins.gpio17); - #[cfg(feature = "esp32h2")] - let (tx_pin, rx_pin) = (io.pins.gpio24, io.pins.gpio23); - #[cfg(feature = "esp32s2")] - let (tx_pin, rx_pin) = (io.pins.gpio43, io.pins.gpio44); - #[cfg(feature = "esp32s3")] - let (tx_pin, rx_pin) = (io.pins.gpio43, io.pins.gpio44); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + let (tx_pin, rx_pin) = (io.pins.gpio1, io.pins.gpio3); + } else if #[cfg(feature = "esp32c2")] { + let (tx_pin, rx_pin) = (io.pins.gpio20, io.pins.gpio19); + } else if #[cfg(feature = "esp32c3")] { + let (tx_pin, rx_pin) = (io.pins.gpio21, io.pins.gpio20); + } else if #[cfg(feature = "esp32c6")] { + let (tx_pin, rx_pin) = (io.pins.gpio16, io.pins.gpio17); + } else if #[cfg(feature = "esp32h2")] { + let (tx_pin, rx_pin) = (io.pins.gpio24, io.pins.gpio23); + } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { + let (tx_pin, rx_pin) = (io.pins.gpio43, io.pins.gpio44); + } + } let config = Config::default().rx_fifo_full_threshold(READ_BUF_SIZE as u16); - let mut uart0 = - Uart::new_async_with_config(peripherals.UART0, config, &clocks, tx_pin, rx_pin).unwrap(); + let mut uart0 = Uart::new_async_with_config(peripherals.UART0, config, rx_pin, tx_pin).unwrap(); uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None)); - let (tx, rx) = uart0.split(); + let (rx, tx) = uart0.split(); static SIGNAL: StaticCell> = StaticCell::new(); let signal = &*SIGNAL.init(Signal::new()); diff --git a/examples/src/bin/embassy_spi.rs b/examples/src/bin/embassy_spi.rs index fbfd24dd4a1..9ac562444b3 100644 --- a/examples/src/bin/embassy_spi.rs +++ b/examples/src/bin/embassy_spi.rs @@ -13,7 +13,7 @@ //! CS => GPIO5 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -22,42 +22,21 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, dma::*, - dma_descriptors, + dma_buffers, gpio::Io, - peripherals::Peripherals, prelude::*, - spi::{ - master::{prelude::*, Spi}, - SpiMode, - }, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + spi::{master::Spi, SpiMode}, + timer::timg::TimerGroup, }; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { esp_println::println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let sclk = io.pins.gpio0; @@ -67,20 +46,22 @@ async fn main(_spawner: Spawner) { let dma = Dma::new(peripherals.DMA); - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } - let (descriptors, rx_descriptors) = dma_descriptors!(32000); + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) + let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) .with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs)) - .with_dma( - dma_channel.configure_for_async(false, DmaPriority::Priority0), - descriptors, - rx_descriptors, - ); + .with_dma(dma_channel.configure_for_async(false, DmaPriority::Priority0)) + .with_buffers(dma_rx_buf, dma_tx_buf); let send_buffer = [0, 1, 2, 3, 4, 5, 6, 7]; loop { diff --git a/examples/src/bin/embassy_touch.rs b/examples/src/bin/embassy_touch.rs index 9d677b90085..808caf2a869 100644 --- a/examples/src/bin/embassy_touch.rs +++ b/examples/src/bin/embassy_touch.rs @@ -7,7 +7,7 @@ //! pad on a PCB). //% CHIPS: esp32 -//% FEATURES: async embassy esp-hal-embassy/integrated-timers +//% FEATURES: embassy esp-hal-embassy/integrated-timers #![no_std] #![no_main] @@ -17,39 +17,20 @@ use embassy_futures::select::{select, Either}; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, - peripherals::Peripherals, rtc_cntl::Rtc, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + timer::timg::TimerGroup, touch::{Touch, TouchConfig, TouchPad}, }; use esp_println::println; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let mut rtc = Rtc::new(peripherals.LPWR); diff --git a/examples/src/bin/embassy_twai.rs b/examples/src/bin/embassy_twai.rs index a3d95cefd49..93296075dc2 100644 --- a/examples/src/bin/embassy_twai.rs +++ b/examples/src/bin/embassy_twai.rs @@ -15,7 +15,7 @@ //! - RX => GPIO2 //% CHIPS: esp32c3 esp32c6 esp32s2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -25,29 +25,17 @@ use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; use embedded_can::{Frame, Id}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, interrupt, - peripherals::{self, Peripherals, TWAI0}, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, - twai::{self, EspTwaiFrame, TwaiRx, TwaiTx}, + peripherals::{self, TWAI0}, + timer::timg::TimerGroup, + twai::{self, EspTwaiFrame, TwaiMode, TwaiRx, TwaiTx}, }; use esp_println::println; use static_cell::StaticCell; type TwaiOutbox = Channel; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[embassy_executor::task] async fn receiver( mut rx: TwaiRx<'static, TWAI0, esp_hal::Async>, @@ -94,15 +82,10 @@ async fn transmitter( #[esp_hal_embassy::main] async fn main(spawner: Spawner) { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -118,10 +101,10 @@ async fn main(spawner: Spawner) { // state that prevents transmission but allows configuration. let mut can_config = twai::TwaiConfiguration::new_async_no_transceiver( peripherals.TWAI0, - can_tx_pin, can_rx_pin, - &clocks, + can_tx_pin, CAN_BAUDRATE, + TwaiMode::Normal, ); // Partially filter the incoming messages to reduce overhead of receiving @@ -140,7 +123,7 @@ async fn main(spawner: Spawner) { let can = can_config.start(); // Get separate transmit and receive halves of the peripheral. - let (tx, rx) = can.split(); + let (rx, tx) = can.split(); interrupt::enable( peripherals::Interrupt::TWAI0, diff --git a/examples/src/bin/embassy_usb_serial.rs b/examples/src/bin/embassy_usb_serial.rs index 9e489984e1b..e2054358dd3 100644 --- a/examples/src/bin/embassy_usb_serial.rs +++ b/examples/src/bin/embassy_usb_serial.rs @@ -7,7 +7,7 @@ //! - DM => GPIO19 //% CHIPS: esp32s2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -21,39 +21,21 @@ use embassy_usb::{ }; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, otg_fs::{ asynch::{Config, Driver}, Usb, }, - peripherals::Peripherals, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + timer::timg::TimerGroup, }; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] -async fn main(_spawner: Spawner) -> () { +async fn main(_spawner: Spawner) { esp_println::println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); diff --git a/examples/src/bin/embassy_usb_serial_jtag.rs b/examples/src/bin/embassy_usb_serial_jtag.rs index 2501270ebb3..8944ed2dc92 100644 --- a/examples/src/bin/embassy_usb_serial_jtag.rs +++ b/examples/src/bin/embassy_usb_serial_jtag.rs @@ -3,7 +3,7 @@ //! Most dev-kits use a USB-UART-bridge - in that case you won't see any output. //% CHIPS: esp32c3 esp32c6 esp32h2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -12,10 +12,7 @@ use embassy_executor::Spawner; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, signal::Signal}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + timer::timg::TimerGroup, usb_serial_jtag::{UsbSerialJtag, UsbSerialJtagRx, UsbSerialJtagTx}, Async, }; @@ -23,16 +20,6 @@ use static_cell::StaticCell; const MAX_BUFFER_SIZE: usize = 512; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[embassy_executor::task] async fn writer( mut tx: UsbSerialJtagTx<'static, Async>, @@ -67,25 +54,21 @@ async fn reader( string_buffer.extend_from_slice(&rbuf[..len]).unwrap(); signal.signal(heapless::String::from_utf8(string_buffer).unwrap()); } + #[allow(unreachable_patterns)] Err(e) => esp_println::println!("RX Error: {:?}", e), } } } #[esp_hal_embassy::main] -async fn main(spawner: Spawner) -> () { +async fn main(spawner: Spawner) { esp_println::println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); - let (tx, rx) = UsbSerialJtag::new_async(peripherals.USB_DEVICE).split(); + let (rx, tx) = UsbSerialJtag::new_async(peripherals.USB_DEVICE).split(); static SIGNAL: StaticCell>> = StaticCell::new(); diff --git a/examples/src/bin/embassy_wait.rs b/examples/src/bin/embassy_wait.rs index 2f6c9d30dae..9f80b2dbc9d 100644 --- a/examples/src/bin/embassy_wait.rs +++ b/examples/src/bin/embassy_wait.rs @@ -3,7 +3,7 @@ //! This is an example of asynchronously `Wait`ing for a pin state (boot button) to change. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: async embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers #![no_std] #![no_main] @@ -12,41 +12,27 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::{Input, Io, Pull}, - peripherals::Peripherals, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + timer::timg::TimerGroup, }; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { esp_println::println!("Init!"); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] - let mut input = Input::new(io.pins.gpio0, Pull::Down); - #[cfg(not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")))] - let mut input = Input::new(io.pins.gpio9, Pull::Down); + + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { + let mut input = Input::new(io.pins.gpio0, Pull::Down); + } else { + let mut input = Input::new(io.pins.gpio9, Pull::Down); + } + } loop { esp_println::println!("Waiting..."); diff --git a/examples/src/bin/etm_blinky_systimer.rs b/examples/src/bin/etm_blinky_systimer.rs index b72ea29ea43..0a295c14ab1 100644 --- a/examples/src/bin/etm_blinky_systimer.rs +++ b/examples/src/bin/etm_blinky_systimer.rs @@ -17,18 +17,18 @@ use esp_hal::{ Level, Pull, }, - peripherals::Peripherals, prelude::*, - timer::systimer::{etm::SysTimerEtmEvent, SystemTimer}, + timer::systimer::{etm::SysTimerEtmEvent, Periodic, SystemTimer}, }; use fugit::ExtU32; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let syst = SystemTimer::new(peripherals.SYSTIMER); - let mut alarm0 = syst.alarm0.into_periodic(); + let syst_alarms = syst.split::(); + let mut alarm0 = syst_alarms.alarm0; alarm0.set_period(1u32.secs()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); diff --git a/examples/src/bin/etm_gpio.rs b/examples/src/bin/etm_gpio.rs index ecced8d304e..112fa2db789 100644 --- a/examples/src/bin/etm_gpio.rs +++ b/examples/src/bin/etm_gpio.rs @@ -15,18 +15,18 @@ use esp_hal::{ etm::{GpioEtmChannels, GpioEtmInputConfig, GpioEtmOutputConfig}, Io, Level, + Output, Pull, }, - peripherals::Peripherals, prelude::*, }; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let mut led = io.pins.gpio1; + let mut led = Output::new(io.pins.gpio1, Level::Low); let button = io.pins.gpio9; led.set_high(); diff --git a/examples/src/bin/etm_timer.rs b/examples/src/bin/etm_timer.rs index 802588313a2..d382c2755d3 100644 --- a/examples/src/bin/etm_timer.rs +++ b/examples/src/bin/etm_timer.rs @@ -11,12 +11,10 @@ use core::cell::RefCell; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, etm::Etm, - peripherals::{Peripherals, TIMG0}, + peripherals::TIMG0, prelude::*, - system::SystemControl, timer::timg::{ etm::{TimerEtmEvents, TimerEtmTasks}, Timer, @@ -30,11 +28,9 @@ static TIMER0: Mutex, esp_hal::Blocking>>>> = #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let timg0 = TimerGroup::new(peripherals.TIMG0); let timer0 = timg0.timer0; timer0.set_interrupt_handler(tg0_t0_level); @@ -59,7 +55,7 @@ fn main() -> ! { TIMER0.borrow_ref_mut(cs).replace(timer0); }); - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { delay.delay_millis(500u32); diff --git a/examples/src/bin/gpio_interrupt.rs b/examples/src/bin/gpio_interrupt.rs index a4f0cfad313..c599555491a 100644 --- a/examples/src/bin/gpio_interrupt.rs +++ b/examples/src/bin/gpio_interrupt.rs @@ -16,35 +16,30 @@ use core::cell::RefCell; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, - gpio::{self, Event, Input, Io, Level, Output, Pull}, + gpio::{Event, Input, Io, Level, Output, Pull}, macros::ram, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; -#[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] -static BUTTON: Mutex>>> = Mutex::new(RefCell::new(None)); -#[cfg(not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")))] -static BUTTON: Mutex>>> = Mutex::new(RefCell::new(None)); +static BUTTON: Mutex>> = Mutex::new(RefCell::new(None)); #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); // Set GPIO2 as an output, and set its state high initially. let mut io = Io::new(peripherals.GPIO, peripherals.IO_MUX); io.set_interrupt_handler(handler); let mut led = Output::new(io.pins.gpio2, Level::Low); - #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] - let button = io.pins.gpio0; - #[cfg(not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")))] - let button = io.pins.gpio9; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { + let button = io.pins.gpio0; + } else { + let button = io.pins.gpio9; + } + } let mut button = Input::new(button, Pull::Up); @@ -54,7 +49,7 @@ fn main() -> ! { }); led.set_high(); - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { led.toggle(); @@ -65,13 +60,16 @@ fn main() -> ! { #[handler] #[ram] fn handler() { - #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] - esp_println::println!( - "GPIO Interrupt with priority {}", - esp_hal::xtensa_lx::interrupt::get_level() - ); - #[cfg(not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")))] - esp_println::println!("GPIO Interrupt"); + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { + esp_println::println!( + "GPIO Interrupt with priority {}", + esp_hal::xtensa_lx::interrupt::get_level() + ); + } else { + esp_println::println!("GPIO Interrupt"); + } + } if critical_section::with(|cs| { BUTTON diff --git a/examples/src/bin/hello_rgb.rs b/examples/src/bin/hello_rgb.rs index e1223a06e5c..46c38dab930 100644 --- a/examples/src/bin/hello_rgb.rs +++ b/examples/src/bin/hello_rgb.rs @@ -25,15 +25,7 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - delay::Delay, - gpio::Io, - peripherals::Peripherals, - prelude::*, - rmt::Rmt, - system::SystemControl, -}; +use esp_hal::{delay::Delay, gpio::Io, prelude::*, rmt::Rmt}; use esp_hal_smartled::{smartLedBuffer, SmartLedsAdapter}; use smart_leds::{ brightness, @@ -44,9 +36,7 @@ use smart_leds::{ #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -67,17 +57,22 @@ fn main() -> ! { } // Configure RMT peripheral globally - #[cfg(not(feature = "esp32h2"))] - let rmt = Rmt::new(peripherals.RMT, 80.MHz(), &clocks).unwrap(); - #[cfg(feature = "esp32h2")] - let rmt = Rmt::new(peripherals.RMT, 32.MHz(), &clocks).unwrap(); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32h2")] { + let freq = 32.MHz(); + } else { + let freq = 80.MHz(); + } + } + + let rmt = Rmt::new(peripherals.RMT, freq).unwrap(); // We use one of the RMT channels to instantiate a `SmartLedsAdapter` which can // be used directly with all `smart_led` implementations let rmt_buffer = smartLedBuffer!(1); - let mut led = SmartLedsAdapter::new(rmt.channel0, led_pin, rmt_buffer, &clocks); + let mut led = SmartLedsAdapter::new(rmt.channel0, led_pin, rmt_buffer); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut color = Hsv { hue: 0, diff --git a/examples/src/bin/hello_world.rs b/examples/src/bin/hello_world.rs index 59a819914a9..44d18dc9a7e 100644 --- a/examples/src/bin/hello_world.rs +++ b/examples/src/bin/hello_world.rs @@ -15,44 +15,35 @@ use core::fmt::Write; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - delay::Delay, - gpio::Io, - peripherals::Peripherals, - prelude::*, - system::SystemControl, - uart::Uart, -}; +use esp_hal::{delay::Delay, gpio::Io, prelude::*, uart::Uart}; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); // Default pins for Uart/Serial communication - #[cfg(feature = "esp32")] - let (mut tx_pin, mut rx_pin) = (io.pins.gpio1, io.pins.gpio3); - #[cfg(feature = "esp32c2")] - let (mut tx_pin, mut rx_pin) = (io.pins.gpio20, io.pins.gpio19); - #[cfg(feature = "esp32c3")] - let (mut tx_pin, mut rx_pin) = (io.pins.gpio21, io.pins.gpio20); - #[cfg(feature = "esp32c6")] - let (mut tx_pin, mut rx_pin) = (io.pins.gpio16, io.pins.gpio17); - #[cfg(feature = "esp32h2")] - let (mut tx_pin, mut rx_pin) = (io.pins.gpio24, io.pins.gpio23); - #[cfg(feature = "esp32s2")] - let (mut tx_pin, mut rx_pin) = (io.pins.gpio43, io.pins.gpio44); - #[cfg(feature = "esp32s3")] - let (mut tx_pin, mut rx_pin) = (io.pins.gpio43, io.pins.gpio44); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + let (mut tx_pin, mut rx_pin) = (io.pins.gpio1, io.pins.gpio3); + } else if #[cfg(feature = "esp32c2")] { + let (mut tx_pin, mut rx_pin) = (io.pins.gpio20, io.pins.gpio19); + } else if #[cfg(feature = "esp32c3")] { + let (mut tx_pin, mut rx_pin) = (io.pins.gpio21, io.pins.gpio20); + } else if #[cfg(feature = "esp32c6")] { + let (mut tx_pin, mut rx_pin) = (io.pins.gpio16, io.pins.gpio17); + } else if #[cfg(feature = "esp32h2")] { + let (mut tx_pin, mut rx_pin) = (io.pins.gpio24, io.pins.gpio23); + } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { + let (mut tx_pin, mut rx_pin) = (io.pins.gpio43, io.pins.gpio44); + } + } let mut uart0 = - Uart::new_with_default_pins(peripherals.UART0, &clocks, &mut tx_pin, &mut rx_pin).unwrap(); + Uart::new_with_default_pins(peripherals.UART0, &mut rx_pin, &mut tx_pin).unwrap(); loop { writeln!(uart0, "Hello world!").unwrap(); diff --git a/examples/src/bin/hmac.rs b/examples/src/bin/hmac.rs index 3df3ef86400..87b5118c1bd 100644 --- a/examples/src/bin/hmac.rs +++ b/examples/src/bin/hmac.rs @@ -59,12 +59,9 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, hmac::{Hmac, HmacPurpose, KeyId}, - peripherals::Peripherals, prelude::*, rng::Rng, - system::SystemControl, timer::systimer::SystemTimer, }; use esp_println::println; @@ -76,9 +73,7 @@ type HmacSha256 = HmacSw; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let mut rng = Rng::new(peripherals.RNG); diff --git a/examples/src/bin/i2c_bmp180_calibration_data.rs b/examples/src/bin/i2c_bmp180_calibration_data.rs index adf19b1da9c..0f703abe1c4 100644 --- a/examples/src/bin/i2c_bmp180_calibration_data.rs +++ b/examples/src/bin/i2c_bmp180_calibration_data.rs @@ -12,33 +12,18 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - gpio::Io, - i2c::I2C, - peripherals::Peripherals, - prelude::*, - system::SystemControl, -}; +use esp_hal::{gpio::Io, i2c::I2C, prelude::*}; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); // Create a new peripheral object with the described wiring and standard // I2C clock speed: - let mut i2c = I2C::new( - peripherals.I2C0, - io.pins.gpio4, - io.pins.gpio5, - 100.kHz(), - &clocks, - ); + let mut i2c = I2C::new(peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, 100.kHz()); loop { let mut data = [0u8; 22]; diff --git a/examples/src/bin/i2c_display.rs b/examples/src/bin/i2c_display.rs index 7b874872a25..05a950ae850 100644 --- a/examples/src/bin/i2c_display.rs +++ b/examples/src/bin/i2c_display.rs @@ -8,7 +8,6 @@ //! - SCL => GPIO5 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: embedded-hal-02 #![no_std] #![no_main] @@ -23,35 +22,19 @@ use embedded_graphics::{ text::{Alignment, Text}, }; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - delay::Delay, - gpio::Io, - i2c::I2C, - peripherals::Peripherals, - prelude::*, - system::SystemControl, -}; +use esp_hal::{delay::Delay, gpio::Io, i2c::I2C, prelude::*}; use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); // Create a new peripheral object with the described wiring // and standard I2C clock speed - let i2c = I2C::new( - peripherals.I2C0, - io.pins.gpio4, - io.pins.gpio5, - 100.kHz(), - &clocks, - ); + let i2c = I2C::new(peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, 100.kHz()); // Initialize display let interface = I2CDisplayInterface::new(i2c); diff --git a/examples/src/bin/i2s_read.rs b/examples/src/bin/i2s_read.rs index 395e1b23257..ecfa922186c 100644 --- a/examples/src/bin/i2s_read.rs +++ b/examples/src/bin/i2s_read.rs @@ -1,4 +1,4 @@ -//! This shows how to continously receive data via I2S. +//! This shows how to continuously receive data via I2S. //! //! Without an additional I2S source device you can connect 3V3 or GND to DIN //! to read 0 or 0xFF or connect DIN to WS to read two different values. @@ -18,22 +18,17 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, dma::{Dma, DmaPriority}, dma_buffers, gpio::Io, i2s::{DataFormat, I2s, I2sReadDma, Standard}, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -43,7 +38,7 @@ fn main() -> ! { #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] let dma_channel = dma.channel0; - let (_, tx_descriptors, mut rx_buffer, rx_descriptors) = dma_buffers!(0, 4 * 4092); + let (mut rx_buffer, rx_descriptors, _, tx_descriptors) = dma_buffers!(0, 4 * 4092); // Here we test that the type is // 1) reasonably simple (or at least this will flag changes that may make it @@ -55,9 +50,8 @@ fn main() -> ! { DataFormat::Data16Channel16, 44100.Hz(), dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, rx_descriptors, - &clocks, + tx_descriptors, ); #[cfg(not(feature = "esp32"))] diff --git a/examples/src/bin/i2s_sound.rs b/examples/src/bin/i2s_sound.rs index 991b39d3381..ec06e6b627a 100644 --- a/examples/src/bin/i2s_sound.rs +++ b/examples/src/bin/i2s_sound.rs @@ -1,7 +1,7 @@ -//! This shows how to transmit data continously via I2S. +//! This shows how to transmit data continuously via I2S. //! //! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS -//! andDOUT with a logic analyzer. +//! and DOUT with a logic analyzer. //! //! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full //! scale), so turn down the volume before running this example. @@ -32,14 +32,11 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, dma::{Dma, DmaPriority}, dma_buffers, gpio::Io, i2s::{DataFormat, I2s, I2sWriteDma, Standard}, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; const SINE: [i16; 64] = [ @@ -52,9 +49,7 @@ const SINE: [i16; 64] = [ #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -64,7 +59,7 @@ fn main() -> ! { #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] let dma_channel = dma.channel0; - let (tx_buffer, tx_descriptors, _, rx_descriptors) = dma_buffers!(32000, 0); + let (_, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000, 0); let i2s = I2s::new( peripherals.I2S0, @@ -72,9 +67,8 @@ fn main() -> ! { DataFormat::Data16Channel16, 44100.Hz(), dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, rx_descriptors, - &clocks, + tx_descriptors, ); let mut i2s_tx = i2s diff --git a/examples/src/bin/ieee802154_receive_all_frames.rs b/examples/src/bin/ieee802154_receive_all_frames.rs index 5adb3c0ad0c..02a46286552 100644 --- a/examples/src/bin/ieee802154_receive_all_frames.rs +++ b/examples/src/bin/ieee802154_receive_all_frames.rs @@ -4,13 +4,13 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{peripherals::Peripherals, prelude::*}; -use esp_ieee802154::*; +use esp_hal::prelude::*; +use esp_ieee802154::{Config, Ieee802154}; use esp_println::println; #[entry] fn main() -> ! { - let mut peripherals = Peripherals::take(); + let mut peripherals = esp_hal::init(esp_hal::Config::default()); let mut ieee802154 = Ieee802154::new(peripherals.IEEE802154, &mut peripherals.RADIO_CLK); ieee802154.set_config(Config { @@ -19,7 +19,7 @@ fn main() -> ! { rx_when_idle: true, auto_ack_rx: false, auto_ack_tx: false, - ..Config::default() + ..Default::default() }); println!("Start receiving:"); diff --git a/examples/src/bin/ieee802154_receive_frame.rs b/examples/src/bin/ieee802154_receive_frame.rs index 07e5de3f071..53b6f5c9a65 100644 --- a/examples/src/bin/ieee802154_receive_frame.rs +++ b/examples/src/bin/ieee802154_receive_frame.rs @@ -4,13 +4,13 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{peripherals::Peripherals, prelude::*}; -use esp_ieee802154::*; +use esp_hal::prelude::*; +use esp_ieee802154::{Config, Ieee802154}; use esp_println::println; #[entry] fn main() -> ! { - let mut peripherals = Peripherals::take(); + let mut peripherals = esp_hal::init(esp_hal::Config::default()); let mut ieee802154 = Ieee802154::new(peripherals.IEEE802154, &mut peripherals.RADIO_CLK); ieee802154.set_config(Config { @@ -21,7 +21,7 @@ fn main() -> ! { auto_ack_tx: true, pan_id: Some(0x4242), short_addr: Some(0x2323), - ..Config::default() + ..Default::default() }); println!("Start receiving:"); diff --git a/examples/src/bin/ieee802154_send_broadcast_frame.rs b/examples/src/bin/ieee802154_send_broadcast_frame.rs index 070cf8e7983..7c8de3c34e4 100644 --- a/examples/src/bin/ieee802154_send_broadcast_frame.rs +++ b/examples/src/bin/ieee802154_send_broadcast_frame.rs @@ -4,14 +4,8 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - delay::Delay, - peripherals::Peripherals, - prelude::*, - system::SystemControl, -}; -use esp_ieee802154::*; +use esp_hal::{delay::Delay, prelude::*}; +use esp_ieee802154::{Config, Frame, Ieee802154}; use esp_println::println; use ieee802154::mac::{ Address, @@ -25,11 +19,9 @@ use ieee802154::mac::{ #[entry] fn main() -> ! { - let mut peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + let mut peripherals = esp_hal::init(esp_hal::Config::default()); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut ieee802154 = Ieee802154::new(peripherals.IEEE802154, &mut peripherals.RADIO_CLK); @@ -38,7 +30,7 @@ fn main() -> ! { promiscuous: false, pan_id: Some(0x4242), short_addr: Some(0x2323), - ..Config::default() + ..Default::default() }); let mut seq_number = 0u8; diff --git a/examples/src/bin/ieee802154_send_frame.rs b/examples/src/bin/ieee802154_send_frame.rs index 7992a71694e..851ce007997 100644 --- a/examples/src/bin/ieee802154_send_frame.rs +++ b/examples/src/bin/ieee802154_send_frame.rs @@ -4,14 +4,8 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - delay::Delay, - peripherals::Peripherals, - prelude::*, - system::SystemControl, -}; -use esp_ieee802154::*; +use esp_hal::{delay::Delay, prelude::*}; +use esp_ieee802154::{Config, Frame, Ieee802154}; use esp_println::println; use ieee802154::mac::{ Address, @@ -25,11 +19,9 @@ use ieee802154::mac::{ #[entry] fn main() -> ! { - let mut peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + let mut peripherals = esp_hal::init(esp_hal::Config::default()); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut ieee802154 = Ieee802154::new(peripherals.IEEE802154, &mut peripherals.RADIO_CLK); @@ -38,7 +30,7 @@ fn main() -> ! { promiscuous: false, pan_id: Some(0x4242), short_addr: Some(0x2222), - ..Config::default() + ..Default::default() }); let mut seq_number = 0u8; diff --git a/examples/src/bin/ieee802154_sniffer.rs b/examples/src/bin/ieee802154_sniffer.rs index 09bd2dd84bb..8544f9c61bc 100644 --- a/examples/src/bin/ieee802154_sniffer.rs +++ b/examples/src/bin/ieee802154_sniffer.rs @@ -8,34 +8,27 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - gpio::Io, - peripherals::Peripherals, - prelude::*, - reset::software_reset, - system::SystemControl, - uart::Uart, -}; -use esp_ieee802154::*; +use esp_hal::{gpio::Io, prelude::*, reset::software_reset, uart::Uart}; +use esp_ieee802154::{Config, Ieee802154}; use esp_println::println; #[entry] fn main() -> ! { - let mut peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + let mut peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); // Default pins for Uart/Serial communication - #[cfg(feature = "esp32c6")] - let (mut tx_pin, mut rx_pin) = (io.pins.gpio16, io.pins.gpio17); - #[cfg(feature = "esp32h2")] - let (mut tx_pin, mut rx_pin) = (io.pins.gpio24, io.pins.gpio23); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32c6")] { + let (mut tx_pin, mut rx_pin) = (io.pins.gpio16, io.pins.gpio17); + } else if #[cfg(feature = "esp32h2")] { + let (mut tx_pin, mut rx_pin) = (io.pins.gpio24, io.pins.gpio23); + } + } let mut uart0 = - Uart::new_with_default_pins(peripherals.UART0, &clocks, &mut tx_pin, &mut rx_pin).unwrap(); + Uart::new_with_default_pins(peripherals.UART0, &mut rx_pin, &mut tx_pin).unwrap(); // read two characters which get parsed as the channel let mut cnt = 0; @@ -66,7 +59,7 @@ fn main() -> ! { rx_when_idle: true, auto_ack_rx: false, auto_ack_tx: false, - ..Config::default() + ..Default::default() }); ieee802154.start_receive(); diff --git a/examples/src/bin/lcd_cam_ov2640.rs b/examples/src/bin/lcd_cam_ov2640.rs index 07f12f8a1cf..916bc716529 100644 --- a/examples/src/bin/lcd_cam_ov2640.rs +++ b/examples/src/bin/lcd_cam_ov2640.rs @@ -25,7 +25,6 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, dma::{Dma, DmaPriority}, dma_buffers, @@ -36,25 +35,21 @@ use esp_hal::{ cam::{Camera, RxEightBits}, LcdCam, }, - peripherals::Peripherals, prelude::*, - system::SystemControl, Blocking, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let dma = Dma::new(peripherals.DMA); let channel = dma.channel0; - let (_, _, rx_buffer, rx_descriptors) = dma_buffers!(0, 32678); + let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(0, 32678); let channel = channel.configure(false, DmaPriority::Priority0); @@ -82,20 +77,19 @@ fn main() -> ! { rx_descriptors, cam_data_pins, 20u32.MHz(), - &clocks, ) .with_master_clock(cam_xclk) .with_pixel_clock(cam_pclk) .with_ctrl_pins(cam_vsync, cam_href); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut buffer = rx_buffer; buffer.fill(0u8); delay.delay_millis(500u32); - let i2c = I2C::new(peripherals.I2C0, cam_siod, cam_sioc, 100u32.kHz(), &clocks); + let i2c = I2C::new(peripherals.I2C0, cam_siod, cam_sioc, 100u32.kHz()); let mut sccb = Sccb::new(i2c); diff --git a/examples/src/bin/lcd_i8080.rs b/examples/src/bin/lcd_i8080.rs index b3b02a06292..e41436bf37d 100644 --- a/examples/src/bin/lcd_i8080.rs +++ b/examples/src/bin/lcd_i8080.rs @@ -23,7 +23,6 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, dma::{Dma, DmaPriority}, dma_buffers, @@ -32,17 +31,13 @@ use esp_hal::{ lcd::i8080::{Config, TxEightBits, I8080}, LcdCam, }, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -55,11 +50,11 @@ fn main() -> ! { let dma = Dma::new(peripherals.DMA); let channel = dma.channel0; - let (tx_buffer, tx_descriptors, _, _) = dma_buffers!(32678, 0); + let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(32678, 0); let channel = channel.configure(false, DmaPriority::Priority0); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut backlight = Output::new(lcd_backlight, Level::Low); let mut reset = Output::new(lcd_reset, Level::Low); @@ -83,7 +78,6 @@ fn main() -> ! { tx_pins, 20.MHz(), Config::default(), - &clocks, ) .with_ctrl_pins(lcd_rs, lcd_wr); diff --git a/examples/src/bin/ledc.rs b/examples/src/bin/ledc.rs index 94d8dbdb3c9..a9e8225a290 100644 --- a/examples/src/bin/ledc.rs +++ b/examples/src/bin/ledc.rs @@ -11,7 +11,6 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, ledc::{ channel::{self, ChannelIFace}, @@ -20,21 +19,17 @@ use esp_hal::{ Ledc, LowSpeed, }, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let led = io.pins.gpio0; - let mut ledc = Ledc::new(peripherals.LEDC, &clocks); + let mut ledc = Ledc::new(peripherals.LEDC); ledc.set_global_slow_clock(LSGlobalClkSource::APBClk); diff --git a/examples/src/bin/lp_core_basic.rs b/examples/src/bin/lp_core_basic.rs index 81dc1a7a632..d0f38c45e14 100644 --- a/examples/src/bin/lp_core_basic.rs +++ b/examples/src/bin/lp_core_basic.rs @@ -17,14 +17,13 @@ use esp_backtrace as _; use esp_hal::{ gpio::{lp_io::LowPowerOutput, Io}, lp_core::{LpCore, LpCoreWakeupSource}, - peripherals::Peripherals, prelude::*, }; use esp_println::{print, println}; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); // configure GPIO 1 as LP output pin let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); diff --git a/examples/src/bin/lp_core_i2c.rs b/examples/src/bin/lp_core_i2c.rs index b285b4b172d..55aa731c1bd 100644 --- a/examples/src/bin/lp_core_i2c.rs +++ b/examples/src/bin/lp_core_i2c.rs @@ -19,14 +19,13 @@ use esp_hal::{ gpio::{lp_io::LowPowerOutputOpenDrain, Io}, i2c::lp_i2c::LpI2c, lp_core::{LpCore, LpCoreWakeupSource}, - peripherals::Peripherals, prelude::*, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); diff --git a/examples/src/bin/lp_core_uart.rs b/examples/src/bin/lp_core_uart.rs index df795db9682..4ebe0ff1560 100644 --- a/examples/src/bin/lp_core_uart.rs +++ b/examples/src/bin/lp_core_uart.rs @@ -16,24 +16,19 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::{ lp_io::{LowPowerInput, LowPowerOutput}, Io, }, lp_core::{LpCore, LpCoreWakeupSource}, - peripherals::Peripherals, prelude::*, - system::SystemControl, uart::{config::Config, lp_uart::LpUart, Uart}, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -42,7 +37,6 @@ fn main() -> ! { let mut uart1 = Uart::new_with_config( peripherals.UART1, Config::default(), - &clocks, io.pins.gpio6, io.pins.gpio7, ) diff --git a/examples/src/bin/mcpwm.rs b/examples/src/bin/mcpwm.rs index 2e81f992f4b..17e08e8c801 100644 --- a/examples/src/bin/mcpwm.rs +++ b/examples/src/bin/mcpwm.rs @@ -11,28 +11,28 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, mcpwm::{operator::PwmPinConfig, timer::PwmWorkingMode, McPwm, PeripheralClockConfig}, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let pin = io.pins.gpio0; // initialize peripheral - #[cfg(feature = "esp32h2")] - let clock_cfg = PeripheralClockConfig::with_frequency(&clocks, 40.MHz()).unwrap(); - #[cfg(not(feature = "esp32h2"))] - let clock_cfg = PeripheralClockConfig::with_frequency(&clocks, 32.MHz()).unwrap(); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32h2")] { + let freq = 40.MHz(); + } else { + let freq = 32.MHz(); + } + } + + let clock_cfg = PeripheralClockConfig::with_frequency(freq).unwrap(); let mut mcpwm = McPwm::new(peripherals.MCPWM0, clock_cfg); diff --git a/examples/src/bin/multicore.rs b/examples/src/bin/multicore.rs index 1d028bdc9b9..89a5a8bce0a 100644 --- a/examples/src/bin/multicore.rs +++ b/examples/src/bin/multicore.rs @@ -13,12 +13,9 @@ use core::{cell::RefCell, ptr::addr_of_mut}; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, cpu_control::{CpuControl, Stack}, delay::Delay, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; use esp_println::println; @@ -26,11 +23,9 @@ static mut APP_CORE_STACK: Stack<8192> = Stack::new(); #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let counter = Mutex::new(RefCell::new(0u32)); diff --git a/examples/src/bin/parl_io_rx.rs b/examples/src/bin/parl_io_rx.rs index 0606667f75f..5ea1b8ae73e 100644 --- a/examples/src/bin/parl_io_rx.rs +++ b/examples/src/bin/parl_io_rx.rs @@ -11,27 +11,22 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, dma::{Dma, DmaPriority}, dma_buffers, gpio::Io, parl_io::{no_clk_pin, BitPackOrder, ParlIoRxOnly, RxFourBits}, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let (_, _, rx_buffer, rx_descriptors) = dma_buffers!(0, 32000); + let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(0, 32000); let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; @@ -43,7 +38,6 @@ fn main() -> ! { dma_channel.configure(false, DmaPriority::Priority0), rx_descriptors, 1.MHz(), - &clocks, ) .unwrap(); @@ -55,7 +49,7 @@ fn main() -> ! { let mut buffer = rx_buffer; buffer.fill(0u8); - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { let transfer = parl_io_rx.read_dma(&mut buffer).unwrap(); diff --git a/examples/src/bin/parl_io_tx.rs b/examples/src/bin/parl_io_tx.rs index 60536033a9e..ed5b3a45e03 100644 --- a/examples/src/bin/parl_io_tx.rs +++ b/examples/src/bin/parl_io_tx.rs @@ -15,7 +15,6 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, dma::{Dma, DmaPriority}, dma_buffers, @@ -28,21 +27,17 @@ use esp_hal::{ TxFourBits, TxPinConfigWithValidPin, }, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let (tx_buffer, tx_descriptors, _, _) = dma_buffers!(32000, 0); + let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(32000, 0); let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; @@ -56,7 +51,6 @@ fn main() -> ! { dma_channel.configure(false, DmaPriority::Priority0), tx_descriptors, 1.MHz(), - &clocks, ) .unwrap(); @@ -78,7 +72,7 @@ fn main() -> ! { buffer[i] = (i % 255) as u8; } - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { let transfer = parl_io_tx.write_dma(&buffer).unwrap(); diff --git a/examples/src/bin/pcnt_encoder.rs b/examples/src/bin/pcnt_encoder.rs index 8e22d236b77..f02c0ddbddd 100644 --- a/examples/src/bin/pcnt_encoder.rs +++ b/examples/src/bin/pcnt_encoder.rs @@ -1,6 +1,6 @@ //! PCNT Encoder Demo //! -//! This example decodes a quadrature encoder +//! This example decodes a quadrature encoder. //! //! Since the PCNT units reset to zero when they reach their limits //! we enable an interrupt on the upper and lower limits and @@ -27,7 +27,6 @@ use esp_hal::{ unit, Pcnt, }, - peripherals::Peripherals, prelude::*, }; use esp_println::println; @@ -38,7 +37,7 @@ static VALUE: AtomicI32 = AtomicI32::new(0); #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); diff --git a/examples/src/bin/psram_octal.rs b/examples/src/bin/psram_octal.rs index 1916ba4d0c3..c8cf9567853 100644 --- a/examples/src/bin/psram_octal.rs +++ b/examples/src/bin/psram_octal.rs @@ -12,25 +12,27 @@ extern crate alloc; use alloc::{string::String, vec::Vec}; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{peripherals::Peripherals, prelude::*, psram}; +use esp_hal::{prelude::*, psram}; use esp_println::println; -#[global_allocator] -static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); - fn init_psram_heap() { unsafe { - ALLOCATOR.init(psram::psram_vaddr_start() as *mut u8, psram::PSRAM_BYTES); + esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( + psram::psram_vaddr_start() as *mut u8, + psram::PSRAM_BYTES, + esp_alloc::MemoryCapability::External.into(), + )); } } +#[cfg(is_not_release)] +compile_error!("PSRAM example must be built in release mode!"); + #[entry] fn main() -> ! { - #[cfg(debug_assertions)] - compile_error!("This example MUST be built in release mode!"); - - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); psram::init_psram(peripherals.PSRAM); init_psram_heap(); diff --git a/examples/src/bin/psram_quad.rs b/examples/src/bin/psram_quad.rs index 47dfc69ceb8..28718265771 100644 --- a/examples/src/bin/psram_quad.rs +++ b/examples/src/bin/psram_quad.rs @@ -1,6 +1,6 @@ //! This shows how to use PSRAM as heap-memory via esp-alloc //! -//! You need an ESP32, ESP32-S2, or ESP32-S3 with at least 2 MB of PSRAM memory. +//! You need an ESP32, ESP32-S2 or ESP32-S3 with at least 2 MB of PSRAM memory. //% CHIPS: esp32 esp32s2 esp32s3 //% FEATURES: psram-2m @@ -12,38 +12,31 @@ extern crate alloc; use alloc::{string::String, vec::Vec}; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - prelude::*, - psram, - system::SystemControl, -}; +use esp_hal::{prelude::*, psram}; use esp_println::println; -#[global_allocator] -static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); - fn init_psram_heap() { unsafe { - ALLOCATOR.init(psram::psram_vaddr_start() as *mut u8, psram::PSRAM_BYTES); + esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( + psram::psram_vaddr_start() as *mut u8, + psram::PSRAM_BYTES, + esp_alloc::MemoryCapability::External.into(), + )); } } +#[cfg(is_not_release)] +compile_error!("PSRAM example must be built in release mode!"); + #[entry] fn main() -> ! { - #[cfg(debug_assertions)] - compile_error!("PSRAM example must be built in release mode!"); - - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); psram::init_psram(peripherals.PSRAM); init_psram_heap(); - let system = SystemControl::new(peripherals.SYSTEM); - let _clocks = ClockControl::max(system.clock_control).freeze(); - println!("Going to access PSRAM"); let mut large_vec = Vec::::with_capacity(500 * 1024 / 4); diff --git a/examples/src/bin/qspi_flash.rs b/examples/src/bin/qspi_flash.rs index cabe617872a..005819e2881 100644 --- a/examples/src/bin/qspi_flash.rs +++ b/examples/src/bin/qspi_flash.rs @@ -29,27 +29,22 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, - dma::{Dma, DmaPriority}, + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::Io, - peripherals::Peripherals, prelude::*, spi::{ - master::{prelude::*, Address, Command, Spi}, + master::{Address, Command, Spi}, SpiDataMode, SpiMode, }, - system::SystemControl, }; use esp_println::{print, println}; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); cfg_if::cfg_if! { @@ -71,14 +66,20 @@ fn main() -> ! { } let dma = Dma::new(peripherals.DMA); - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(256, 320); + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } + + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(256, 320); + let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) + let mut spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) .with_pins( Some(sclk), Some(mosi), @@ -87,30 +88,23 @@ fn main() -> ! { Some(sio3), Some(cs), ) - .with_dma( - dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - ); - - let delay = Delay::new(&clocks); + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); - // DMA buffer require a static life-time - let (zero_buf, _, _, _) = dma_buffers!(0); - let send = tx_buffer; - let mut receive = rx_buffer; + let delay = Delay::new(); // write enable + dma_tx_buf.set_length(0); let transfer = spi .write( SpiDataMode::Single, Command::Command8(0x06, SpiDataMode::Single), Address::None, 0, - &zero_buf, + dma_tx_buf, ) + .map_err(|e| e.0) .unwrap(); - transfer.wait().unwrap(); + (spi, dma_tx_buf) = transfer.wait(); delay.delay_millis(250); // erase sector @@ -120,10 +114,11 @@ fn main() -> ! { Command::Command8(0x20, SpiDataMode::Single), Address::Address24(0x000000, SpiDataMode::Single), 0, - &zero_buf, + dma_tx_buf, ) + .map_err(|e| e.0) .unwrap(); - transfer.wait().unwrap(); + (spi, dma_tx_buf) = transfer.wait(); delay.delay_millis(250); // write enable @@ -133,25 +128,28 @@ fn main() -> ! { Command::Command8(0x06, SpiDataMode::Single), Address::None, 0, - &zero_buf, + dma_tx_buf, ) + .map_err(|e| e.0) .unwrap(); - transfer.wait().unwrap(); + (spi, dma_tx_buf) = transfer.wait(); delay.delay_millis(250); // write data / program page - send.fill(b'!'); - send[0..][..5].copy_from_slice(&b"Hello"[..]); + dma_tx_buf.set_length(dma_tx_buf.capacity()); + dma_tx_buf.as_mut_slice().fill(b'!'); + dma_tx_buf.as_mut_slice()[0..][..5].copy_from_slice(&b"Hello"[..]); let transfer = spi .write( SpiDataMode::Quad, Command::Command8(0x32, SpiDataMode::Single), Address::Address24(0x000000, SpiDataMode::Single), 0, - &send, + dma_tx_buf, ) + .map_err(|e| e.0) .unwrap(); - transfer.wait().unwrap(); + (spi, _) = transfer.wait(); delay.delay_millis(250); loop { @@ -162,17 +160,18 @@ fn main() -> ! { Command::Command8(0xeb, SpiDataMode::Single), Address::Address32(0x000000 << 8, SpiDataMode::Quad), 4, - &mut receive, + dma_rx_buf, ) + .map_err(|e| e.0) .unwrap(); // here we could do something else while DMA transfer is in progress // the buffers and spi is moved into the transfer and we can get it back via // `wait` - transfer.wait().unwrap(); + (spi, dma_rx_buf) = transfer.wait(); - println!("{:x?}", &receive); - for b in &mut receive.iter() { + println!("{:x?}", dma_rx_buf.as_slice()); + for b in &mut dma_rx_buf.as_slice().iter() { if *b >= 32 && *b <= 127 { print!("{}", *b as char); } else { diff --git a/examples/src/bin/ram.rs b/examples/src/bin/ram.rs index bd165d99e2d..c8c802fcc9b 100644 --- a/examples/src/bin/ram.rs +++ b/examples/src/bin/ram.rs @@ -17,15 +17,7 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - delay::Delay, - macros::ram, - peripherals::Peripherals, - prelude::*, - rtc_cntl::Rtc, - system::SystemControl, -}; +use esp_hal::{delay::Delay, macros::ram, prelude::*, rtc_cntl::Rtc}; use esp_println::println; #[ram(rtc_fast)] @@ -39,11 +31,9 @@ static mut SOME_ZEROED_DATA: [u8; 8] = [0; 8]; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let delay = Delay::new(&clocks); + let delay = Delay::new(); // The RWDT flash boot protection must be enabled, as it is triggered as part of // the example. diff --git a/examples/src/bin/rmt_rx.rs b/examples/src/bin/rmt_rx.rs index ca9984cec28..948b6c8c6ef 100644 --- a/examples/src/bin/rmt_rx.rs +++ b/examples/src/bin/rmt_rx.rs @@ -13,13 +13,10 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, - gpio::Io, - peripherals::Peripherals, + gpio::{Io, Level, Output}, prelude::*, rmt::{PulseCode, Rmt, RxChannel, RxChannelConfig, RxChannelCreator}, - system::SystemControl, }; use esp_println::{print, println}; @@ -27,12 +24,10 @@ const WIDTH: usize = 80; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let mut out = io.pins.gpio5; + let mut out = Output::new(io.pins.gpio5, Level::Low); cfg_if::cfg_if! { if #[cfg(feature = "esp32h2")] { @@ -42,7 +37,7 @@ fn main() -> ! { } }; - let rmt = Rmt::new(peripherals.RMT, freq, &clocks).unwrap(); + let rmt = Rmt::new(peripherals.RMT, freq).unwrap(); let rx_config = RxChannelConfig { clk_divider: 1, @@ -60,7 +55,7 @@ fn main() -> ! { } } - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut data = [PulseCode { level1: true, diff --git a/examples/src/bin/rmt_tx.rs b/examples/src/bin/rmt_tx.rs index d6ef6cf3953..6d16c26298f 100644 --- a/examples/src/bin/rmt_tx.rs +++ b/examples/src/bin/rmt_tx.rs @@ -12,20 +12,15 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, gpio::Io, - peripherals::Peripherals, prelude::*, rmt::{PulseCode, Rmt, TxChannel, TxChannelConfig, TxChannelCreator}, - system::SystemControl, }; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -37,7 +32,7 @@ fn main() -> ! { } }; - let rmt = Rmt::new(peripherals.RMT, freq, &clocks).unwrap(); + let rmt = Rmt::new(peripherals.RMT, freq).unwrap(); let tx_config = TxChannelConfig { clk_divider: 255, @@ -46,7 +41,7 @@ fn main() -> ! { let mut channel = rmt.channel0.configure(io.pins.gpio4, tx_config).unwrap(); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut data = [PulseCode { level1: true, diff --git a/examples/src/bin/rng.rs b/examples/src/bin/rng.rs index fc87b7d8fda..36e84f69e0f 100644 --- a/examples/src/bin/rng.rs +++ b/examples/src/bin/rng.rs @@ -6,12 +6,12 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{peripherals::Peripherals, prelude::*, rng::Rng}; +use esp_hal::{prelude::*, rng::Rng}; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let mut rng = Rng::new(peripherals.RNG); // Generate a random word (u32): diff --git a/examples/src/bin/rtc_time.rs b/examples/src/bin/rtc_time.rs index c4e9b876b06..e0c809da0ba 100644 --- a/examples/src/bin/rtc_time.rs +++ b/examples/src/bin/rtc_time.rs @@ -6,23 +6,14 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - delay::Delay, - peripherals::Peripherals, - prelude::*, - rtc_cntl::Rtc, - system::SystemControl, -}; +use esp_hal::{delay::Delay, prelude::*, rtc_cntl::Rtc}; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let rtc = Rtc::new(peripherals.LPWR); - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { esp_println::println!("rtc time in milliseconds is {}", rtc.get_time_ms()); diff --git a/examples/src/bin/rtc_watchdog.rs b/examples/src/bin/rtc_watchdog.rs index cddd09dcf8e..255f0bf1225 100644 --- a/examples/src/bin/rtc_watchdog.rs +++ b/examples/src/bin/rtc_watchdog.rs @@ -1,4 +1,4 @@ -//! This demos the RTC Watchdog Timer (RWDT). +//! This example demonstrates the RTC Watchdog Timer (RWDT). //! //! The RWDT is initially configured to trigger an interrupt after a given //! timeout. Then, upon expiration, the RWDT is restarted and then reconfigured @@ -14,16 +14,17 @@ use core::cell::RefCell; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - peripherals::Peripherals, + interrupt::Priority, prelude::*, rtc_cntl::{Rtc, Rwdt}, }; +use esp_println::println; static RWDT: Mutex>> = Mutex::new(RefCell::new(None)); #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let mut rtc = Rtc::new(peripherals.LPWR); rtc.set_interrupt_handler(interrupt_handler); @@ -35,16 +36,16 @@ fn main() -> ! { loop {} } -#[handler(priority = esp_hal::interrupt::Priority::min())] +#[handler(priority = Priority::min())] fn interrupt_handler() { critical_section::with(|cs| { - esp_println::println!("RWDT Interrupt"); + println!("RWDT Interrupt"); let mut rwdt = RWDT.borrow_ref_mut(cs); let rwdt = rwdt.as_mut().unwrap(); rwdt.clear_interrupt(); - esp_println::println!("Restarting in 5 seconds..."); + println!("Restarting in 5 seconds..."); rwdt.set_timeout(5000.millis()); rwdt.unlisten(); diff --git a/examples/src/bin/serial_interrupts.rs b/examples/src/bin/serial_interrupts.rs index 8af7b9a31e8..34f01add849 100644 --- a/examples/src/bin/serial_interrupts.rs +++ b/examples/src/bin/serial_interrupts.rs @@ -12,12 +12,10 @@ use core::{cell::RefCell, fmt::Write}; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, gpio::Io, - peripherals::{Peripherals, UART0}, + peripherals::UART0, prelude::*, - system::SystemControl, uart::{ config::{AtCmdConfig, Config}, Uart, @@ -29,33 +27,31 @@ static SERIAL: Mutex>>> = Mutex::new(RefCel #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); // Default pins for Uart/Serial communication - #[cfg(feature = "esp32")] - let (tx_pin, rx_pin) = (io.pins.gpio1, io.pins.gpio3); - #[cfg(feature = "esp32c2")] - let (tx_pin, rx_pin) = (io.pins.gpio20, io.pins.gpio19); - #[cfg(feature = "esp32c3")] - let (tx_pin, rx_pin) = (io.pins.gpio21, io.pins.gpio20); - #[cfg(feature = "esp32c6")] - let (tx_pin, rx_pin) = (io.pins.gpio16, io.pins.gpio17); - #[cfg(feature = "esp32h2")] - let (tx_pin, rx_pin) = (io.pins.gpio24, io.pins.gpio23); - #[cfg(feature = "esp32s2")] - let (tx_pin, rx_pin) = (io.pins.gpio43, io.pins.gpio44); - #[cfg(feature = "esp32s3")] - let (tx_pin, rx_pin) = (io.pins.gpio43, io.pins.gpio44); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + let (tx_pin, rx_pin) = (io.pins.gpio1, io.pins.gpio3); + } else if #[cfg(feature = "esp32c2")] { + let (tx_pin, rx_pin) = (io.pins.gpio20, io.pins.gpio19); + } else if #[cfg(feature = "esp32c3")] { + let (tx_pin, rx_pin) = (io.pins.gpio21, io.pins.gpio20); + } else if #[cfg(feature = "esp32c6")] { + let (tx_pin, rx_pin) = (io.pins.gpio16, io.pins.gpio17); + } else if #[cfg(feature = "esp32h2")] { + let (tx_pin, rx_pin) = (io.pins.gpio24, io.pins.gpio23); + } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { + let (tx_pin, rx_pin) = (io.pins.gpio43, io.pins.gpio44); + } + } let config = Config::default().rx_fifo_full_threshold(30); - let mut uart0 = - Uart::new_with_config(peripherals.UART0, config, &clocks, tx_pin, rx_pin).unwrap(); + let mut uart0 = Uart::new_with_config(peripherals.UART0, config, tx_pin, rx_pin).unwrap(); uart0.set_interrupt_handler(interrupt_handler); critical_section::with(|cs| { diff --git a/examples/src/bin/sleep_timer.rs b/examples/src/bin/sleep_timer.rs index fa882bd9ac4..b742c9c5c84 100644 --- a/examples/src/bin/sleep_timer.rs +++ b/examples/src/bin/sleep_timer.rs @@ -1,6 +1,6 @@ //! Demonstrates deep sleep with timer wakeup -//% CHIPS: esp32 esp32c3 esp32c6 esp32s3 +//% CHIPS: esp32 esp32c3 esp32c6 esp32s3 esp32c2 #![no_std] #![no_main] @@ -9,23 +9,18 @@ use core::time::Duration; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, entry, - peripherals::Peripherals, rtc_cntl::{get_reset_reason, get_wakeup_cause, sleep::TimerWakeupSource, Rtc, SocResetReason}, - system::SystemControl, Cpu, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut rtc = Rtc::new(peripherals.LPWR); println!("up and runnning!"); diff --git a/examples/src/bin/sleep_timer_ext0.rs b/examples/src/bin/sleep_timer_ext0.rs index b54e646871a..c7dd2418eca 100644 --- a/examples/src/bin/sleep_timer_ext0.rs +++ b/examples/src/bin/sleep_timer_ext0.rs @@ -12,11 +12,9 @@ use core::time::Duration; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, entry, gpio::Io, - peripherals::Peripherals, rtc_cntl::{ get_reset_reason, get_wakeup_cause, @@ -24,16 +22,13 @@ use esp_hal::{ Rtc, SocResetReason, }, - system::SystemControl, Cpu, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let mut rtc = Rtc::new(peripherals.LPWR); @@ -46,7 +41,7 @@ fn main() -> ! { let wake_reason = get_wakeup_cause(); println!("wake reason: {:?}", wake_reason); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let timer = TimerWakeupSource::new(Duration::from_secs(30)); let ext0 = Ext0WakeupSource::new(&mut ext0_pin, WakeupLevel::High); diff --git a/examples/src/bin/sleep_timer_ext1.rs b/examples/src/bin/sleep_timer_ext1.rs index 2f702642ffe..b9b7ba2a295 100644 --- a/examples/src/bin/sleep_timer_ext1.rs +++ b/examples/src/bin/sleep_timer_ext1.rs @@ -12,11 +12,9 @@ use core::time::Duration; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, entry, gpio::{Io, RtcPin}, - peripherals::Peripherals, rtc_cntl::{ get_reset_reason, get_wakeup_cause, @@ -24,16 +22,13 @@ use esp_hal::{ Rtc, SocResetReason, }, - system::SystemControl, Cpu, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let mut rtc = Rtc::new(peripherals.LPWR); @@ -47,7 +42,7 @@ fn main() -> ! { let wake_reason = get_wakeup_cause(); println!("wake reason: {:?}", wake_reason); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let timer = TimerWakeupSource::new(Duration::from_secs(30)); let mut wakeup_pins: [&mut dyn RtcPin; 2] = [&mut pin_0, &mut pin_2]; diff --git a/examples/src/bin/sleep_timer_lpio.rs b/examples/src/bin/sleep_timer_lpio.rs index 63c7074f422..cfb2690efd2 100644 --- a/examples/src/bin/sleep_timer_lpio.rs +++ b/examples/src/bin/sleep_timer_lpio.rs @@ -13,11 +13,9 @@ use core::time::Duration; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, entry, gpio::{Io, RtcPinWithResistors}, - peripherals::Peripherals, rtc_cntl::{ get_reset_reason, get_wakeup_cause, @@ -25,16 +23,13 @@ use esp_hal::{ Rtc, SocResetReason, }, - system::SystemControl, Cpu, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let mut rtc = Rtc::new(peripherals.LPWR); @@ -48,7 +43,7 @@ fn main() -> ! { let wake_reason = get_wakeup_cause(); println!("wake reason: {:?}", wake_reason); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let timer = TimerWakeupSource::new(Duration::from_secs(10)); let wakeup_pins: &mut [(&mut dyn RtcPinWithResistors, WakeupLevel)] = &mut [ diff --git a/examples/src/bin/sleep_timer_rtcio.rs b/examples/src/bin/sleep_timer_rtcio.rs index b8e6e727448..baa4ecc9db5 100644 --- a/examples/src/bin/sleep_timer_rtcio.rs +++ b/examples/src/bin/sleep_timer_rtcio.rs @@ -7,7 +7,7 @@ //! The following wiring is assumed for ESP32S3: //! - RTC wakeup pin => GPIO18 (low level) -//% CHIPS: esp32c3 esp32s3 +//% CHIPS: esp32c3 esp32s3 esp32c2 #![no_std] #![no_main] @@ -16,12 +16,10 @@ use core::time::Duration; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, entry, gpio, gpio::Io, - peripherals::Peripherals, rtc_cntl::{ get_reset_reason, get_wakeup_cause, @@ -29,16 +27,13 @@ use esp_hal::{ Rtc, SocResetReason, }, - system::SystemControl, Cpu, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let mut io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let mut rtc = Rtc::new(peripherals.LPWR); @@ -49,18 +44,21 @@ fn main() -> ! { let wake_reason = get_wakeup_cause(); println!("wake reason: {:?}", wake_reason); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let timer = TimerWakeupSource::new(Duration::from_secs(10)); - #[cfg(feature = "esp32c3")] - let wakeup_pins: &mut [(&mut dyn gpio::RtcPinWithResistors, WakeupLevel)] = &mut [ - (&mut io.pins.gpio2, WakeupLevel::Low), - (&mut io.pins.gpio3, WakeupLevel::High), - ]; - - #[cfg(feature = "esp32s3")] - let wakeup_pins: &mut [(&mut dyn gpio::RtcPin, WakeupLevel)] = - &mut [(&mut io.pins.gpio18, WakeupLevel::Low)]; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32c3", feature = "esp32c2"))] { + let wakeup_pins: &mut [(&mut dyn gpio::RtcPinWithResistors, WakeupLevel)] = &mut [ + (&mut io.pins.gpio2, WakeupLevel::Low), + (&mut io.pins.gpio3, WakeupLevel::High), + ]; + } else if #[cfg(feature = "esp32s3")] { + let wakeup_pins: &mut [(&mut dyn gpio::RtcPin, WakeupLevel)] = &mut [ + (&mut io.pins.gpio18, WakeupLevel::Low) + ]; + } + } let rtcio = RtcioWakeupSource::new(wakeup_pins); println!("sleeping!"); diff --git a/examples/src/bin/software_interrupts.rs b/examples/src/bin/software_interrupts.rs index 5fa0f5252b9..98fc3d96947 100644 --- a/examples/src/bin/software_interrupts.rs +++ b/examples/src/bin/software_interrupts.rs @@ -14,11 +14,9 @@ use core::cell::RefCell; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, - peripherals::Peripherals, + interrupt::software::{SoftwareInterrupt, SoftwareInterruptControl}, prelude::*, - system::{SoftwareInterrupt, SystemControl}, }; static SWINT0: Mutex>>> = Mutex::new(RefCell::new(None)); @@ -28,42 +26,41 @@ static SWINT3: Mutex>>> = Mutex::new(RefCell #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let mut sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); - let mut sw_int = system.software_interrupt_control; critical_section::with(|cs| { - sw_int + sw_ints .software_interrupt0 .set_interrupt_handler(swint0_handler); SWINT0 .borrow_ref_mut(cs) - .replace(sw_int.software_interrupt0); + .replace(sw_ints.software_interrupt0); - sw_int + sw_ints .software_interrupt1 .set_interrupt_handler(swint1_handler); SWINT1 .borrow_ref_mut(cs) - .replace(sw_int.software_interrupt1); + .replace(sw_ints.software_interrupt1); - sw_int + sw_ints .software_interrupt2 .set_interrupt_handler(swint2_handler); SWINT2 .borrow_ref_mut(cs) - .replace(sw_int.software_interrupt2); + .replace(sw_ints.software_interrupt2); - sw_int + sw_ints .software_interrupt3 .set_interrupt_handler(swint3_handler); SWINT3 .borrow_ref_mut(cs) - .replace(sw_int.software_interrupt3); + .replace(sw_ints.software_interrupt3); }); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut counter = 0; loop { diff --git a/examples/src/bin/spi_halfduplex_read_manufacturer_id.rs b/examples/src/bin/spi_halfduplex_read_manufacturer_id.rs index 8e56ab17b17..b6f20d7897e 100644 --- a/examples/src/bin/spi_halfduplex_read_manufacturer_id.rs +++ b/examples/src/bin/spi_halfduplex_read_manufacturer_id.rs @@ -29,25 +29,20 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, gpio::Io, - peripherals::Peripherals, prelude::*, spi::{ master::{Address, Command, HalfDuplexReadWrite, Spi}, SpiDataMode, SpiMode, }, - system::SystemControl, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); cfg_if::cfg_if! { @@ -68,17 +63,16 @@ fn main() -> ! { } } - let mut spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) - .with_pins( - Some(sclk), - Some(mosi), - Some(miso), - Some(sio2), - Some(sio3), - Some(cs), - ); + let mut spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0).with_pins( + Some(sclk), + Some(mosi), + Some(miso), + Some(sio2), + Some(sio3), + Some(cs), + ); - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { // READ MANUFACTURER ID FROM FLASH CHIP diff --git a/examples/src/bin/spi_loopback.rs b/examples/src/bin/spi_loopback.rs index 735321644a1..fca0d8b2f91 100644 --- a/examples/src/bin/spi_loopback.rs +++ b/examples/src/bin/spi_loopback.rs @@ -20,21 +20,16 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, - gpio::{any_pin::AnyPin, Io}, - peripherals::Peripherals, + gpio::{AnyPin, Io}, prelude::*, spi::{master::Spi, SpiMode}, - system::SystemControl, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let sclk = io.pins.gpio0; @@ -45,14 +40,14 @@ fn main() -> ! { let miso = AnyPin::new(miso); let mosi = AnyPin::new(mosi); - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks).with_pins( + let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0).with_pins( Some(sclk), Some(mosi), Some(miso), Some(cs), ); - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { let mut data = [0xde, 0xca, 0xfb, 0xad]; diff --git a/examples/src/bin/spi_loopback_dma.rs b/examples/src/bin/spi_loopback_dma.rs index 991b856a163..93167c145b3 100644 --- a/examples/src/bin/spi_loopback_dma.rs +++ b/examples/src/bin/spi_loopback_dma.rs @@ -20,26 +20,18 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, - dma::{Dma, DmaPriority}, + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::Io, - peripherals::Peripherals, prelude::*, - spi::{ - master::{prelude::*, Spi}, - SpiMode, - }, - system::SystemControl, + spi::{master::Spi, SpiMode}, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let sclk = io.pins.gpio0; @@ -49,38 +41,39 @@ fn main() -> ! { let dma = Dma::new(peripherals.DMA); - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(32000); + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000); + let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) + let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) .with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs)) - .with_dma( - dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - ); + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); - let delay = Delay::new(&clocks); + let delay = Delay::new(); - // DMA buffer require a static life-time - let mut send = tx_buffer; - let mut receive = rx_buffer; let mut i = 0; - for (i, v) in send.iter_mut().enumerate() { + for (i, v) in dma_tx_buf.as_mut_slice().iter_mut().enumerate() { *v = (i % 255) as u8; } loop { - send[0] = i; - send[send.len() - 1] = i; + dma_tx_buf.as_mut_slice()[0] = i; + *dma_tx_buf.as_mut_slice().last_mut().unwrap() = i; i = i.wrapping_add(1); - let mut transfer = spi.dma_transfer(&mut send, &mut receive).unwrap(); + let transfer = spi + .dma_transfer(dma_rx_buf, dma_tx_buf) + .map_err(|e| e.0) + .unwrap(); // here we could do something else while DMA transfer is in progress let mut n = 0; // Check is_done until the transfer is almost done (32000 bytes at 100kHz is @@ -90,11 +83,11 @@ fn main() -> ! { n += 1; } - transfer.wait().unwrap(); + (spi, (dma_rx_buf, dma_tx_buf)) = transfer.wait(); println!( "{:x?} .. {:x?}", - &receive[..10], - &receive[receive.len() - 10..] + &dma_rx_buf.as_slice()[..10], + &dma_rx_buf.as_slice().last_chunk::<10>().unwrap() ); delay.delay_millis(250); diff --git a/examples/src/bin/spi_slave_dma.rs b/examples/src/bin/spi_slave_dma.rs index 6ab99df99a1..e448594583b 100644 --- a/examples/src/bin/spi_slave_dma.rs +++ b/examples/src/bin/spi_slave_dma.rs @@ -31,39 +31,33 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, dma::{Dma, DmaPriority}, dma_buffers, - gpio::{Gpio4, Gpio5, Gpio8, Gpio9, Input, Io, Level, Output, Pull}, - peripherals::Peripherals, + gpio::{Input, Io, Level, Output, Pull}, prelude::*, spi::{ slave::{prelude::*, Spi}, SpiMode, }, - system::SystemControl, }; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let slave_sclk = io.pins.gpio0; + let mut master_sclk = Output::new(io.pins.gpio4, Level::Low); - let slave_miso = io.pins.gpio1; let master_miso = Input::new(io.pins.gpio5, Pull::None); - let slave_mosi = io.pins.gpio2; let mut master_mosi = Output::new(io.pins.gpio8, Level::Low); + let mut master_cs = Output::new(io.pins.gpio9, Level::High); + + let slave_sclk = io.pins.gpio0; + let slave_miso = io.pins.gpio1; + let slave_mosi = io.pins.gpio2; let slave_cs = io.pins.gpio3; - let mut master_cs = Output::new(io.pins.gpio9, Level::Low); - master_cs.set_high(); - master_sclk.set_low(); - master_mosi.set_low(); let dma = Dma::new(peripherals.DMA); cfg_if::cfg_if! { @@ -74,7 +68,7 @@ fn main() -> ! { } } - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(32000); + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000); let mut spi = Spi::new( peripherals.SPI2, @@ -90,7 +84,7 @@ fn main() -> ! { rx_descriptors, ); - let delay = Delay::new(&clocks); + let delay = Delay::new(); // DMA buffer require a static life-time let master_send = &mut [0u8; 32000]; @@ -119,7 +113,7 @@ fn main() -> ! { println!("Do `dma_transfer`"); let transfer = spi - .dma_transfer(&mut slave_send, &mut slave_receive) + .dma_transfer(&mut slave_receive, &mut slave_send) .unwrap(); bitbang_master( @@ -194,10 +188,10 @@ fn main() -> ! { fn bitbang_master( master_send: &[u8], master_receive: &mut [u8], - master_cs: &mut Output, - master_mosi: &mut Output, - master_sclk: &mut Output, - master_miso: &Input, + master_cs: &mut Output, + master_mosi: &mut Output, + master_sclk: &mut Output, + master_miso: &Input, ) { // Bit-bang out the contents of master_send and read into master_receive // as quickly as manageable. MSB first. Mode 0, so sampled on the rising diff --git a/examples/src/bin/systimer.rs b/examples/src/bin/systimer.rs index 8c738033ac5..e648e98d897 100644 --- a/examples/src/bin/systimer.rs +++ b/examples/src/bin/systimer.rs @@ -12,46 +12,68 @@ use core::cell::RefCell; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, - interrupt::{self, Priority}, - peripherals::{Interrupt, Peripherals}, prelude::*, - system::SystemControl, - timer::systimer::{Alarm, Periodic, SystemTimer, Target}, + timer::systimer::{ + Alarm, + FrozenUnit, + Periodic, + SpecificComparator, + SpecificUnit, + SystemTimer, + Target, + }, Blocking, }; use esp_println::println; use fugit::ExtU32; - -static ALARM0: Mutex>>> = - Mutex::new(RefCell::new(None)); -static ALARM1: Mutex>>> = Mutex::new(RefCell::new(None)); -static ALARM2: Mutex>>> = Mutex::new(RefCell::new(None)); +use static_cell::StaticCell; + +static ALARM0: Mutex< + RefCell< + Option, SpecificUnit<'static, 0>>>, + >, +> = Mutex::new(RefCell::new(None)); +static ALARM1: Mutex< + RefCell< + Option, SpecificUnit<'static, 0>>>, + >, +> = Mutex::new(RefCell::new(None)); +static ALARM2: Mutex< + RefCell< + Option, SpecificUnit<'static, 0>>>, + >, +> = Mutex::new(RefCell::new(None)); #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let systimer = SystemTimer::new(peripherals.SYSTIMER); println!("SYSTIMER Current value = {}", SystemTimer::now()); + static UNIT0: StaticCell> = StaticCell::new(); + + let unit0 = UNIT0.init(systimer.unit0); + + let frozen_unit = FrozenUnit::new(unit0); + + let alarm0 = Alarm::new(systimer.comparator0, &frozen_unit); + let alarm1 = Alarm::new(systimer.comparator1, &frozen_unit); + let alarm2 = Alarm::new(systimer.comparator2, &frozen_unit); + critical_section::with(|cs| { - let alarm0 = systimer.alarm0.into_periodic(); + let alarm0 = alarm0.into_periodic(); alarm0.set_interrupt_handler(systimer_target0); alarm0.set_period(1u32.secs()); alarm0.enable_interrupt(true); - let alarm1 = systimer.alarm1; alarm1.set_interrupt_handler(systimer_target1); - alarm1.set_target(SystemTimer::now() + (SystemTimer::TICKS_PER_SECOND * 2)); + alarm1.set_target(SystemTimer::now() + (SystemTimer::ticks_per_second() * 2)); alarm1.enable_interrupt(true); - let alarm2 = systimer.alarm2; alarm2.set_interrupt_handler(systimer_target2); - alarm2.set_target(SystemTimer::now() + (SystemTimer::TICKS_PER_SECOND * 3)); + alarm2.set_target(SystemTimer::now() + (SystemTimer::ticks_per_second() * 3)); alarm2.enable_interrupt(true); ALARM0.borrow_ref_mut(cs).replace(alarm0); @@ -59,11 +81,7 @@ fn main() -> ! { ALARM2.borrow_ref_mut(cs).replace(alarm2); }); - interrupt::enable(Interrupt::SYSTIMER_TARGET0, Priority::Priority1).unwrap(); - interrupt::enable(Interrupt::SYSTIMER_TARGET1, Priority::Priority3).unwrap(); - interrupt::enable(Interrupt::SYSTIMER_TARGET2, Priority::Priority3).unwrap(); - - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { delay.delay_millis(500); diff --git a/examples/src/bin/timer_interrupt.rs b/examples/src/bin/timer_interrupt.rs index b22d10aadd4..db8c82aa880 100644 --- a/examples/src/bin/timer_interrupt.rs +++ b/examples/src/bin/timer_interrupt.rs @@ -12,11 +12,9 @@ use core::cell::RefCell; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, interrupt::{self, Priority}, - peripherals::{Interrupt, Peripherals, TIMG0}, + peripherals::{Interrupt, TIMG0}, prelude::*, - system::SystemControl, timer::timg::{Timer, Timer0, TimerGroup}, }; @@ -25,11 +23,9 @@ static TIMER0: Mutex, esp_hal::Blocking>>>> = #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let timg0 = TimerGroup::new(peripherals.TIMG0); let timer0 = timg0.timer0; timer0.set_interrupt_handler(tg0_t0_level); @@ -50,9 +46,7 @@ fn tg0_t0_level() { critical_section::with(|cs| { esp_println::println!( "Interrupt at {} ms", - esp_hal::time::current_time() - .duration_since_epoch() - .to_millis() + esp_hal::time::now().duration_since_epoch().to_millis() ); let mut timer0 = TIMER0.borrow_ref_mut(cs); diff --git a/examples/src/bin/touch.rs b/examples/src/bin/touch.rs index f7870b07c22..22a324c28e7 100644 --- a/examples/src/bin/touch.rs +++ b/examples/src/bin/touch.rs @@ -16,21 +16,17 @@ use core::cell::RefCell; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, - gpio, - gpio::Io, + gpio::{GpioPin, Io}, macros::ram, - peripherals::Peripherals, prelude::*, rtc_cntl::Rtc, - system::SystemControl, - touch::{Continous, Touch, TouchConfig, TouchPad}, + touch::{Continuous, Touch, TouchConfig, TouchPad}, Blocking, }; use esp_println::println; -static TOUCH1: Mutex>>> = +static TOUCH1: Mutex, Continuous, Blocking>>>> = Mutex::new(RefCell::new(None)); #[handler] @@ -51,9 +47,7 @@ fn interrupt_handler() { #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -68,11 +62,11 @@ fn main() -> ! { ..Default::default() }); - let touch = Touch::continous_mode(peripherals.TOUCH, touch_cfg); + let touch = Touch::continuous_mode(peripherals.TOUCH, touch_cfg); let mut touch0 = TouchPad::new(touch_pin0, &touch); let mut touch1 = TouchPad::new(touch_pin1, &touch); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let touch1_baseline = touch1.read(); diff --git a/examples/src/bin/twai.rs b/examples/src/bin/twai.rs index e3c6d3256fc..de01a2a5e2a 100644 --- a/examples/src/bin/twai.rs +++ b/examples/src/bin/twai.rs @@ -13,6 +13,9 @@ //! ESP1/GPIO0 --- ESP1/GPIO2 --- ESP2/GPIO0 --- ESP2/GPIO2 --- 4.8kOhm --- ESP1/5V //! //! `IS_FIRST_SENDER` below must be set to false on one of the ESP's +//! +//! In case you want to use `self-testing`, get rid of everything related to the aforementioned `IS_FIRST_SENDER` +//! and follow the advice in the comments related to this mode. //% CHIPS: esp32c3 esp32c6 esp32s2 esp32s3 @@ -23,21 +26,16 @@ const IS_FIRST_SENDER: bool = true; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, - peripherals::Peripherals, prelude::*, - system::SystemControl, - twai::{self, filter::SingleStandardFilter, EspTwaiFrame, StandardId}, + twai::{self, filter::SingleStandardFilter, EspTwaiFrame, StandardId, TwaiMode}, }; use esp_println::println; use nb::block; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -48,15 +46,17 @@ fn main() -> ! { const CAN_BAUDRATE: twai::BaudRate = twai::BaudRate::B1000K; // !!! Use `new` when using a transceiver. `new_no_transceiver` sets TX to open-drain + // Self-testing also works using the regular `new` function. // Begin configuring the TWAI peripheral. The peripheral is in a reset like // state that prevents transmission but allows configuration. + // For self-testing use `SelfTest` mode of the TWAI peripheral. let mut can_config = twai::TwaiConfiguration::new_no_transceiver( peripherals.TWAI0, - can_tx_pin, can_rx_pin, - &clocks, + can_tx_pin, CAN_BAUDRATE, + TwaiMode::Normal, ); // Partially filter the incoming messages to reduce overhead of receiving @@ -76,6 +76,7 @@ fn main() -> ! { if IS_FIRST_SENDER { // Send a frame to the other ESP + // Use `new_self_reception` if you want to use self-testing. let frame = EspTwaiFrame::new(StandardId::ZERO.into(), &[1, 2, 3]).unwrap(); block!(can.transmit(&frame)).unwrap(); println!("Sent a frame"); diff --git a/examples/src/bin/ulp_riscv_core_basic.rs b/examples/src/bin/ulp_riscv_core_basic.rs index 578e7e6a31d..d89eda74521 100644 --- a/examples/src/bin/ulp_riscv_core_basic.rs +++ b/examples/src/bin/ulp_riscv_core_basic.rs @@ -14,7 +14,6 @@ use esp_backtrace as _; use esp_hal::{ gpio::{rtc_io::*, Io}, - peripherals::Peripherals, prelude::*, ulp_core, }; @@ -22,7 +21,7 @@ use esp_println::{print, println}; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let pin = LowPowerOutput::new(io.pins.gpio1); diff --git a/examples/src/bin/usb_serial.rs b/examples/src/bin/usb_serial.rs index 4032bc8a0e4..aee87614333 100644 --- a/examples/src/bin/usb_serial.rs +++ b/examples/src/bin/usb_serial.rs @@ -17,7 +17,6 @@ use esp_backtrace as _; use esp_hal::{ gpio::Io, otg_fs::{Usb, UsbBus}, - peripherals::Peripherals, prelude::*, }; use usb_device::prelude::{UsbDeviceBuilder, UsbVidPid}; @@ -27,7 +26,7 @@ static mut EP_MEMORY: [u32; 1024] = [0; 1024]; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); diff --git a/examples/src/bin/usb_serial_jtag.rs b/examples/src/bin/usb_serial_jtag.rs index d87ab1a7978..b6caa22bcf8 100644 --- a/examples/src/bin/usb_serial_jtag.rs +++ b/examples/src/bin/usb_serial_jtag.rs @@ -16,26 +16,16 @@ use core::{cell::RefCell, fmt::Write}; use critical_section::Mutex; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - delay::Delay, - peripherals::Peripherals, - prelude::*, - system::SystemControl, - usb_serial_jtag::UsbSerialJtag, - Blocking, -}; +use esp_hal::{delay::Delay, prelude::*, usb_serial_jtag::UsbSerialJtag, Blocking}; static USB_SERIAL: Mutex>>> = Mutex::new(RefCell::new(None)); #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let mut usb_serial = UsbSerialJtag::new(peripherals.USB_DEVICE); usb_serial.set_interrupt_handler(usb_device); diff --git a/examples/src/bin/watchdog.rs b/examples/src/bin/watchdog.rs index 00ba024813c..ee01618c217 100644 --- a/examples/src/bin/watchdog.rs +++ b/examples/src/bin/watchdog.rs @@ -9,25 +9,16 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - delay::Delay, - peripherals::Peripherals, - prelude::*, - system::SystemControl, - timer::timg::TimerGroup, -}; +use esp_hal::{delay::Delay, prelude::*, timer::timg::TimerGroup}; use esp_println::println; #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); - let delay = Delay::new(&clocks); + let delay = Delay::new(); - let timg0 = TimerGroup::new_async(peripherals.TIMG0, &clocks); + let timg0 = TimerGroup::new_async(peripherals.TIMG0); let mut wdt0 = timg0.wdt; wdt0.enable(); wdt0.set_timeout(2u64.secs()); diff --git a/examples/src/bin/wifi_80211_tx.rs b/examples/src/bin/wifi_80211_tx.rs new file mode 100644 index 00000000000..88b93dc3c7d --- /dev/null +++ b/examples/src/bin/wifi_80211_tx.rs @@ -0,0 +1,122 @@ +//! WiFi frame injection example +//! +//! Periodically transmits a beacon frame. + +//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer +//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 + +#![no_std] +#![no_main] + +use core::marker::PhantomData; + +use esp_alloc as _; +use esp_backtrace as _; +use esp_hal::{ + delay::Delay, + prelude::*, + rng::Rng, + timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer}, +}; +use esp_wifi::{initialize, wifi, EspWifiInitFor}; +use ieee80211::{ + common::{CapabilitiesInformation, FCFFlags}, + element_chain, + elements::{DSSSParameterSetElement, RawIEEE80211Element, SSIDElement}, + mgmt_frame::{body::BeaconBody, header::ManagementFrameHeader, BeaconFrame}, + scroll::Pwrite, + supported_rates, +}; + +const SSID: &str = "esp-wifi 802.11 injection"; +/// This is an arbitrary MAC address, used for the fake beacon frames. +const MAC_ADDRESS: [u8; 6] = [0x00, 0x80, 0x41, 0x13, 0x37, 0x42]; + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); + + esp_alloc::heap_allocator!(72 * 1024); + + let delay = Delay::new(); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + let timer0: ErasedTimer = timg0.timer0.into(); + let timer = PeriodicTimer::new(timer0); + + let init = initialize( + EspWifiInitFor::Wifi, + timer, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap(); + + let wifi = peripherals.WIFI; + + // We must initialize some kind of interface and start it. + let (_, mut controller) = wifi::new_with_mode(&init, wifi, wifi::WifiApDevice).unwrap(); + controller.start().unwrap(); + + let mut sniffer = controller.take_sniffer().unwrap(); + + // Create a buffer, which can hold the enitre serialized beacon frame. + let mut beacon = [0u8; 300]; + let length = beacon + .pwrite( + BeaconFrame { + header: ManagementFrameHeader { + fcf_flags: FCFFlags::new(), + duration: 0, + receiver_address: [0xff; 6].into(), + transmitter_address: MAC_ADDRESS.into(), + bssid: MAC_ADDRESS.into(), + ..Default::default() + }, + body: BeaconBody { + timestamp: 0, + // We transmit a beacon every 100 ms/TUs + beacon_interval: 100, + capabilities_info: CapabilitiesInformation::new().with_is_ess(true), + elements: element_chain! { + SSIDElement::new(SSID).unwrap(), + // These are known good values. + supported_rates![ + 1 B, + 2 B, + 5.5 B, + 11 B, + 6, + 9, + 12, + 18 + ], + DSSSParameterSetElement { + current_channel: 1, + }, + // This contains the Traffic Indication Map(TIM), for which `ieee80211-rs` currently lacks support. + RawIEEE80211Element { + tlv_type: 5, + slice: [0x01, 0x02, 0x00, 0x00].as_slice(), + _phantom: PhantomData + } + }, + _phantom: PhantomData, + }, + }, + 0, + ) + .unwrap(); + // Only use the actually written bytes. + let beacon = &beacon[..length]; + + loop { + sniffer.send_raw_frame(false, beacon, false).unwrap(); + delay.delay(100.millis()); + } +} diff --git a/examples/src/bin/wifi_access_point.rs b/examples/src/bin/wifi_access_point.rs index 06e449cc84d..d4171d51edf 100644 --- a/examples/src/bin/wifi_access_point.rs +++ b/examples/src/bin/wifi_access_point.rs @@ -14,15 +14,9 @@ #![no_main] use embedded_io::*; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - prelude::*, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::{print, println}; use esp_wifi::{ current_millis, @@ -41,22 +35,21 @@ use smoltcp::iface::SocketStorage; #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); diff --git a/examples/src/bin/wifi_access_point_with_sta.rs b/examples/src/bin/wifi_access_point_with_sta.rs index b47feead409..4417621cb49 100644 --- a/examples/src/bin/wifi_access_point_with_sta.rs +++ b/examples/src/bin/wifi_access_point_with_sta.rs @@ -15,15 +15,9 @@ #![no_main] use embedded_io::*; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - prelude::*, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::{print, println}; use esp_wifi::{ current_millis, @@ -48,22 +42,21 @@ const PASSWORD: &str = env!("PASSWORD"); #[entry] fn main() -> ! { esp_println::logger::init_logger(log::LevelFilter::Info); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); diff --git a/examples/src/bin/wifi_bench.rs b/examples/src/bin/wifi_bench.rs index 83005f34fd3..ae9924840b2 100644 --- a/examples/src/bin/wifi_bench.rs +++ b/examples/src/bin/wifi_bench.rs @@ -14,16 +14,9 @@ #![no_main] use embedded_io::*; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - delay::Delay, - peripherals::Peripherals, - prelude::*, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer}, -}; +use esp_hal::{delay::Delay, prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::println; use esp_wifi::{ current_millis, @@ -59,24 +52,23 @@ const UPLOAD_DOWNLOAD_PORT: u16 = 4323; #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); let server_address: Ipv4Address = HOST_IP.parse().expect("Invalid HOST_IP address"); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); @@ -141,7 +133,7 @@ fn main() -> ! { let mut tx_buffer = [0u8; TX_BUFFER_SIZE]; let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer); - let delay = Delay::new(&clocks); + let delay = Delay::new(); loop { test_download(server_address, &mut socket); diff --git a/examples/src/bin/wifi_ble.rs b/examples/src/bin/wifi_ble.rs index b264ded3df6..dfbf3690c32 100644 --- a/examples/src/bin/wifi_ble.rs +++ b/examples/src/bin/wifi_ble.rs @@ -22,15 +22,13 @@ use bleps::{ Ble, HciConnector, }; +use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::{Input, Io, Pull}, - peripherals::*, prelude::*, rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer}, + timer::timg::TimerGroup, }; use esp_println::println; use esp_wifi::{ble::controller::BleConnector, initialize, EspWifiInitFor}; @@ -38,35 +36,33 @@ use esp_wifi::{ble::controller::BleConnector, initialize, EspWifiInitFor}; #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); + esp_alloc::heap_allocator!(72 * 1024); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); - - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Ble, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] - let button = Input::new(io.pins.gpio0, Pull::Down); - #[cfg(any( - feature = "esp32c2", - feature = "esp32c3", - feature = "esp32c6", - feature = "esp32h2" - ))] - let button = Input::new(io.pins.gpio9, Pull::Down); + + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { + let button = Input::new(io.pins.gpio0, Pull::Down); + } else { + let button = Input::new(io.pins.gpio9, Pull::Down); + } + } let mut debounce_cnt = 500; diff --git a/examples/src/bin/wifi_coex.rs b/examples/src/bin/wifi_coex.rs index 16fa94e1cd6..3c70dba79e5 100644 --- a/examples/src/bin/wifi_coex.rs +++ b/examples/src/bin/wifi_coex.rs @@ -25,15 +25,9 @@ use bleps::{ HciConnector, }; use embedded_io::*; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - prelude::*, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::{print, println}; use esp_wifi::{ ble::controller::BleConnector, @@ -54,22 +48,39 @@ const PASSWORD: &str = env!("PASSWORD"); #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); + + static mut HEAP: core::mem::MaybeUninit<[u8; 72 * 1024]> = core::mem::MaybeUninit::uninit(); - let peripherals = Peripherals::take(); + #[link_section = ".dram2_uninit"] + static mut HEAP2: core::mem::MaybeUninit<[u8; 64 * 1024]> = core::mem::MaybeUninit::uninit(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + unsafe { + esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( + HEAP.as_mut_ptr() as *mut u8, + core::mem::size_of_val(&*core::ptr::addr_of!(HEAP)), + esp_alloc::MemoryCapability::Internal.into(), + )); + + // COEX needs more RAM - add some more + esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( + HEAP2.as_mut_ptr() as *mut u8, + core::mem::size_of_val(&*core::ptr::addr_of!(HEAP2)), + esp_alloc::MemoryCapability::Internal.into(), + )); + } - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::WifiBle, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); diff --git a/examples/src/bin/wifi_dhcp.rs b/examples/src/bin/wifi_dhcp.rs index 72183c5ece8..63006626a69 100644 --- a/examples/src/bin/wifi_dhcp.rs +++ b/examples/src/bin/wifi_dhcp.rs @@ -11,16 +11,12 @@ #![no_std] #![no_main] +extern crate alloc; + use embedded_io::*; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - prelude::*, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::{print, println}; use esp_wifi::{ current_millis, @@ -47,22 +43,21 @@ const PASSWORD: &str = env!("PASSWORD"); #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); diff --git a/examples/src/bin/wifi_embassy_access_point.rs b/examples/src/bin/wifi_embassy_access_point.rs index 9d1f9f1c800..43879decc2f 100644 --- a/examples/src/bin/wifi_embassy_access_point.rs +++ b/examples/src/bin/wifi_embassy_access_point.rs @@ -8,8 +8,8 @@ //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//% FEATURES: async embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils -//% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] #![no_main] @@ -17,7 +17,6 @@ use embassy_executor::Spawner; use embassy_net::{ tcp::TcpSocket, - Config, IpListenEndpoint, Ipv4Address, Ipv4Cidr, @@ -26,14 +25,9 @@ use embassy_net::{ StaticConfigV4, }; use embassy_time::{Duration, Timer}; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::{print, println}; use esp_wifi::{ initialize, @@ -62,22 +56,21 @@ macro_rules! mk_static { #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); @@ -85,25 +78,18 @@ async fn main(spawner: Spawner) -> ! { let (wifi_interface, controller) = esp_wifi::wifi::new_with_mode(&init, wifi, WifiApDevice).unwrap(); - #[cfg(feature = "esp32")] - { - let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks); - let timer0: ErasedTimer = timg1.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - } - - #[cfg(not(feature = "esp32"))] - { - let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + let timg1 = TimerGroup::new(peripherals.TIMG1); + esp_hal_embassy::init(timg1.timer0); + } else { + use esp_hal::timer::systimer::{SystemTimer, Target}; + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init(systimer.alarm0); + } } - let config = Config::ipv4_static(StaticConfigV4 { + let config = embassy_net::Config::ipv4_static(StaticConfigV4 { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 2, 1), 24), gateway: Some(Ipv4Address::from_bytes(&[192, 168, 2, 1])), dns_servers: Default::default(), diff --git a/examples/src/bin/wifi_embassy_access_point_with_sta.rs b/examples/src/bin/wifi_embassy_access_point_with_sta.rs index 360a2d60a0e..de9cf87b50a 100644 --- a/examples/src/bin/wifi_embassy_access_point_with_sta.rs +++ b/examples/src/bin/wifi_embassy_access_point_with_sta.rs @@ -11,8 +11,8 @@ //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//% FEATURES: async embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils -//% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] #![no_main] @@ -20,7 +20,6 @@ use embassy_executor::Spawner; use embassy_net::{ tcp::TcpSocket, - Config, IpListenEndpoint, Ipv4Address, Ipv4Cidr, @@ -29,14 +28,9 @@ use embassy_net::{ StaticConfigV4, }; use embassy_time::{Duration, Timer}; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::{print, println}; use esp_wifi::{ initialize, @@ -70,22 +64,21 @@ macro_rules! mk_static { #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); @@ -93,30 +86,23 @@ async fn main(spawner: Spawner) -> ! { let (wifi_ap_interface, wifi_sta_interface, mut controller) = esp_wifi::wifi::new_ap_sta(&init, wifi).unwrap(); - #[cfg(feature = "esp32")] - { - let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks); - let timer0: ErasedTimer = timg1.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - } - - #[cfg(not(feature = "esp32"))] - { - let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + let timg1 = TimerGroup::new(peripherals.TIMG1); + esp_hal_embassy::init(timg1.timer0); + } else { + use esp_hal::timer::systimer::{SystemTimer, Target}; + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init(systimer.alarm0); + } } - let ap_config = Config::ipv4_static(StaticConfigV4 { + let ap_config = embassy_net::Config::ipv4_static(StaticConfigV4 { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 2, 1), 24), gateway: Some(Ipv4Address::from_bytes(&[192, 168, 2, 1])), dns_servers: Default::default(), }); - let sta_config = Config::dhcpv4(Default::default()); + let sta_config = embassy_net::Config::dhcpv4(Default::default()); let seed = 1234; // very random, very secure seed diff --git a/examples/src/bin/wifi_embassy_bench.rs b/examples/src/bin/wifi_embassy_bench.rs index bebb4795695..7eaa1997e71 100644 --- a/examples/src/bin/wifi_embassy_bench.rs +++ b/examples/src/bin/wifi_embassy_bench.rs @@ -10,24 +10,19 @@ //! Because of the huge task-arena size configured this won't work on ESP32-S2 and ESP32-C2 //! -//% FEATURES: async embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils -//% CHIPS: esp32 esp32s3 esp32c3 esp32c6 +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% CHIPS: esp32 esp32s2 esp32s3 esp32c3 esp32c6 #![no_std] #![no_main] use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_net::{tcp::TcpSocket, Config, Ipv4Address, Stack, StackResources}; +use embassy_net::{tcp::TcpSocket, Ipv4Address, Stack, StackResources}; use embassy_time::{with_timeout, Duration, Timer}; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::println; use esp_wifi::{ initialize, @@ -72,24 +67,41 @@ static mut TX_BUFFER: [u8; TX_BUFFER_SIZE] = [0; TX_BUFFER_SIZE]; #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); - - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); + + static mut HEAP: core::mem::MaybeUninit<[u8; 32 * 1024]> = core::mem::MaybeUninit::uninit(); + + #[link_section = ".dram2_uninit"] + static mut HEAP2: core::mem::MaybeUninit<[u8; 64 * 1024]> = core::mem::MaybeUninit::uninit(); + + unsafe { + esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( + HEAP.as_mut_ptr() as *mut u8, + core::mem::size_of_val(&*core::ptr::addr_of!(HEAP)), + esp_alloc::MemoryCapability::Internal.into(), + )); + + // add some more RAM + esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( + HEAP2.as_mut_ptr() as *mut u8, + core::mem::size_of_val(&*core::ptr::addr_of!(HEAP2)), + esp_alloc::MemoryCapability::Internal.into(), + )); + } let server_address: Ipv4Address = HOST_IP.parse().expect("Invalid HOST_IP address"); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); @@ -97,25 +109,18 @@ async fn main(spawner: Spawner) -> ! { let (wifi_interface, controller) = esp_wifi::wifi::new_with_mode(&init, wifi, WifiStaDevice).unwrap(); - #[cfg(feature = "esp32")] - { - let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks); - let timer0: ErasedTimer = timg1.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - } - - #[cfg(not(feature = "esp32"))] - { - let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + let timg1 = TimerGroup::new(peripherals.TIMG1); + esp_hal_embassy::init(timg1.timer0); + } else { + use esp_hal::timer::systimer::{SystemTimer, Target}; + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init(systimer.alarm0); + } } - let config = Config::dhcpv4(Default::default()); + let config = embassy_net::Config::dhcpv4(Default::default()); let seed = 1234; // very random, very secure seed diff --git a/examples/src/bin/wifi_embassy_ble.rs b/examples/src/bin/wifi_embassy_ble.rs index 606e18eda28..d70d3414c17 100644 --- a/examples/src/bin/wifi_embassy_ble.rs +++ b/examples/src/bin/wifi_embassy_ble.rs @@ -4,7 +4,7 @@ //! - offers one service with three characteristics (one is read/write, one is write only, one is read/write/notify) //! - pressing the boot-button on a dev-board will send a notification if it is subscribed -//% FEATURES: async embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/ble +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/ble //% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 esp32h2 #![no_std] @@ -25,77 +25,56 @@ use bleps::{ gatt, }; use embassy_executor::Spawner; +use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::{Input, Io, Pull}, - peripherals::*, + prelude::*, rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer, PeriodicTimer}, + timer::timg::TimerGroup, }; use esp_println::println; use esp_wifi::{ble::controller::asynch::BleConnector, initialize, EspWifiInitFor}; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] async fn main(_spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Ble, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] - let button = Input::new(io.pins.gpio0, Pull::Down); - #[cfg(any( - feature = "esp32c2", - feature = "esp32c3", - feature = "esp32c6", - feature = "esp32h2" - ))] - let button = Input::new(io.pins.gpio9, Pull::Down); - - #[cfg(feature = "esp32")] - { - let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks); - let timer0: ErasedTimer = timg1.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { + let button = Input::new(io.pins.gpio0, Pull::Down); + } else { + let button = Input::new(io.pins.gpio9, Pull::Down); + } } - #[cfg(not(feature = "esp32"))] - { - let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + let timg1 = TimerGroup::new(peripherals.TIMG1); + esp_hal_embassy::init(timg1.timer0); + } else { + use esp_hal::timer::systimer::{SystemTimer, Target}; + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init(systimer.alarm0); + } } let mut bluetooth = peripherals.BT; diff --git a/examples/src/bin/wifi_embassy_dhcp.rs b/examples/src/bin/wifi_embassy_dhcp.rs index 66636de046a..bc4237cb997 100644 --- a/examples/src/bin/wifi_embassy_dhcp.rs +++ b/examples/src/bin/wifi_embassy_dhcp.rs @@ -7,23 +7,18 @@ //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//% FEATURES: async embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils -//% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] #![no_main] use embassy_executor::Spawner; -use embassy_net::{tcp::TcpSocket, Config, Ipv4Address, Stack, StackResources}; +use embassy_net::{tcp::TcpSocket, Ipv4Address, Stack, StackResources}; use embassy_time::{Duration, Timer}; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::println; use esp_wifi::{ initialize, @@ -55,22 +50,21 @@ const PASSWORD: &str = env!("PASSWORD"); #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); @@ -78,25 +72,18 @@ async fn main(spawner: Spawner) -> ! { let (wifi_interface, controller) = esp_wifi::wifi::new_with_mode(&init, wifi, WifiStaDevice).unwrap(); - #[cfg(feature = "esp32")] - { - let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks); - let timer0: ErasedTimer = timg1.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - } - - #[cfg(not(feature = "esp32"))] - { - let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + let timg1 = TimerGroup::new(peripherals.TIMG1); + esp_hal_embassy::init(timg1.timer0); + } else { + use esp_hal::timer::systimer::{SystemTimer, Target}; + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init(systimer.alarm0); + } } - let config = Config::dhcpv4(Default::default()); + let config = embassy_net::Config::dhcpv4(Default::default()); let seed = 1234; // very random, very secure seed diff --git a/examples/src/bin/wifi_embassy_esp_now.rs b/examples/src/bin/wifi_embassy_esp_now.rs index c3d42f881de..a81d714cd8e 100644 --- a/examples/src/bin/wifi_embassy_esp_now.rs +++ b/examples/src/bin/wifi_embassy_esp_now.rs @@ -4,8 +4,8 @@ //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//% FEATURES: async embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now -//% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now +//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] #![no_main] @@ -13,14 +13,9 @@ use embassy_executor::Spawner; use embassy_futures::select::{select, Either}; use embassy_time::{Duration, Ticker}; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::println; use esp_wifi::{ esp_now::{PeerInfo, BROADCAST_ADDRESS}, @@ -28,35 +23,24 @@ use esp_wifi::{ EspWifiInitFor, }; -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - #[esp_hal_embassy::main] async fn main(_spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); @@ -64,22 +48,15 @@ async fn main(_spawner: Spawner) -> ! { let mut esp_now = esp_wifi::esp_now::EspNow::new(&init, wifi).unwrap(); println!("esp-now version {}", esp_now.get_version().unwrap()); - #[cfg(feature = "esp32")] - { - let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks); - let timer0: ErasedTimer = timg1.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - } - - #[cfg(not(feature = "esp32"))] - { - let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + let timg1 = TimerGroup::new(peripherals.TIMG1); + esp_hal_embassy::init(timg1.timer0); + } else { + use esp_hal::timer::systimer::{SystemTimer, Target}; + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init(systimer.alarm0); + } } let mut ticker = Ticker::every(Duration::from_secs(5)); diff --git a/examples/src/bin/wifi_embassy_esp_now_duplex.rs b/examples/src/bin/wifi_embassy_esp_now_duplex.rs index 5ef0efe17fe..060c58215f1 100644 --- a/examples/src/bin/wifi_embassy_esp_now_duplex.rs +++ b/examples/src/bin/wifi_embassy_esp_now_duplex.rs @@ -4,8 +4,8 @@ //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//% FEATURES: async embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now -//% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now +//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] #![no_main] @@ -13,14 +13,9 @@ use embassy_executor::Spawner; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, mutex::Mutex}; use embassy_time::{Duration, Ticker}; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::println; use esp_wifi::{ esp_now::{EspNowManager, EspNowReceiver, EspNowSender, PeerInfo, BROADCAST_ADDRESS}, @@ -41,22 +36,21 @@ macro_rules! mk_static { #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); @@ -64,22 +58,15 @@ async fn main(spawner: Spawner) -> ! { let esp_now = esp_wifi::esp_now::EspNow::new(&init, wifi).unwrap(); println!("esp-now version {}", esp_now.get_version().unwrap()); - #[cfg(feature = "esp32")] - { - let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks); - let timer0: ErasedTimer = timg1.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - } - - #[cfg(not(feature = "esp32"))] - { - let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + let timg1 = TimerGroup::new(peripherals.TIMG1); + esp_hal_embassy::init(timg1.timer0); + } else { + use esp_hal::timer::systimer::{SystemTimer, Target}; + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init(systimer.alarm0); + } } let (manager, sender, receiver) = esp_now.split(); diff --git a/examples/src/bin/wifi_embassy_trouble.rs b/examples/src/bin/wifi_embassy_trouble.rs new file mode 100644 index 00000000000..ce71add3f70 --- /dev/null +++ b/examples/src/bin/wifi_embassy_trouble.rs @@ -0,0 +1,157 @@ +//! Embassy BLE Example using Trouble +//! +//! - starts Bluetooth advertising +//! - offers a GATT service providing a battery percentage reading +//! - automatically notifies subscribers every second +//! + +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/ble +//% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 esp32h2 + +#![no_std] +#![no_main] + +use bt_hci::controller::ExternalController; +use embassy_executor::Spawner; +use embassy_futures::join::join3; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_time::{Duration, Timer}; +use esp_backtrace as _; +use esp_hal::{prelude::*, timer::timg::TimerGroup}; +use esp_wifi::ble::controller::asynch::BleConnector; +use log::*; +use static_cell::StaticCell; +use trouble_host::{ + advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}, + attribute::{AttributeTable, CharacteristicProp, Service, Uuid}, + Address, + BleHost, + BleHostResources, + PacketQos, +}; + +#[esp_hal_embassy::main] +async fn main(_s: Spawner) { + esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); + let timg0 = TimerGroup::new(peripherals.TIMG0); + + let init = esp_wifi::initialize( + esp_wifi::EspWifiInitFor::Ble, + timg0.timer0, + esp_hal::rng::Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap(); + + #[cfg(feature = "esp32")] + { + let timg1 = TimerGroup::new(peripherals.TIMG1); + esp_hal_embassy::init(timg1.timer0); + } + + #[cfg(not(feature = "esp32"))] + { + let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER) + .split::(); + esp_hal_embassy::init(systimer.alarm0); + } + + let mut bluetooth = peripherals.BT; + let connector = BleConnector::new(&init, &mut bluetooth); + let controller: ExternalController<_, 20> = ExternalController::new(connector); + + static HOST_RESOURCES: StaticCell> = StaticCell::new(); + let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None)); + + let mut ble: BleHost<'_, _> = BleHost::new(controller, host_resources); + + ble.set_random_address(Address::random([0xff, 0x9f, 0x1a, 0x05, 0xe4, 0xff])); + let mut table: AttributeTable<'_, NoopRawMutex, 10> = AttributeTable::new(); + + let id = b"Trouble ESP32"; + let appearance = [0x80, 0x07]; + let mut bat_level = [0; 1]; + // Generic Access Service (mandatory) + let mut svc = table.add_service(Service::new(0x1800)); + let _ = svc.add_characteristic_ro(0x2a00, id); + let _ = svc.add_characteristic_ro(0x2a01, &appearance[..]); + svc.build(); + + // Generic attribute service (mandatory) + table.add_service(Service::new(0x1801)); + + // Battery service + let bat_level_handle = table.add_service(Service::new(0x180f)).add_characteristic( + 0x2a19, + &[CharacteristicProp::Read, CharacteristicProp::Notify], + &mut bat_level, + ); + + let mut adv_data = [0; 31]; + AdStructure::encode_slice( + &[ + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]), + AdStructure::CompleteLocalName(b"Trouble ESP32"), + ], + &mut adv_data[..], + ) + .unwrap(); + + let server = ble.gatt_server::(&table); + + info!("Starting advertising and GATT service"); + // Run all 3 tasks in a join. They can also be separate embassy tasks. + let _ = join3( + // Runs the BLE host task + ble.run(), + // Processing events from GATT server (if an attribute was written) + async { + loop { + match server.next().await { + Ok(_event) => { + info!("Gatt event!"); + } + Err(e) => { + warn!("Error processing GATT events: {:?}", e); + } + } + } + }, + // Advertise our presence to the world. + async { + loop { + let mut advertiser = ble + .advertise( + &Default::default(), + Advertisement::ConnectableScannableUndirected { + adv_data: &adv_data[..], + scan_data: &[], + }, + ) + .await + .unwrap(); + let conn = advertiser.accept().await.unwrap(); + // Keep connection alive and notify with value change + let mut tick: u8 = 0; + loop { + if !conn.is_connected() { + break; + } + Timer::after(Duration::from_secs(1)).await; + tick = tick.wrapping_add(1); + server + .notify(&ble, bat_level_handle, &conn, &[tick]) + .await + .ok(); + } + } + }, + ) + .await; +} diff --git a/examples/src/bin/wifi_esp_now.rs b/examples/src/bin/wifi_esp_now.rs index 35e61e43b9b..51432b49260 100644 --- a/examples/src/bin/wifi_esp_now.rs +++ b/examples/src/bin/wifi_esp_now.rs @@ -8,15 +8,9 @@ #![no_std] #![no_main] +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - prelude::*, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::println; use esp_wifi::{ current_millis, @@ -28,22 +22,21 @@ use esp_wifi::{ #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); diff --git a/examples/src/bin/wifi_sniffer.rs b/examples/src/bin/wifi_sniffer.rs new file mode 100644 index 00000000000..99837ea5113 --- /dev/null +++ b/examples/src/bin/wifi_sniffer.rs @@ -0,0 +1,80 @@ +//! WiFi sniffer example +//! +//! Sniffs for beacon frames. + +//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer +//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 + +#![no_std] +#![no_main] + +extern crate alloc; + +use alloc::{ + collections::btree_set::BTreeSet, + string::{String, ToString}, +}; +use core::cell::RefCell; + +use critical_section::Mutex; +use esp_backtrace as _; +use esp_hal::{ + prelude::*, + rng::Rng, + timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer}, +}; +use esp_println::println; +use esp_wifi::{initialize, wifi, EspWifiInitFor}; +use ieee80211::{match_frames, mgmt_frame::BeaconFrame}; + +static KNOWN_SSIDS: Mutex>> = Mutex::new(RefCell::new(BTreeSet::new())); + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); + + esp_alloc::heap_allocator!(72 * 1024); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + let timer0: ErasedTimer = timg0.timer0.into(); + let timer = PeriodicTimer::new(timer0); + + let init = initialize( + EspWifiInitFor::Wifi, + timer, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap(); + + let wifi = peripherals.WIFI; + + // We must initialize some kind of interface and start it. + let (_, mut controller) = wifi::new_with_mode(&init, wifi, wifi::WifiApDevice).unwrap(); + controller.start().unwrap(); + + let mut sniffer = controller.take_sniffer().unwrap(); + sniffer.set_promiscuous_mode(true).unwrap(); + sniffer.set_receive_cb(|packet| { + let _ = match_frames! { + packet.data, + beacon = BeaconFrame => { + let Some(ssid) = beacon.ssid() else { + return; + }; + if critical_section::with(|cs| { + KNOWN_SSIDS.borrow_ref_mut(cs).insert(ssid.to_string()) + }) { + println!("Found new AP with SSID: {ssid}"); + } + } + }; + }); + + loop {} +} diff --git a/examples/src/bin/wifi_static_ip.rs b/examples/src/bin/wifi_static_ip.rs index 855ceca940f..4f544626bf7 100644 --- a/examples/src/bin/wifi_static_ip.rs +++ b/examples/src/bin/wifi_static_ip.rs @@ -13,15 +13,9 @@ #![no_main] use embedded_io::*; +use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - prelude::*, - rng::Rng, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::{print, println}; use esp_wifi::{ current_millis, @@ -47,22 +41,21 @@ const GATEWAY_IP: &str = env!("GATEWAY_IP"); #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); - let peripherals = Peripherals::take(); - - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::max(system.clock_control).freeze(); + esp_alloc::heap_allocator!(72 * 1024); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); + let timg0 = TimerGroup::new(peripherals.TIMG0); let init = initialize( EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, - &clocks, ) .unwrap(); diff --git a/extras/bench-server/src/main.rs b/extras/bench-server/src/main.rs index 21ab64867c7..7f38acf749d 100644 --- a/extras/bench-server/src/main.rs +++ b/extras/bench-server/src/main.rs @@ -1,7 +1,9 @@ -use std::io::{Read, Write}; -use std::net::{TcpListener, TcpStream}; -use std::thread::spawn; -use std::time::Duration; +use std::{ + io::{Read, Write}, + net::{TcpListener, TcpStream}, + thread::spawn, + time::Duration, +}; use log::info; @@ -22,8 +24,12 @@ fn tx_listen() { } fn tx_conn(mut socket: TcpStream) { - socket.set_read_timeout(Some(Duration::from_secs(30))).unwrap(); - socket.set_write_timeout(Some(Duration::from_secs(30))).unwrap(); + socket + .set_read_timeout(Some(Duration::from_secs(30))) + .unwrap(); + socket + .set_write_timeout(Some(Duration::from_secs(30))) + .unwrap(); let buf = [0; 1024]; loop { @@ -44,8 +50,12 @@ fn rx_listen() { } fn rx_conn(mut socket: TcpStream) { - socket.set_read_timeout(Some(Duration::from_secs(30))).unwrap(); - socket.set_write_timeout(Some(Duration::from_secs(30))).unwrap(); + socket + .set_read_timeout(Some(Duration::from_secs(30))) + .unwrap(); + socket + .set_write_timeout(Some(Duration::from_secs(30))) + .unwrap(); let mut buf = [0; 1024]; loop { @@ -66,8 +76,12 @@ fn rxtx_listen() { } fn rxtx_conn(mut socket: TcpStream) { - socket.set_read_timeout(Some(Duration::from_secs(30))).unwrap(); - socket.set_write_timeout(Some(Duration::from_secs(30))).unwrap(); + socket + .set_read_timeout(Some(Duration::from_secs(30))) + .unwrap(); + socket + .set_write_timeout(Some(Duration::from_secs(30))) + .unwrap(); let mut buf = [0; 1024]; loop { @@ -84,4 +98,4 @@ fn rxtx_conn(mut socket: TcpStream) { } } } -} \ No newline at end of file +} diff --git a/extras/esp-wifishark/Cargo.toml b/extras/esp-wifishark/Cargo.toml index af5aae771b5..8414d6e37da 100644 --- a/extras/esp-wifishark/Cargo.toml +++ b/extras/esp-wifishark/Cargo.toml @@ -8,4 +8,3 @@ r-extcap = "0.2.4" pcap-file = "2.0.0" serialport = "4.2.1" clap = { version = "4.3.5", features = ["derive"] } -lazy_static = "1.4.0" diff --git a/extras/esp-wifishark/src/main.rs b/extras/esp-wifishark/src/main.rs index 6d541c8f484..b6863599670 100644 --- a/extras/esp-wifishark/src/main.rs +++ b/extras/esp-wifishark/src/main.rs @@ -4,7 +4,6 @@ use std::{ }; use clap::Parser; -use lazy_static::lazy_static; use pcap_file::{ pcap::{PcapHeader, PcapPacket, PcapWriter}, DataLink, @@ -25,13 +24,16 @@ pub struct AppArgs { serialport: String, } -lazy_static! { - static ref METADATA: Metadata = Metadata { - help_url: "http://github.com/esp-rs/esp-wifi".into(), - display_description: "esp-wifi".into(), - ..r_extcap::cargo_metadata!() - }; - static ref WIFI_CAPTURE_INTERFACE: Interface = Interface { +fn main() { + let args = AppArgs::parse(); + + if !args.extcap.capture { + if let Some(_filter) = args.extcap.extcap_capture_filter { + std::process::exit(0); + } + } + + let wifi_capture_interface = Interface { value: "wifi".into(), display: "esp-wifi Ethernet capture".into(), dlt: Dlt { @@ -40,7 +42,8 @@ lazy_static! { display: "Ethernet".into(), }, }; - static ref BT_CAPTURE_INTERFACE: Interface = Interface { + + let bt_capture_interface = Interface { value: "bt".into(), display: "esp-wifi HCI capture".into(), dlt: Dlt { @@ -49,44 +52,43 @@ lazy_static! { display: "HCI".into(), }, }; - static ref CONFIG_SERIALPORT: StringConfig = StringConfig::builder() - .config_number(1) - .call("serialport") - .display("Serialport") - .tooltip("Serialport to connect to") - .required(false) - .placeholder("") - .build(); -} - -fn main() { - let args = AppArgs::parse(); - - if !args.extcap.capture { - if let Some(_filter) = args.extcap.extcap_capture_filter { - std::process::exit(0); - } - } match args.extcap.run().unwrap() { ExtcapStep::Interfaces(interfaces_step) => { + let metadata = Metadata { + help_url: "http://github.com/esp-rs/esp-wifi".into(), + display_description: "esp-wifi".into(), + ..r_extcap::cargo_metadata!() + }; + interfaces_step.list_interfaces( - &METADATA, - &[&*WIFI_CAPTURE_INTERFACE, &*BT_CAPTURE_INTERFACE], + &metadata, + &[&wifi_capture_interface, &bt_capture_interface], &[], ); } ExtcapStep::Dlts(dlts_step) => { dlts_step - .print_from_interfaces(&[&*WIFI_CAPTURE_INTERFACE, &*BT_CAPTURE_INTERFACE]) + .print_from_interfaces(&[&wifi_capture_interface, &bt_capture_interface]) .unwrap(); } - ExtcapStep::Config(config_step) => config_step.list_configs(&[&*CONFIG_SERIALPORT]), + ExtcapStep::Config(config_step) => { + let config_serialport = StringConfig::builder() + .config_number(1) + .call("serialport") + .display("Serialport") + .tooltip("Serialport to connect to") + .required(false) + .placeholder("") + .build(); + + config_step.list_configs(&[&config_serialport]) + } ExtcapStep::ReloadConfig(_reload_config_step) => { panic!("Unsupported operation"); } ExtcapStep::Capture(capture_step) => { - let (data_link, prefix) = if capture_step.interface == WIFI_CAPTURE_INTERFACE.value { + let (data_link, prefix) = if capture_step.interface == wifi_capture_interface.value { (DataLink::ETHERNET, "@WIFIFRAME [") } else { (DataLink::BLUETOOTH_HCI_H4, "@HCIFRAME [") diff --git a/extras/ieee802154-sniffer/Cargo.toml b/extras/ieee802154-sniffer/Cargo.toml index a15f339ea4d..c590be02960 100644 --- a/extras/ieee802154-sniffer/Cargo.toml +++ b/extras/ieee802154-sniffer/Cargo.toml @@ -8,4 +8,3 @@ r-extcap = "0.2.4" pcap-file = "2.0.0" serialport = "4.2.0" clap = { version = "4.1.7", features = ["derive"] } -lazy_static = "1.4.0" diff --git a/extras/ieee802154-sniffer/src/main.rs b/extras/ieee802154-sniffer/src/main.rs index 8e1e7d2d1bb..097ad184302 100644 --- a/extras/ieee802154-sniffer/src/main.rs +++ b/extras/ieee802154-sniffer/src/main.rs @@ -4,7 +4,6 @@ use std::{ }; use clap::Parser; -use lazy_static::lazy_static; use pcap_file::{ pcap::{PcapHeader, PcapPacket, PcapWriter}, DataLink, @@ -28,102 +27,11 @@ pub struct AppArgs { channel: String, } -lazy_static! { - static ref METADATA: Metadata = Metadata { - help_url: "http://github.com/esp-rs".into(), - display_description: "esp-ieee802154".into(), - ..r_extcap::cargo_metadata!() - }; - static ref WIFI_CAPTURE_INTERFACE: Interface = Interface { - value: "802.15.4".into(), - display: "esp-ieee802154 Sniffer".into(), - dlt: Dlt { - data_link_type: DataLink::USER0, - name: "USER0".into(), - display: "IEEE802.15.4".into(), - }, - }; - static ref CONFIG_SERIALPORT: StringConfig = StringConfig::builder() - .config_number(1) - .call("serialport") - .display("Serialport") - .tooltip("Serialport to connect to") - .required(false) - .placeholder("") - .build(); - static ref CONFIG_CHANNEL: SelectorConfig = SelectorConfig::builder() - .config_number(3) - .call("channel") - .display("Channel") - .tooltip("Channel Selector") - .default_options([ - ConfigOptionValue::builder() - .value("11") - .display("11") - .default(true) - .build(), - ConfigOptionValue::builder() - .value("12") - .display("12") - .build(), - ConfigOptionValue::builder() - .value("13") - .display("13") - .build(), - ConfigOptionValue::builder() - .value("14") - .display("14") - .build(), - ConfigOptionValue::builder() - .value("15") - .display("15") - .build(), - ConfigOptionValue::builder() - .value("16") - .display("16") - .build(), - ConfigOptionValue::builder() - .value("17") - .display("17") - .build(), - ConfigOptionValue::builder() - .value("18") - .display("18") - .build(), - ConfigOptionValue::builder() - .value("19") - .display("19") - .build(), - ConfigOptionValue::builder() - .value("20") - .display("20") - .build(), - ConfigOptionValue::builder() - .value("21") - .display("21") - .build(), - ConfigOptionValue::builder() - .value("22") - .display("22") - .build(), - ConfigOptionValue::builder() - .value("23") - .display("23") - .build(), - ConfigOptionValue::builder() - .value("24") - .display("24") - .build(), - ConfigOptionValue::builder() - .value("25") - .display("25") - .build(), - ConfigOptionValue::builder() - .value("26") - .display("26") - .build(), - ]) - .build(); +fn config_option_value(value: &'static str) -> ConfigOptionValue { + ConfigOptionValue::builder() + .value(value) + .display(value) + .build() } fn main() { @@ -135,17 +43,71 @@ fn main() { } } + let wifi_capture_interface = Interface { + value: "802.15.4".into(), + display: "esp-ieee802154 Sniffer".into(), + dlt: Dlt { + data_link_type: DataLink::USER0, + name: "USER0".into(), + display: "IEEE802.15.4".into(), + }, + }; + match args.extcap.run().unwrap() { ExtcapStep::Interfaces(interfaces_step) => { - interfaces_step.list_interfaces(&METADATA, &[&*WIFI_CAPTURE_INTERFACE], &[]); + let metadata = Metadata { + help_url: "http://github.com/esp-rs".into(), + display_description: "esp-ieee802154".into(), + ..r_extcap::cargo_metadata!() + }; + + interfaces_step.list_interfaces(&metadata, &[&wifi_capture_interface], &[]); } ExtcapStep::Dlts(dlts_step) => { dlts_step - .print_from_interfaces(&[&*WIFI_CAPTURE_INTERFACE]) + .print_from_interfaces(&[&wifi_capture_interface]) .unwrap(); } ExtcapStep::Config(config_step) => { - config_step.list_configs(&[&*CONFIG_SERIALPORT, &*CONFIG_CHANNEL]) + let config_serialport = StringConfig::builder() + .config_number(1) + .call("serialport") + .display("Serialport") + .tooltip("Serialport to connect to") + .required(false) + .placeholder("") + .build(); + + let config_channel = SelectorConfig::builder() + .config_number(3) + .call("channel") + .display("Channel") + .tooltip("Channel Selector") + .default_options([ + ConfigOptionValue::builder() + .value("11") + .display("11") + .default(true) + .build(), + config_option_value("12"), + config_option_value("13"), + config_option_value("14"), + config_option_value("15"), + config_option_value("16"), + config_option_value("17"), + config_option_value("18"), + config_option_value("19"), + config_option_value("20"), + config_option_value("21"), + config_option_value("22"), + config_option_value("23"), + config_option_value("24"), + config_option_value("25"), + config_option_value("26"), + ]) + .build(); + + config_step.list_configs(&[&config_serialport, &config_channel]) } ExtcapStep::ReloadConfig(_reload_config_step) => { panic!("Unsupported operation"); diff --git a/hil-test/.cargo/config.toml b/hil-test/.cargo/config.toml index 782ecfe704f..26eefc108e8 100644 --- a/hil-test/.cargo/config.toml +++ b/hil-test/.cargo/config.toml @@ -1,5 +1,5 @@ [target.'cfg(target_arch = "riscv32")'] -runner = "probe-rs run" +runner = "probe-rs run --preverify" rustflags = [ "-C", "link-arg=-Tlinkall.x", "-C", "link-arg=-Tembedded-test.x", @@ -8,7 +8,7 @@ rustflags = [ ] [target.'cfg(target_arch = "xtensa")'] -runner = "probe-rs run" +runner = "probe-rs run --preverify" rustflags = [ "-C", "link-arg=-nostartfiles", "-C", "link-arg=-Wl,-Tlinkall.x", diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index d3631cb312c..3e7ca437763 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -4,6 +4,9 @@ version = "0.0.0" edition = "2021" publish = false +[lib] +name = "hil_test" + [[test]] name = "aes" harness = false @@ -48,6 +51,10 @@ harness = false name = "interrupt" harness = false +[[test]] +name = "i2c" +harness = false + [[test]] name = "i2s" harness = false @@ -64,6 +71,18 @@ harness = false name = "lcd_cam_i8080_async" harness = false +[[test]] +name = "qspi_read" +harness = false + +[[test]] +name = "qspi_write" +harness = false + +[[test]] +name = "qspi_write_read" +harness = false + [[test]] name = "spi_full_duplex" harness = false @@ -72,6 +91,14 @@ harness = false name = "spi_full_duplex_dma" harness = false +[[test]] +name = "spi_full_duplex_dma_async" +harness = false + +[[test]] +name = "spi_full_duplex_dma_pcnt" +harness = false + [[test]] name = "spi_half_duplex_read" harness = false @@ -80,6 +107,10 @@ harness = false name = "spi_half_duplex_write" harness = false +[[test]] +name = "systimer" +harness = false + [[test]] name = "pcnt" harness = false @@ -92,6 +123,10 @@ harness = false name = "rsa" harness = false +[[test]] +name = "rsa_async" +harness = false + [[test]] name = "sha" harness = false @@ -100,16 +135,19 @@ harness = false name = "uart" harness = false +[[test]] +name = "usb_serial_jtag" +harness = false + [[test]] name = "uart_async" harness = false -required-features = ["async", "embassy"] +required-features = ["embassy"] [[test]] name = "uart_tx_rx" harness = false - [[test]] name = "uart_tx_rx_async" harness = false @@ -117,38 +155,63 @@ harness = false [[test]] name = "embassy_timers_executors" harness = false -required-features = ["async", "embassy"] +required-features = ["embassy"] + +[[test]] +name = "embassy_interrupt_executor" +harness = false +required-features = ["embassy"] + +[[test]] +name = "embassy_interrupt_spi_dma" +harness = false +required-features = ["embassy"] + +[[test]] +name = "twai" +harness = false [dependencies] cfg-if = "1.0.0" critical-section = "1.1.2" defmt = "0.3.8" -defmt-rtt = "0.4.1" +defmt-rtt = { version = "0.4.1", optional = true } embassy-futures = "0.1.1" -embassy-time = { version = "0.3.1", features = ["generic-queue-64"] } +embassy-sync = "0.6.0" +embassy-time = { version = "0.3.1" } embedded-hal = "1.0.0" embedded-hal-02 = { version = "0.2.7", package = "embedded-hal", features = ["unproven"] } -embedded-hal-async = { version = "1.0.0", optional = true } +embedded-hal-async = "1.0.0" embedded-hal-nb = { version = "1.0.0", optional = true } esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["exception-handler", "panic-handler", "defmt", "semihosting"] } -esp-hal = { path = "../esp-hal", features = ["defmt", "embedded-hal", "embedded-hal-02"], optional = true } +esp-hal = { path = "../esp-hal", features = ["defmt", "digest"], optional = true } esp-hal-embassy = { path = "../esp-hal-embassy", optional = true } portable-atomic = "1.6.0" static_cell = { version = "2.1.0", features = ["nightly"] } [dev-dependencies] crypto-bigint = { version = "0.5.5", default-features = false } +digest = { version = "0.10.7", default-features = false } elliptic-curve = { version = "0.13.8", default-features = false, features = ["sec1"] } -embassy-executor = { version = "0.5.0", default-features = false } +embassy-executor = { version = "0.6.0", default-features = false } # Add the `embedded-test/defmt` feature for more verbose testing embedded-test = { version = "0.4.0", default-features = false } +fugit = "0.3.7" hex-literal = "0.4.1" nb = "1.1.0" p192 = { version = "0.13.0", default-features = false, features = ["arithmetic"] } p256 = { version = "0.13.2", default-features = false, features = ["arithmetic"] } +sha1 = { version = "0.10.6", default-features = false } +sha2 = { version = "0.10.8", default-features = false } + +[build-dependencies] +esp-build = { version = "0.1.0", path = "../esp-build" } +esp-metadata = { version = "0.3.0", path = "../esp-metadata" } [features] -default = ["async", "embassy"] +default = ["embassy"] + +defmt = ["dep:defmt-rtt"] # Device support (required!): esp32 = [ @@ -174,12 +237,17 @@ esp32s3 = [ "esp-hal-embassy/esp32s3", ] # Async & Embassy: -async = ["dep:embedded-hal-async", "esp-hal?/async"] embassy = [ "embedded-test/embassy", "embedded-test/external-executor", "dep:esp-hal-embassy", ] +generic-queue = [ + "embassy-time/generic-queue-64" +] +integrated-timers = [ + "esp-hal-embassy/integrated-timers", +] # https://doc.rust-lang.org/cargo/reference/profiles.html#test # Test and bench profiles inherit from dev and release respectively. diff --git a/hil-test/README.md b/hil-test/README.md index 58a78ebd8b9..39f637b1fd6 100644 --- a/hil-test/README.md +++ b/hil-test/README.md @@ -9,36 +9,42 @@ For assistance with this package please [open an issue] or [start a discussion]. ## Quickstart -We use [embedded-test] as our testing framework, which relies on [defmt] internally. This allows us to write unit and integration tests much in the same way you would for a normal Rust project, when the standard library is available, and to execute them using Cargo's built-in test runner. +We use [embedded-test] as our testing framework. This allows us to write unit and integration tests much in the same way you would for a normal Rust project, when the standard library is available, and to execute them using Cargo's built-in test runner. [embedded-test]: https://github.com/probe-rs/embedded-test -[defmt]: https://github.com/knurling-rs/defmt ### Running Tests Locally -We use [probe-rs] for flashing and running the tests on a target device, however, this **MUST** be installed from the correct revision, and with the correct features enabled: +We use [probe-rs] for flashing and running the tests on a target device, however, this **MUST** be installed from the correct revision: ```text cargo install probe-rs-tools \ --git https://github.com/probe-rs/probe-rs \ - --rev bba1bb5 --force --locked + --rev 9bde591 --force --locked ``` Target device **MUST** connected via its USB-Serial-JTAG port, or if unavailable (eg. ESP32, ESP32-C2, ESP32-S2) then you must connect a compatible debug probe such as an [ESP-Prog]. -You can run all tests for a given device by running the following command from the `xtask` folder: +You can run all tests for a given device by running the following command from the workspace root: ```shell cargo xtask run-tests $CHIP ``` -For running a single test on a target, from the `xtask` folder run: +To run a single test on a target, run the following command from the workspace root: ```shell # Run GPIO tests for ESP32-C6 cargo xtask run-tests esp32c6 --test gpio ``` +If you want to run a test multiple times: + +```shell +# Run GPIO tests for ESP32-C6 +cargo xtask run-tests esp32c6 --test gpio --repeat 10 +``` + Another alternative way of running a single test is, from the `hil-tests` folder: ```shell # Run GPIO tests for ESP32-C6 @@ -62,40 +68,52 @@ The [`hil.yml`] workflow builds the test suite for all our available targets and Our self-hosted runners have the following setup: - ESP32-C2 (`esp32c2-jtag`): - Devkit: `ESP8684-DevKitM-1` connected via UART. + - `GPIO18` and `GPIO9` are I2C pins. - `GPIO2` and `GPIO3` are connected. - Probe: `ESP-Prog` connected with the [following connections][connection_c2] - RPi: Raspbian 12 configured with the following [setup] - ESP32-C3 (`rustboard`): - Devkit: `ESP32-C3-DevKit-RUST-1` connected via USB-Serial-JTAG. + - `GPIO4` and `GPIO5` are I2C pins. - `GPIO2` and `GPIO3` are connected. - - `GPIO5` and `GPIO6` are connected. - RPi: Raspbian 12 configured with the following [setup] - ESP32-C6 (`esp32c6-usb`): - Devkit: `ESP32-C6-DevKitC-1 V1.2` connected via USB-Serial-JTAG (`USB` port). + - `GPIO6` and `GPIO7` are I2C pins. - `GPIO2` and `GPIO3` are connected. - - `GPIO5` and `GPIO6` are connected. + - `GPIO4` and `GPIO5` are connected. - RPi: Raspbian 12 configured with the following [setup] - ESP32-H2 (`esp32h2-usb`): - Devkit: `ESP32-H2-DevKitM-1` connected via USB-Serial-JTAG (`USB` port). + - `GPIO12` and `GPIO22` are I2C pins. - `GPIO2` and `GPIO3` are connected. - - `GPIO5` and `GPIO8` are connected. + - `GPIO4` and `GPIO5` are connected. - RPi: Raspbian 12 configured with the following [setup] - ESP32-S2 (`esp32s2-jtag`): - Devkit: `ESP32-S2-Saola-1` connected via UART. - - `GPIO2` and `GPIO3` are connected. - - `GPIO5` and `GPIO6` are connected. + - `GPIO2` and `GPIO3` are I2C pins. + - `GPIO9` and `GPIO10` are connected. - Probe: `ESP-Prog` connected with the [following connections][connection_s2] - RPi: Raspbian 12 configured with the following [setup] - ESP32-S3 (`esp32s3-usb`): - Devkit: `ESP32-S3-DevKitC-1` connected via USB-Serial-JTAG. - - `GPIO2` and `GPIO3` are connected. - - `GPIO5` and `GPIO6` are connected. + - `GPIO2` and `GPIO3` are I2C pins. + - `GPIO4` and `GPIO5` are connected. - `GPIO1` and `GPIO21` are connected. + - `GPIO9` and `GPIO10` are connected. - `GPIO43 (TX)` and `GPIO45` are connected. - RPi: Raspbian 12 configured with the following [setup] +- ESP32 (`esp32-jtag`): + - Devkit: `ESP32-DevKitC-V4` connected via UART. + - `GPIO32` and `GPIO33` are I2C pins. + - `GPIO4` and `GPIO5` are connected. + - `GPIO26` and `GPIO27` are connected. + - Probe: `ESP-Prog` connected with the [following connections][connection_esp32] + - RPi: Raspbian 12 configured with the following [setup] [connection_c2]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32c2/api-guides/jtag-debugging/configure-other-jtag.html#configure-hardware [connection_s2]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-guides/jtag-debugging/configure-other-jtag.html#configure-hardware +[connection_esp32]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/jtag-debugging/configure-other-jtag.html#configure-hardware.html#configure-hardware [`hil.yml`]: https://github.com/esp-rs/esp-hal/blob/main/.github/workflows/hil.yml [setup]: #rpi-setup @@ -106,9 +124,9 @@ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-t # Source the current shell: . "$HOME/.cargo/env" # Install dependencies -sudo apt install -y pkg-config libudev-dev +sudo apt install -y pkg-config libudev-dev uhubctl # Install probe-rs -cargo install probe-rs-tools --git https://github.com/probe-rs/probe-rs --rev bba1bb5 --force +cargo install probe-rs-tools --git https://github.com/probe-rs/probe-rs --rev 9bde591 --force # Add the udev rules wget -O - https://probe.rs/files/69-probe-rs.rules | sudo tee /etc/udev/rules.d/69-probe-rs.rules > /dev/null # Add the user to plugdev group @@ -138,3 +156,23 @@ sudo reboot If the test is supported by all the targets, you can omit the header. 6. Write some documentation at the top of the `tests/$PERIPHERAL.rs` file with the pins being used and the required connections, if applicable. + +## Logging in tests + +The tests can use [defmt] to print logs. To enable log output, add the `defmt` feature to the test +you want to run. Eg: + +```rust +//! AES Test + +//% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: defmt +``` + +Make sure to remove this addition before you commit any modifications. + +> NOTE: log output is disabled by default. Enabling it can introduce some timing issues, which +makes some tests fail randomly. This issue affects all Xtensa devices, as well as ESP32-C2 and +ESP32-C3 currently. + +[defmt]: https://github.com/knurling-rs/defmt diff --git a/hil-test/build.rs b/hil-test/build.rs new file mode 100644 index 00000000000..1c7ecfc2ff6 --- /dev/null +++ b/hil-test/build.rs @@ -0,0 +1,41 @@ +use std::{error::Error, str::FromStr}; + +use esp_build::assert_unique_used_features; +use esp_metadata::{Chip, Config}; + +fn main() -> Result<(), Box> { + // NOTE: update when adding new device support! + // Ensure that exactly one chip has been specified: + assert_unique_used_features!( + "esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32s2", "esp32s3" + ); + + // NOTE: update when adding new device support! + // Determine the name of the configured device: + let device_name = if cfg!(feature = "esp32") { + "esp32" + } else if cfg!(feature = "esp32c2") { + "esp32c2" + } else if cfg!(feature = "esp32c3") { + "esp32c3" + } else if cfg!(feature = "esp32c6") { + "esp32c6" + } else if cfg!(feature = "esp32h2") { + "esp32h2" + } else if cfg!(feature = "esp32s2") { + "esp32s2" + } else if cfg!(feature = "esp32s3") { + "esp32s3" + } else { + unreachable!() // We've confirmed exactly one known device was selected + }; + + // Load the configuration file for the configured device: + let chip = Chip::from_str(device_name)?; + let config = Config::for_chip(&chip); + + // Define all necessary configuration symbols for the configured device: + config.define_symbols(); + + Ok(()) +} diff --git a/hil-test/src/lib.rs b/hil-test/src/lib.rs new file mode 100644 index 00000000000..cabe27dcce8 --- /dev/null +++ b/hil-test/src/lib.rs @@ -0,0 +1,63 @@ +#![no_std] + +// By default, we don't want probe-rs to interfere with test timings by halting +// cores and polling RTT. The tests don't produce output most of the time +// anyway. The only cases where output can be interesting are: during +// development, and when a test fails. In these cases, you can enable +// the `defmt` feature to get the output. + +#[cfg(not(feature = "defmt"))] +#[defmt::global_logger] +struct Logger; + +#[cfg(not(feature = "defmt"))] +unsafe impl defmt::Logger for Logger { + fn acquire() {} + unsafe fn flush() {} + unsafe fn release() {} + unsafe fn write(_bytes: &[u8]) {} +} + +#[cfg(feature = "defmt")] +use defmt_rtt as _; +// Make sure esp_backtrace is not removed. +use esp_backtrace as _; + +#[macro_export] +macro_rules! i2c_pins { + ($io:expr) => {{ + // Order: (SDA, SCL) + cfg_if::cfg_if! { + if #[cfg(any(esp32s2, esp32s3))] { + ($io.pins.gpio2, $io.pins.gpio3) + } else if #[cfg(esp32)] { + ($io.pins.gpio32, $io.pins.gpio33) + } else if #[cfg(esp32c6)] { + ($io.pins.gpio6, $io.pins.gpio7) + } else if #[cfg(esp32h2)] { + ($io.pins.gpio12, $io.pins.gpio22) + } else if #[cfg(esp32c2)] { + ($io.pins.gpio18, $io.pins.gpio9) + } else { + ($io.pins.gpio4, $io.pins.gpio5) + } + } + }}; +} + +#[macro_export] +macro_rules! common_test_pins { + ($io:expr) => {{ + cfg_if::cfg_if! { + if #[cfg(any(esp32s2, esp32s3))] { + ($io.pins.gpio9, $io.pins.gpio10) + } + else if #[cfg(esp32)] { + ($io.pins.gpio26, $io.pins.gpio27) + } + else { + ($io.pins.gpio2, $io.pins.gpio3) + } + } + }}; +} diff --git a/hil-test/tests/aes.rs b/hil-test/tests/aes.rs index 4dee6ddcb4b..cbf90e8efc1 100644 --- a/hil-test/tests/aes.rs +++ b/hil-test/tests/aes.rs @@ -5,26 +5,16 @@ #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ aes::{Aes, Mode}, - peripherals::Peripherals, + prelude::*, }; +use hil_test as _; struct Context<'a> { aes: Aes<'a>, } -impl Context<'_> { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let aes = Aes::new(peripherals.AES); - - Context { aes } - } -} - #[cfg(test)] #[embedded_test::tests] mod tests { @@ -34,13 +24,20 @@ mod tests { #[init] fn init() -> Context<'static> { - Context::init() + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); + let aes = Aes::new(peripherals.AES); + + Context { aes } } #[test] fn test_aes_128_encryption(mut ctx: Context<'static>) { - let keytext = "SUp4SeCp@sSw0rd".as_bytes(); - let plaintext = "message".as_bytes(); + let keytext = b"SUp4SeCp@sSw0rd"; + let plaintext = b"message"; let encrypted_message = [ 0xb3, 0xc8, 0xd2, 0x3b, 0xa7, 0x36, 0x5f, 0x18, 0x61, 0x70, 0x0, 0x3e, 0xd9, 0x3a, 0x31, 0x96, @@ -59,8 +56,8 @@ mod tests { #[test] fn test_aes_128_decryption(mut ctx: Context<'static>) { - let keytext = "SUp4SeCp@sSw0rd".as_bytes(); - let plaintext = "message".as_bytes(); + let keytext = b"SUp4SeCp@sSw0rd"; + let plaintext = b"message"; let mut encrypted_message = [ 0xb3, 0xc8, 0xd2, 0x3b, 0xa7, 0x36, 0x5f, 0x18, 0x61, 0x70, 0x0, 0x3e, 0xd9, 0x3a, 0x31, 0x96, @@ -77,8 +74,8 @@ mod tests { #[test] #[cfg(any(feature = "esp32", feature = "esp32s2"))] fn test_aes_192_encryption(mut ctx: Context<'static>) { - let keytext = "SUp4SeCp@sSw0rd".as_bytes(); - let plaintext = "message".as_bytes(); + let keytext = b"SUp4SeCp@sSw0rd"; + let plaintext = b"message"; let encrypted_message = [ 0x79, 0x88, 0x3f, 0x9d, 0x67, 0x27, 0xf4, 0x18, 0x3, 0xe3, 0xc6, 0x6a, 0x2e, 0x76, 0xb6, 0xf7, @@ -98,8 +95,8 @@ mod tests { #[test] #[cfg(any(feature = "esp32", feature = "esp32s2"))] fn test_aes_192_decryption(mut ctx: Context<'static>) { - let keytext = "SUp4SeCp@sSw0rd".as_bytes(); - let plaintext = "message".as_bytes(); + let keytext = b"SUp4SeCp@sSw0rd"; + let plaintext = b"message"; let mut encrypted_message = [ 0x79, 0x88, 0x3f, 0x9d, 0x67, 0x27, 0xf4, 0x18, 0x3, 0xe3, 0xc6, 0x6a, 0x2e, 0x76, 0xb6, 0xf7, @@ -115,8 +112,8 @@ mod tests { #[test] fn test_aes_256_encryption(mut ctx: Context<'static>) { - let keytext = "SUp4SeCp@sSw0rd".as_bytes(); - let plaintext = "message".as_bytes(); + let keytext = b"SUp4SeCp@sSw0rd"; + let plaintext = b"message"; let encrypted_message = [ 0x0, 0x63, 0x3f, 0x2, 0xa4, 0x53, 0x9, 0x72, 0x20, 0x6d, 0xc9, 0x8, 0x7c, 0xe5, 0xfd, 0xc, @@ -135,8 +132,8 @@ mod tests { #[test] fn test_aes_256_decryption(mut ctx: Context<'static>) { - let keytext = "SUp4SeCp@sSw0rd".as_bytes(); - let plaintext = "message".as_bytes(); + let keytext = b"SUp4SeCp@sSw0rd"; + let plaintext = b"message"; let mut encrypted_message = [ 0x0, 0x63, 0x3f, 0x2, 0xa4, 0x53, 0x9, 0x72, 0x20, 0x6d, 0xc9, 0x8, 0x7c, 0xe5, 0xfd, 0xc, diff --git a/hil-test/tests/aes_dma.rs b/hil-test/tests/aes_dma.rs index 9ccafb2f10a..044cd642c10 100644 --- a/hil-test/tests/aes_dma.rs +++ b/hil-test/tests/aes_dma.rs @@ -5,8 +5,6 @@ #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ aes::{ dma::{CipherMode, WithDmaAes}, @@ -17,6 +15,7 @@ use esp_hal::{ dma_buffers, peripherals::Peripherals, }; +use hil_test as _; const DMA_BUFFER_SIZE: usize = 16; @@ -28,28 +27,28 @@ mod tests { use super::*; #[init] - fn init() {} + fn init() -> Peripherals { + esp_hal::init(esp_hal::Config::default()) + } #[test] - fn test_aes_128_dma_encryption() { - let peripherals = Peripherals::take(); - + fn test_aes_128_dma_encryption(peripherals: Peripherals) { let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; - let (input, tx_descriptors, mut output, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); + let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); let mut aes = Aes::new(peripherals.AES).with_dma( dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, rx_descriptors, + tx_descriptors, ); - let keytext = "SUp4SeCp@sSw0rd".as_bytes(); + let keytext = b"SUp4SeCp@sSw0rd"; let mut keybuf = [0_u8; 16]; keybuf[..keytext.len()].copy_from_slice(keytext); - let plaintext = "message".as_bytes(); + let plaintext = b"message"; input[..plaintext.len()].copy_from_slice(plaintext); let encrypted_message = [ @@ -75,25 +74,23 @@ mod tests { } #[test] - fn test_aes_128_dma_decryption() { - let peripherals = Peripherals::take(); - + fn test_aes_128_dma_decryption(peripherals: Peripherals) { let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; - let (input, tx_descriptors, mut output, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); + let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); let mut aes = Aes::new(peripherals.AES).with_dma( dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, rx_descriptors, + tx_descriptors, ); - let keytext = "SUp4SeCp@sSw0rd".as_bytes(); + let keytext = b"SUp4SeCp@sSw0rd"; let mut keybuf = [0_u8; 16]; keybuf[..keytext.len()].copy_from_slice(keytext); - let plaintext = "message".as_bytes(); + let plaintext = b"message"; let encrypted_message = [ 0xb3, 0xc8, 0xd2, 0x3b, 0xa7, 0x36, 0x5f, 0x18, 0x61, 0x70, 0x0, 0x3e, 0xd9, 0x3a, 0x31, 0x96, @@ -118,25 +115,23 @@ mod tests { } #[test] - fn test_aes_256_dma_encryption() { - let peripherals = Peripherals::take(); - + fn test_aes_256_dma_encryption(peripherals: Peripherals) { let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; - let (input, tx_descriptors, mut output, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); + let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); let mut aes = Aes::new(peripherals.AES).with_dma( dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, rx_descriptors, + tx_descriptors, ); - let keytext = "SUp4SeCp@sSw0rd".as_bytes(); + let keytext = b"SUp4SeCp@sSw0rd"; let mut keybuf = [0_u8; 16]; keybuf[..keytext.len()].copy_from_slice(keytext); - let plaintext = "message".as_bytes(); + let plaintext = b"message"; input[..plaintext.len()].copy_from_slice(plaintext); let encrypted_message = [ @@ -162,25 +157,23 @@ mod tests { } #[test] - fn test_aes_256_dma_decryption() { - let peripherals = Peripherals::take(); - + fn test_aes_256_dma_decryption(peripherals: Peripherals) { let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; - let (input, tx_descriptors, mut output, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); + let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); let mut aes = Aes::new(peripherals.AES).with_dma( dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, rx_descriptors, + tx_descriptors, ); - let keytext = "SUp4SeCp@sSw0rd".as_bytes(); + let keytext = b"SUp4SeCp@sSw0rd"; let mut keybuf = [0_u8; 16]; keybuf[..keytext.len()].copy_from_slice(keytext); - let plaintext = "message".as_bytes(); + let plaintext = b"message"; let encrypted_message = [ 0x0, 0x63, 0x3f, 0x2, 0xa4, 0x53, 0x9, 0x72, 0x20, 0x6d, 0xc9, 0x8, 0x7c, 0xe5, 0xfd, 0xc, diff --git a/hil-test/tests/clock_monitor.rs b/hil-test/tests/clock_monitor.rs index 6c73a6e66d3..31ac0715e63 100644 --- a/hil-test/tests/clock_monitor.rs +++ b/hil-test/tests/clock_monitor.rs @@ -5,31 +5,13 @@ #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - peripherals::Peripherals, - rtc_cntl::Rtc, - system::SystemControl, -}; +use esp_hal::rtc_cntl::Rtc; +use hil_test as _; struct Context<'a> { rtc: Rtc<'a>, } -impl Context<'_> { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - ClockControl::boot_defaults(system.clock_control).freeze(); - - let rtc = Rtc::new(peripherals.LPWR); - - Context { rtc } - } -} - #[cfg(test)] #[embedded_test::tests] mod tests { @@ -37,16 +19,32 @@ mod tests { #[init] fn init() -> Context<'static> { - Context::init() + let peripherals = esp_hal::init(esp_hal::Config::default()); + let rtc = Rtc::new(peripherals.LPWR); + + Context { rtc } } #[test] fn test_estimated_clock(mut ctx: Context<'static>) { - #[cfg(feature = "esp32c2")] // 26 MHz - defmt::assert!((23..=29).contains(&ctx.rtc.estimate_xtal_frequency())); - #[cfg(feature = "esp32h2")] // 32 MHz - defmt::assert!((29..=35).contains(&ctx.rtc.estimate_xtal_frequency())); - #[cfg(not(any(feature = "esp32h2", feature = "esp32c2")))] // 40 MHz - defmt::assert!((35..=45).contains(&ctx.rtc.estimate_xtal_frequency())); + cfg_if::cfg_if! { + if #[cfg(feature = "esp32c2")] { + // 26 MHz + let expected_range = 23..=29; + } else if #[cfg(feature = "esp32h2")] { + // 32 MHz + let expected_range = 29..=35; + } else { + // 40 MHz + let expected_range = 35..=45; + } + } + + let measured_frequency = ctx.rtc.estimate_xtal_frequency(); + defmt::assert!( + expected_range.contains(&measured_frequency), + "Measured frequency: {}", + measured_frequency + ); } } diff --git a/hil-test/tests/crc.rs b/hil-test/tests/crc.rs index f9cf9c56014..68fb5f7074f 100644 --- a/hil-test/tests/crc.rs +++ b/hil-test/tests/crc.rs @@ -7,9 +7,8 @@ use core::ops::Deref; -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::rom::{crc, md5}; +use hil_test as _; #[cfg(test)] #[embedded_test::tests] diff --git a/hil-test/tests/delay.rs b/hil-test/tests/delay.rs index f939e244b90..69e1186d99d 100644 --- a/hil-test/tests/delay.rs +++ b/hil-test/tests/delay.rs @@ -1,32 +1,18 @@ //! Delay Test -// esp32c2 is disabled currently as it fails -//% CHIPS: esp32 esp32c3 esp32c6 esp32s3 +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32s2 esp32s3 #![no_std] #![no_main] -use defmt_rtt as _; use embedded_hal::delay::DelayNs; -use esp_backtrace as _; -use esp_hal::{clock::ClockControl, delay::Delay, peripherals::Peripherals, system::SystemControl}; +use esp_hal::delay::Delay; +use hil_test as _; struct Context { delay: Delay, } -impl Context { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let delay = Delay::new(&clocks); - - Context { delay } - } -} - #[cfg(test)] #[embedded_test::tests] mod tests { @@ -34,50 +20,69 @@ mod tests { #[init] fn init() -> Context { - Context::init() + let _peripherals = esp_hal::init(esp_hal::Config::default()); + let delay = Delay::new(); + + Context { delay } } #[test] - #[timeout(1)] + #[timeout(2)] fn delay_ns(mut ctx: Context) { - let t1 = esp_hal::time::current_time(); + let t1 = esp_hal::time::now(); ctx.delay.delay_ns(600_000_000); - let t2 = esp_hal::time::current_time(); + let t2 = esp_hal::time::now(); assert!(t2 > t1); - assert!((t2 - t1).to_nanos() >= 600_000_000u64); + assert!( + (t2 - t1).to_nanos() >= 600_000_000u64, + "diff: {:?}", + (t2 - t1).to_nanos() + ); } #[test] - #[timeout(1)] + #[timeout(2)] fn delay_700millis(ctx: Context) { - let t1 = esp_hal::time::current_time(); + let t1 = esp_hal::time::now(); ctx.delay.delay_millis(700); - let t2 = esp_hal::time::current_time(); + let t2 = esp_hal::time::now(); assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 700u64); + assert!( + (t2 - t1).to_millis() >= 700u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); } #[test] #[timeout(2)] fn delay_1_500_000us(mut ctx: Context) { - let t1 = esp_hal::time::current_time(); + let t1 = esp_hal::time::now(); ctx.delay.delay_us(1_500_000); - let t2 = esp_hal::time::current_time(); + let t2 = esp_hal::time::now(); assert!(t2 > t1); - assert!((t2 - t1).to_micros() >= 1_500_000u64); + assert!( + (t2 - t1).to_micros() >= 1_500_000u64, + "diff: {:?}", + (t2 - t1).to_micros() + ); } #[test] #[timeout(5)] fn delay_3_000ms(mut ctx: Context) { - let t1 = esp_hal::time::current_time(); + let t1 = esp_hal::time::now(); ctx.delay.delay_ms(3000); - let t2 = esp_hal::time::current_time(); + let t2 = esp_hal::time::now(); assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 3000u64); + assert!( + (t2 - t1).to_millis() >= 3000u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); } } diff --git a/hil-test/tests/dma_macros.rs b/hil-test/tests/dma_macros.rs index d5193e40672..f0707303482 100644 --- a/hil-test/tests/dma_macros.rs +++ b/hil-test/tests/dma_macros.rs @@ -5,8 +5,7 @@ #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; +use hil_test as _; const DATA_SIZE: usize = 1024 * 10; @@ -35,33 +34,33 @@ mod tests { #[test] fn test_dma_descriptors_same_size() { use esp_hal::dma::CHUNK_SIZE; - let (tx_descriptors, rx_descriptors) = esp_hal::dma_descriptors!(DATA_SIZE); - assert_eq!(tx_descriptors.len(), rx_descriptors.len()); - assert_eq!(tx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); + let (rx_descriptors, tx_descriptors) = esp_hal::dma_descriptors!(DATA_SIZE); + assert_eq!(rx_descriptors.len(), tx_descriptors.len()); assert_eq!(rx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); + assert_eq!(tx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); } #[test] fn test_dma_descriptors_different_size() { use esp_hal::dma::CHUNK_SIZE; - const TX_SIZE: usize = DATA_SIZE; const RX_SIZE: usize = DATA_SIZE / 2; - let (tx_descriptors, rx_descriptors) = esp_hal::dma_descriptors!(TX_SIZE, RX_SIZE); - assert_eq!(tx_descriptors.len(), compute_size(TX_SIZE, CHUNK_SIZE)); + const TX_SIZE: usize = DATA_SIZE; + let (rx_descriptors, tx_descriptors) = esp_hal::dma_descriptors!(RX_SIZE, TX_SIZE); assert_eq!(rx_descriptors.len(), compute_size(RX_SIZE, CHUNK_SIZE)); + assert_eq!(tx_descriptors.len(), compute_size(TX_SIZE, CHUNK_SIZE)); } #[test] fn test_dma_circular_descriptors_same_size() { use esp_hal::dma::CHUNK_SIZE; - let (tx_descriptors, rx_descriptors) = esp_hal::dma_circular_descriptors!(DATA_SIZE); - assert_eq!(tx_descriptors.len(), rx_descriptors.len()); + let (rx_descriptors, tx_descriptors) = esp_hal::dma_circular_descriptors!(DATA_SIZE); + assert_eq!(rx_descriptors.len(), tx_descriptors.len()); assert_eq!( - tx_descriptors.len(), + rx_descriptors.len(), compute_circular_size(DATA_SIZE, CHUNK_SIZE) ); assert_eq!( - rx_descriptors.len(), + tx_descriptors.len(), compute_circular_size(DATA_SIZE, CHUNK_SIZE) ); } @@ -69,59 +68,59 @@ mod tests { #[test] fn test_dma_circular_descriptors_different_size() { use esp_hal::dma::CHUNK_SIZE; - const TX_SIZE: usize = CHUNK_SIZE * 2; const RX_SIZE: usize = DATA_SIZE / 2; - let (tx_descriptors, rx_descriptors) = esp_hal::dma_circular_descriptors!(TX_SIZE, RX_SIZE); - assert_eq!( - tx_descriptors.len(), - compute_circular_size(TX_SIZE, CHUNK_SIZE) - ); + const TX_SIZE: usize = CHUNK_SIZE * 2; + let (rx_descriptors, tx_descriptors) = esp_hal::dma_circular_descriptors!(RX_SIZE, TX_SIZE); assert_eq!( rx_descriptors.len(), compute_circular_size(RX_SIZE, CHUNK_SIZE) ); + assert_eq!( + tx_descriptors.len(), + compute_circular_size(TX_SIZE, CHUNK_SIZE) + ); } #[test] fn test_dma_buffers_same_size() { use esp_hal::dma::CHUNK_SIZE; - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = esp_hal::dma_buffers!(DATA_SIZE); - assert_eq!(tx_buffer.len(), DATA_SIZE); assert_eq!(rx_buffer.len(), DATA_SIZE); + assert_eq!(tx_buffer.len(), DATA_SIZE); assert_eq!(tx_descriptors.len(), rx_descriptors.len()); - assert_eq!(tx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); assert_eq!(rx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); + assert_eq!(tx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); } #[test] fn test_dma_buffers_different_size() { use esp_hal::dma::CHUNK_SIZE; - const TX_SIZE: usize = DATA_SIZE; const RX_SIZE: usize = DATA_SIZE / 2; + const TX_SIZE: usize = DATA_SIZE; - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = - esp_hal::dma_buffers!(TX_SIZE, RX_SIZE); - assert_eq!(tx_buffer.len(), TX_SIZE); + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = + esp_hal::dma_buffers!(RX_SIZE, TX_SIZE); assert_eq!(rx_buffer.len(), RX_SIZE); - assert_eq!(tx_descriptors.len(), compute_size(TX_SIZE, CHUNK_SIZE)); + assert_eq!(tx_buffer.len(), TX_SIZE); assert_eq!(rx_descriptors.len(), compute_size(RX_SIZE, CHUNK_SIZE)); + assert_eq!(tx_descriptors.len(), compute_size(TX_SIZE, CHUNK_SIZE)); } #[test] fn test_dma_circular_buffers_same_size() { use esp_hal::dma::CHUNK_SIZE; - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = esp_hal::dma_circular_buffers!(DATA_SIZE); - assert_eq!(tx_buffer.len(), DATA_SIZE); assert_eq!(rx_buffer.len(), DATA_SIZE); - assert_eq!(tx_descriptors.len(), rx_descriptors.len()); + assert_eq!(tx_buffer.len(), DATA_SIZE); + assert_eq!(rx_descriptors.len(), tx_descriptors.len()); assert_eq!( - tx_descriptors.len(), + rx_descriptors.len(), compute_circular_size(DATA_SIZE, CHUNK_SIZE) ); assert_eq!( - rx_descriptors.len(), + tx_descriptors.len(), compute_circular_size(DATA_SIZE, CHUNK_SIZE) ); } @@ -129,57 +128,57 @@ mod tests { #[test] fn test_dma_circular_buffers_different_size() { use esp_hal::dma::CHUNK_SIZE; - const TX_SIZE: usize = CHUNK_SIZE * 4; const RX_SIZE: usize = CHUNK_SIZE * 2; - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = - esp_hal::dma_circular_buffers!(TX_SIZE, RX_SIZE); - assert_eq!(tx_buffer.len(), TX_SIZE); + const TX_SIZE: usize = CHUNK_SIZE * 4; + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = + esp_hal::dma_circular_buffers!(RX_SIZE, TX_SIZE); assert_eq!(rx_buffer.len(), RX_SIZE); - assert_eq!( - tx_descriptors.len(), - compute_circular_size(TX_SIZE, CHUNK_SIZE) - ); + assert_eq!(tx_buffer.len(), TX_SIZE); assert_eq!( rx_descriptors.len(), compute_circular_size(RX_SIZE, CHUNK_SIZE) ); + assert_eq!( + tx_descriptors.len(), + compute_circular_size(TX_SIZE, CHUNK_SIZE) + ); } #[test] fn test_dma_descriptors_chunk_size_same_size() { const CHUNK_SIZE: usize = 2048; - let (tx_descriptors, rx_descriptors) = + let (rx_descriptors, tx_descriptors) = esp_hal::dma_descriptors_chunk_size!(DATA_SIZE, CHUNK_SIZE); - assert_eq!(tx_descriptors.len(), rx_descriptors.len()); - assert_eq!(tx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); + assert_eq!(rx_descriptors.len(), tx_descriptors.len()); assert_eq!(rx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); + assert_eq!(tx_descriptors.len(), compute_size(DATA_SIZE, CHUNK_SIZE)); } #[test] fn test_dma_descriptors_chunk_size_different_size() { const CHUNK_SIZE: usize = 2048; - const TX_SIZE: usize = DATA_SIZE; const RX_SIZE: usize = DATA_SIZE / 2; - let (tx_descriptors, rx_descriptors) = - esp_hal::dma_descriptors_chunk_size!(TX_SIZE, RX_SIZE, CHUNK_SIZE); - assert_eq!(tx_descriptors.len(), compute_size(TX_SIZE, CHUNK_SIZE)); + const TX_SIZE: usize = DATA_SIZE; + let (rx_descriptors, tx_descriptors) = + esp_hal::dma_descriptors_chunk_size!(RX_SIZE, TX_SIZE, CHUNK_SIZE); assert_eq!(rx_descriptors.len(), compute_size(RX_SIZE, CHUNK_SIZE)); + assert_eq!(tx_descriptors.len(), compute_size(TX_SIZE, CHUNK_SIZE)); } #[test] fn test_dma_circular_buffers_chunk_size_same_size() { const CHUNK_SIZE: usize = 2048; - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = esp_hal::dma_circular_buffers_chunk_size!(DATA_SIZE, CHUNK_SIZE); - assert_eq!(tx_buffer.len(), DATA_SIZE); assert_eq!(rx_buffer.len(), DATA_SIZE); - assert_eq!(tx_descriptors.len(), rx_descriptors.len()); + assert_eq!(tx_buffer.len(), DATA_SIZE); + assert_eq!(rx_descriptors.len(), tx_descriptors.len()); assert_eq!( - tx_descriptors.len(), + rx_descriptors.len(), compute_circular_size(DATA_SIZE, CHUNK_SIZE) ); assert_eq!( - rx_descriptors.len(), + tx_descriptors.len(), compute_circular_size(DATA_SIZE, CHUNK_SIZE) ); } @@ -187,19 +186,19 @@ mod tests { #[test] fn test_dma_circular_buffers_chunk_size_different_size() { const CHUNK_SIZE: usize = 2048; - const TX_SIZE: usize = DATA_SIZE; const RX_SIZE: usize = DATA_SIZE / 2; - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = - esp_hal::dma_circular_buffers_chunk_size!(TX_SIZE, RX_SIZE, CHUNK_SIZE); - assert_eq!(tx_buffer.len(), TX_SIZE); + const TX_SIZE: usize = DATA_SIZE; + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = + esp_hal::dma_circular_buffers_chunk_size!(RX_SIZE, TX_SIZE, CHUNK_SIZE); assert_eq!(rx_buffer.len(), RX_SIZE); - assert_eq!( - tx_descriptors.len(), - compute_circular_size(TX_SIZE, CHUNK_SIZE) - ); + assert_eq!(tx_buffer.len(), TX_SIZE); assert_eq!( rx_descriptors.len(), compute_circular_size(RX_SIZE, CHUNK_SIZE) ); + assert_eq!( + tx_descriptors.len(), + compute_circular_size(TX_SIZE, CHUNK_SIZE) + ); } } diff --git a/hil-test/tests/dma_mem2mem.rs b/hil-test/tests/dma_mem2mem.rs index 4f31c75d1e3..86d46037b0a 100644 --- a/hil-test/tests/dma_mem2mem.rs +++ b/hil-test/tests/dma_mem2mem.rs @@ -5,20 +5,30 @@ #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, - dma::{Dma, DmaError, DmaPriority, Mem2Mem}, + dma::{Channel, Dma, DmaChannel0, DmaError, DmaPriority, Mem2Mem}, dma_buffers, dma_buffers_chunk_size, dma_descriptors, - peripherals::Peripherals, - system::SystemControl, + Blocking, }; +use hil_test as _; const DATA_SIZE: usize = 1024 * 10; +cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] { + type DmaPeripheralType = esp_hal::peripherals::SPI2; + } else { + type DmaPeripheralType = esp_hal::peripherals::MEM2MEM1; + } +} + +struct Context { + channel: Channel<'static, DmaChannel0, Blocking>, + dma_peripheral: DmaPeripheralType, +} + #[cfg(test)] #[embedded_test::tests] mod tests { @@ -27,30 +37,42 @@ mod tests { use super::*; #[init] - fn init() {} - - #[test] - fn test_internal_mem2mem() { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let (tx_buffer, tx_descriptors, mut rx_buffer, rx_descriptors) = dma_buffers!(DATA_SIZE); + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); let dma = Dma::new(peripherals.DMA); let channel = dma.channel0.configure(false, DmaPriority::Priority0); - #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] - let dma_peripheral = peripherals.SPI2; - #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] - let dma_peripheral = peripherals.MEM2MEM1; - let mut mem2mem = - Mem2Mem::new(channel, dma_peripheral, tx_descriptors, rx_descriptors).unwrap(); + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] { + let dma_peripheral = peripherals.SPI2; + } else { + let dma_peripheral = peripherals.MEM2MEM1; + } + } + + Context { + channel, + dma_peripheral, + } + } + + #[test] + fn test_internal_mem2mem(ctx: Context) { + let (mut rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(DATA_SIZE); + + let mut mem2mem = Mem2Mem::new( + ctx.channel, + ctx.dma_peripheral, + rx_descriptors, + tx_descriptors, + ) + .unwrap(); for i in 0..core::mem::size_of_val(tx_buffer) { tx_buffer[i] = (i % 256) as u8; } - let dma_wait = mem2mem.start_transfer(&tx_buffer, &mut rx_buffer).unwrap(); + let dma_wait = mem2mem.start_transfer(&mut rx_buffer, &tx_buffer).unwrap(); dma_wait.wait().unwrap(); for i in 0..core::mem::size_of_val(tx_buffer) { assert_eq!(rx_buffer[i], tx_buffer[i]); @@ -58,27 +80,17 @@ mod tests { } #[test] - fn test_internal_mem2mem_chunk_size() { + fn test_internal_mem2mem_chunk_size(ctx: Context) { const CHUNK_SIZE: usize = 2048; - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); let (tx_buffer, tx_descriptors, mut rx_buffer, rx_descriptors) = dma_buffers_chunk_size!(DATA_SIZE, CHUNK_SIZE); - let dma = Dma::new(peripherals.DMA); - let channel = dma.channel0.configure(false, DmaPriority::Priority0); - #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] - let dma_peripheral = peripherals.SPI2; - #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] - let dma_peripheral = peripherals.MEM2MEM1; - let mut mem2mem = Mem2Mem::new_with_chunk_size( - channel, - dma_peripheral, - tx_descriptors, + ctx.channel, + ctx.dma_peripheral, rx_descriptors, + tx_descriptors, CHUNK_SIZE, ) .unwrap(); @@ -86,7 +98,7 @@ mod tests { for i in 0..core::mem::size_of_val(tx_buffer) { tx_buffer[i] = (i % 256) as u8; } - let dma_wait = mem2mem.start_transfer(&tx_buffer, &mut rx_buffer).unwrap(); + let dma_wait = mem2mem.start_transfer(&mut rx_buffer, &tx_buffer).unwrap(); dma_wait.wait().unwrap(); for i in 0..core::mem::size_of_val(tx_buffer) { assert_eq!(rx_buffer[i], tx_buffer[i]); @@ -94,26 +106,15 @@ mod tests { } #[test] - fn test_mem2mem_errors_zero_tx() { + fn test_mem2mem_errors_zero_tx(ctx: Context) { use esp_hal::dma::CHUNK_SIZE; - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let dma = Dma::new(peripherals.DMA); - let channel = dma.channel0.configure(false, DmaPriority::Priority0); - #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] - let dma_peripheral = peripherals.SPI2; - #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] - let dma_peripheral = peripherals.MEM2MEM1; - - let (tx_descriptors, rx_descriptors) = dma_descriptors!(0, 1024); + let (rx_descriptors, tx_descriptors) = dma_descriptors!(1024, 0); match Mem2Mem::new_with_chunk_size( - channel, - dma_peripheral, - tx_descriptors, + ctx.channel, + ctx.dma_peripheral, rx_descriptors, + tx_descriptors, CHUNK_SIZE, ) { Err(DmaError::OutOfDescriptors) => (), @@ -122,26 +123,15 @@ mod tests { } #[test] - fn test_mem2mem_errors_zero_rx() { + fn test_mem2mem_errors_zero_rx(ctx: Context) { use esp_hal::dma::CHUNK_SIZE; - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let dma = Dma::new(peripherals.DMA); - let channel = dma.channel0.configure(false, DmaPriority::Priority0); - #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] - let dma_peripheral = peripherals.SPI2; - #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] - let dma_peripheral = peripherals.MEM2MEM1; - - let (tx_descriptors, rx_descriptors) = dma_descriptors!(1024, 0); + let (rx_descriptors, tx_descriptors) = dma_descriptors!(0, 1024); match Mem2Mem::new_with_chunk_size( - channel, - dma_peripheral, - tx_descriptors, + ctx.channel, + ctx.dma_peripheral, rx_descriptors, + tx_descriptors, CHUNK_SIZE, ) { Err(DmaError::OutOfDescriptors) => (), @@ -150,24 +140,13 @@ mod tests { } #[test] - fn test_mem2mem_errors_chunk_size_too_small() { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let dma = Dma::new(peripherals.DMA); - let channel = dma.channel0.configure(false, DmaPriority::Priority0); - #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] - let dma_peripheral = peripherals.SPI2; - #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] - let dma_peripheral = peripherals.MEM2MEM1; - - let (tx_descriptors, rx_descriptors) = dma_descriptors!(1024, 1024); + fn test_mem2mem_errors_chunk_size_too_small(ctx: Context) { + let (rx_descriptors, tx_descriptors) = dma_descriptors!(1024, 1024); match Mem2Mem::new_with_chunk_size( - channel, - dma_peripheral, - tx_descriptors, + ctx.channel, + ctx.dma_peripheral, rx_descriptors, + tx_descriptors, 0, ) { Err(DmaError::InvalidChunkSize) => (), @@ -176,24 +155,13 @@ mod tests { } #[test] - fn test_mem2mem_errors_chunk_size_too_big() { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let dma = Dma::new(peripherals.DMA); - let channel = dma.channel0.configure(false, DmaPriority::Priority0); - #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] - let dma_peripheral = peripherals.SPI2; - #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] - let dma_peripheral = peripherals.MEM2MEM1; - - let (tx_descriptors, rx_descriptors) = dma_descriptors!(1024, 1024); + fn test_mem2mem_errors_chunk_size_too_big(ctx: Context) { + let (rx_descriptors, tx_descriptors) = dma_descriptors!(1024, 1024); match Mem2Mem::new_with_chunk_size( - channel, - dma_peripheral, - tx_descriptors, + ctx.channel, + ctx.dma_peripheral, rx_descriptors, + tx_descriptors, 4093, ) { Err(DmaError::InvalidChunkSize) => (), diff --git a/hil-test/tests/ecc.rs b/hil-test/tests/ecc.rs index 0f11a2e66e8..13e97740cfc 100644 --- a/hil-test/tests/ecc.rs +++ b/hil-test/tests/ecc.rs @@ -13,18 +13,17 @@ use crypto_bigint::{ U192, U256, }; -use defmt_rtt as _; use elliptic_curve::sec1::ToEncodedPoint; -use esp_backtrace as _; #[cfg(feature = "esp32h2")] use esp_hal::ecc::WorkMode; use esp_hal::{ ecc::{Ecc, EllipticCurve, Error}, - peripherals::Peripherals, + prelude::*, rng::Rng, Blocking, }; use hex_literal::hex; +use hil_test as _; struct TestParams<'a> { prime_fields: &'a [&'a [u8]], @@ -48,16 +47,6 @@ struct Context<'a> { rng: Rng, } -impl Context<'_> { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let ecc = Ecc::new(peripherals.ECC); - let rng = Rng::new(peripherals.RNG); - - Context { ecc, rng } - } -} - #[cfg(test)] #[embedded_test::tests] mod tests { @@ -67,7 +56,16 @@ mod tests { #[init] fn init() -> Context<'static> { - Context::init() + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); + + let ecc = Ecc::new(peripherals.ECC); + let rng = Rng::new(peripherals.RNG); + + Context { ecc, rng } } #[test] diff --git a/hil-test/tests/embassy_interrupt_executor.rs b/hil-test/tests/embassy_interrupt_executor.rs new file mode 100644 index 00000000000..863c81942b0 --- /dev/null +++ b/hil-test/tests/embassy_interrupt_executor.rs @@ -0,0 +1,69 @@ +//! Test that the interrupt executor correctly gives back control to thread mode +//! code. + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: integrated-timers +//% FEATURES: generic-queue + +#![no_std] +#![no_main] + +use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal}; +use esp_hal::{ + interrupt::{ + software::{SoftwareInterrupt, SoftwareInterruptControl}, + Priority, + }, + timer::timg::TimerGroup, +}; +use esp_hal_embassy::InterruptExecutor; +use hil_test as _; + +macro_rules! mk_static { + ($t:ty,$val:expr) => {{ + static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); + #[deny(unused_attributes)] + let x = STATIC_CELL.uninit().write(($val)); + x + }}; +} + +#[embassy_executor::task] +async fn interrupt_driven_task(signal: &'static Signal) { + loop { + signal.wait().await; + } +} + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod test { + use hil_test as _; + + use super::*; + + #[init] + fn init() -> SoftwareInterrupt<1> { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); + + let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); + sw_ints.software_interrupt1 + } + + #[test] + #[timeout(3)] + fn run_interrupt_executor_test(interrupt: SoftwareInterrupt<1>) { + let interrupt_executor = + mk_static!(InterruptExecutor<1>, InterruptExecutor::new(interrupt)); + let signal = mk_static!(Signal, Signal::new()); + + let spawner = interrupt_executor.start(Priority::Priority3); + + spawner.spawn(interrupt_driven_task(signal)).unwrap(); + + signal.signal(()); + } +} diff --git a/hil-test/tests/embassy_interrupt_spi_dma.rs b/hil-test/tests/embassy_interrupt_spi_dma.rs new file mode 100644 index 00000000000..eb45632f076 --- /dev/null +++ b/hil-test/tests/embassy_interrupt_spi_dma.rs @@ -0,0 +1,127 @@ +//! Reproduction and regression test for a sneaky issue. + +//% CHIPS: esp32 esp32s2 esp32s3 +//% FEATURES: integrated-timers +//% FEATURES: generic-queue + +#![no_std] +#![no_main] + +use embassy_time::{Duration, Instant, Ticker}; +use esp_hal::{ + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, + dma_buffers, + interrupt::{software::SoftwareInterruptControl, Priority}, + peripherals::SPI3, + prelude::*, + spi::{ + master::{Spi, SpiDma}, + FullDuplexMode, + SpiMode, + }, + timer::{timg::TimerGroup, ErasedTimer}, + Async, +}; +use esp_hal_embassy::InterruptExecutor; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi3DmaChannel as DmaChannel1; + } else { + use esp_hal::dma::DmaChannel1; + } +} + +macro_rules! mk_static { + ($t:ty,$val:expr) => {{ + static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); + #[deny(unused_attributes)] + let x = STATIC_CELL.uninit().write(($val)); + x + }}; +} + +#[embassy_executor::task] +async fn interrupt_driven_task(spi: SpiDma<'static, SPI3, DmaChannel1, FullDuplexMode, Async>) { + let mut ticker = Ticker::every(Duration::from_millis(1)); + + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(128); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + + let mut spi = spi.with_buffers(dma_rx_buf, dma_tx_buf); + + loop { + let mut buffer: [u8; 8] = [0; 8]; + + spi.transfer_in_place_async(&mut buffer).await.unwrap(); + + ticker.next().await; + } +} + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod test { + use super::*; + + #[test] + #[timeout(3)] + async fn run_interrupt_executor_test() { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init([ + ErasedTimer::from(timg0.timer0), + ErasedTimer::from(timg0.timer1), + ]); + + let dma = Dma::new(peripherals.DMA); + + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel1 = dma.spi2channel; + let dma_channel2 = dma.spi3channel; + } else { + let dma_channel1 = dma.channel0; + let dma_channel2 = dma.channel1; + } + } + + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(1024); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + + let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) + .with_dma(dma_channel1.configure_for_async(false, DmaPriority::Priority0)) + .with_buffers(dma_rx_buf, dma_tx_buf); + + let spi2 = Spi::new(peripherals.SPI3, 100.kHz(), SpiMode::Mode0) + .with_dma(dma_channel2.configure_for_async(false, DmaPriority::Priority0)); + + let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); + + let interrupt_executor = mk_static!( + InterruptExecutor<1>, + InterruptExecutor::new(sw_ints.software_interrupt1) + ); + + let spawner = interrupt_executor.start(Priority::Priority3); + + spawner.spawn(interrupt_driven_task(spi2)).unwrap(); + + let start = Instant::now(); + let mut buffer: [u8; 1024] = [0; 1024]; + loop { + spi.transfer_in_place_async(&mut buffer).await.unwrap(); + + if start.elapsed() > Duration::from_secs(1) { + break; + } + } + } +} diff --git a/hil-test/tests/embassy_timers_executors.rs b/hil-test/tests/embassy_timers_executors.rs index e30bd5672a5..edc8479b6ac 100644 --- a/hil-test/tests/embassy_timers_executors.rs +++ b/hil-test/tests/embassy_timers_executors.rs @@ -1,47 +1,30 @@ //! Embassy timer and executor Test -//! -//! Description of implemented tests: -//! - run_test_one_shot_timg(): Tests Timg configured as OneShotTimer -//! - run_test_periodic_timg(): Tests Timg configured as PeriodicTimer -//! - run_test_one_shot_systimer(): Tests systimer configured as OneShotTimer -//! - run_test_periodic_systimer(): Tests systimer configured as PeriodicTimer -//! - run_test_periodic_oneshot_timg(): Tests Timg configured as PeriodicTimer -//! and then reconfigured as OneShotTimer -//! - run_test_periodic_oneshot_systimer(): Tests systimer configured as -//! PeriodicTimer and then reconfigured as OneShotTimer -//! - run_test_join_timg(): Tests Timg configured as OneShotTimer and wait on -//! two different timeouts via join -//! - run_test_join_systimer(): Tests systimer configured as OneShotTimer and -//! wait on two different timeouts via join -//! - run_test_interrupt_executor(): Tests InterruptExecutor and Thread -//! (default) executor in parallel -//! - run_tick_test_timg(): Tests Timg configured as OneShotTimer if it fires -//! immediately in the case of the time scheduling was already in the past -//! (timestamp being too big) - -// esp32c2 is disabled currently as it fails -//% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s3 + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: integrated-timers +//% FEATURES: generic-queue #![no_std] #![no_main] -use defmt_rtt as _; use embassy_time::{Duration, Ticker, Timer}; -use esp_backtrace as _; +#[cfg(not(feature = "esp32"))] +use esp_hal::{ + interrupt::software::SoftwareInterruptControl, + interrupt::Priority, + timer::systimer::{Alarm, FrozenUnit, Periodic, SystemTimer, Target}, + timer::ErasedTimer, +}; use esp_hal::{ - clock::ClockControl, peripherals::Peripherals, prelude::*, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer, PeriodicTimer}, + timer::{timg::TimerGroup, OneShotTimer, PeriodicTimer}, }; #[cfg(not(feature = "esp32"))] -use esp_hal::{interrupt::Priority, timer::systimer::SystemTimer}; -#[cfg(not(feature = "esp32"))] use esp_hal_embassy::InterruptExecutor; -#[cfg(not(feature = "esp32"))] -use static_cell::StaticCell; +use hil_test as _; +#[cfg(not(feature = "esp32"))] macro_rules! mk_static { ($t:ty,$val:expr) => {{ static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); @@ -51,444 +34,261 @@ macro_rules! mk_static { }}; } -unsafe fn __make_static(t: &mut T) -> &'static mut T { - ::core::mem::transmute(t) -} - -// we need to tell the test framework somehow, if the async test passed or -// failed, this mod is the list of functions that are spawned as an actual tests -mod task_invokers { - use test_helpers::*; - - use crate::*; - #[embassy_executor::task] - pub async fn test_one_shot_timg_invoker() { - let outcome; - { - outcome = test_helpers::test_one_shot_timg().await; - } - embedded_test::export::check_outcome(outcome); - } - - #[embassy_executor::task] - #[cfg(not(feature = "esp32"))] - pub async fn test_one_shot_systimer_invoker() { - let outcome; - { - outcome = task_invokers::test_one_shot_systimer().await; - } - embedded_test::export::check_outcome(outcome); - } - - #[embassy_executor::task] - pub async fn test_join_timg_invoker() { - let outcome; - { - outcome = test_join_timg().await; - } - embedded_test::export::check_outcome(outcome); - } - - #[embassy_executor::task] - #[cfg(not(feature = "esp32"))] - pub async fn test_join_systimer_invoker() { - let outcome; - { - outcome = test_join_systimer().await; - } - embedded_test::export::check_outcome(outcome); - } - - #[embassy_executor::task] - #[cfg(not(feature = "esp32"))] - pub async fn test_interrupt_executor_invoker() { - let outcome; - { - outcome = test_interrupt_executor().await; - } - embedded_test::export::check_outcome(outcome); - } - - #[embassy_executor::task] - pub async fn test_tick_and_increment_invoker() { - let outcome; - { - outcome = tick_and_increment().await; - } - embedded_test::export::check_outcome(outcome); - } -} - // List of the functions that are ACTUALLY TESTS but are called in the invokers mod test_helpers { - use crate::*; - pub async fn test_one_shot_timg() { - let peripherals = unsafe { Peripherals::steal() }; - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - - let t1 = esp_hal::time::current_time(); - Timer::after_millis(500).await; - task300ms().await; - let t2 = esp_hal::time::current_time(); - - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 500u64); - } - - #[cfg(not(feature = "esp32"))] - pub async fn test_one_shot_systimer() { - let peripherals = unsafe { Peripherals::steal() }; - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let systimer = SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - - let t1 = esp_hal::time::current_time(); - Timer::after_millis(500).await; - task300ms().await; - let t2 = esp_hal::time::current_time(); + use super::*; - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 500u64); + #[embassy_executor::task] + pub async fn e_task30ms() { + Timer::after_millis(30).await; } +} - pub async fn test_join_timg() { - let peripherals = unsafe { Peripherals::steal() }; - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); +mod test_cases { + use esp_hal::peripheral::Peripheral; - let t1 = esp_hal::time::current_time(); - embassy_futures::join::join(task500ms(), task300ms()).await; - task500ms().await; - let t2 = esp_hal::time::current_time(); + use super::*; - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); + pub async fn run_test_one_shot_async() { + let t1 = esp_hal::time::now(); + Timer::after_millis(50).await; + Timer::after_millis(30).await; + let t2 = esp_hal::time::now(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 80u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); } - #[cfg(not(feature = "esp32"))] - pub async fn test_join_systimer() { - let peripherals = unsafe { Peripherals::steal() }; - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + pub fn run_test_periodic_timer(timer: impl Peripheral

) { + let mut periodic = PeriodicTimer::new(timer); - let systimer = SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let t1 = esp_hal::time::now(); + periodic.start(100.millis()).unwrap(); - let t1 = esp_hal::time::current_time(); - embassy_futures::join::join(task500ms(), task300ms()).await; - task500ms().await; - let t2 = esp_hal::time::current_time(); + nb::block!(periodic.wait()).unwrap(); + let t2 = esp_hal::time::now(); - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 100u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); } - #[cfg(not(feature = "esp32"))] - pub async fn test_interrupt_executor() { - let mut ticker = Ticker::every(Duration::from_millis(300)); + pub fn run_test_oneshot_timer(timer: impl Peripheral

) { + let timer = OneShotTimer::new(timer); - let t1 = esp_hal::time::current_time(); - ticker.next().await; - ticker.next().await; - ticker.next().await; - let t2 = esp_hal::time::current_time(); + let t1 = esp_hal::time::now(); + timer.delay_millis(50); + let t2 = esp_hal::time::now(); - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 900u64); + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 50u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); } - pub async fn tick_and_increment() { - const HZ: u64 = 100_000u64; - let mut counter = 0; - let mut ticker = Ticker::every(Duration::from_hz(HZ)); - - let t1 = esp_hal::time::current_time(); - let t2; - - loop { - ticker.next().await; - counter += 1; - - if counter > 100_000 { - t2 = esp_hal::time::current_time(); - break; - } - } - - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1000u64); - assert!((t2 - t1).to_millis() <= 1300u64); - } - - #[embassy_executor::task] - pub async fn tick() { - const HZ: u64 = 1000u64; - let mut ticker = Ticker::every(Duration::from_hz(HZ)); - - loop { - ticker.next().await; - } - } - - pub async fn task500ms() { - Timer::after_millis(500).await; + pub async fn run_join_test() { + let t1 = esp_hal::time::now(); + embassy_futures::join::join(Timer::after_millis(50), Timer::after_millis(30)).await; + Timer::after_millis(50).await; + let t2 = esp_hal::time::now(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 100u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); } +} - pub async fn task300ms() { - Timer::after_millis(300).await; - } +fn set_up_embassy_with_timg0(peripherals: Peripherals) { + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); +} - #[embassy_executor::task] - pub async fn e_task300ms() { - task300ms().await; - } +#[cfg(not(feature = "esp32"))] +fn set_up_embassy_with_systimer(peripherals: Peripherals) { + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init(systimer.alarm0); } #[cfg(test)] #[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] mod test { use super::*; - use crate::{task_invokers::*, test_helpers::*}; + use crate::test_cases::*; + #[cfg(not(feature = "esp32"))] + use crate::test_helpers::*; - #[test] - #[timeout(3)] - fn run_test_one_shot_timg() { - let mut executor = esp_hal_embassy::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(task_invokers::test_one_shot_timg_invoker()); - }); + #[init] + fn init() -> Peripherals { + esp_hal::init(esp_hal::Config::default()) } #[test] #[timeout(3)] - fn run_test_periodic_timg() { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - - let mut periodic = PeriodicTimer::new(timg0.timer0); + async fn test_one_shot_timg(peripherals: Peripherals) { + set_up_embassy_with_timg0(peripherals); - let t1 = esp_hal::time::current_time(); - periodic.start(1.secs()).unwrap(); - - let t2; - loop { - nb::block!(periodic.wait()).unwrap(); - t2 = esp_hal::time::current_time(); - break; - } - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); + run_test_one_shot_async().await; } #[test] #[timeout(3)] #[cfg(not(feature = "esp32"))] - fn run_test_one_shot_systimer() { - let mut executor = esp_hal_embassy::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(test_one_shot_systimer_invoker()); - }); + async fn test_one_shot_systimer(peripherals: Peripherals) { + set_up_embassy_with_systimer(peripherals); + + run_test_one_shot_async().await; } #[test] #[timeout(3)] - #[cfg(not(feature = "esp32"))] - fn run_test_periodic_systimer() { - let peripherals = Peripherals::take(); + fn test_periodic_timg(peripherals: Peripherals) { + let timg0 = TimerGroup::new(peripherals.TIMG0); - let systimer = SystemTimer::new(peripherals.SYSTIMER); - - let mut periodic = PeriodicTimer::new(systimer.alarm0); - - let t1 = esp_hal::time::current_time(); - periodic.start(1.secs()).unwrap(); - - let t2; - loop { - nb::block!(periodic.wait()).unwrap(); - t2 = esp_hal::time::current_time(); - break; - } - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); + run_test_periodic_timer(timg0.timer0); } #[test] #[timeout(3)] - fn run_test_periodic_oneshot_timg() { - let mut peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let mut timg0 = TimerGroup::new(&mut peripherals.TIMG0, &clocks); - - let mut periodic = PeriodicTimer::new(&mut timg0.timer0); - - let t1 = esp_hal::time::current_time(); - periodic.start(1.secs()).unwrap(); - - let t2; - loop { - nb::block!(periodic.wait()).unwrap(); - t2 = esp_hal::time::current_time(); - break; - } - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); - - core::mem::drop(periodic); - - let timg0 = TimerGroup::new(&mut peripherals.TIMG0, &clocks); + #[cfg(not(feature = "esp32"))] + fn test_periodic_systimer(peripherals: Peripherals) { + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); - let timer0 = OneShotTimer::new(timg0.timer0); + run_test_periodic_timer(systimer.alarm0); + } - let t1 = esp_hal::time::current_time(); - timer0.delay_millis(500); - let t2 = esp_hal::time::current_time(); + #[test] + #[timeout(3)] + fn test_periodic_oneshot_timg(mut peripherals: Peripherals) { + let mut timg0 = TimerGroup::new(&mut peripherals.TIMG0); + run_test_periodic_timer(&mut timg0.timer0); - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 500u64); + let mut timg0 = TimerGroup::new(&mut peripherals.TIMG0); + run_test_oneshot_timer(&mut timg0.timer0); } #[test] #[timeout(3)] #[cfg(not(feature = "esp32"))] - fn run_test_periodic_oneshot_systimer() { - let mut peripherals = Peripherals::take(); - + fn test_periodic_oneshot_systimer(mut peripherals: Peripherals) { let mut systimer = SystemTimer::new(&mut peripherals.SYSTIMER); + let unit = FrozenUnit::new(&mut systimer.unit0); + let mut alarm: Alarm<'_, Periodic, _, _, _> = Alarm::new(systimer.comparator0, &unit); + run_test_periodic_timer(&mut alarm); - let mut periodic = PeriodicTimer::new(&mut systimer.alarm0); - - let t1 = esp_hal::time::current_time(); - periodic.start(1.secs()).unwrap(); - - let t2; - loop { - nb::block!(periodic.wait()).unwrap(); - t2 = esp_hal::time::current_time(); - break; - } - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); - - core::mem::drop(periodic); - - let systimer = SystemTimer::new(&mut peripherals.SYSTIMER); - - let timer0 = OneShotTimer::new(systimer.alarm0); - - let t1 = esp_hal::time::current_time(); - timer0.delay_millis(500); - let t2 = esp_hal::time::current_time(); - - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 500u64); + let mut systimer = SystemTimer::new(&mut peripherals.SYSTIMER); + let unit = FrozenUnit::new(&mut systimer.unit0); + let mut alarm: Alarm<'_, Target, _, _, _> = Alarm::new(systimer.comparator0, &unit); + run_test_oneshot_timer(&mut alarm); } #[test] #[timeout(3)] - fn run_test_join_timg() { - let mut executor = esp_hal_embassy::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(test_join_timg_invoker()); - }); + async fn test_join_timg(peripherals: Peripherals) { + set_up_embassy_with_timg0(peripherals); + + run_join_test().await; } #[test] #[timeout(3)] #[cfg(not(feature = "esp32"))] - fn run_test_join_systimer() { - let mut executor = esp_hal_embassy::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(test_join_systimer_invoker()); - }); + async fn test_join_systimer(peripherals: Peripherals) { + set_up_embassy_with_systimer(peripherals); + + run_join_test().await; } + /// Test that the ticker works in tasks ran by the interrupt executors. #[test] #[timeout(3)] #[cfg(not(feature = "esp32"))] - async fn run_test_interrupt_executor() { - let spawner = embassy_executor::Spawner::for_current_executor().await; + async fn test_interrupt_executor(peripherals: Peripherals) { + let timg0 = TimerGroup::new(peripherals.TIMG0); + let timer0: ErasedTimer = timg0.timer0.into(); + + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + let alarm0: ErasedTimer = systimer.alarm0.into(); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + esp_hal_embassy::init([timer0, alarm0]); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timer0 = OneShotTimer::new(timer0); + let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); - let timer1 = { - let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - OneShotTimer::new(alarm0) - }; + let executor = mk_static!( + InterruptExecutor<2>, + InterruptExecutor::new(sw_ints.software_interrupt2) + ); - let timers = [timer0, timer1]; - let timers = mk_static!([OneShotTimer; 2], timers); - esp_hal_embassy::init(&clocks, timers); + #[embassy_executor::task] + #[cfg(not(feature = "esp32"))] + async fn test_interrupt_executor_invoker() { + let outcome = async { + let mut ticker = Ticker::every(Duration::from_millis(30)); - static EXECUTOR: StaticCell> = StaticCell::new(); - let executor = - InterruptExecutor::new(system.software_interrupt_control.software_interrupt2); - let executor = EXECUTOR.init(executor); + let t1 = esp_hal::time::now(); + ticker.next().await; + ticker.next().await; + ticker.next().await; + let t2 = esp_hal::time::now(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_micros() >= 85000u64, + "diff: {:?}", + (t2 - t1).to_micros() + ); + }; + + embedded_test::export::check_outcome(outcome.await); + } let spawner_int = executor.start(Priority::Priority3); spawner_int.must_spawn(test_interrupt_executor_invoker()); - spawner.must_spawn(e_task300ms()); - - // we need to delay so the e_task300ms() could be spawned - task500ms().await; + let spawner = embassy_executor::Spawner::for_current_executor().await; + spawner.must_spawn(e_task30ms()); + // The test ends once the interrupt executor's task has finished loop {} } + /// Test that timg0 and systimer don't have vastly different tick rates. #[test] #[timeout(3)] - async fn run_tick_test_timg() { - let spawner = embassy_executor::Spawner::for_current_executor().await; + async fn tick_test_timer_tick_rates(peripherals: Peripherals) { + set_up_embassy_with_timg0(peripherals); - let peripherals = unsafe { Peripherals::steal() }; - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + // We are retrying 5 times because probe-rs polling RTT may introduce some + // jitter. + for _ in 0..5 { + let t1 = esp_hal::time::now(); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let mut ticker = Ticker::every(Duration::from_hz(100_000)); + for _ in 0..2000 { + ticker.next().await; + } + let t2 = esp_hal::time::now(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + let duration = (t2 - t1).to_micros(); + + assert!(duration >= 19000, "diff: {:?}", (t2 - t1).to_micros()); + if duration <= 21000 { + return; + } + } - spawner.must_spawn(tick()); - tick_and_increment().await; + assert!(false, "Test failed after 5 retries"); } } diff --git a/hil-test/tests/get_time.rs b/hil-test/tests/get_time.rs index 2670ef80333..5d5448e686e 100644 --- a/hil-test/tests/get_time.rs +++ b/hil-test/tests/get_time.rs @@ -1,29 +1,23 @@ -//! current_time Test +//! time::now Test -// esp32c2 is disabled currently as it fails -//% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; -use esp_hal::{clock::ClockControl, delay::Delay, peripherals::Peripherals, system::SystemControl}; +use esp_hal::delay::Delay; +use hil_test as _; struct Context { delay: Delay, } -impl Context { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); +fn time_moves_forward_during(ctx: Context, f: F) { + let t1 = esp_hal::time::now(); + f(ctx); + let t2 = esp_hal::time::now(); - let delay = Delay::new(&clocks); - - Context { delay } - } + assert!(t2 > t1); } #[cfg(test)] @@ -33,16 +27,45 @@ mod tests { #[init] fn init() -> Context { - Context::init() + let _ = esp_hal::init(esp_hal::Config::default()); + + let delay = Delay::new(); + + Context { delay } } #[test] + #[timeout(3)] fn test_current_time(ctx: Context) { - let t1 = esp_hal::time::current_time(); + let t1 = esp_hal::time::now(); ctx.delay.delay_millis(500); - let t2 = esp_hal::time::current_time(); + let t2 = esp_hal::time::now(); assert!(t2 > t1); assert!((t2 - t1).to_millis() >= 500u64); } + + #[cfg(systimer)] + #[test] + #[timeout(3)] + fn test_current_time_construct_systimer(ctx: Context) { + time_moves_forward_during(ctx, |_| { + // construct the timer in between calls to current_time + let _ = esp_hal::timer::systimer::SystemTimer::new(unsafe { + esp_hal::peripherals::SYSTIMER::steal() + }); + }) + } + + #[cfg(esp32)] + #[test] + #[timeout(3)] + fn test_current_time_construct_timg0(ctx: Context) { + time_moves_forward_during(ctx, |_| { + // construct the timer in between calls to current_time + let _ = esp_hal::timer::timg::TimerGroup::new(unsafe { + esp_hal::peripherals::TIMG0::steal() + }); + }) + } } diff --git a/hil-test/tests/gpio.rs b/hil-test/tests/gpio.rs index 0c5cbdb0e59..288df83c402 100644 --- a/hil-test/tests/gpio.rs +++ b/hil-test/tests/gpio.rs @@ -1,10 +1,11 @@ //! GPIO Test //! //! Folowing pins are used: -//! GPIO2 -//! GPIO3 +//! GPIO2 / GPIO9 (esp32s2 / esp32s3) / GPIO26 (esp32) +//! GPIO3 / GPIO10 (esp32s2 / esp32s3) / GPIO27 (esp32) //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: generic-queue #![no_std] #![no_main] @@ -12,62 +13,24 @@ use core::cell::RefCell; use critical_section::Mutex; -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, - gpio::{Gpio2, Gpio3, GpioPin, Input, Io, Level, Output, Pull}, + gpio::{AnyPin, ErasedPin, Input, Io, Level, Output, Pin, Pull}, macros::handler, - peripherals::Peripherals, - system::SystemControl, - timer::{timg::TimerGroup, ErasedTimer, OneShotTimer}, + timer::timg::TimerGroup, InterruptConfigurable, }; - -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} +use hil_test as _; static COUNTER: Mutex> = Mutex::new(RefCell::new(0)); -static INPUT_PIN: Mutex>>> = Mutex::new(RefCell::new(None)); +static INPUT_PIN: Mutex>> = Mutex::new(RefCell::new(None)); -struct Context<'d> { - io2: Input<'d, Gpio2>, - io3: Output<'d, Gpio3>, +struct Context { + test_gpio1: ErasedPin, + test_gpio2: ErasedPin, delay: Delay, } -impl<'d> Context<'d> { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let mut io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - io.set_interrupt_handler(interrupt_handler); - - let delay = Delay::new(&clocks); - - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - - Context { - io2: Input::new(io.pins.gpio2, Pull::Down), - io3: Output::new(io.pins.gpio3, Level::Low), - delay, - } - } -} - #[handler] pub fn interrupt_handler() { critical_section::with(|cs| { @@ -90,31 +53,48 @@ mod tests { use super::*; #[init] - fn init() -> Context<'static> { - let mut ctx = Context::init(); - // make sure tests don't interfere with each other - ctx.io3.set_low(); - ctx + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let mut io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + io.set_interrupt_handler(interrupt_handler); + + let delay = Delay::new(); + + let (gpio1, gpio2) = hil_test::common_test_pins!(io); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); + + Context { + test_gpio1: gpio1.degrade(), + test_gpio2: gpio2.degrade(), + delay, + } } #[test] - async fn test_async_edge(ctx: Context<'static>) { + async fn test_async_edge(ctx: Context) { let counter = AtomicUsize::new(0); let Context { - mut io2, mut io3, .. + test_gpio1, + test_gpio2, + .. } = ctx; + let mut test_gpio1 = Input::new(test_gpio1, Pull::Down); + let mut test_gpio2 = Output::new(test_gpio2, Level::Low); embassy_futures::select::select( async { loop { - io2.wait_for_rising_edge().await; + test_gpio1.wait_for_rising_edge().await; counter.fetch_add(1, Ordering::SeqCst); } }, async { for _ in 0..5 { - io3.set_high(); + test_gpio2.set_high(); Timer::after(Duration::from_millis(25)).await; - io3.set_low(); + test_gpio2.set_low(); Timer::after(Duration::from_millis(25)).await; } }, @@ -124,8 +104,8 @@ mod tests { } #[test] - async fn test_a_pin_can_wait(_ctx: Context<'static>) { - let mut first = Input::new(unsafe { GpioPin::<0>::steal() }, Pull::Down); + async fn test_a_pin_can_wait(ctx: Context) { + let mut first = Input::new(ctx.test_gpio1, Pull::Down); embassy_futures::select::select( first.wait_for_rising_edge(), @@ -137,146 +117,181 @@ mod tests { } #[test] - fn test_gpio_input(ctx: Context<'static>) { + fn test_gpio_input(ctx: Context) { + let test_gpio1 = Input::new(ctx.test_gpio1, Pull::Down); // `InputPin`: - assert_eq!(ctx.io2.is_low(), true); - assert_eq!(ctx.io2.is_high(), false); + assert_eq!(test_gpio1.is_low(), true); + assert_eq!(test_gpio1.is_high(), false); } #[test] - fn test_gpio_output(mut ctx: Context<'static>) { + fn test_gpio_output(ctx: Context) { + let mut test_gpio2 = Output::new(ctx.test_gpio2, Level::Low); + // `StatefulOutputPin`: - assert_eq!(ctx.io3.is_set_low(), true); - assert_eq!(ctx.io3.is_set_high(), false); - ctx.io3.set_high(); - assert_eq!(ctx.io3.is_set_low(), false); - assert_eq!(ctx.io3.is_set_high(), true); + assert_eq!(test_gpio2.is_set_low(), true); + assert_eq!(test_gpio2.is_set_high(), false); + test_gpio2.set_high(); + assert_eq!(test_gpio2.is_set_low(), false); + assert_eq!(test_gpio2.is_set_high(), true); // `ToggleableOutputPin`: - ctx.io3.toggle(); - assert_eq!(ctx.io3.is_set_low(), true); - assert_eq!(ctx.io3.is_set_high(), false); - ctx.io3.toggle(); - assert_eq!(ctx.io3.is_set_low(), false); - assert_eq!(ctx.io3.is_set_high(), true); + test_gpio2.toggle(); + assert_eq!(test_gpio2.is_set_low(), true); + assert_eq!(test_gpio2.is_set_high(), false); + test_gpio2.toggle(); + assert_eq!(test_gpio2.is_set_low(), false); + assert_eq!(test_gpio2.is_set_high(), true); } #[test] - fn test_gpio_interrupt(mut ctx: Context<'static>) { + fn test_gpio_interrupt(ctx: Context) { + let mut test_gpio1 = Input::new(ctx.test_gpio1, Pull::Down); + let mut test_gpio2 = Output::new(ctx.test_gpio2, Level::Low); + critical_section::with(|cs| { *COUNTER.borrow_ref_mut(cs) = 0; - ctx.io2.listen(Event::AnyEdge); - INPUT_PIN.borrow_ref_mut(cs).replace(ctx.io2); + test_gpio1.listen(Event::AnyEdge); + INPUT_PIN.borrow_ref_mut(cs).replace(test_gpio1); }); - ctx.io3.set_high(); + test_gpio2.set_high(); ctx.delay.delay_millis(1); - ctx.io3.set_low(); + test_gpio2.set_low(); ctx.delay.delay_millis(1); - ctx.io3.set_high(); + test_gpio2.set_high(); ctx.delay.delay_millis(1); - ctx.io3.set_low(); + test_gpio2.set_low(); ctx.delay.delay_millis(1); - ctx.io3.set_high(); + test_gpio2.set_high(); ctx.delay.delay_millis(1); - ctx.io3.set_low(); + test_gpio2.set_low(); ctx.delay.delay_millis(1); - ctx.io3.set_high(); + test_gpio2.set_high(); ctx.delay.delay_millis(1); - ctx.io3.set_low(); + test_gpio2.set_low(); ctx.delay.delay_millis(1); - ctx.io3.set_high(); + test_gpio2.set_high(); ctx.delay.delay_millis(1); let count = critical_section::with(|cs| *COUNTER.borrow_ref(cs)); assert_eq!(count, 9); - ctx.io2 = critical_section::with(|cs| INPUT_PIN.borrow_ref_mut(cs).take().unwrap()); - ctx.io2.unlisten(); + let mut test_gpio1 = + critical_section::with(|cs| INPUT_PIN.borrow_ref_mut(cs).take().unwrap()); + test_gpio1.unlisten(); } #[test] - fn test_gpio_od(ctx: Context<'static>) { - let mut io2 = OutputOpenDrain::new(unsafe { GpioPin::<2>::steal() }, Level::High, Pull::Up); - let mut io3 = OutputOpenDrain::new(unsafe { GpioPin::<3>::steal() }, Level::High, Pull::Up); + fn test_gpio_od(ctx: Context) { + let mut test_gpio1 = OutputOpenDrain::new(ctx.test_gpio1, Level::High, Pull::Up); + let mut test_gpio2 = OutputOpenDrain::new(ctx.test_gpio2, Level::High, Pull::Up); ctx.delay.delay_millis(1); - assert_eq!(io2.is_high(), true); - assert_eq!(io3.is_high(), true); + assert_eq!(test_gpio1.is_high(), true); + assert_eq!(test_gpio2.is_high(), true); - io2.set_low(); - io3.set_high(); + test_gpio1.set_low(); + test_gpio2.set_high(); ctx.delay.delay_millis(1); - assert_eq!(io2.is_low(), true); - assert_eq!(io3.is_low(), true); + assert_eq!(test_gpio1.is_low(), true); + assert_eq!(test_gpio2.is_low(), true); - io2.set_high(); - io3.set_high(); + test_gpio1.set_high(); + test_gpio2.set_high(); ctx.delay.delay_millis(1); - assert_eq!(io2.is_high(), true); - assert_eq!(io3.is_high(), true); + assert_eq!(test_gpio1.is_high(), true); + assert_eq!(test_gpio2.is_high(), true); - io2.set_high(); - io3.set_low(); + test_gpio1.set_high(); + test_gpio2.set_low(); ctx.delay.delay_millis(1); - assert_eq!(io2.is_low(), true); - assert_eq!(io3.is_low(), true); + assert_eq!(test_gpio1.is_low(), true); + assert_eq!(test_gpio2.is_low(), true); - io2.set_high(); - io3.set_high(); + test_gpio1.set_high(); + test_gpio2.set_high(); ctx.delay.delay_millis(1); - assert_eq!(io2.is_high(), true); - assert_eq!(io3.is_high(), true); + assert_eq!(test_gpio1.is_high(), true); + assert_eq!(test_gpio2.is_high(), true); - io2.set_low(); - io3.set_low(); + test_gpio1.set_low(); + test_gpio2.set_low(); ctx.delay.delay_millis(1); - assert_eq!(io2.is_low(), true); - assert_eq!(io3.is_low(), true); + assert_eq!(test_gpio1.is_low(), true); + assert_eq!(test_gpio2.is_low(), true); } #[test] - fn test_gpio_flex(ctx: Context<'static>) { - let mut io2 = Flex::new(unsafe { GpioPin::<2>::steal() }); - let mut io3 = Flex::new(unsafe { GpioPin::<3>::steal() }); + fn test_gpio_flex(ctx: Context) { + let mut test_gpio1 = Flex::new(ctx.test_gpio1); + let mut test_gpio2 = Flex::new(ctx.test_gpio2); - io2.set_high(); - io2.set_as_output(); - io3.set_as_input(Pull::None); + test_gpio1.set_high(); + test_gpio1.set_as_output(); + test_gpio2.set_as_input(Pull::None); ctx.delay.delay_millis(1); - assert_eq!(io2.is_set_high(), true); - assert_eq!(io3.is_high(), true); + assert_eq!(test_gpio1.is_set_high(), true); + assert_eq!(test_gpio2.is_high(), true); - io2.set_low(); + test_gpio1.set_low(); ctx.delay.delay_millis(1); - assert_eq!(io2.is_set_high(), false); - assert_eq!(io3.is_high(), false); + assert_eq!(test_gpio1.is_set_high(), false); + assert_eq!(test_gpio2.is_high(), false); - io2.set_as_input(Pull::None); - io3.set_as_output(); + test_gpio1.set_as_input(Pull::None); + test_gpio2.set_as_output(); ctx.delay.delay_millis(1); - assert_eq!(io2.is_high(), false); - assert_eq!(io3.is_set_high(), false); + assert_eq!(test_gpio1.is_high(), false); + assert_eq!(test_gpio2.is_set_high(), false); - io3.set_high(); + test_gpio2.set_high(); ctx.delay.delay_millis(1); - assert_eq!(io2.is_high(), true); - assert_eq!(io3.is_set_high(), true); + assert_eq!(test_gpio1.is_high(), true); + assert_eq!(test_gpio2.is_set_high(), true); - io3.set_low(); + test_gpio2.set_low(); ctx.delay.delay_millis(1); - assert_eq!(io2.is_low(), true); - assert_eq!(io3.is_set_low(), true); + assert_eq!(test_gpio1.is_low(), true); + assert_eq!(test_gpio2.is_set_low(), true); + } + + // Tests touch pin (GPIO2) as AnyPin and Output + // https://github.com/esp-rs/esp-hal/issues/1943 + #[test] + fn test_gpio_touch_anypin_output(ctx: Context) { + let any_pin2 = AnyPin::new(ctx.test_gpio1); + let any_pin3 = AnyPin::new(ctx.test_gpio2); + + let out_pin = Output::new(any_pin2, Level::High); + let in_pin = Input::new(any_pin3, Pull::Down); + + assert_eq!(out_pin.is_set_high(), true); + assert_eq!(in_pin.is_high(), true); + } + + // Tests touch pin (GPIO2) as AnyPin and Input + // https://github.com/esp-rs/esp-hal/issues/1943 + #[test] + fn test_gpio_touch_anypin_input(ctx: Context) { + let any_pin2 = AnyPin::new(ctx.test_gpio1); + let any_pin3 = AnyPin::new(ctx.test_gpio2); + + let out_pin = Output::new(any_pin3, Level::Low); + let in_pin = Input::new(any_pin2, Pull::Down); + + assert_eq!(out_pin.is_set_high(), false); + assert_eq!(in_pin.is_high(), false); } } diff --git a/hil-test/tests/i2c.rs b/hil-test/tests/i2c.rs new file mode 100644 index 00000000000..e85e223241b --- /dev/null +++ b/hil-test/tests/i2c.rs @@ -0,0 +1,57 @@ +//! I2C test +//! +//! Folowing pins are used: +//! SDA GPIO2 (esp32s2 and esp32s3) +//! GPIO6 (esp32c6) +//! GPIO18 (esp32c2) +//! GPIO4 (esp32h2 and esp32c3) +//! GPIO32 (esp32) +//! +//! SCL GPIO3 (esp32s2 and esp32s3) +//! GPIO7 (esp32c6 and esp32c3) +//! GPIO22 (esp32h2) +//! GPIO19 (esp32c2) +//! GPIO33 (esp32) + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 + +#![no_std] +#![no_main] + +use esp_hal::{gpio::Io, i2c::I2C, peripherals::I2C0, prelude::*, Blocking}; +use hil_test as _; + +struct Context { + i2c: I2C<'static, I2C0, Blocking>, +} +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use defmt::assert_ne; + + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let (sda, scl) = hil_test::i2c_pins!(io); + + // Create a new peripheral object with the described wiring and standard + // I2C clock speed: + let i2c = I2C::new(peripherals.I2C0, sda, scl, 100.kHz()); + + Context { i2c } + } + + #[test] + #[timeout(3)] + fn test_read_cali(mut ctx: Context) { + let mut read_data = [0u8; 22]; + + ctx.i2c.write_read(0x77, &[0xaa], &mut read_data).ok(); + + assert_ne!(read_data, [0u8; 22]) + } +} diff --git a/hil-test/tests/i2s.rs b/hil-test/tests/i2s.rs index 4c40add1155..7b0de40addb 100644 --- a/hil-test/tests/i2s.rs +++ b/hil-test/tests/i2s.rs @@ -1,6 +1,7 @@ //! I2S Loopback Test //! //! It's assumed GPIO2 is connected to GPIO3 +//! (GPIO9 and GPIO10 for esp32s3) //! //! This test uses I2S TX to transmit known data to I2S RX (forced to slave mode //! with loopback mode enabled). It's using circular DMA mode @@ -10,48 +11,59 @@ #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, delay::Delay, dma::{Dma, DmaPriority}, dma_buffers, gpio::Io, i2s::{DataFormat, I2s, I2sReadDma, I2sWriteDma, Standard}, peripheral::Peripheral, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; +use hil_test as _; -// choose values which DON'T restart on every descriptor buffer's start -const ADD: u8 = 5; -const CUT_OFF: u8 = 113; +#[derive(Clone)] +struct SampleSource { + i: u8, +} + +impl SampleSource { + // choose values which DON'T restart on every descriptor buffer's start + const ADD: u8 = 5; + const CUT_OFF: u8 = 113; + + fn new() -> Self { + Self { i: 0 } + } +} + +impl Iterator for SampleSource { + type Item = u8; + + fn next(&mut self) -> Option { + let i = self.i; + self.i = (i + Self::ADD) % Self::CUT_OFF; + Some(i) + } +} #[cfg(test)] #[embedded_test::tests] mod tests { - use super::*; - #[init] - fn init() {} - #[test] fn test_i2s_loopback() { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let mut io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let delay = Delay::new(&clocks); + let delay = Delay::new(); let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; - let (tx_buffer, tx_descriptors, mut rx_buffer, rx_descriptors) = dma_buffers!(16000, 16000); + let (mut rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(16000, 16000); let i2s = I2s::new( peripherals.I2S0, @@ -59,23 +71,24 @@ mod tests { DataFormat::Data16Channel16, 16000.Hz(), dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, rx_descriptors, - &clocks, + tx_descriptors, ); + let (dout, din) = hil_test::common_test_pins!(io); + let mut i2s_tx = i2s .i2s_tx .with_bclk(unsafe { io.pins.gpio0.clone_unchecked() }) .with_ws(unsafe { io.pins.gpio1.clone_unchecked() }) - .with_dout(unsafe { io.pins.gpio2.clone_unchecked() }) + .with_dout(dout) .build(); let mut i2s_rx = i2s .i2s_rx .with_bclk(io.pins.gpio0) .with_ws(io.pins.gpio1) - .with_din(io.pins.gpio3) + .with_din(din) .build(); // enable loopback testing @@ -92,13 +105,9 @@ mod tests { i2s.rx_conf().modify(|_, w| w.rx_update().set_bit()); } - let mut iteration = 0; - let mut failed = false; - let mut check_i: u8 = 0; - let mut i = 0; + let mut samples = SampleSource::new(); for b in tx_buffer.iter_mut() { - *b = i; - i = (i + ADD) % CUT_OFF; + *b = samples.next().unwrap(); } let mut rcv = [0u8; 11000]; @@ -113,14 +122,16 @@ mod tests { let mut tx_transfer = i2s_tx.write_dma_circular(&tx_buffer).unwrap(); - 'outer: loop { + let mut iteration = 0; + let mut sample_idx = 0; + let mut check_samples = SampleSource::new(); + loop { let tx_avail = tx_transfer.available(); // make sure there are more than one descriptor buffers ready to push if tx_avail > 5000 { for b in &mut filler[0..tx_avail].iter_mut() { - *b = i; - i = (i + ADD) % CUT_OFF; + *b = samples.next().unwrap(); } tx_transfer.push(&filler[0..tx_avail]).unwrap(); } @@ -147,11 +158,13 @@ mod tests { assert!(len > 0); for &b in &rcv[..len] { - if b != check_i { - failed = true; - break 'outer; - } - check_i = (check_i + ADD) % CUT_OFF; + let expected = check_samples.next().unwrap(); + assert_eq!( + b, expected, + "Sample #{} does not match ({} != {})", + sample_idx, b, expected + ); + sample_idx += 1; } iteration += 1; @@ -167,7 +180,5 @@ mod tests { break; } } - - assert!(!failed); } } diff --git a/hil-test/tests/i2s_async.rs b/hil-test/tests/i2s_async.rs index bc6b816cec1..a37f9ba236b 100644 --- a/hil-test/tests/i2s_async.rs +++ b/hil-test/tests/i2s_async.rs @@ -1,49 +1,80 @@ //! I2S Loopback Test (Async) //! //! It's assumed GPIO2 is connected to GPIO3 +//! (GPIO9 and GPIO10 for esp32s3) //! //! This test uses I2S TX to transmit known data to I2S RX (forced to slave mode //! with loopback mode enabled). It's using circular DMA mode //% CHIPS: esp32c3 esp32c6 esp32s3 esp32h2 +//% FEATURES: generic-queue #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, - dma::{Dma, DmaChannel0, DmaPriority}, + dma::{Dma, DmaPriority}, gpio::Io, - i2s::{asynch::*, DataFormat, I2s, Standard}, + i2s::{asynch::*, DataFormat, I2s, I2sTx, Standard}, peripheral::Peripheral, - peripherals::Peripherals, + peripherals::I2S0, prelude::*, - system::SystemControl, + Async, }; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} -// choose values which DON'T restart on every descriptor buffer's start -const ADD: u8 = 5; -const CUT_OFF: u8 = 113; +const BUFFER_SIZE: usize = 2000; -#[embassy_executor::task] -async fn writer( +#[derive(Clone)] +struct SampleSource { i: u8, - mut transfer: I2sWriteDmaTransferAsync< - 'static, - esp_hal::peripherals::I2S0, - DmaChannel0, - &'static mut [u8; 2000], - >, -) { - let mut i = i; +} + +impl SampleSource { + // choose values which DON'T restart on every descriptor buffer's start + const ADD: u8 = 5; + const CUT_OFF: u8 = 113; + + fn new() -> Self { + Self { i: 0 } + } +} + +impl Iterator for SampleSource { + type Item = u8; + + fn next(&mut self) -> Option { + let i = self.i; + self.i = (i + Self::ADD) % Self::CUT_OFF; + Some(i) + } +} + +#[embassy_executor::task] +async fn writer(tx_buffer: &'static mut [u8], i2s_tx: I2sTx<'static, I2S0, DmaChannel0, Async>) { + let mut samples = SampleSource::new(); + for b in tx_buffer.iter_mut() { + *b = samples.next().unwrap(); + } + + let mut tx_transfer = i2s_tx.write_dma_circular_async(tx_buffer).unwrap(); + loop { - transfer + tx_transfer .push_with(|buffer| { for b in buffer.iter_mut() { - *b = i; - i = (i + ADD) % CUT_OFF; + *b = samples.next().unwrap(); } buffer.len() }) @@ -55,27 +86,33 @@ async fn writer( #[cfg(test)] #[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] mod tests { - use super::*; + // defmt::* is load-bearing, it ensures that the assert in dma_buffers! is not + // using defmt's non-const assert. Doing so would result in a compile error. + #[allow(unused_imports)] + use defmt::{assert_eq, *}; - #[init] - async fn init() {} + use super::*; #[test] async fn test_i2s_loopback() { let spawner = embassy_executor::Spawner::for_current_executor().await; - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let mut io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; - #[allow(non_upper_case_globals)] - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = - esp_hal::dma_circular_buffers!(2000, 2000); + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } + + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = + esp_hal::dma_circular_buffers!(BUFFER_SIZE, BUFFER_SIZE); let i2s = I2s::new( peripherals.I2S0, @@ -83,23 +120,24 @@ mod tests { DataFormat::Data16Channel16, 16000.Hz(), dma_channel.configure_for_async(false, DmaPriority::Priority0), - tx_descriptors, rx_descriptors, - &clocks, + tx_descriptors, ); + let (dout, din) = hil_test::common_test_pins!(io); + let i2s_tx = i2s .i2s_tx .with_bclk(unsafe { io.pins.gpio0.clone_unchecked() }) .with_ws(unsafe { io.pins.gpio1.clone_unchecked() }) - .with_dout(unsafe { io.pins.gpio2.clone_unchecked() }) + .with_dout(dout) .build(); let i2s_rx = i2s .i2s_rx .with_bclk(io.pins.gpio0) .with_ws(io.pins.gpio1) - .with_din(io.pins.gpio3) + .with_din(din) .build(); // enable loopback testing @@ -116,38 +154,23 @@ mod tests { i2s.rx_conf().modify(|_, w| w.rx_update().set_bit()); } - let mut iteration = 0; - let mut failed = false; - let mut check_i: u8 = 0; - let mut i = 0; - for b in tx_buffer.iter_mut() { - *b = i; - i = (i + ADD) % CUT_OFF; - } - - let mut rcv = [0u8; 2000]; - let mut rx_transfer = i2s_rx.read_dma_circular_async(rx_buffer).unwrap(); - let tx_transfer = i2s_tx.write_dma_circular_async(tx_buffer).unwrap(); - - spawner.must_spawn(writer(i, tx_transfer)); + spawner.must_spawn(writer(tx_buffer, i2s_tx)); - 'outer: loop { + let mut rcv = [0u8; BUFFER_SIZE]; + let mut sample_idx = 0; + let mut samples = SampleSource::new(); + for _ in 0..30 { let len = rx_transfer.pop(&mut rcv).await.unwrap(); for &b in &rcv[..len] { - if b != check_i { - failed = true; - break 'outer; - } - check_i = (check_i + ADD) % CUT_OFF; - } - iteration += 1; - - if iteration > 30 { - break; + let expected = samples.next().unwrap(); + assert_eq!( + b, expected, + "Sample #{} does not match ({} != {})", + sample_idx, b, expected + ); + sample_idx += 1; } } - - assert!(!failed); } } diff --git a/hil-test/tests/interrupt.rs b/hil-test/tests/interrupt.rs index 3120a9d7cc7..7388bf75ccb 100644 --- a/hil-test/tests/interrupt.rs +++ b/hil-test/tests/interrupt.rs @@ -10,15 +10,17 @@ use core::{arch::asm, cell::RefCell}; use critical_section::Mutex; -use defmt::info; -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, - interrupt::{self, *}, - peripherals::{Interrupt, Peripherals}, - system::{SoftwareInterrupt, SystemControl}, + interrupt::{ + self, + software::{SoftwareInterrupt, SoftwareInterruptControl}, + CpuInterrupt, + Priority, + }, + peripherals::Interrupt, + prelude::*, }; +use hil_test as _; static SWINT0: Mutex>>> = Mutex::new(RefCell::new(None)); @@ -27,41 +29,6 @@ struct Context { sw0_trigger_addr: u32, } -impl Context { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - - cfg_if::cfg_if! { - if #[cfg(any(feature = "esp32c6", feature = "esp32h2"))] { - let cpu_intr = &peripherals.INTPRI; - } else { - let cpu_intr = &peripherals.SYSTEM; - } - } - - let sw0_trigger_addr = cpu_intr.cpu_intr_from_cpu_0() as *const _ as u32; - - let system = SystemControl::new(peripherals.SYSTEM); - let _clocks = ClockControl::max(system.clock_control).freeze(); - - let sw_int = system.software_interrupt_control; - - critical_section::with(|cs| { - SWINT0 - .borrow_ref_mut(cs) - .replace(sw_int.software_interrupt0) - }); - interrupt::enable_direct( - Interrupt::FROM_CPU_INTR0, - Priority::Priority3, - CpuInterrupt::Interrupt20, - ) - .unwrap(); - - Context { sw0_trigger_addr } - } -} - #[no_mangle] fn interrupt20() { unsafe { asm!("csrrwi x0, 0x7e1, 0 #disable timer") } @@ -79,7 +46,7 @@ fn interrupt20() { x = inout(reg) perf_counter, ) }; - info!("Performance counter:{}", perf_counter); + defmt::info!("Performance counter:{}", perf_counter); // TODO these values should be adjusted to catch smaller regressions cfg_if::cfg_if! { if #[cfg(any(feature = "esp32c3", feature = "esp32c2"))] { @@ -97,7 +64,36 @@ mod tests { #[init] fn init() -> Context { - Context::init() + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); + let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); + + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32c6", feature = "esp32h2"))] { + let cpu_intr = &peripherals.INTPRI; + } else { + let cpu_intr = &peripherals.SYSTEM; + } + } + + let sw0_trigger_addr = cpu_intr.cpu_intr_from_cpu_0() as *const _ as u32; + + critical_section::with(|cs| { + SWINT0 + .borrow_ref_mut(cs) + .replace(sw_ints.software_interrupt0) + }); + interrupt::enable_direct( + Interrupt::FROM_CPU_INTR0, + Priority::Priority3, + CpuInterrupt::Interrupt20, + ) + .unwrap(); + + Context { sw0_trigger_addr } } #[test] diff --git a/hil-test/tests/lcd_cam_i8080.rs b/hil-test/tests/lcd_cam_i8080.rs index ea8ab112385..131c159ddc0 100644 --- a/hil-test/tests/lcd_cam_i8080.rs +++ b/hil-test/tests/lcd_cam_i8080.rs @@ -5,63 +5,46 @@ #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - clock::{ClockControl, Clocks}, dma::{Dma, DmaDescriptor, DmaPriority}, dma_buffers, - gpio::dummy_pin::DummyPin, + gpio::DummyPin, lcd_cam::{ - lcd::{ - i8080, - i8080::{Command, TxEightBits, I8080}, - }, + lcd::i8080::{Command, Config, TxEightBits, I8080}, LcdCam, }, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; +use hil_test as _; const DATA_SIZE: usize = 1024 * 10; struct Context<'d> { lcd_cam: LcdCam<'d, esp_hal::Blocking>, - clocks: Clocks<'d>, dma: Dma<'d>, tx_buffer: &'static [u8], tx_descriptors: &'static mut [DmaDescriptor], } -impl<'d> Context<'d> { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use super::*; + + #[init] + fn init() -> Context<'static> { + let peripherals = esp_hal::init(esp_hal::Config::default()); let dma = Dma::new(peripherals.DMA); let lcd_cam = LcdCam::new(peripherals.LCD_CAM); - let (tx_buffer, tx_descriptors, _, _) = dma_buffers!(DATA_SIZE, 0); + let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(DATA_SIZE, 0); - Self { + Context { lcd_cam, - clocks, dma, tx_buffer, tx_descriptors, } } -} - -#[cfg(test)] -#[embedded_test::tests] -mod tests { - use super::*; - - #[init] - fn init() -> Context<'static> { - Context::init() - } #[test] fn test_i8080_8bit(ctx: Context<'static>) { @@ -84,8 +67,7 @@ mod tests { ctx.tx_descriptors, pins, 20.MHz(), - i8080::Config::default(), - &ctx.clocks, + Config::default(), ); let xfer = i8080 @@ -117,8 +99,7 @@ mod tests { ctx.tx_descriptors, pins, 20.MHz(), - i8080::Config::default(), - &ctx.clocks, + Config::default(), ); let xfer = i8080 diff --git a/hil-test/tests/lcd_cam_i8080_async.rs b/hil-test/tests/lcd_cam_i8080_async.rs index 4d8432660d2..29f2e74aaf5 100644 --- a/hil-test/tests/lcd_cam_i8080_async.rs +++ b/hil-test/tests/lcd_cam_i8080_async.rs @@ -1,67 +1,52 @@ //! lcd_cam i8080 tests //% CHIPS: esp32s3 +//% FEATURES: generic-queue #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - clock::{ClockControl, Clocks}, dma::{Dma, DmaDescriptor, DmaPriority}, dma_buffers, - gpio::dummy_pin::DummyPin, + gpio::DummyPin, lcd_cam::{ - lcd::{ - i8080, - i8080::{Command, TxEightBits, I8080}, - }, + lcd::i8080::{Command, Config, TxEightBits, I8080}, LcdCam, }, - peripherals::Peripherals, prelude::*, - system::SystemControl, }; +use hil_test as _; const DATA_SIZE: usize = 1024 * 10; struct Context<'d> { lcd_cam: LcdCam<'d, esp_hal::Async>, - clocks: Clocks<'d>, dma: Dma<'d>, tx_buffer: &'static [u8], tx_descriptors: &'static mut [DmaDescriptor], } -impl<'d> Context<'d> { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod tests { + use super::*; + + #[init] + async fn init() -> Context<'static> { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let dma = Dma::new(peripherals.DMA); let lcd_cam = LcdCam::new_async(peripherals.LCD_CAM); - let (tx_buffer, tx_descriptors, _, _) = dma_buffers!(DATA_SIZE, 0); + let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(DATA_SIZE, 0); - Self { + Context { lcd_cam, - clocks, dma, tx_buffer, tx_descriptors, } } -} - -#[cfg(test)] -#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] -mod tests { - use super::*; - - #[init] - async fn init() -> Context<'static> { - Context::init() - } #[test] async fn test_i8080_8bit(ctx: Context<'static>) { @@ -83,8 +68,7 @@ mod tests { ctx.tx_descriptors, pins, 20.MHz(), - i8080::Config::default(), - &ctx.clocks, + Config::default(), ); i8080 @@ -116,8 +100,7 @@ mod tests { ctx.tx_descriptors, pins, 20.MHz(), - i8080::Config::default(), - &ctx.clocks, + Config::default(), ); i8080 diff --git a/hil-test/tests/pcnt.rs b/hil-test/tests/pcnt.rs index 9cb9da7ec4a..422991640d3 100644 --- a/hil-test/tests/pcnt.rs +++ b/hil-test/tests/pcnt.rs @@ -1,50 +1,52 @@ //! PCNT tests //! //! It's assumed GPIO2 is connected to GPIO3 +//! (GPIO9 and GPIO10 for esp32s2 and esp32s3) +//! (GPIO26 and GPIO27 for esp32) //% CHIPS: esp32 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; -use esp_hal::{delay::Delay, gpio::GpioPin, pcnt::Pcnt}; +use esp_hal::{ + delay::Delay, + gpio::{AnyPin, Io, Level, Output, Pull}, + pcnt::{ + channel::{EdgeMode, PcntInputConfig, PcntSource}, + Pcnt, + }, +}; +use hil_test as _; struct Context<'d> { pcnt: Pcnt<'d>, - gpio2: GpioPin<2>, - gpio3: GpioPin<3>, + input: AnyPin<'d>, + output: AnyPin<'d>, delay: Delay, } #[cfg(test)] #[embedded_test::tests] mod tests { - use esp_hal::{ - clock::ClockControl, - delay::Delay, - gpio::{Io, Level, Output, Pull}, - pcnt::channel::{EdgeMode, PcntInputConfig, PcntSource}, - peripherals::Peripherals, - system::SystemControl, - }; - use super::*; #[init] fn init() -> Context<'static> { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let (din, dout) = hil_test::common_test_pins!(io); + + let din = AnyPin::new(din); + let dout = AnyPin::new(dout); + Context { pcnt: Pcnt::new(peripherals.PCNT), - gpio2: io.pins.gpio2, - gpio3: io.pins.gpio3, - delay: Delay::new(&clocks), + input: din, + output: dout, + delay: Delay::new(), } } @@ -54,13 +56,13 @@ mod tests { // Setup channel 0 to increment the count when gpio2 does LOW -> HIGH unit.channel0.set_edge_signal(PcntSource::from_pin( - ctx.gpio2, + ctx.input, PcntInputConfig { pull: Pull::Down }, )); unit.channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - let mut output = Output::new(ctx.gpio3, Level::Low); + let mut output = Output::new(ctx.output, Level::Low); unit.resume(); @@ -93,13 +95,13 @@ mod tests { // Setup channel 0 to increment the count when gpio2 does LOW -> HIGH unit.channel0.set_edge_signal(PcntSource::from_pin( - ctx.gpio2, + ctx.input, PcntInputConfig { pull: Pull::Up }, )); unit.channel0 .set_input_mode(EdgeMode::Increment, EdgeMode::Hold); - let mut output = Output::new(ctx.gpio3, Level::High); + let mut output = Output::new(ctx.output, Level::High); unit.resume(); @@ -134,13 +136,13 @@ mod tests { // Setup channel 0 to increment the count when gpio2 does LOW -> HIGH unit.channel0.set_edge_signal(PcntSource::from_pin( - ctx.gpio2, + ctx.input, PcntInputConfig { pull: Pull::Up }, )); unit.channel0 .set_input_mode(EdgeMode::Increment, EdgeMode::Hold); - let mut output = Output::new(ctx.gpio3, Level::High); + let mut output = Output::new(ctx.output, Level::High); unit.resume(); @@ -197,13 +199,13 @@ mod tests { // Setup channel 0 to increment the count when gpio2 does LOW -> HIGH unit.channel0.set_edge_signal(PcntSource::from_pin( - ctx.gpio2, + ctx.input, PcntInputConfig { pull: Pull::Up }, )); unit.channel0 .set_input_mode(EdgeMode::Increment, EdgeMode::Hold); - let mut output = Output::new(ctx.gpio3, Level::High); + let mut output = Output::new(ctx.output, Level::High); unit.resume(); @@ -264,13 +266,13 @@ mod tests { // Setup channel 0 to decrement the count when gpio2 does LOW -> HIGH unit.channel0.set_edge_signal(PcntSource::from_pin( - ctx.gpio2, + ctx.input, PcntInputConfig { pull: Pull::Up }, )); unit.channel0 .set_input_mode(EdgeMode::Decrement, EdgeMode::Hold); - let mut output = Output::new(ctx.gpio3, Level::High); + let mut output = Output::new(ctx.output, Level::High); unit.resume(); @@ -322,13 +324,13 @@ mod tests { // Setup channel 1 to increment the count when gpio2 does LOW -> HIGH unit.channel1.set_edge_signal(PcntSource::from_pin( - ctx.gpio2, + ctx.input, PcntInputConfig { pull: Pull::Up }, )); unit.channel1 .set_input_mode(EdgeMode::Increment, EdgeMode::Hold); - let mut output = Output::new(ctx.gpio3, Level::High); + let mut output = Output::new(ctx.output, Level::High); unit.resume(); diff --git a/hil-test/tests/qspi_read.rs b/hil-test/tests/qspi_read.rs new file mode 100644 index 00000000000..211fc8c1830 --- /dev/null +++ b/hil-test/tests/qspi_read.rs @@ -0,0 +1,200 @@ +//! QSPI Read Test +//! +//! Following pins are used: +//! MISO GPIO2 / GPIO9 (esp32s2 and esp32s3) +//! +//! GPIO GPIO3 / GPIO10 (esp32s2 and esp32s3) +//! +//! Connect MISO and GPIO pins. + +//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 + +#![no_std] +#![no_main] + +use esp_hal::{ + dma::{Channel, Dma, DmaPriority, DmaRxBuf}, + dma_buffers, + gpio::{ErasedPin, Io, Level, Output}, + prelude::*, + spi::{ + master::{Address, Command, Spi, SpiDma}, + HalfDuplexMode, + SpiDataMode, + SpiMode, + }, + Blocking, +}; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +struct Context { + spi: esp_hal::peripherals::SPI2, + dma_channel: Channel<'static, DmaChannel0, Blocking>, + miso: ErasedPin, + miso_mirror: Output<'static>, +} + +fn execute( + mut spi: SpiDma<'static, esp_hal::peripherals::SPI2, DmaChannel0, HalfDuplexMode, Blocking>, + mut miso_mirror: Output<'static>, + wanted: u8, +) { + const DMA_BUFFER_SIZE: usize = 4; + + let (_, _, buffer, descriptors) = dma_buffers!(0, DMA_BUFFER_SIZE); + let mut dma_rx_buf = DmaRxBuf::new(descriptors, buffer).unwrap(); + + miso_mirror.set_low(); + + let transfer = spi + .read( + SpiDataMode::Quad, + Command::None, + Address::None, + 0, + dma_rx_buf, + ) + .map_err(|e| e.0) + .unwrap(); + (spi, dma_rx_buf) = transfer.wait(); + + assert_eq!(dma_rx_buf.as_slice(), &[wanted; DMA_BUFFER_SIZE]); + + // SPI should read all '1's + miso_mirror.set_high(); + + let transfer = spi + .read( + SpiDataMode::Quad, + Command::None, + Address::None, + 0, + dma_rx_buf, + ) + .map_err(|e| e.0) + .unwrap(); + + (_, dma_rx_buf) = transfer.wait(); + + assert_eq!(dma_rx_buf.as_slice(), &[0xFF; DMA_BUFFER_SIZE]); +} + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let (miso, miso_mirror) = hil_test::common_test_pins!(io); + + let miso = miso.degrade(); + let miso_mirror = Output::new(miso_mirror, Level::High); + + let dma = Dma::new(peripherals.DMA); + + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } + + let dma_channel = dma_channel.configure(false, DmaPriority::Priority0); + + Context { + spi: peripherals.SPI2, + dma_channel, + miso, + miso_mirror, + } + } + + #[test] + #[timeout(3)] + fn test_spi_reads_correctly_from_gpio_pin_0(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + Some(ctx.miso), + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + // SPI should read '0b11101110' + super::execute(spi, ctx.miso_mirror, 238); + } + + #[test] + #[timeout(3)] + fn test_spi_reads_correctly_from_gpio_pin_1(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + Some(ctx.miso), + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + // SPI should read '0b11011101' + super::execute(spi, ctx.miso_mirror, 221); + } + + #[test] + #[timeout(3)] + fn test_spi_reads_correctly_from_gpio_pin_2(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + Some(ctx.miso), + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + // SPI should read '0b10111011' + super::execute(spi, ctx.miso_mirror, 187); + } + + #[test] + #[timeout(3)] + fn test_spi_reads_correctly_from_gpio_pin_3(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + Some(ctx.miso), + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + // SPI should read '0b01110111' + super::execute(spi, ctx.miso_mirror, 119); + } +} diff --git a/hil-test/tests/qspi_write.rs b/hil-test/tests/qspi_write.rs new file mode 100644 index 00000000000..45ab25392e3 --- /dev/null +++ b/hil-test/tests/qspi_write.rs @@ -0,0 +1,248 @@ +//! QSPI Write Test +//! +//! This uses PCNT to count the edges of the MOSI signal +//! +//! Following pins are used: +//! MOSI GPIO2 / GPIO9 (esp32s2 and esp32s3) +//! +//! PCNT GPIO3 / GPIO10 (esp32s2 and esp32s3) +//! +//! Connect MOSI and PCNT pins. + +//% CHIPS: esp32c6 esp32h2 esp32s2 esp32s3 + +#![no_std] +#![no_main] + +use esp_hal::{ + dma::{Channel, Dma, DmaPriority, DmaTxBuf}, + dma_buffers, + gpio::{AnyPin, Io, Pull}, + pcnt::{ + channel::{EdgeMode, PcntInputConfig, PcntSource}, + unit::Unit, + Pcnt, + }, + prelude::*, + spi::{ + master::{Address, Command, Spi, SpiDma}, + HalfDuplexMode, + SpiDataMode, + SpiMode, + }, + Blocking, +}; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +struct Context { + spi: esp_hal::peripherals::SPI2, + pcnt: esp_hal::peripherals::PCNT, + dma_channel: Channel<'static, DmaChannel0, Blocking>, + mosi: AnyPin<'static>, + mosi_mirror: AnyPin<'static>, +} + +fn execute( + unit: Unit<'static, 0>, + mut spi: SpiDma<'static, esp_hal::peripherals::SPI2, DmaChannel0, HalfDuplexMode, Blocking>, + write: u8, +) { + const DMA_BUFFER_SIZE: usize = 4; + + let (_, _, buffer, descriptors) = dma_buffers!(0, DMA_BUFFER_SIZE); + let mut dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap(); + + dma_tx_buf.fill(&[write; DMA_BUFFER_SIZE]); + + let transfer = spi + .write( + SpiDataMode::Quad, + Command::Command8(write as u16, SpiDataMode::Quad), + Address::Address24( + write as u32 | (write as u32) << 8 | (write as u32) << 16, + SpiDataMode::Quad, + ), + 0, + dma_tx_buf, + ) + .map_err(|e| e.0) + .unwrap(); + (spi, dma_tx_buf) = transfer.wait(); + + assert_eq!(unit.get_value(), 8); + + dma_tx_buf.set_length(0); + let transfer = spi + .write( + SpiDataMode::Quad, + Command::Command8(write as u16, SpiDataMode::Quad), + Address::Address24( + write as u32 | (write as u32) << 8 | (write as u32) << 16, + SpiDataMode::Quad, + ), + 0, + dma_tx_buf, + ) + .map_err(|e| e.0) + .unwrap(); + _ = transfer.wait(); + + assert_eq!(unit.get_value(), 8 + 4); +} + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let (mosi, mosi_mirror) = hil_test::common_test_pins!(io); + + let mosi = AnyPin::new(mosi); + let mosi_mirror = AnyPin::new(mosi_mirror); + + let dma = Dma::new(peripherals.DMA); + + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } + + let dma_channel = dma_channel.configure(false, DmaPriority::Priority0); + + Context { + spi: peripherals.SPI2, + pcnt: peripherals.PCNT, + dma_channel, + mosi, + mosi_mirror, + } + } + + #[test] + #[timeout(3)] + fn test_spi_writes_correctly_to_pin_0(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + Some(ctx.mosi), + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + let pcnt = Pcnt::new(ctx.pcnt); + let unit = pcnt.unit0; + + unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::None }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + super::execute(unit, spi, 0b0000_0001); + } + + #[test] + #[timeout(3)] + fn test_spi_writes_correctly_to_pin_1(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + Some(ctx.mosi), + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + let pcnt = Pcnt::new(ctx.pcnt); + let unit = pcnt.unit0; + + unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::None }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + super::execute(unit, spi, 0b0000_0010); + } + + #[test] + #[timeout(3)] + fn test_spi_writes_correctly_to_pin_2(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + Some(ctx.mosi), + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + let pcnt = Pcnt::new(ctx.pcnt); + let unit = pcnt.unit0; + + unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::None }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + super::execute(unit, spi, 0b0000_0100); + } + + #[test] + #[timeout(3)] + fn test_spi_writes_correctly_to_pin_3(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + Some(ctx.mosi), + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + let pcnt = Pcnt::new(ctx.pcnt); + let unit = pcnt.unit0; + + unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::None }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + super::execute(unit, spi, 0b0000_1000); + } +} diff --git a/hil-test/tests/qspi_write_read.rs b/hil-test/tests/qspi_write_read.rs new file mode 100644 index 00000000000..b2b362d832b --- /dev/null +++ b/hil-test/tests/qspi_write_read.rs @@ -0,0 +1,198 @@ +//! QSPI Write + Read Test +//! +//! Make sure issue #1860 doesn't affect us +//! +//! Following pins are used: +//! MOSI/MISO GPIO2 / GPIO9 (esp32s2 and esp32s3) +//! +//! GPIO GPIO3 / GPIO10 (esp32s2 and esp32s3) +//! +//! Connect MOSI/MISO and GPIO pins. + +//% CHIPS: esp32c6 esp32h2 esp32s2 esp32s3 + +#![no_std] +#![no_main] + +use esp_hal::{ + dma::{Channel, Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, + dma_buffers, + gpio::{ErasedPin, Io, Level, Output}, + prelude::*, + spi::{ + master::{Address, Command, Spi, SpiDma}, + HalfDuplexMode, + SpiDataMode, + SpiMode, + }, + Blocking, +}; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +struct Context { + spi: esp_hal::peripherals::SPI2, + dma_channel: Channel<'static, DmaChannel0, Blocking>, + mosi: ErasedPin, + mosi_mirror: Output<'static>, +} + +fn execute( + mut spi: SpiDma<'static, esp_hal::peripherals::SPI2, DmaChannel0, HalfDuplexMode, Blocking>, + mut mosi_mirror: Output<'static>, + wanted: u8, +) { + const DMA_BUFFER_SIZE: usize = 4; + + let (rx_buffer, rx_descriptors, buffer, descriptors) = + dma_buffers!(DMA_BUFFER_SIZE, DMA_BUFFER_SIZE); + let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let mut dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap(); + + dma_tx_buf.fill(&[0xff; DMA_BUFFER_SIZE]); + + let transfer = spi + .write( + SpiDataMode::Quad, + Command::Command8(wanted as u16, SpiDataMode::Quad), + Address::Address24( + wanted as u32 | (wanted as u32) << 8 | (wanted as u32) << 16, + SpiDataMode::Quad, + ), + 0, + dma_tx_buf, + ) + .map_err(|e| e.0) + .unwrap(); + (spi, _) = transfer.wait(); + + mosi_mirror.set_low(); + + let transfer = spi + .read( + SpiDataMode::Quad, + Command::None, + Address::None, + 0, + dma_rx_buf, + ) + .map_err(|e| e.0) + .unwrap(); + (_, dma_rx_buf) = transfer.wait(); + assert_eq!(dma_rx_buf.as_slice(), &[wanted; DMA_BUFFER_SIZE]); +} + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let (mosi, mosi_mirror) = hil_test::common_test_pins!(io); + + let mosi = mosi.degrade(); + let mosi_mirror = Output::new(mosi_mirror, Level::High); + + let dma = Dma::new(peripherals.DMA); + + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } + + let dma_channel = dma_channel.configure(false, DmaPriority::Priority0); + + Context { + spi: peripherals.SPI2, + dma_channel, + mosi, + mosi_mirror, + } + } + + #[test] + #[timeout(3)] + fn test_spi_writes_correctly_to_pin_0(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + Some(ctx.mosi), + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + super::execute(spi, ctx.mosi_mirror, !0b0001_0001); + } + + #[test] + #[timeout(3)] + fn test_spi_writes_correctly_to_pin_1(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + Some(ctx.mosi), + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + super::execute(spi, ctx.mosi_mirror, !0b0010_0010); + } + + #[test] + #[timeout(3)] + fn test_spi_writes_correctly_to_pin_2(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + Some(ctx.mosi), + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + super::execute(spi, ctx.mosi_mirror, !0b0100_0100); + } + + #[test] + #[timeout(3)] + fn test_spi_writes_correctly_to_pin_3(ctx: Context) { + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins( + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + esp_hal::gpio::NO_PIN, + Some(ctx.mosi), + esp_hal::gpio::NO_PIN, + ) + .with_dma(ctx.dma_channel); + + super::execute(spi, ctx.mosi_mirror, !0b1000_1000); + } +} diff --git a/hil-test/tests/rmt.rs b/hil-test/tests/rmt.rs index d402fddad9f..2734e355bde 100644 --- a/hil-test/tests/rmt.rs +++ b/hil-test/tests/rmt.rs @@ -1,22 +1,20 @@ //! RMT Loopback Test //! //! It's assumed GPIO2 is connected to GPIO3 +//! (GPIO9 and GPIO10 for esp32s2 and esp32s3) +//! (GPIO26 and GPIO27 for esp32) //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, - peripherals::Peripherals, prelude::*, rmt::{PulseCode, Rmt, RxChannel, RxChannelConfig, TxChannel, TxChannelConfig}, - system::SystemControl, }; +use hil_test as _; #[cfg(test)] #[embedded_test::tests] @@ -29,9 +27,7 @@ mod tests { #[test] #[timeout(1)] fn rmt_loopback() { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -43,7 +39,9 @@ mod tests { } }; - let rmt = Rmt::new(peripherals.RMT, freq, &clocks).unwrap(); + let rmt = Rmt::new(peripherals.RMT, freq).unwrap(); + + let (rx, tx) = hil_test::common_test_pins!(io); let tx_config = TxChannelConfig { clk_divider: 255, @@ -52,7 +50,7 @@ mod tests { let tx_channel = { use esp_hal::rmt::TxChannelCreator; - rmt.channel0.configure(io.pins.gpio2, tx_config).unwrap() + rmt.channel0.configure(tx, tx_config).unwrap() }; let rx_config = RxChannelConfig { @@ -62,20 +60,25 @@ mod tests { }; cfg_if::cfg_if! { - if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + if #[cfg(feature = "esp32")] { let rx_channel = { use esp_hal::rmt::RxChannelCreator; - rmt.channel1.configure(io.pins.gpio3, rx_config).unwrap() + rmt.channel1.configure(rx, rx_config).unwrap() + }; + } else if #[cfg(feature = "esp32s2")] { + let rx_channel = { + use esp_hal::rmt::RxChannelCreator; + rmt.channel1.configure(rx, rx_config).unwrap() }; } else if #[cfg(feature = "esp32s3")] { let rx_channel = { use esp_hal::rmt::RxChannelCreator; - rmt.channel7.configure(io.pins.gpio3, rx_config).unwrap() + rmt.channel7.configure(rx, rx_config).unwrap() }; } else { let rx_channel = { use esp_hal::rmt::RxChannelCreator; - rmt.channel2.configure(io.pins.gpio3, rx_config).unwrap() + rmt.channel2.configure(rx, rx_config).unwrap() }; } } @@ -104,11 +107,12 @@ mod tests { let rx_transaction = rx_channel.receive(&mut rcv_data).unwrap(); let tx_transaction = tx_channel.transmit(&tx_data); - tx_transaction.wait().unwrap(); + rx_transaction.wait().unwrap(); + tx_transaction.wait().unwrap(); - // the last two pulse-codes are the ones which wait for the timeout so they - // can't be equal + // the last two pulse-codes are the ones which wait for the timeout so + // they can't be equal assert_eq!(&tx_data[..18], &rcv_data[..18]); } } diff --git a/hil-test/tests/rsa.rs b/hil-test/tests/rsa.rs index a294c46c80d..6651caea2ca 100644 --- a/hil-test/tests/rsa.rs +++ b/hil-test/tests/rsa.rs @@ -6,13 +6,10 @@ #![no_main] use crypto_bigint::{Uint, U1024, U512}; -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - peripherals::Peripherals, prelude::*, rsa::{ - operand_sizes, + operand_sizes::*, Rsa, RsaModularExponentiation, RsaModularMultiplication, @@ -20,6 +17,8 @@ use esp_hal::{ }, Blocking, }; +use hil_test as _; + const BIGNUM_1: U512 = Uint::from_be_hex( "c7f61058f96db3bd87dbab08ab03b4f7f2f864eac249144adea6a65f97803b719d8ca980b7b3c0389c1c7c6\ 7dc353c5e0ec11f5fc8ce7f6073796cc8f73fa878", @@ -37,16 +36,6 @@ struct Context<'a> { rsa: Rsa<'a, Blocking>, } -impl Context<'_> { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let mut rsa = Rsa::new(peripherals.RSA); - nb::block!(rsa.ready()).unwrap(); - - Context { rsa } - } -} - const fn compute_r(modulus: &U512) -> U512 { let mut d = [0_u32; U512::LIMBS * 2 + 1]; d[d.len() - 1] = 1; @@ -68,10 +57,15 @@ mod tests { #[init] fn init() -> Context<'static> { - Context::init() + let peripherals = esp_hal::init(esp_hal::Config::default()); + let mut rsa = Rsa::new(peripherals.RSA); + nb::block!(rsa.ready()).unwrap(); + + Context { rsa } } #[test] + #[timeout(5)] fn test_modular_exponentiation(mut ctx: Context<'static>) { const EXPECTED_OUTPUT: [u32; U512::LIMBS] = [ 1601059419, 3994655875, 2600857657, 1530060852, 64828275, 4221878473, 2751381085, @@ -85,20 +79,20 @@ mod tests { ctx.rsa.enable_disable_search_acceleration(true); } let mut outbuf = [0_u32; U512::LIMBS]; - let mut mod_exp = RsaModularExponentiation::::new( + let mut mod_exp = RsaModularExponentiation::::new( &mut ctx.rsa, BIGNUM_2.as_words(), BIGNUM_3.as_words(), compute_mprime(&BIGNUM_3), ); let r = compute_r(&BIGNUM_3); - let base = &BIGNUM_1.as_words(); - mod_exp.start_exponentiation(&base, r.as_words()); + mod_exp.start_exponentiation(BIGNUM_1.as_words(), r.as_words()); mod_exp.read_results(&mut outbuf); assert_eq!(EXPECTED_OUTPUT, outbuf); } #[test] + #[timeout(5)] fn test_modular_multiplication(mut ctx: Context<'static>) { const EXPECTED_OUTPUT: [u32; U512::LIMBS] = [ 1868256644, 833470784, 4187374062, 2684021027, 191862388, 1279046003, 1929899870, @@ -107,31 +101,21 @@ mod tests { ]; let mut outbuf = [0_u32; U512::LIMBS]; - let mut mod_multi = - RsaModularMultiplication::::new( - &mut ctx.rsa, - #[cfg(not(feature = "esp32"))] - BIGNUM_1.as_words(), - #[cfg(not(feature = "esp32"))] - BIGNUM_2.as_words(), - BIGNUM_3.as_words(), - compute_mprime(&BIGNUM_3), - ); let r = compute_r(&BIGNUM_3); - #[cfg(feature = "esp32")] - { - mod_multi.start_step1(BIGNUM_1.as_words(), r.as_words()); - mod_multi.start_step2(BIGNUM_2.as_words()); - } - #[cfg(not(feature = "esp32"))] - { - mod_multi.start_modular_multiplication(r.as_words()); - } + let mut mod_multi = RsaModularMultiplication::::new( + &mut ctx.rsa, + BIGNUM_1.as_words(), + BIGNUM_3.as_words(), + r.as_words(), + compute_mprime(&BIGNUM_3), + ); + mod_multi.start_modular_multiplication(BIGNUM_2.as_words()); mod_multi.read_results(&mut outbuf); assert_eq!(EXPECTED_OUTPUT, outbuf); } #[test] + #[timeout(5)] fn test_multiplication(mut ctx: Context<'static>) { const EXPECTED_OUTPUT: [u32; U1024::LIMBS] = [ 1264702968, 3552243420, 2602501218, 498422249, 2431753435, 2307424767, 349202767, @@ -145,22 +129,10 @@ mod tests { let operand_a = BIGNUM_1.as_words(); let operand_b = BIGNUM_2.as_words(); - #[cfg(feature = "esp32")] - { - let mut rsamulti = - RsaMultiplication::::new(&mut ctx.rsa); - rsamulti.start_multiplication(operand_a, operand_b); - rsamulti.read_results(&mut outbuf); - } - #[cfg(not(feature = "esp32"))] - { - let mut rsamulti = RsaMultiplication::::new( - &mut ctx.rsa, - operand_a, - ); - rsamulti.start_multiplication(operand_b); - rsamulti.read_results(&mut outbuf); - } + let mut rsamulti = RsaMultiplication::::new(&mut ctx.rsa, operand_a); + rsamulti.start_multiplication(operand_b); + rsamulti.read_results(&mut outbuf); + assert_eq!(EXPECTED_OUTPUT, outbuf) } } diff --git a/hil-test/tests/rsa_async.rs b/hil-test/tests/rsa_async.rs new file mode 100644 index 00000000000..45c57a7d08c --- /dev/null +++ b/hil-test/tests/rsa_async.rs @@ -0,0 +1,139 @@ +//! Async RSA Test + +//% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 + +#![no_std] +#![no_main] + +use crypto_bigint::{Uint, U1024, U512}; +use esp_hal::{ + prelude::*, + rsa::{ + operand_sizes::*, + Rsa, + RsaModularExponentiation, + RsaModularMultiplication, + RsaMultiplication, + }, + Async, +}; +use hil_test as _; + +const BIGNUM_1: U512 = Uint::from_be_hex( + "c7f61058f96db3bd87dbab08ab03b4f7f2f864eac249144adea6a65f97803b719d8ca980b7b3c0389c1c7c6\ + 7dc353c5e0ec11f5fc8ce7f6073796cc8f73fa878", +); +const BIGNUM_2: U512 = Uint::from_be_hex( + "1763db3344e97be15d04de4868badb12a38046bb793f7630d87cf100aa1c759afac15a01f3c4c83ec2d2f66\ + 6bd22f71c3c1f075ec0e2cb0cb29994d091b73f51", +); +const BIGNUM_3: U512 = Uint::from_be_hex( + "6b6bb3d2b6cbeb45a769eaa0384e611e1b89b0c9b45a045aca1c5fd6e8785b38df7118cf5dd45b9b63d293b\ + 67aeafa9ba25feb8712f188cb139b7d9b9af1c361", +); + +struct Context<'a> { + rsa: Rsa<'a, Async>, +} + +const fn compute_r(modulus: &U512) -> U512 { + let mut d = [0_u32; U512::LIMBS * 2 + 1]; + d[d.len() - 1] = 1; + let d = Uint::from_words(d); + d.const_rem(&modulus.resize()).0.resize() +} + +const fn compute_mprime(modulus: &U512) -> u32 { + let m_inv = modulus.inv_mod2k(32).to_words()[0]; + (-1 * m_inv as i64 % 4294967296) as u32 +} + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod tests { + use defmt::assert_eq; + + use super::*; + + #[init] + fn init() -> Context<'static> { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let mut rsa = Rsa::new_async(peripherals.RSA); + nb::block!(rsa.ready()).unwrap(); + + Context { rsa } + } + + #[test] + #[timeout(5)] + async fn modular_exponentiation(mut ctx: Context<'static>) { + const EXPECTED_OUTPUT: [u32; U512::LIMBS] = [ + 1601059419, 3994655875, 2600857657, 1530060852, 64828275, 4221878473, 2751381085, + 1938128086, 625895085, 2087010412, 2133352910, 101578249, 3798099415, 3357588690, + 2065243474, 330914193, + ]; + + #[cfg(not(feature = "esp32"))] + { + ctx.rsa.enable_disable_constant_time_acceleration(true); + ctx.rsa.enable_disable_search_acceleration(true); + } + let mut outbuf = [0_u32; U512::LIMBS]; + let mut mod_exp = RsaModularExponentiation::::new( + &mut ctx.rsa, + BIGNUM_2.as_words(), + BIGNUM_3.as_words(), + compute_mprime(&BIGNUM_3), + ); + let r = compute_r(&BIGNUM_3); + mod_exp + .exponentiation(BIGNUM_1.as_words(), r.as_words(), &mut outbuf) + .await; + assert_eq!(EXPECTED_OUTPUT, outbuf); + } + + #[test] + #[timeout(5)] + async fn test_modular_multiplication(mut ctx: Context<'static>) { + const EXPECTED_OUTPUT: [u32; U512::LIMBS] = [ + 1868256644, 833470784, 4187374062, 2684021027, 191862388, 1279046003, 1929899870, + 4209598061, 3830489207, 1317083344, 2666864448, 3701382766, 3232598924, 2904609522, + 747558855, 479377985, + ]; + + let mut outbuf = [0_u32; U512::LIMBS]; + let r = compute_r(&BIGNUM_3); + let mut mod_multi = RsaModularMultiplication::::new( + &mut ctx.rsa, + BIGNUM_1.as_words(), + BIGNUM_3.as_words(), + r.as_words(), + compute_mprime(&BIGNUM_3), + ); + mod_multi + .modular_multiplication(BIGNUM_2.as_words(), &mut outbuf) + .await; + assert_eq!(EXPECTED_OUTPUT, outbuf); + } + + #[test] + #[timeout(5)] + async fn test_multiplication(mut ctx: Context<'static>) { + const EXPECTED_OUTPUT: [u32; U1024::LIMBS] = [ + 1264702968, 3552243420, 2602501218, 498422249, 2431753435, 2307424767, 349202767, + 2269697177, 1525551459, 3623276361, 3146383138, 191420847, 4252021895, 9176459, + 301757643, 4220806186, 434407318, 3722444851, 1850128766, 928651940, 107896699, + 563405838, 1834067613, 1289630401, 3145128058, 3300293535, 3077505758, 1926648662, + 1264151247, 3626086486, 3701894076, 306518743, + ]; + let mut outbuf = [0_u32; U1024::LIMBS]; + + let operand_a = BIGNUM_1.as_words(); + let operand_b = BIGNUM_2.as_words(); + + let mut rsamulti = RsaMultiplication::::new(&mut ctx.rsa, operand_a); + rsamulti.multiplication(operand_b, &mut outbuf).await; + + assert_eq!(EXPECTED_OUTPUT, outbuf) + } +} diff --git a/hil-test/tests/sha.rs b/hil-test/tests/sha.rs index e9e010edbb7..b89afed8ba6 100644 --- a/hil-test/tests/sha.rs +++ b/hil-test/tests/sha.rs @@ -5,181 +5,369 @@ #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; +use digest::{Digest, Update}; +#[cfg(not(feature = "esp32"))] +use esp_hal::sha::Sha224; +#[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] +use esp_hal::sha::{Sha384, Sha512}; +#[cfg(any(feature = "esp32s2", feature = "esp32s3"))] +use esp_hal::sha::{Sha512_224, Sha512_256}; use esp_hal::{ - peripherals::Peripherals, prelude::*, - sha::{Sha, ShaMode}, + rng::Rng, + sha::{Sha, Sha1, Sha256, ShaAlgorithm, ShaDigest}, }; +use hil_test as _; use nb::block; -#[cfg(test)] -#[embedded_test::tests] -mod tests { - use defmt::assert_eq; +/// Dummy data used to feed the hasher. +const SOURCE_DATA: &[u8] = &[b'a'; 258]; - use super::*; +#[track_caller] +fn assert_sw_hash(input: &[u8], expected_output: &[u8]) { + let mut hasher = D::new(); + hasher.update(input); + let soft_result = hasher.finalize(); - #[init] - fn init() {} + defmt::assert_eq!(expected_output, &soft_result[..]); +} - #[test] - fn test_sha_1() { - let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA1); +fn hash_sha(sha: &mut Sha<'static>, mut input: &[u8], output: &mut [u8]) { + let mut digest = sha.start::(); + while !input.is_empty() { + input = block!(digest.update(input)).unwrap(); + } + block!(digest.finish(output)).unwrap(); +} - let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); - let mut remaining = source_data; - let expected_output = [ - 0x57, 0xf5, 0x3e, 0xd5, 0x59, 0x85, 0x24, 0x49, 0x3e, 0xc5, 0x76, 0x77, 0xa, 0xaf, - 0x3b, 0xb1, 0x0, 0x63, 0xe3, 0xce, 0xef, 0x5, 0xf8, 0xe3, 0xfe, 0x3d, 0x96, 0xa4, 0x63, - 0x29, 0xa5, 0x78, - ]; - let mut output = [0u8; 32]; +fn hash_digest<'a, S: ShaAlgorithm>(sha: &'a mut Sha<'static>, input: &[u8], output: &mut [u8]) { + let mut hasher = ShaDigest::::new(sha); + Update::update(&mut hasher, input); + output.copy_from_slice(&digest::FixedOutput::finalize_fixed(hasher)); +} - while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); - } - block!(sha.finish(output.as_mut_slice())).unwrap(); +/// A simple test using the Sha trait. This will compare the result with a +/// software implementation. +#[track_caller] +fn assert_sha(sha: &mut Sha<'static>, input: &[u8]) { + let mut output = [0u8; N]; + hash_sha::(sha, input, &mut output); + + // Compare against Software result. + match N { + 20 => assert_sw_hash::(input, &output), + 28 => assert_sw_hash::(input, &output), + 32 => assert_sw_hash::(input, &output), + 48 => assert_sw_hash::(input, &output), + 64 => assert_sw_hash::(input, &output), + _ => unreachable!(), + } +} - assert_eq!(expected_output, output); +/// A simple test using the Digest trait. This will compare the result with a +/// software implementation. +#[track_caller] +fn assert_digest<'a, S: ShaAlgorithm, const N: usize>(sha: &'a mut Sha<'static>, input: &[u8]) { + let mut output = [0u8; N]; + hash_digest::(sha, input, &mut output); + + // Compare against Software result. + match N { + 20 => assert_sw_hash::(input, &output), + 28 => assert_sw_hash::(input, &output), + 32 => assert_sw_hash::(input, &output), + 48 => assert_sw_hash::(input, &output), + 64 => assert_sw_hash::(input, &output), + _ => unreachable!(), } +} - #[test] +#[allow(unused_mut)] +fn with_random_data( + mut rng: Rng, + mut f: impl FnMut( + (&[u8], &mut [u8]), + (&[u8], &mut [u8]), + (&[u8], &mut [u8]), + (&[u8], &mut [u8]), + (&[u8], &mut [u8]), + ), +) { + const BUFFER_LEN: usize = 256; + + let mut sha1_random = [0u8; BUFFER_LEN]; + let mut sha224_random = [0u8; BUFFER_LEN]; + let mut sha256_random = [0u8; BUFFER_LEN]; + let mut sha384_random = [0u8; BUFFER_LEN]; + let mut sha512_random = [0u8; BUFFER_LEN]; + + // Fill source data with random data + rng.read(&mut sha1_random); #[cfg(not(feature = "esp32"))] - fn test_sha_224() { - let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA224); - - let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); - let mut remaining = source_data; - let expected_output = [ - 0x3b, 0x29, 0x33, 0xca, 0xfa, 0x6, 0xc0, 0x29, 0x68, 0x10, 0xa1, 0x3e, 0x54, 0x5f, - 0x25, 0x40, 0xa4, 0x35, 0x17, 0x3, 0x6d, 0xa2, 0xb, 0xeb, 0x8c, 0xbe, 0x79, 0x3b, 0xb6, - 0xa8, 0x8c, 0xff, - ]; - let mut output = [0u8; 32]; - - while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); - } - block!(sha.finish(output.as_mut_slice())).unwrap(); - - assert_eq!(expected_output, output); + { + rng.read(&mut sha224_random); + } + rng.read(&mut sha256_random); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + { + rng.read(&mut sha384_random); + rng.read(&mut sha512_random); } - #[test] - fn test_sha_256() { - let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA256); - - let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); - let mut remaining = source_data; - let expected_output = [ - 0x1e, 0xbb, 0xda, 0xb3, 0x35, 0xe0, 0x54, 0x01, 0x5f, 0x0f, 0xc1, 0x7f, 0x62, 0x77, - 0x06, 0x09, 0x72, 0x3d, 0x92, 0xc6, 0x40, 0xb6, 0x5b, 0xa9, 0x97, 0x4d, 0x66, 0x6c, - 0x36, 0x4a, 0x3a, 0x63, - ]; - let mut output = [0u8; 32]; - - while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); + for size in [1, 64, 128, 256] { + let mut sha1_output = [0u8; 20]; + let mut sha224_output = [0u8; 28]; + let mut sha256_output = [0u8; 32]; + let mut sha384_output = [0u8; 48]; + let mut sha512_output = [0u8; 64]; + f( + (&sha1_random[..size], &mut sha1_output[..]), + (&sha224_random[..size], &mut sha224_output[..]), + (&sha256_random[..size], &mut sha256_output[..]), + (&sha384_random[..size], &mut sha384_output[..]), + (&sha512_random[..size], &mut sha512_output[..]), + ); + + // Calculate software result to compare against + // Sha1 + assert_sw_hash::(&sha1_random[..size], &sha1_output); + + // Sha224 + #[cfg(not(feature = "esp32"))] + { + assert_sw_hash::(&sha224_random[..size], &sha224_output); } - block!(sha.finish(output.as_mut_slice())).unwrap(); - assert_eq!(expected_output, output); - } + // Sha256 + assert_sw_hash::(&sha256_random[..size], &sha256_output); - #[test] - #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] - fn test_sha_384() { - let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA384); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + { + // Sha384 + assert_sw_hash::(&sha384_random[..size], &sha384_output); - let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); - let mut remaining = source_data; - let expected_output = [ - 0x8a, 0x1d, 0xe0, 0x7f, 0xa9, 0xc, 0x4c, 0xbb, 0xac, 0xe4, 0x62, 0xbd, 0xd9, 0x2f, - 0x90, 0x88, 0x61, 0x69, 0x40, 0xc0, 0x55, 0x6b, 0x80, 0x6, 0xaa, 0xfc, 0xd4, 0xff, - 0xc1, 0x8, 0xe9, 0xb2, - ]; - let mut output = [0u8; 32]; - - while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); + // Sha512 + assert_sw_hash::(&sha512_random[..size], &sha512_output); } - block!(sha.finish(output.as_mut_slice())).unwrap(); - - assert_eq!(expected_output, output); } +} - #[test] - #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] - fn test_sha_512() { - let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA512); +pub struct Context { + rng: Rng, + sha: Sha<'static>, +} - let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); - let mut remaining = source_data; - let expected_output = [ - 0xee, 0x8d, 0xe, 0x15, 0xde, 0xdc, 0xd8, 0xc8, 0x86, 0xa2, 0xef, 0xb1, 0xac, 0x6a, - 0x49, 0xcf, 0xd8, 0x3f, 0x67, 0x65, 0x64, 0xb3, 0x0, 0xce, 0x48, 0x51, 0x5e, 0xce, - 0x5f, 0x4b, 0xee, 0x10, - ]; - let mut output = [0u8; 32]; +#[cfg(test)] +#[embedded_test::tests] +mod tests { + #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] + use defmt::assert_eq; + + use super::*; - while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); + #[init] + fn init() -> Context { + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + // FIXME: max speed fails...? + let config = esp_hal::Config::default(); + } else { + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + } } - block!(sha.finish(output.as_mut_slice())).unwrap(); - assert_eq!(expected_output, output); + let peripherals = esp_hal::init(config); + Context { + rng: Rng::new(peripherals.RNG), + sha: Sha::new(peripherals.SHA), + } } #[test] #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] - fn test_sha_512_224() { - let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA512_224); - - let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); - let mut remaining = source_data; + fn test_sha_512_224(mut ctx: Context) { let expected_output = [ 0x19, 0xf2, 0xb3, 0x88, 0x22, 0x86, 0x94, 0x38, 0xee, 0x24, 0xc1, 0xc3, 0xb0, 0xb1, - 0x21, 0x6a, 0xf4, 0x81, 0x14, 0x8f, 0x4, 0x34, 0xfd, 0xd7, 0x54, 0x3, 0x2b, 0x88, 0xa3, - 0xc1, 0xb8, 0x60, + 0x21, 0x6a, 0xf4, 0x81, 0x14, 0x8f, 0x4, 0x34, 0xfd, 0xd7, 0x54, 0x3, 0x2b, 0x88, ]; - let mut output = [0u8; 32]; + let mut output = [0u8; 28]; + hash_sha::(&mut ctx.sha, SOURCE_DATA, &mut output); + assert_eq!(output, expected_output); - while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); - } - block!(sha.finish(output.as_mut_slice())).unwrap(); - - assert_eq!(expected_output, output); + let mut output = [0u8; 28]; + hash_digest::(&mut ctx.sha, SOURCE_DATA, &mut output); + assert_eq!(output, expected_output); } #[test] #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] - fn test_sha_512_256() { - let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA512_256); - - let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); - let mut remaining = source_data; + fn test_sha_512_256(mut ctx: Context) { let expected_output = [ 0xb7, 0x49, 0x4e, 0xe1, 0xdb, 0xcd, 0xe5, 0x47, 0x5a, 0x61, 0x25, 0xac, 0x27, 0xc2, 0x1b, 0x53, 0xcd, 0x6b, 0x16, 0x33, 0xb4, 0x94, 0xac, 0xa4, 0x2a, 0xe6, 0x99, 0x2f, 0xe7, 0xd, 0x83, 0x19, ]; let mut output = [0u8; 32]; + hash_sha::(&mut ctx.sha, SOURCE_DATA, &mut output); + assert_eq!(output, expected_output); + + let mut output = [0u8; 32]; + hash_digest::(&mut ctx.sha, SOURCE_DATA, &mut output); + assert_eq!(output, expected_output); + } - while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); + /// A test that runs a hashing on a digest of every size between 1 and 200 + /// inclusively. + #[test] + fn test_digest_of_size_1_to_200(mut ctx: Context) { + for i in 1..=200 { + assert_sha::(&mut ctx.sha, &SOURCE_DATA[..i]); + assert_digest::(&mut ctx.sha, &SOURCE_DATA[..i]); + + #[cfg(not(feature = "esp32"))] + { + assert_sha::(&mut ctx.sha, &SOURCE_DATA[..i]); + assert_digest::(&mut ctx.sha, &SOURCE_DATA[..i]); + } + + assert_sha::(&mut ctx.sha, &SOURCE_DATA[..i]); + assert_digest::(&mut ctx.sha, &SOURCE_DATA[..i]); + + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + { + assert_sha::(&mut ctx.sha, &SOURCE_DATA[..i]); + assert_digest::(&mut ctx.sha, &SOURCE_DATA[..i]); + + assert_sha::(&mut ctx.sha, &SOURCE_DATA[..i]); + assert_digest::(&mut ctx.sha, &SOURCE_DATA[..i]); + } } - block!(sha.finish(output.as_mut_slice())).unwrap(); + } - assert_eq!(expected_output, output); + #[cfg(not(feature = "esp32"))] + /// A rolling test that loops between hasher for every step to test + /// interleaving. This specifically test the Sha trait implementation + #[test] + fn test_sha_rolling(mut ctx: Context) { + #[allow(unused)] + with_random_data(ctx.rng, |sha1_p, sha224_p, sha256_p, sha384_p, sha512_p| { + let mut sha1_remaining = sha1_p.0; + let mut sha224_remaining = sha224_p.0; + let mut sha256_remaining = sha256_p.0; + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha384_remaining = sha384_p.0; + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha512_remaining = sha512_p.0; + + let mut sha1 = esp_hal::sha::Context::::new(); + let mut sha224 = esp_hal::sha::Context::::new(); + let mut sha256 = esp_hal::sha::Context::::new(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha384 = esp_hal::sha::Context::::new(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha512 = esp_hal::sha::Context::::new(); + + loop { + let mut all_done = true; + if !sha1_remaining.is_empty() { + let mut digest = ShaDigest::restore(&mut ctx.sha, &mut sha1); + sha1_remaining = block!(digest.update(sha1_remaining)).unwrap(); + block!(digest.save(&mut sha1)); + all_done = false; + } + #[cfg(not(feature = "esp32"))] + if !sha224_remaining.is_empty() { + let mut digest = ShaDigest::restore(&mut ctx.sha, &mut sha224); + sha224_remaining = block!(digest.update(sha224_remaining)).unwrap(); + block!(digest.save(&mut sha224)); + all_done = false; + } + + if !sha256_remaining.is_empty() { + let mut digest = ShaDigest::restore(&mut ctx.sha, &mut sha256); + sha256_remaining = block!(digest.update(sha256_remaining)).unwrap(); + block!(digest.save(&mut sha256)); + all_done = false; + } + + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + { + if !sha384_remaining.is_empty() { + let mut digest = ShaDigest::restore(&mut ctx.sha, &mut sha384); + sha384_remaining = block!(digest.update(sha384_remaining)).unwrap(); + block!(digest.save(&mut sha384)); + all_done = false; + } + + if !sha512_remaining.is_empty() { + let mut digest = ShaDigest::restore(&mut ctx.sha, &mut sha512); + sha512_remaining = block!(digest.update(sha512_remaining)).unwrap(); + block!(digest.save(&mut sha512)); + all_done = false; + } + } + + if all_done { + break; + } + } + + let mut digest = ShaDigest::restore(&mut ctx.sha, &mut sha1); + block!(digest.finish(sha1_p.1)).unwrap(); + let mut digest = ShaDigest::restore(&mut ctx.sha, &mut sha224); + block!(digest.finish(sha224_p.1)).unwrap(); + let mut digest = ShaDigest::restore(&mut ctx.sha, &mut sha256); + block!(digest.finish(sha256_p.1)).unwrap(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + { + let mut digest = ShaDigest::restore(&mut ctx.sha, &mut sha384); + block!(digest.finish(sha384_p.1)).unwrap(); + let mut digest = ShaDigest::restore(&mut ctx.sha, &mut sha512); + block!(digest.finish(sha512_p.1)).unwrap(); + } + }); + } + + /// A rolling test that loops between hasher for every step to test + /// interleaving. This specifically test the Digest trait implementation + #[test] + fn test_for_digest_rolling(mut ctx: Context) { + #[allow(unused)] + with_random_data(ctx.rng, |sha1_p, sha224_p, sha256_p, sha384_p, sha512_p| { + // The Digest::update will consume the entirety of remaining. We don't need to + // loop until remaining is fully consumed. + + let mut sha1 = ctx.sha.start::(); + Update::update(&mut sha1, sha1_p.0); + let sha1_output = digest::FixedOutput::finalize_fixed(sha1); + sha1_p.1.copy_from_slice(&sha1_output); + + #[cfg(not(feature = "esp32"))] + { + let mut sha224 = ctx.sha.start::(); + Update::update(&mut sha224, sha224_p.0); + let sha224_output = digest::FixedOutput::finalize_fixed(sha224); + sha224_p.1.copy_from_slice(&sha224_output); + } + + let mut sha256 = ctx.sha.start::(); + Update::update(&mut sha256, sha256_p.0); + let sha256_output = digest::FixedOutput::finalize_fixed(sha256); + sha256_p.1.copy_from_slice(&sha256_output); + + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + { + let mut sha384 = ctx.sha.start::(); + Update::update(&mut sha384, sha384_p.0); + let sha384_output = digest::FixedOutput::finalize_fixed(sha384); + sha384_p.1.copy_from_slice(&sha384_output); + + let mut sha512 = ctx.sha.start::(); + Update::update(&mut sha512, sha512_p.0); + let sha512_output = digest::FixedOutput::finalize_fixed(sha512); + sha512_p.1.copy_from_slice(&sha512_output); + } + }); } } diff --git a/hil-test/tests/spi_full_duplex.rs b/hil-test/tests/spi_full_duplex.rs index 36844c88ce0..188b2724fb7 100644 --- a/hil-test/tests/spi_full_duplex.rs +++ b/hil-test/tests/spi_full_duplex.rs @@ -2,56 +2,28 @@ //! //! Folowing pins are used: //! SCLK GPIO0 -//! MISO GPIO2 -//! MOSI GPIO3 -//! CS GPIO8 +//! MISO GPIO2 / GPIO9 (esp32s2 / esp32s3) / GPIO26 (esp32) +//! MOSI GPIO3 / GPIO10 (esp32s2 / esp32s3) / GPIO27 (esp32) //! -//! Connect MISO (GPIO2) and MOSI (GPIO3) pins. +//! Connect MISO and MOSI pins. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] -use defmt_rtt as _; use embedded_hal::spi::SpiBus; -use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, - peripherals::Peripherals, prelude::*, spi::{master::Spi, FullDuplexMode, SpiMode}, - system::SystemControl, }; +use hil_test as _; struct Context { spi: Spi<'static, esp_hal::peripherals::SPI2, FullDuplexMode>, } -impl Context { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; - let mosi = io.pins.gpio3; - let cs = io.pins.gpio8; - - let spi = Spi::new(peripherals.SPI2, 1000u32.kHz(), SpiMode::Mode0, &clocks).with_pins( - Some(sclk), - Some(mosi), - Some(miso), - Some(cs), - ); - - Context { spi } - } -} - #[cfg(test)] #[embedded_test::tests] mod tests { @@ -61,7 +33,19 @@ mod tests { #[init] fn init() -> Context { - Context::init() + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let sclk = io.pins.gpio0; + let (miso, mosi) = hil_test::common_test_pins!(io); + + let spi = Spi::new(peripherals.SPI2, 1000u32.kHz(), SpiMode::Mode0) + .with_sck(sclk) + .with_mosi(mosi) + .with_miso(miso); + + Context { spi } } #[test] diff --git a/hil-test/tests/spi_full_duplex_dma.rs b/hil-test/tests/spi_full_duplex_dma.rs index a07438ef8e3..812f8b2e5e4 100644 --- a/hil-test/tests/spi_full_duplex_dma.rs +++ b/hil-test/tests/spi_full_duplex_dma.rs @@ -1,33 +1,46 @@ -//! SPI Full Duplex DMA Test +//! SPI Full Duplex DMA ASYNC Test //! //! Folowing pins are used: //! SCLK GPIO0 -//! MISO GPIO2 -//! MOSI GPIO3 -//! CS GPIO8 +//! MISO GPIO2 / GPIO9 (esp32s2 / esp32s3) / GPIO26 (esp32) +//! MOSI GPIO3 / GPIO10 (esp32s2 / esp32s3) / GPIO27 (esp32) //! -//! Connect MISO (GPIO2) and MOSI (GPIO3) pins. +//! Connect MISO and MOSI pins. -//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s3 +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, - dma::{Dma, DmaPriority}, + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::Io, - peripherals::Peripherals, + peripherals::SPI2, prelude::*, spi::{ - master::{prelude::*, Spi}, + master::{Spi, SpiDma}, + FullDuplexMode, SpiMode, }, - system::SystemControl, + Blocking, }; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +struct Context { + spi: SpiDma<'static, SPI2, DmaChannel0, FullDuplexMode, Blocking>, +} #[cfg(test)] #[embedded_test::tests] @@ -36,286 +49,140 @@ mod tests { use super::*; - #[test] - #[timeout(3)] - fn test_symmetric_dma_transfer() { - const DMA_BUFFER_SIZE: usize = 4; - - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; - let mosi = io.pins.gpio3; - let cs = io.pins.gpio8; + let (miso, mosi) = hil_test::common_test_pins!(io); let dma = Dma::new(peripherals.DMA); - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; - - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); - - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) - .with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs)) - .with_dma( - dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - ); - - // DMA buffer require a static life-time - let mut send = tx_buffer; - let mut receive = rx_buffer; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } - send.copy_from_slice(&[0xde, 0xad, 0xbe, 0xef]); + let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) + .with_sck(sclk) + .with_mosi(mosi) + .with_miso(miso) + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); - let transfer = spi.dma_transfer(&mut send, &mut receive).unwrap(); - transfer.wait().unwrap(); - assert_eq!(send, receive); + Context { spi } } #[test] #[timeout(3)] - // S3 is disabled due to https://github.com/esp-rs/esp-hal/issues/1524#issuecomment-2255306292 - #[cfg(not(feature = "esp32s3"))] - fn test_asymmetric_dma_transfer() { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; - let mosi = io.pins.gpio3; - let cs = io.pins.gpio8; - - let dma = Dma::new(peripherals.DMA); - - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; - - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(4, 2); - - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) - .with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs)) - .with_dma( - dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - ); - - // DMA buffer require a static life-time - let mut send = tx_buffer; - let mut receive = rx_buffer; - - send.copy_from_slice(&[0xde, 0xad, 0xbe, 0xef]); - - let transfer = spi.dma_transfer(&mut send, &mut receive).unwrap(); - transfer.wait().unwrap(); - assert_eq!(send[0..1], receive[0..1]); + fn test_symmetric_dma_transfer(ctx: Context) { + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(4); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + + dma_tx_buf.fill(&[0xde, 0xad, 0xbe, 0xef]); + + let transfer = ctx + .spi + .dma_transfer(dma_rx_buf, dma_tx_buf) + .map_err(|e| e.0) + .unwrap(); + let (_, (dma_rx_buf, dma_tx_buf)) = transfer.wait(); + assert_eq!(dma_tx_buf.as_slice(), dma_rx_buf.as_slice()); } #[test] #[timeout(3)] - fn test_symmetric_dma_transfer_huge_buffer() { - const DMA_BUFFER_SIZE: usize = 4096; - - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; - let mosi = io.pins.gpio3; - let cs = io.pins.gpio8; - - let dma = Dma::new(peripherals.DMA); - - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; - - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); - - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) - .with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs)) - .with_dma( - dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - ); + #[cfg(not(feature = "esp32s2"))] + fn test_asymmetric_dma_transfer(ctx: Context) { + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(2, 4); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + + dma_tx_buf.fill(&[0xde, 0xad, 0xbe, 0xef]); + + let transfer = ctx + .spi + .dma_transfer(dma_rx_buf, dma_tx_buf) + .map_err(|e| e.0) + .unwrap(); + let (_, (dma_rx_buf, dma_tx_buf)) = transfer.wait(); + assert_eq!(dma_tx_buf.as_slice()[0..1], dma_rx_buf.as_slice()[0..1]); + } - // DMA buffer require a static life-time - let mut send = tx_buffer; - let mut receive = rx_buffer; + #[test] + #[timeout(3)] + fn test_symmetric_dma_transfer_huge_buffer(ctx: Context) { + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(4096); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - send.copy_from_slice(&[0x55u8; 4096]); - for byte in 0..send.len() { - send[byte] = byte as u8; + for (i, d) in dma_tx_buf.as_mut_slice().iter_mut().enumerate() { + *d = i as _; } - let transfer = spi.dma_transfer(&mut send, &mut receive).unwrap(); - transfer.wait().unwrap(); - assert_eq!(send, receive); + let transfer = ctx + .spi + .dma_transfer(dma_rx_buf, dma_tx_buf) + .map_err(|e| e.0) + .unwrap(); + let (_, (dma_rx_buf, dma_tx_buf)) = transfer.wait(); + assert_eq!(dma_tx_buf.as_slice(), dma_rx_buf.as_slice()); } #[test] #[timeout(3)] - fn test_try_using_non_dma_memory_tx_buffer() { - const DMA_BUFFER_SIZE: usize = 4096; + fn test_dma_bus_symmetric_transfer(ctx: Context) { + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(4); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut spi = ctx.spi.with_buffers(dma_rx_buf, dma_tx_buf); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; - let mosi = io.pins.gpio3; - let cs = io.pins.gpio8; + let tx_buf = [0xde, 0xad, 0xbe, 0xef]; + let mut rx_buf = [0; 4]; - let dma = Dma::new(peripherals.DMA); + spi.transfer(&mut rx_buf, &tx_buf).unwrap(); - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; - - let (_, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); - - let tx_buffer = { - // using `static`, not `static mut`, places the array in .rodata - static TX_BUFFER: [u8; DMA_BUFFER_SIZE] = [42u8; DMA_BUFFER_SIZE]; - unsafe { - core::slice::from_raw_parts( - &mut *(core::ptr::addr_of!(TX_BUFFER) as *mut u8), - DMA_BUFFER_SIZE, - ) - } - }; - - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) - .with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs)) - .with_dma( - dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - ); - - let mut receive = rx_buffer; - - assert!(matches!( - spi.dma_transfer(&tx_buffer, &mut receive), - Err(esp_hal::spi::Error::DmaError( - esp_hal::dma::DmaError::UnsupportedMemoryRegion - )) - )); + assert_eq!(tx_buf, rx_buf); } #[test] #[timeout(3)] - fn test_try_using_non_dma_memory_rx_buffer() { - const DMA_BUFFER_SIZE: usize = 4096; + fn test_dma_bus_asymmetric_transfer(ctx: Context) { + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(4); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut spi = ctx.spi.with_buffers(dma_rx_buf, dma_tx_buf); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; - let mosi = io.pins.gpio3; - let cs = io.pins.gpio8; + let tx_buf = [0xde, 0xad, 0xbe, 0xef]; + let mut rx_buf = [0; 4]; - let dma = Dma::new(peripherals.DMA); + spi.transfer(&mut rx_buf, &tx_buf).unwrap(); - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; - - let (tx_buffer, tx_descriptors, _, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); - - let rx_buffer = { - // using `static`, not `static mut`, places the array in .rodata - static RX_BUFFER: [u8; DMA_BUFFER_SIZE] = [42u8; DMA_BUFFER_SIZE]; - unsafe { - core::slice::from_raw_parts_mut( - &mut *(core::ptr::addr_of!(RX_BUFFER) as *mut u8), - DMA_BUFFER_SIZE, - ) - } - }; - - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) - .with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs)) - .with_dma( - dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - ); - - let mut receive = rx_buffer; - assert!(matches!( - spi.dma_transfer(&tx_buffer, &mut receive), - Err(esp_hal::spi::Error::DmaError( - esp_hal::dma::DmaError::UnsupportedMemoryRegion - )) - )); + assert_eq!(&tx_buf[0..1], &rx_buf[0..1]); } #[test] #[timeout(3)] - fn test_symmetric_dma_transfer_owned() { + fn test_dma_bus_symmetric_transfer_huge_buffer(ctx: Context) { const DMA_BUFFER_SIZE: usize = 4096; - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(40); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; - let mosi = io.pins.gpio3; - let cs = io.pins.gpio8; + let mut spi = ctx.spi.with_buffers(dma_rx_buf, dma_tx_buf); - let dma = Dma::new(peripherals.DMA); - - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; - - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); + let tx_buf = core::array::from_fn(|i| i as _); + let mut rx_buf = [0; DMA_BUFFER_SIZE]; - let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) - .with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs)) - .with_dma( - dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - ); - - // DMA buffer require a static life-time - let send = tx_buffer; - let receive = rx_buffer; - - send.copy_from_slice(&[0x55u8; 4096]); - for byte in 0..send.len() { - send[byte] = byte as u8; - } + spi.transfer(&mut rx_buf, &tx_buf).unwrap(); - let transfer = spi.dma_transfer_owned(send, receive).unwrap(); - let (_, send, receive) = transfer.wait().unwrap(); - assert_eq!(send, receive); + assert_eq!(tx_buf, rx_buf); } } diff --git a/hil-test/tests/spi_full_duplex_dma_async.rs b/hil-test/tests/spi_full_duplex_dma_async.rs new file mode 100644 index 00000000000..502280756cc --- /dev/null +++ b/hil-test/tests/spi_full_duplex_dma_async.rs @@ -0,0 +1,172 @@ +//! SPI Full Duplex DMA Test +//! +//! Following pins are used: +//! SCLK GPIO0 +//! MOSI GPIO3 / GPIO10 (esp32s3) +//! MISO GPIO4 +//! +//! PCNT GPIO2 / GPIO9 (esp32s3) +//! OUTPUT GPIO5 (helper to keep MISO LOW) +//! +//! The idea of using PCNT (input) here is to connect MOSI to it and count the +//! edges of whatever SPI writes (in this test case 3 pos edges). +//! +//! Connect PCNT and MOSI, MISO and GPIO5 pins. + +//% CHIPS: esp32c6 esp32h2 esp32s3 +//% FEATURES: generic-queue + +#![no_std] +#![no_main] + +use embedded_hal_async::spi::SpiBus; +use esp_hal::{ + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, + dma_buffers, + gpio::{ErasedPin, Io, Level, Output, Pull}, + pcnt::{ + channel::{EdgeMode, PcntInputConfig, PcntSource}, + unit::Unit, + Pcnt, + }, + peripherals::SPI2, + prelude::*, + spi::{ + master::{Spi, SpiDmaBus}, + FullDuplexMode, + SpiMode, + }, + Async, +}; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +const DMA_BUFFER_SIZE: usize = 5; + +struct Context { + spi: SpiDmaBus<'static, SPI2, DmaChannel0, FullDuplexMode, Async>, + pcnt_unit: Unit<'static, 0>, + out_pin: Output<'static>, + mosi_mirror: ErasedPin, +} + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod tests { + use defmt::assert_eq; + + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let pcnt = Pcnt::new(peripherals.PCNT); + let sclk = io.pins.gpio0; + + let (mosi_mirror, mosi) = hil_test::common_test_pins!(io); + let miso = io.pins.gpio4; + let mosi_mirror = mosi_mirror.degrade(); + + let mut out_pin = Output::new(io.pins.gpio5, Level::Low); + out_pin.set_low(); + assert_eq!(out_pin.is_set_low(), true); + + let dma = Dma::new(peripherals.DMA); + + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } + + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + + let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) + .with_sck(sclk) + .with_mosi(mosi) + .with_miso(miso) + .with_dma(dma_channel.configure_for_async(false, DmaPriority::Priority0)) + .with_buffers(dma_rx_buf, dma_tx_buf); + + Context { + spi, + pcnt_unit: pcnt.unit0, + out_pin, + mosi_mirror, + } + } + + #[test] + #[timeout(3)] + async fn test_async_dma_read_dma_write_pcnt(mut ctx: Context) { + ctx.pcnt_unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + ctx.pcnt_unit + .channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let mut receive = [0; DMA_BUFFER_SIZE]; + + // Fill the buffer where each byte has 3 pos edges. + let transmit = [0b0110_1010; DMA_BUFFER_SIZE]; + + assert_eq!(ctx.out_pin.is_set_low(), true); + + for i in 1..4 { + receive.copy_from_slice(&[5, 5, 5, 5, 5]); + SpiBus::read(&mut ctx.spi, &mut receive).await.unwrap(); + assert_eq!(receive, [0, 0, 0, 0, 0]); + + SpiBus::write(&mut ctx.spi, &transmit).await.unwrap(); + assert_eq!(ctx.pcnt_unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + } + } + + #[test] + #[timeout(3)] + async fn test_async_dma_read_dma_transfer_pcnt(mut ctx: Context) { + ctx.pcnt_unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + ctx.pcnt_unit + .channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let mut receive = [0; DMA_BUFFER_SIZE]; + + // Fill the buffer where each byte has 3 pos edges. + let transmit = [0b0110_1010; DMA_BUFFER_SIZE]; + + assert_eq!(ctx.out_pin.is_set_low(), true); + + for i in 1..4 { + receive.copy_from_slice(&[5, 5, 5, 5, 5]); + SpiBus::read(&mut ctx.spi, &mut receive).await.unwrap(); + assert_eq!(receive, [0, 0, 0, 0, 0]); + + SpiBus::transfer(&mut ctx.spi, &mut receive, &transmit) + .await + .unwrap(); + assert_eq!(ctx.pcnt_unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + } + } +} diff --git a/hil-test/tests/spi_full_duplex_dma_pcnt.rs b/hil-test/tests/spi_full_duplex_dma_pcnt.rs new file mode 100644 index 00000000000..1447629bc85 --- /dev/null +++ b/hil-test/tests/spi_full_duplex_dma_pcnt.rs @@ -0,0 +1,176 @@ +//! SPI Full Duplex DMA ASYNC Test with PCNT readback. +//! +//! Folowing pins are used: +//! SCLK GPIO0 +//! MOSI GPIO3 / GPIO10 (esp32s3) +//! PCNT GPIO2 / GPIO9 (esp32s3) +//! OUTPUT GPIO5 (helper to keep MISO LOW) +//! +//! The idea of using PCNT (input) here is to connect MOSI to it and count the +//! edges of whatever SPI writes (in this test case 3 pos edges). +//! +//! Connect MISO and MOSI pins. + +//% CHIPS: esp32c6 esp32h2 esp32s3 + +#![no_std] +#![no_main] + +use esp_hal::{ + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, + dma_buffers, + gpio::{ErasedPin, Io, Level, Output, Pull}, + pcnt::{ + channel::{EdgeMode, PcntInputConfig, PcntSource}, + unit::Unit, + Pcnt, + }, + peripherals::SPI2, + prelude::*, + spi::{ + master::{Spi, SpiDma}, + FullDuplexMode, + SpiMode, + }, + Blocking, +}; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +struct Context { + spi: SpiDma<'static, SPI2, DmaChannel0, FullDuplexMode, Blocking>, + pcnt_unit: Unit<'static, 0>, + out_pin: Output<'static>, + mosi_mirror: ErasedPin, +} + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use defmt::assert_eq; + + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio0; + let (mosi_mirror, mosi) = hil_test::common_test_pins!(io); + let miso = io.pins.gpio4; + + let dma = Dma::new(peripherals.DMA); + + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } + + let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) + .with_sck(sclk) + .with_mosi(mosi) + .with_miso(miso) + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); + + let pcnt = Pcnt::new(peripherals.PCNT); + + let mut out_pin = Output::new(io.pins.gpio5, Level::Low); + out_pin.set_low(); + assert_eq!(out_pin.is_set_low(), true); + let mosi_mirror = mosi_mirror.degrade(); + + Context { + spi, + pcnt_unit: pcnt.unit0, + out_pin, + mosi_mirror, + } + } + + #[test] + #[timeout(3)] + fn test_dma_read_dma_write_pcnt(ctx: Context) { + const DMA_BUFFER_SIZE: usize = 5; + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); + let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + + let unit = ctx.pcnt_unit; + let mut spi = ctx.spi; + + unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + // Fill the buffer where each byte has 3 pos edges. + dma_tx_buf.as_mut_slice().fill(0b0110_1010); + + assert_eq!(ctx.out_pin.is_set_low(), true); + + for i in 1..4 { + dma_rx_buf.as_mut_slice().copy_from_slice(&[5, 5, 5, 5, 5]); + let transfer = spi.dma_read(dma_rx_buf).map_err(|e| e.0).unwrap(); + (spi, dma_rx_buf) = transfer.wait(); + assert_eq!(dma_rx_buf.as_slice(), &[0, 0, 0, 0, 0]); + + let transfer = spi.dma_write(dma_tx_buf).map_err(|e| e.0).unwrap(); + (spi, dma_tx_buf) = transfer.wait(); + assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + } + } + + #[test] + #[timeout(3)] + fn test_dma_read_dma_transfer_pcnt(ctx: Context) { + const DMA_BUFFER_SIZE: usize = 5; + let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); + let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + + let unit = ctx.pcnt_unit; + let mut spi = ctx.spi; + + unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + // Fill the buffer where each byte has 3 pos edges. + dma_tx_buf.as_mut_slice().fill(0b0110_1010); + + assert_eq!(ctx.out_pin.is_set_low(), true); + + for i in 1..4 { + dma_rx_buf.as_mut_slice().copy_from_slice(&[5, 5, 5, 5, 5]); + let transfer = spi.dma_read(dma_rx_buf).map_err(|e| e.0).unwrap(); + (spi, dma_rx_buf) = transfer.wait(); + assert_eq!(dma_rx_buf.as_slice(), &[0, 0, 0, 0, 0]); + + let transfer = spi + .dma_transfer(dma_rx_buf, dma_tx_buf) + .map_err(|e| e.0) + .unwrap(); + (spi, (dma_rx_buf, dma_tx_buf)) = transfer.wait(); + assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + } + } +} diff --git a/hil-test/tests/spi_half_duplex_read.rs b/hil-test/tests/spi_half_duplex_read.rs index a3c95dacd5d..dd43edb4cc4 100644 --- a/hil-test/tests/spi_half_duplex_read.rs +++ b/hil-test/tests/spi_half_duplex_read.rs @@ -2,79 +2,96 @@ //! //! Folowing pins are used: //! SCLK GPIO0 -//! MISO GPIO2 +//! MISO GPIO2 / GPIO9 (esp32s2 / esp32s3) / GPIO26 (esp32) //! -//! GPIO GPIO3 +//! GPIO GPIO3 / GPIO10 (esp32s2 / esp32s3) / GPIO27 (esp32) //! -//! Connect MISO (GPIO2) and GPIO (GPIO3) pins. +//! Connect MISO and GPIO pins. -//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s3 +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; +use esp_hal::{ + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, + dma_buffers, + gpio::{Io, Level, Output}, + peripherals::SPI2, + prelude::*, + spi::{ + master::{Address, Command, HalfDuplexReadWrite, Spi, SpiDma}, + HalfDuplexMode, + SpiDataMode, + SpiMode, + }, + Blocking, +}; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +struct Context { + spi: SpiDma<'static, SPI2, DmaChannel0, HalfDuplexMode, Blocking>, + miso_mirror: Output<'static>, +} #[cfg(test)] #[embedded_test::tests] mod tests { - use esp_hal::{ - clock::ClockControl, - dma::{Dma, DmaPriority}, - dma_buffers, - gpio::{Io, Level, Output}, - peripherals::Peripherals, - prelude::_fugit_RateExtU32, - spi::{ - master::{prelude::*, Address, Command, Spi}, - SpiDataMode, - SpiMode, - }, - system::SystemControl, - }; + use defmt::assert_eq; - #[init] - fn init() {} - - #[test] - #[timeout(3)] - fn test_spi_reads_correctly_from_gpio_pin() { - const DMA_BUFFER_SIZE: usize = 4; + use super::*; - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; + let (miso, miso_mirror) = hil_test::common_test_pins!(io); - let mut miso_mirror = Output::new(io.pins.gpio3, Level::High); + let miso_mirror = Output::new(miso_mirror, Level::High); let dma = Dma::new(peripherals.DMA); - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } - let (_, tx_descriptors, mut rx_buffer, rx_descriptors) = dma_buffers!(0, DMA_BUFFER_SIZE); - - let mut spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) + let spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) .with_sck(sclk) .with_miso(miso) - .with_dma( - dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - ); + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); + + Context { spi, miso_mirror } + } + + #[test] + #[timeout(3)] + fn test_spi_reads_correctly_from_gpio_pin(mut ctx: Context) { + const DMA_BUFFER_SIZE: usize = 4; - // Fill with neither 0x00 nor 0xFF. - rx_buffer.fill(5); + let (buffer, descriptors, _, _) = dma_buffers!(DMA_BUFFER_SIZE, 0); + let mut dma_rx_buf = DmaRxBuf::new(descriptors, buffer).unwrap(); // SPI should read '0's from the MISO pin - miso_mirror.set_low(); + ctx.miso_mirror.set_low(); + + let mut spi = ctx.spi; let transfer = spi .read( @@ -82,15 +99,16 @@ mod tests { Command::None, Address::None, 0, - &mut rx_buffer, + dma_rx_buf, ) + .map_err(|e| e.0) .unwrap(); - transfer.wait().unwrap(); + (spi, dma_rx_buf) = transfer.wait(); - assert_eq!(rx_buffer, &[0x00; DMA_BUFFER_SIZE]); + assert_eq!(dma_rx_buf.as_slice(), &[0x00; DMA_BUFFER_SIZE]); // SPI should read '1's from the MISO pin - miso_mirror.set_high(); + ctx.miso_mirror.set_high(); let transfer = spi .read( @@ -98,11 +116,55 @@ mod tests { Command::None, Address::None, 0, - &mut rx_buffer, + dma_rx_buf, ) + .map_err(|e| e.0) .unwrap(); - transfer.wait().unwrap(); - assert_eq!(rx_buffer, &[0xFF; DMA_BUFFER_SIZE]); + (_, dma_rx_buf) = transfer.wait(); + + assert_eq!(dma_rx_buf.as_slice(), &[0xFF; DMA_BUFFER_SIZE]); + } + + #[test] + #[timeout(3)] + fn test_spidmabus_reads_correctly_from_gpio_pin(mut ctx: Context) { + const DMA_BUFFER_SIZE: usize = 4; + + // WAS THIS AN ERROR? + let (buffer, descriptors, tx, txd) = dma_buffers!(DMA_BUFFER_SIZE, 1); + let dma_rx_buf = DmaRxBuf::new(descriptors, buffer).unwrap(); + let dma_tx_buf = DmaTxBuf::new(txd, tx).unwrap(); + + let mut spi = ctx.spi.with_buffers(dma_rx_buf, dma_tx_buf); + + // SPI should read '0's from the MISO pin + ctx.miso_mirror.set_low(); + + let mut buffer = [0xAA; DMA_BUFFER_SIZE]; + spi.read( + SpiDataMode::Single, + Command::None, + Address::None, + 0, + &mut buffer, + ) + .unwrap(); + + assert_eq!(buffer.as_slice(), &[0x00; DMA_BUFFER_SIZE]); + + // SPI should read '1's from the MISO pin + ctx.miso_mirror.set_high(); + + spi.read( + SpiDataMode::Single, + Command::None, + Address::None, + 0, + &mut buffer, + ) + .unwrap(); + + assert_eq!(buffer.as_slice(), &[0xFF; DMA_BUFFER_SIZE]); } } diff --git a/hil-test/tests/spi_half_duplex_write.rs b/hil-test/tests/spi_half_duplex_write.rs index c5aed0ec133..830ba83baca 100644 --- a/hil-test/tests/spi_half_duplex_write.rs +++ b/hil-test/tests/spi_half_duplex_write.rs @@ -2,88 +2,118 @@ //! //! Following pins are used: //! SCLK GPIO0 -//! MOSI GPIO2 +//! MOSI GPIO2 / GPIO9 (esp32s2 / esp32s3) / GPIO26 (esp32) //! -//! PCNT GPIO3 +//! PCNT GPIO3 / GPIO10 (esp32s2 / esp32s3) / GPIO27 (esp32) //! -//! Connect MOSI (GPIO2) and PCNT (GPIO3) pins. +//! Connect MOSI and PCNT pins. -//% CHIPS: esp32 esp32c6 esp32h2 esp32s3 +//% CHIPS: esp32 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; +use esp_hal::{ + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, + dma_buffers, + gpio::{AnyPin, Io, Pull}, + pcnt::{ + channel::{EdgeMode, PcntInputConfig, PcntSource}, + unit::Unit, + Pcnt, + }, + peripherals::SPI2, + prelude::*, + spi::{ + master::{Address, Command, HalfDuplexReadWrite, Spi, SpiDma}, + HalfDuplexMode, + SpiDataMode, + SpiMode, + }, + Blocking, +}; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +struct Context { + spi: SpiDma<'static, SPI2, DmaChannel0, HalfDuplexMode, Blocking>, + pcnt_unit: Unit<'static, 0>, + mosi_mirror: AnyPin<'static>, +} #[cfg(test)] #[embedded_test::tests] mod tests { - use esp_hal::{ - clock::ClockControl, - dma::{Dma, DmaPriority}, - dma_buffers, - gpio::{Io, Pull}, - pcnt::{ - channel::{EdgeMode, PcntInputConfig, PcntSource}, - Pcnt, - }, - peripherals::Peripherals, - prelude::_fugit_RateExtU32, - spi::{ - master::{prelude::*, Address, Command, Spi}, - SpiDataMode, - SpiMode, - }, - system::SystemControl, - }; + // defmt::* is load-bearing, it ensures that the assert in dma_buffers! is not + // using defmt's non-const assert. Doing so would result in a compile error. + #[allow(unused_imports)] + use defmt::{assert_eq, *}; + + use super::*; #[init] - fn init() {} + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); - #[test] - #[timeout(3)] - fn test_spi_writes_are_correctly_by_pcnt() { - const DMA_BUFFER_SIZE: usize = 4; + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio0; + let (mosi, mosi_mirror) = hil_test::common_test_pins!(io); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mosi_mirror = AnyPin::new(mosi_mirror); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let pcnt = Pcnt::new(peripherals.PCNT); let dma = Dma::new(peripherals.DMA); - let sclk = io.pins.gpio0; - let mosi = io.pins.gpio2; - let mosi_mirror = io.pins.gpio3; - - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; - - let (tx_buffer, tx_descriptors, _, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE, 0); + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } - let mut spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) + let spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) .with_sck(sclk) .with_mosi(mosi) - .with_dma( - dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - ); + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); - let unit = pcnt.unit0; - unit.channel0.set_edge_signal(PcntSource::from_pin( + Context { + spi, mosi_mirror, + pcnt_unit: pcnt.unit0, + } + } + + #[test] + #[timeout(3)] + fn test_spi_writes_are_correctly_by_pcnt(ctx: Context) { + const DMA_BUFFER_SIZE: usize = 4; + + let (_, _, buffer, descriptors) = dma_buffers!(0, DMA_BUFFER_SIZE); + let mut dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap(); + + let unit = ctx.pcnt_unit; + let mut spi = ctx.spi; + + unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, PcntInputConfig { pull: Pull::Down }, )); unit.channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - // Fill the buffer where each byte 3 pos edges. - tx_buffer.fill(0b0110_1010); + // Fill the buffer where each byte has 3 pos edges. + dma_tx_buf.fill(&[0b0110_1010; DMA_BUFFER_SIZE]); let transfer = spi .write( @@ -91,10 +121,11 @@ mod tests { Command::None, Address::None, 0, - &tx_buffer, + dma_tx_buf, ) + .map_err(|e| e.0) .unwrap(); - transfer.wait().unwrap(); + (spi, dma_tx_buf) = transfer.wait(); assert_eq!(unit.get_value(), (3 * DMA_BUFFER_SIZE) as _); @@ -104,10 +135,55 @@ mod tests { Command::None, Address::None, 0, - &tx_buffer, + dma_tx_buf, ) + .map_err(|e| e.0) .unwrap(); - transfer.wait().unwrap(); + transfer.wait(); + + assert_eq!(unit.get_value(), (6 * DMA_BUFFER_SIZE) as _); + } + + #[test] + #[timeout(3)] + fn test_spidmabus_writes_are_correctly_by_pcnt(ctx: Context) { + const DMA_BUFFER_SIZE: usize = 4; + + let (rx, rxd, buffer, descriptors) = dma_buffers!(1, DMA_BUFFER_SIZE); + let dma_rx_buf = DmaRxBuf::new(rxd, rx).unwrap(); + let dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap(); + + let unit = ctx.pcnt_unit; + let mut spi = ctx.spi.with_buffers(dma_rx_buf, dma_tx_buf); + + unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let buffer = [0b0110_1010; DMA_BUFFER_SIZE]; + // Write the buffer where each byte has 3 pos edges. + spi.write( + SpiDataMode::Single, + Command::None, + Address::None, + 0, + &buffer, + ) + .unwrap(); + + assert_eq!(unit.get_value(), (3 * DMA_BUFFER_SIZE) as _); + + spi.write( + SpiDataMode::Single, + Command::None, + Address::None, + 0, + &buffer, + ) + .unwrap(); assert_eq!(unit.get_value(), (6 * DMA_BUFFER_SIZE) as _); } diff --git a/hil-test/tests/systimer.rs b/hil-test/tests/systimer.rs new file mode 100644 index 00000000000..cd2d53f14da --- /dev/null +++ b/hil-test/tests/systimer.rs @@ -0,0 +1,182 @@ +//! System Timer Test + +// esp32 disabled as it does not have a systimer +//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 + +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use critical_section::Mutex; +use embedded_hal::delay::DelayNs; +use esp_hal::{ + delay::Delay, + prelude::*, + timer::systimer::{ + Alarm, + FrozenUnit, + Periodic, + SpecificComparator, + SpecificUnit, + SystemTimer, + Target, + }, + Blocking, +}; +use fugit::ExtU32; +use hil_test as _; +use portable_atomic::{AtomicUsize, Ordering}; +use static_cell::StaticCell; + +type TestAlarm = + Alarm<'static, M, Blocking, SpecificComparator<'static, C>, SpecificUnit<'static, 0>>; + +static ALARM_TARGET: Mutex>>> = Mutex::new(RefCell::new(None)); +static ALARM_PERIODIC: Mutex>>> = + Mutex::new(RefCell::new(None)); + +struct Context { + unit: FrozenUnit<'static, SpecificUnit<'static, 0>>, + comparator0: SpecificComparator<'static, 0>, + comparator1: SpecificComparator<'static, 1>, +} + +#[handler(priority = esp_hal::interrupt::Priority::min())] +fn pass_test_if_called() { + critical_section::with(|cs| { + ALARM_TARGET + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt() + }); + embedded_test::export::check_outcome(()); +} + +#[handler(priority = esp_hal::interrupt::Priority::min())] +fn handle_periodic_interrupt() { + critical_section::with(|cs| { + ALARM_PERIODIC + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt() + }); +} + +static COUNTER: AtomicUsize = AtomicUsize::new(0); + +#[handler(priority = esp_hal::interrupt::Priority::min())] +fn pass_test_if_called_twice() { + critical_section::with(|cs| { + ALARM_PERIODIC + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt() + }); + COUNTER.fetch_add(1, Ordering::Relaxed); + if COUNTER.load(Ordering::Relaxed) == 2 { + embedded_test::export::check_outcome(()); + } +} + +#[handler(priority = esp_hal::interrupt::Priority::min())] +fn target_fail_test_if_called_twice() { + critical_section::with(|cs| { + ALARM_TARGET + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt() + }); + COUNTER.fetch_add(1, Ordering::Relaxed); + assert!(COUNTER.load(Ordering::Relaxed) != 2); +} + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let systimer = SystemTimer::new(peripherals.SYSTIMER); + static UNIT0: StaticCell> = StaticCell::new(); + + let unit0 = UNIT0.init(systimer.unit0); + let frozen_unit = FrozenUnit::new(unit0); + + Context { + unit: frozen_unit, + comparator0: systimer.comparator0, + comparator1: systimer.comparator1, + } + } + + #[test] + #[timeout(3)] + fn target_interrupt_is_handled(ctx: Context) { + let alarm0 = Alarm::new(ctx.comparator0, &ctx.unit); + + critical_section::with(|cs| { + alarm0.set_interrupt_handler(pass_test_if_called); + alarm0.set_target(SystemTimer::now() + SystemTimer::ticks_per_second() / 10); + alarm0.enable_interrupt(true); + + ALARM_TARGET.borrow_ref_mut(cs).replace(alarm0); + }); + + // We'll end the test in the interrupt handler. + loop {} + } + + #[test] + #[timeout(3)] + fn target_interrupt_is_handled_once(ctx: Context) { + let alarm0 = Alarm::new(ctx.comparator0, &ctx.unit); + let alarm1 = Alarm::new(ctx.comparator1, &ctx.unit); + + COUNTER.store(0, Ordering::Relaxed); + + critical_section::with(|cs| { + alarm0.set_interrupt_handler(target_fail_test_if_called_twice); + alarm0.set_target(SystemTimer::now() + SystemTimer::ticks_per_second() / 10); + alarm0.enable_interrupt(true); + + let alarm1 = alarm1.into_periodic(); + alarm1.set_interrupt_handler(handle_periodic_interrupt); + alarm1.set_period(100u32.millis()); + alarm1.enable_interrupt(true); + + ALARM_TARGET.borrow_ref_mut(cs).replace(alarm0); + ALARM_PERIODIC.borrow_ref_mut(cs).replace(alarm1); + }); + + let mut delay = Delay::new(); + delay.delay_ms(300); + } + + #[test] + #[timeout(3)] + fn periodic_interrupt_is_handled(ctx: Context) { + let alarm1 = Alarm::new(ctx.comparator1, &ctx.unit); + + COUNTER.store(0, Ordering::Relaxed); + + critical_section::with(|cs| { + let alarm1 = alarm1.into_periodic(); + alarm1.set_interrupt_handler(pass_test_if_called_twice); + alarm1.set_period(100u32.millis()); + alarm1.enable_interrupt(true); + + ALARM_PERIODIC.borrow_ref_mut(cs).replace(alarm1); + }); + + // We'll end the test in the interrupt handler. + loop {} + } +} diff --git a/hil-test/tests/twai.rs b/hil-test/tests/twai.rs new file mode 100644 index 00000000000..4ca8c64ed5a --- /dev/null +++ b/hil-test/tests/twai.rs @@ -0,0 +1,71 @@ +//! TWAI test +//! +//! Folowing pins are used: +//! TX GPIO2 / GPIO9 (esp32s2 / esp32s3) / GPIO26 (esp32) +//! RX GPIO3 / GPIO10 (esp32s2 / esp32s3) / GPIO27 (esp32) +//! +//! Connect TX and RX pins. + +//% CHIPS: esp32c3 esp32c6 esp32s2 esp32s3 + +#![no_std] +#![no_main] + +use embedded_hal_02::can::Frame; +use esp_hal::{ + gpio::Io, + peripherals::TWAI0, + prelude::*, + twai::{self, filter::SingleStandardFilter, EspTwaiFrame, StandardId, TwaiMode}, + Blocking, +}; +use hil_test as _; +use nb::block; + +struct Context { + twai: twai::Twai<'static, TWAI0, Blocking>, +} + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use defmt::assert_eq; + + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let (can_tx_pin, can_rx_pin) = hil_test::common_test_pins!(io); + + let mut config = twai::TwaiConfiguration::new( + peripherals.TWAI0, + can_rx_pin, + can_tx_pin, + twai::BaudRate::B1000K, + TwaiMode::SelfTest, + ); + + const FILTER: SingleStandardFilter = + SingleStandardFilter::new(b"00000000000", b"x", [b"xxxxxxxx", b"xxxxxxxx"]); + config.set_filter(FILTER); + + let twai = config.start(); + + Context { twai } + } + + #[test] + #[timeout(3)] + fn test_send_receive(mut ctx: Context) { + let frame = EspTwaiFrame::new_self_reception(StandardId::ZERO.into(), &[1, 2, 3]).unwrap(); + block!(ctx.twai.transmit(&frame)).unwrap(); + + let frame = block!(ctx.twai.receive()).unwrap(); + + assert_eq!(frame.data(), &[1, 2, 3]) + } +} diff --git a/hil-test/tests/uart.rs b/hil-test/tests/uart.rs index 535650b0d10..6fa2a28ffdb 100644 --- a/hil-test/tests/uart.rs +++ b/hil-test/tests/uart.rs @@ -1,49 +1,31 @@ //! UART Test //! //! Folowing pins are used: -//! TX GPIP2 -//! RX GPIO3 +//! TX GPIO2 / GPIO9 (esp32s2 / esp32s3) / GPIO26 (esp32) +//! RX GPIO3 / GPIO10 (esp32s2 / esp32s3) / GPIO27 (esp32) //! -//! Connect TX (GPIO2) and RX (GPIO3) pins. +//! Connect TX and RX pins. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] -use defmt_rtt as _; use embedded_hal_02::serial::{Read, Write}; -use esp_backtrace as _; use esp_hal::{ - clock::{ClockControl, Clocks}, gpio::Io, - peripherals::{Peripherals, UART1}, + peripherals::UART1, prelude::*, - system::SystemControl, uart::{ClockSource, Uart}, Blocking, }; +use hil_test as _; use nb::block; struct Context { - clocks: Clocks<'static>, uart: Uart<'static, UART1, Blocking>, } -impl Context { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let uart = Uart::new(peripherals.UART1, &clocks, io.pins.gpio2, io.pins.gpio3).unwrap(); - - Context { clocks, uart } - } -} - #[cfg(test)] #[embedded_test::tests] mod tests { @@ -53,7 +35,15 @@ mod tests { #[init] fn init() -> Context { - Context::init() + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let (rx, tx) = hil_test::common_test_pins!(io); + + let uart = Uart::new(peripherals.UART1, tx, rx).unwrap(); + + Context { uart } } #[test] @@ -103,7 +93,7 @@ mod tests { #[cfg(not(any(feature = "esp32", feature = "esp32c3", feature = "esp32c2")))] { // 9600 baud, RC FAST clock source: - ctx.uart.change_baud(9600, ClockSource::RcFast, &ctx.clocks); + ctx.uart.change_baud(9600, ClockSource::RcFast); ctx.uart.write(7).ok(); let read = block!(ctx.uart.read()); assert_eq!(read, Ok(7)); @@ -112,14 +102,14 @@ mod tests { // 19,200 baud, XTAL clock source: #[cfg(not(feature = "esp32"))] { - ctx.uart.change_baud(19_200, ClockSource::Xtal, &ctx.clocks); + ctx.uart.change_baud(19_200, ClockSource::Xtal); ctx.uart.write(55).ok(); let read = block!(ctx.uart.read()); assert_eq!(read, Ok(55)); } // 921,600 baud, APB clock source: - ctx.uart.change_baud(921_600, ClockSource::Apb, &ctx.clocks); + ctx.uart.change_baud(921_600, ClockSource::Apb); ctx.uart.write(253).ok(); let read = block!(ctx.uart.read()); assert_eq!(read, Ok(253)); @@ -128,14 +118,13 @@ mod tests { #[cfg(feature = "esp32s2")] { // 9600 baud, REF TICK clock source: - ctx.uart - .change_baud(9600, ClockSource::RefTick, &ctx.clocks); + ctx.uart.change_baud(9600, ClockSource::RefTick); ctx.uart.write(7).ok(); let read = block!(ctx.uart.read()); assert_eq!(read, Ok(7)); // 921,600 baud, APB clock source: - ctx.uart.change_baud(921_600, ClockSource::Apb, &ctx.clocks); + ctx.uart.change_baud(921_600, ClockSource::Apb); ctx.uart.write(253).ok(); let read = block!(ctx.uart.read()); assert_eq!(read, Ok(253)); diff --git a/hil-test/tests/uart_async.rs b/hil-test/tests/uart_async.rs index 8435f52af5a..a98efad19d2 100644 --- a/hil-test/tests/uart_async.rs +++ b/hil-test/tests/uart_async.rs @@ -1,45 +1,24 @@ //! UART Test //! //! Folowing pins are used: -//! TX GPIP2 -//! RX GPIO3 +//! TX GPIO2 / GPIO9 (esp32s2 / esp32s3) / GPIO26 (esp32) +//! RX GPIO3 / GPIO10 (esp32s2 / esp32s3) / GPIO27 (esp32) //! -//! Connect TX (GPIO2) and RX (GPIO3) pins. +//! Connect TX and RX pins. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: generic-queue #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - gpio::Io, - peripherals::{Peripherals, UART0}, - system::SystemControl, - uart::Uart, - Async, -}; +use esp_hal::{gpio::Io, peripherals::UART0, uart::Uart, Async}; +use hil_test as _; struct Context { uart: Uart<'static, UART0, Async>, } -impl Context { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let uart = - Uart::new_async(peripherals.UART0, &clocks, io.pins.gpio2, io.pins.gpio3).unwrap(); - - Context { uart } - } -} - #[cfg(test)] #[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] mod tests { @@ -49,7 +28,15 @@ mod tests { #[init] async fn init() -> Context { - Context::init() + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let (rx, tx) = hil_test::common_test_pins!(io); + + let uart = Uart::new_async(peripherals.UART0, rx, tx).unwrap(); + + Context { uart } } #[test] diff --git a/hil-test/tests/uart_tx_rx.rs b/hil-test/tests/uart_tx_rx.rs index c4e22369fee..9d8fd5506f8 100644 --- a/hil-test/tests/uart_tx_rx.rs +++ b/hil-test/tests/uart_tx_rx.rs @@ -1,47 +1,29 @@ //! UART TX/RX Test //! //! Folowing pins are used: -//! TX GPIP2 -//! RX GPIO3 +//! TX GPIO2 / GPIO9 (esp32s2 / esp32s3) / GPIO26 (esp32) +//! RX GPIO3 / GPIO10 (esp32s2 / esp32s3) / GPIO27 (esp32) //! -//! Connect TX (GPIO2) and RX (GPIO3) pins. +//! Connect TX and RX pins. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, - peripherals::{Peripherals, UART0, UART1}, + peripherals::{UART0, UART1}, prelude::*, - system::SystemControl, uart::{UartRx, UartTx}, Blocking, }; +use hil_test as _; use nb::block; struct Context { - tx: UartTx<'static, UART0, Blocking>, rx: UartRx<'static, UART1, Blocking>, -} - -impl Context { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let tx = UartTx::new(peripherals.UART0, &clocks, io.pins.gpio2).unwrap(); - let rx = UartRx::new(peripherals.UART1, &clocks, io.pins.gpio3).unwrap(); - - Context { tx, rx } - } + tx: UartTx<'static, UART0, Blocking>, } #[cfg(test)] @@ -53,7 +35,16 @@ mod tests { #[init] fn init() -> Context { - Context::init() + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let (rx, tx) = hil_test::common_test_pins!(io); + + let tx = UartTx::new(peripherals.UART0, tx).unwrap(); + let rx = UartRx::new(peripherals.UART1, rx).unwrap(); + + Context { rx, tx } } #[test] diff --git a/hil-test/tests/uart_tx_rx_async.rs b/hil-test/tests/uart_tx_rx_async.rs index d33ea6d7478..40e7af2805e 100644 --- a/hil-test/tests/uart_tx_rx_async.rs +++ b/hil-test/tests/uart_tx_rx_async.rs @@ -1,45 +1,28 @@ //! UART TX/RX Async Test //! //! Folowing pins are used: -//! TX GPIP2 -//! RX GPIO3 +//! TX GPIO2 / GPIO9 (esp32s2 / esp32s3) / GPIO26 (esp32) +//! RX GPIO3 / GPIO10 (esp32s2 / esp32s3) / GPIO27 (esp32) //! -//! Connect TX (GPIO2) and RX (GPIO3) pins. +//! Connect TX and RX pins. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: generic-queue #![no_std] #![no_main] -use defmt_rtt as _; -use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, gpio::Io, - peripherals::{Peripherals, UART0, UART1}, - system::SystemControl, + peripherals::{UART0, UART1}, uart::{UartRx, UartTx}, Async, }; +use hil_test as _; struct Context { - tx: UartTx<'static, UART0, Async>, rx: UartRx<'static, UART1, Async>, -} - -impl Context { - pub fn init() -> Self { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let tx = UartTx::new_async(peripherals.UART0, &clocks, io.pins.gpio2).unwrap(); - let rx = UartRx::new_async(peripherals.UART1, &clocks, io.pins.gpio3).unwrap(); - - Context { tx, rx } - } + tx: UartTx<'static, UART0, Async>, } #[cfg(test)] @@ -51,7 +34,16 @@ mod tests { #[init] async fn init() -> Context { - Context::init() + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let (rx, tx) = hil_test::common_test_pins!(io); + + let tx = UartTx::new_async(peripherals.UART0, tx).unwrap(); + let rx = UartRx::new_async(peripherals.UART1, rx).unwrap(); + + Context { rx, tx } } #[test] diff --git a/hil-test/tests/usb_serial_jtag.rs b/hil-test/tests/usb_serial_jtag.rs new file mode 100644 index 00000000000..c5e5bf44556 --- /dev/null +++ b/hil-test/tests/usb_serial_jtag.rs @@ -0,0 +1,23 @@ +//! USB Serial JTAG tests + +//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s3 + +#![no_std] +#![no_main] + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use esp_hal::{timer::timg::TimerGroup, usb_serial_jtag::UsbSerialJtag}; + use hil_test as _; + + #[test] + fn creating_peripheral_does_not_break_debug_connection() { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); + + _ = UsbSerialJtag::new_async(peripherals.USB_DEVICE).split(); + } +} diff --git a/resources/index.html.jinja b/resources/index.html.jinja index c0cce6f8114..0a7d5506b6b 100644 --- a/resources/index.html.jinja +++ b/resources/index.html.jinja @@ -106,7 +106,7 @@ {{ meta.chip_pretty }} - {{ meta.description }} + {{ meta.name }} (targeting {{ meta.chip_pretty }}) {{ meta.version }} {%- endfor %} diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 06b9ce793c0..f667a1c124f 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -17,4 +17,4 @@ semver = { version = "1.0.23", features = ["serde"] } serde = { version = "1.0.203", features = ["derive"] } strum = { version = "0.26.2", features = ["derive"] } toml_edit = "0.22.13" -esp-metadata = { path = "../esp-metadata" } +esp-metadata = { path = "../esp-metadata", features = ["clap"] } diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index 74d12fa2442..3bc45930421 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -9,7 +9,7 @@ use anyhow::{bail, Result}; use crate::windows_safe_path; -#[derive(Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum CargoAction { Build, Run, @@ -113,6 +113,14 @@ impl CargoArgsBuilder { self } + pub fn add_arg(&mut self, arg: S) -> &mut Self + where + S: Into, + { + self.args.push(arg.into()); + self + } + #[must_use] pub fn build(self) -> Vec { let mut args = vec![]; diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 36ded6ce5b6..97235d13535 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -6,7 +6,7 @@ use std::{ process::Command, }; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use cargo::CargoAction; use clap::ValueEnum; use esp_metadata::Chip; @@ -57,11 +57,11 @@ pub enum Package { pub struct Metadata { example_path: PathBuf, chips: Vec, - features: Vec, + feature_set: Vec, } impl Metadata { - pub fn new(example_path: &Path, chips: Vec, features: Vec) -> Self { + pub fn new(example_path: &Path, chips: Vec, feature_set: Vec) -> Self { let chips = if chips.is_empty() { Chip::iter().collect() } else { @@ -71,7 +71,7 @@ impl Metadata { Self { example_path: example_path.to_path_buf(), chips, - features, + feature_set, } } @@ -89,9 +89,9 @@ impl Metadata { .replace(".rs", "") } - /// A list of all features required for building a given examples. - pub fn features(&self) -> &[String] { - &self.features + /// A list of all features required for building a given example. + pub fn feature_set(&self) -> &[String] { + &self.feature_set } /// If the specified chip is in the list of chips, then it is supported. @@ -114,7 +114,7 @@ pub fn build_documentation( package: Package, chip: Chip, target: &str, -) -> Result<()> { +) -> Result { let package_name = package.to_string(); let package_path = windows_safe_path(&workspace.join(&package_name)); @@ -142,19 +142,28 @@ pub fn build_documentation( // Execute `cargo doc` from the package root: cargo::run(&args, &package_path)?; - Ok(()) + let docs_path = windows_safe_path( + &workspace + .join(package.to_string()) + .join("target") + .join(target) + .join("doc"), + ); + + Ok(docs_path) } /// Load all examples at the given path, and parse their metadata. -pub fn load_examples(path: &Path) -> Result> { +pub fn load_examples(path: &Path, action: CargoAction) -> Result> { let mut examples = Vec::new(); for entry in fs::read_dir(path)? { let path = windows_safe_path(&entry?.path()); - let text = fs::read_to_string(&path)?; + let text = fs::read_to_string(&path) + .with_context(|| format!("Could not read {}", path.display()))?; let mut chips = Vec::new(); - let mut features = Vec::new(); + let mut feature_sets = Vec::new(); // We will indicate metadata lines using the `//%` prefix: for line in text.lines().filter(|line| line.starts_with("//%")) { @@ -163,7 +172,7 @@ pub fn load_examples(path: &Path) -> Result> { .trim() .split_ascii_whitespace() .map(|s| s.to_string()) - .collect::>(); + .collect::>(); if split.len() < 2 { bail!( @@ -173,7 +182,7 @@ pub fn load_examples(path: &Path) -> Result> { } // The trailing ':' on metadata keys is optional :) - let key = split.pop_front().unwrap(); + let key = split.swap_remove(0); let key = key.trim_end_matches(':'); if key == "CHIPS" { @@ -182,15 +191,31 @@ pub fn load_examples(path: &Path) -> Result> { .map(|s| Chip::from_str(s, false).unwrap()) .collect::>(); } else if key == "FEATURES" { - features = split.into(); + // Sort the features so they are in a deterministic order: + split.sort(); + feature_sets.push(split); } else { - log::warn!("Unregognized metadata key '{key}', ignoring"); + log::warn!("Unrecognized metadata key '{key}', ignoring"); } } - examples.push(Metadata::new(&path, chips, features)); + if feature_sets.is_empty() { + feature_sets.push(Vec::new()); + } + if action == CargoAction::Build { + // Only build the first feature set for each example. + // Rebuilding with a different feature set just wastes time because the latter + // one will overwrite the former one(s). + feature_sets.truncate(1); + } + for feature_set in feature_sets { + examples.push(Metadata::new(&path, chips.clone(), feature_set)); + } } + // Sort by feature set, to prevent rebuilding packages if not necessary. + examples.sort_by_key(|e| e.feature_set().join(",")); + Ok(examples) } @@ -200,20 +225,26 @@ pub fn execute_app( chip: Chip, target: &str, app: &Metadata, - action: &CargoAction, + action: CargoAction, + mut repeat: usize, + debug: bool, ) -> Result<()> { log::info!( "Building example '{}' for '{}'", app.example_path().display(), chip ); - if !app.features().is_empty() { - log::info!(" Features: {}", app.features().join(",")); + + let mut features = app.feature_set().to_vec(); + if !features.is_empty() { + log::info!("Features: {}", features.join(",")); } + features.push(chip.to_string()); let package = app.example_path().strip_prefix(package_path)?; - log::info!("Package: {:?}", package); - let (bin, subcommand) = if action == &CargoAction::Build { + log::info!("Package: {}", package.display()); + let (bin, subcommand) = if action == CargoAction::Build { + repeat = 1; // Do not repeat builds in a loop let bin = if package.starts_with("src/bin") { format!("--bin={}", app.name()) } else if package.starts_with("tests") { @@ -230,32 +261,37 @@ pub fn execute_app( (format!("--example={}", app.name()), "run") }; - let mut features = app.features().to_vec(); - features.push(chip.to_string()); - let mut builder = CargoArgsBuilder::default() .subcommand(subcommand) - .arg("--release") .target(target) .features(&features) .arg(bin); - // probe-rs cannot currently do auto detection, so we need to tell probe-rs run - // which chip we are testing + if !debug { + builder.add_arg("--release"); + } + if subcommand == "test" && chip == Chip::Esp32c2 { - builder = builder.arg("--").arg("--speed").arg("15000"); + builder.add_arg("--").add_arg("--speed").add_arg("15000"); } // If targeting an Xtensa device, we must use the '+esp' toolchain modifier: if target.starts_with("xtensa") { builder = builder.toolchain("esp"); - builder = builder.arg("-Zbuild-std=core,alloc") + builder.add_arg("-Zbuild-std=core,alloc"); } let args = builder.build(); log::debug!("{args:#?}"); - cargo::run(&args, package_path) + for i in 0..repeat { + if repeat != 1 { + log::info!("Run {}/{}", i + 1, repeat); + } + cargo::run(&args, package_path)?; + } + + Ok(()) } /// Build the specified package, using the given toolchain/target/features if @@ -311,7 +347,8 @@ pub fn build_package( /// Bump the version of the specified package by the specified amount. pub fn bump_version(workspace: &Path, package: Package, amount: Version) -> Result<()> { let manifest_path = workspace.join(package.to_string()).join("Cargo.toml"); - let manifest = fs::read_to_string(&manifest_path)?; + let manifest = fs::read_to_string(&manifest_path) + .with_context(|| format!("Could not read {}", manifest_path.display()))?; let mut manifest = manifest.parse::()?; @@ -530,7 +567,10 @@ pub fn package_version(workspace: &Path, package: Package) -> Result, + /// Build examples in debug mode only + #[arg(long)] + debug: bool, } #[derive(Debug, Args)] @@ -68,6 +71,9 @@ struct TestArgs { /// Optional test to act on (all tests used if omitted) #[arg(short = 't', long)] test: Option, + /// Repeat the tests for a specific number of times. + #[arg(long)] + repeat: Option, } #[derive(Debug, Args)] @@ -153,8 +159,7 @@ fn main() -> Result<()> { .filter_module("xtask", log::LevelFilter::Info) .init(); - let workspace = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let workspace = workspace.parent().unwrap().canonicalize()?; + let workspace = std::env::current_dir()?; match Cli::parse() { Cli::BuildDocumentation(args) => build_documentation(&workspace, args), @@ -200,7 +205,7 @@ fn examples(workspace: &Path, mut args: ExampleArgs, action: CargoAction) -> Res }; // Load all examples which support the specified chip and parse their metadata: - let mut examples = xtask::load_examples(&example_path)? + let mut examples = xtask::load_examples(&example_path, action)? .iter() .filter_map(|example| { if example.supports_chip(args.chip) { @@ -212,7 +217,7 @@ fn examples(workspace: &Path, mut args: ExampleArgs, action: CargoAction) -> Res .collect::>(); // Sort all examples by name: - examples.sort_by(|a, b| a.name().cmp(&b.name())); + examples.sort_by_key(|a| a.name()); // Execute the specified action: match action { @@ -223,17 +228,26 @@ fn examples(workspace: &Path, mut args: ExampleArgs, action: CargoAction) -> Res fn build_examples(args: ExampleArgs, examples: Vec, package_path: &Path) -> Result<()> { // Determine the appropriate build target for the given package and chip: - let target = target_triple(&args.package, &args.chip)?; + let target = target_triple(args.package, &args.chip)?; - if let Some(example) = examples.iter().find(|ex| Some(ex.name()) == args.example) { + if examples + .iter() + .find(|ex| Some(ex.name()) == args.example) + .is_some() + { // Attempt to build only the specified example: - xtask::execute_app( - &package_path, - args.chip, - target, - example, - &CargoAction::Build, - ) + for example in examples.iter().filter(|ex| Some(ex.name()) == args.example) { + xtask::execute_app( + package_path, + args.chip, + target, + example, + CargoAction::Build, + 1, + args.debug, + )?; + } + Ok(()) } else if args.example.is_some() { // An invalid argument was provided: bail!("Example not found or unsupported for the given chip") @@ -241,11 +255,13 @@ fn build_examples(args: ExampleArgs, examples: Vec, package_path: &Pat // Attempt to build each supported example, with all required features enabled: examples.iter().try_for_each(|example| { xtask::execute_app( - &package_path, + package_path, args.chip, target, example, - &CargoAction::Build, + CargoAction::Build, + 1, + args.debug, ) }) } @@ -253,21 +269,31 @@ fn build_examples(args: ExampleArgs, examples: Vec, package_path: &Pat fn run_example(args: ExampleArgs, examples: Vec, package_path: &Path) -> Result<()> { // Determine the appropriate build target for the given package and chip: - let target = target_triple(&args.package, &args.chip)?; + let target = target_triple(args.package, &args.chip)?; // Filter the examples down to only the binary we're interested in, assuming it // actually supports the specified chip: - if let Some(example) = examples.iter().find(|ex| Some(ex.name()) == args.example) { + let mut found_one = false; + for example in examples.iter().filter(|ex| Some(ex.name()) == args.example) { + found_one = true; xtask::execute_app( - &package_path, + package_path, args.chip, target, - &example, - &CargoAction::Run, - ) - } else { - bail!("Example not found or unsupported for the given chip") + example, + CargoAction::Run, + 1, + args.debug, + )?; } + + ensure!( + found_one, + "Example not found or unsupported for {}", + args.chip + ); + + Ok(()) } fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> { @@ -275,26 +301,51 @@ fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> { let package_path = xtask::windows_safe_path(&workspace.join("hil-test")); // Determine the appropriate build target for the given package and chip: - let target = target_triple(&Package::HilTest, &args.chip)?; + let target = target_triple(Package::HilTest, &args.chip)?; // Load all tests which support the specified chip and parse their metadata: - let mut tests = xtask::load_examples(&package_path.join("tests"))? + let mut tests = xtask::load_examples(&package_path.join("tests"), action)? .into_iter() .filter(|example| example.supports_chip(args.chip)) .collect::>(); // Sort all tests by name: - tests.sort_by(|a, b| a.name().cmp(&b.name())); + tests.sort_by_key(|a| a.name()); // Execute the specified action: - if let Some(test) = tests.iter().find(|test| Some(test.name()) == args.test) { - xtask::execute_app(&package_path, args.chip, target, &test, &action) + if tests + .iter() + .find(|test| Some(test.name()) == args.test) + .is_some() + { + for test in tests.iter().filter(|test| Some(test.name()) == args.test) { + xtask::execute_app( + &package_path, + args.chip, + target, + test, + action, + args.repeat.unwrap_or(1), + false, + )?; + } + Ok(()) } else if args.test.is_some() { bail!("Test not found or unsupported for the given chip") } else { let mut failed = Vec::new(); for test in tests { - if xtask::execute_app(&package_path, args.chip, target, &test, &action).is_err() { + if xtask::execute_app( + &package_path, + args.chip, + target, + &test, + action, + args.repeat.unwrap_or(1), + false, + ) + .is_err() + { failed.push(test.name()); } } @@ -311,6 +362,9 @@ fn build_documentation(workspace: &Path, args: BuildDocumentationArgs) -> Result let output_path = workspace.join("docs"); let resources = workspace.join("resources"); + fs::create_dir_all(&output_path) + .with_context(|| format!("Failed to create {}", output_path.display()))?; + let mut packages = HashMap::new(); for package in args.packages { packages.insert( @@ -320,10 +374,12 @@ fn build_documentation(workspace: &Path, args: BuildDocumentationArgs) -> Result } // Copy any additional assets to the documentation's output path: - fs::copy(resources.join("esp-rs.svg"), output_path.join("esp-rs.svg"))?; + fs::copy(resources.join("esp-rs.svg"), output_path.join("esp-rs.svg")) + .context("Failed to copy esp-rs.svg")?; // Render the index and write it out to the documentaiton's output path: - let source = fs::read_to_string(resources.join("index.html.jinja"))?; + let source = fs::read_to_string(resources.join("index.html.jinja")) + .context("Failed to read index.html.jinja")?; let mut env = minijinja::Environment::new(); env.add_template("index", &source)?; @@ -331,7 +387,7 @@ fn build_documentation(workspace: &Path, args: BuildDocumentationArgs) -> Result let tmpl = env.get_template("index")?; let html = tmpl.render(minijinja::context! { packages => packages })?; - fs::write(output_path.join("index.html"), html)?; + fs::write(output_path.join("index.html"), html).context("Failed to write index.html")?; Ok(()) } @@ -352,18 +408,16 @@ fn build_documentation_for_package( validate_package_chip(&package, chip)?; // Determine the appropriate build target for the given package and chip: - let target = target_triple(&package, &chip)?; + let target = target_triple(package, chip)?; // Build the documentation for the specified package, targeting the // specified chip: - xtask::build_documentation(workspace, package, *chip, target)?; - - let docs_path = xtask::windows_safe_path( - &workspace - .join(package.to_string()) - .join("target") - .join(target) - .join("doc"), + let docs_path = xtask::build_documentation(workspace, package, *chip, target)?; + + ensure!( + docs_path.exists(), + "Documentation not found at {}", + docs_path.display() ); let output_path = output_path @@ -373,8 +427,15 @@ fn build_documentation_for_package( let output_path = xtask::windows_safe_path(&output_path); // Create the output directory, and copy the built documentation into it: - fs::create_dir_all(&output_path)?; - copy_dir_all(&docs_path, &output_path)?; + fs::create_dir_all(&output_path) + .with_context(|| format!("Failed to create {}", output_path.display()))?; + copy_dir_all(&docs_path, &output_path).with_context(|| { + format!( + "Failed to copy {} to {}", + docs_path.display(), + output_path.display() + ) + })?; // Build the context object required for rendering this particular build's // information on the documentation index: @@ -384,7 +445,6 @@ fn build_documentation_for_package( chip => chip.to_string(), chip_pretty => chip.pretty_name(), package => package.to_string().replace('-', "_"), - description => format!("{} (targeting {})", package, chip.pretty_name()), }); } @@ -469,7 +529,7 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> { // is *some* overlap) for chip in &args.chips { - let device = Config::for_chip(&chip); + let device = Config::for_chip(chip); match package { Package::EspBacktrace => { @@ -488,13 +548,13 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> { let mut features = format!("--features={chip},ci"); // Cover all esp-hal features where a device is supported - if device.contains(&"usb0".to_owned()) { + if device.contains("usb0") { features.push_str(",usb-otg") } - if device.contains(&"bt".to_owned()) { + if device.contains("bt") { features.push_str(",bluetooth") } - if device.contains(&"psram".to_owned()) { + if device.contains("psram") { // TODO this doesn't test octal psram as it would require a separate build features.push_str(",psram-4m,psram-80mhz") } @@ -524,7 +584,7 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> { } Package::EspIeee802154 => { - if device.contains(&"ieee802154".to_owned()) { + if device.contains("ieee802154") { lint_package( &path, &[ @@ -536,19 +596,19 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> { } } Package::EspLpHal => { - if device.contains(&"lp_core".to_owned()) { + if device.contains("lp_core") { lint_package( &path, &[ "-Zbuild-std=core", &format!("--target={}", chip.lp_target().unwrap()), - &format!("--features={chip},embedded-io,embedded-hal-02"), + &format!("--features={chip},embedded-io"), ], )?; } } Package::EspHalSmartled => { - if device.contains(&"rmt".to_owned()) { + if device.contains("rmt") { lint_package( &path, &[ @@ -594,20 +654,20 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> { Package::EspWifi => { let mut features = format!("--features={chip},async,ps-min-modem,defmt"); - if device.contains(&"wifi".to_owned()) { + if device.contains("wifi") { features .push_str(",wifi-default,esp-now,embedded-svc,embassy-net,dump-packets") } - if device.contains(&"bt".to_owned()) { + if device.contains("bt") { features.push_str(",ble") } - if device.contains(&"coex".to_owned()) { + if device.contains("coex") { features.push_str(",coex") } lint_package( &path, &[ - "-Zbuild-std=core", + "-Zbuild-std=core,alloc", &format!("--target={}", chip.target()), "--no-default-features", &features, @@ -658,7 +718,7 @@ fn lint_package(path: &Path, args: &[&str]) -> Result<()> { .arg("warnings") .build(); - xtask::cargo::run(&cargo_args, &path) + xtask::cargo::run(&cargo_args, path) } fn run_elfs(args: RunElfArgs) -> Result<()> { @@ -676,24 +736,21 @@ fn run_elfs(args: RunElfArgs) -> Result<()> { log::info!("Running test '{}' for '{}'", elf_name, args.chip); - let command = if args.chip == Chip::Esp32c2 { - Command::new("probe-rs") - .arg("run") - .arg("--speed") - .arg("15000") - .arg(elf_path) - .output()? - } else { - Command::new("probe-rs").arg("run").arg(elf_path).output()? + let mut command = Command::new("probe-rs"); + command.arg("run").arg(elf_path); + + if args.chip == Chip::Esp32c2 { + command.arg("--speed").arg("15000"); }; - println!( - "{}\n{}", - String::from_utf8_lossy(&command.stderr), - String::from_utf8_lossy(&command.stdout) - ); + let mut command = command.spawn().context("Failed to execute probe-rs")?; + let status = command + .wait() + .context("Error while waiting for probe-rs to exit")?; + + log::info!("'{elf_name}' done"); - if !command.status.success() { + if !status.success() { failed.push(elf_name); } } @@ -710,7 +767,7 @@ fn run_doctests(workspace: &Path, args: ExampleArgs) -> Result<()> { let package_path = xtask::windows_safe_path(&workspace.join(&package_name)); // Determine the appropriate build target for the given package and chip: - let target = target_triple(&args.package, &args.chip)?; + let target = target_triple(args.package, &args.chip)?; let features = vec![args.chip.to_string()]; // Build up an array of command-line arguments to pass to `cargo`: @@ -735,8 +792,8 @@ fn run_doctests(workspace: &Path, args: ExampleArgs) -> Result<()> { // ---------------------------------------------------------------------------- // Helper Functions -fn target_triple<'a>(package: &'a Package, chip: &'a Chip) -> Result<&'a str> { - if *package == Package::EspLpHal { +fn target_triple(package: Package, chip: &Chip) -> Result<&str> { + if package == Package::EspLpHal { chip.lp_target() } else { Ok(chip.target()) @@ -744,13 +801,12 @@ fn target_triple<'a>(package: &'a Package, chip: &'a Chip) -> Result<&'a str> { } fn validate_package_chip(package: &Package, chip: &Chip) -> Result<()> { - if *package == Package::EspLpHal && !chip.has_lp_core() { - bail!( - "Invalid chip provided for package '{}': '{}'", - package, - chip - ); - } + ensure!( + *package != Package::EspLpHal || chip.has_lp_core(), + "Invalid chip provided for package '{}': '{}'", + package, + chip + ); Ok(()) } diff --git a/xtensa-lx-rt/CHANGELOG.md b/xtensa-lx-rt/CHANGELOG.md new file mode 100644 index 00000000000..5d3a29173ed --- /dev/null +++ b/xtensa-lx-rt/CHANGELOG.md @@ -0,0 +1,28 @@ +# 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 + +### Added + +### Changed + +### Fixed + +### Removed + +## 0.17.1 - 2024-09-02 + +### Added + +- Better diagnostics when using floats inside an interrupt handler when using the default hard fault handler (#2044) + +### Fixed + +- Store state of FP coprocessor in stack memory (#2057) + +## Initial releases diff --git a/xtensa-lx-rt/Cargo.toml b/xtensa-lx-rt/Cargo.toml index ea79107f5e5..9fce8257d2f 100644 --- a/xtensa-lx-rt/Cargo.toml +++ b/xtensa-lx-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xtensa-lx-rt" -version = "0.16.0" +version = "0.17.1" edition = "2021" rust-version = "1.65" description = "Minimal startup/runtime for Xtensa LX CPUs" @@ -14,18 +14,18 @@ features = ["esp32"] [dependencies] bare-metal = "1.0.0" -document-features = "0.2.8" -macros = { version = "0.2.1", package = "xtensa-lx-rt-proc-macros", path = "./procmacros" } +document-features = "0.2.10" +macros = { version = "0.2.2", package = "xtensa-lx-rt-proc-macros", path = "./procmacros" } r0 = "1.0.0" -xtensa-lx = { version = "0.9.0", path = "../xtensa-lx" } +xtensa-lx = "0.9.0" [build-dependencies] anyhow = "1.0.86" enum-as-inner = "0.6.0" -minijinja = "2.0.3" -serde = { version = "1.0.204", features = ["derive"] } +minijinja = "2.2.0" +serde = { version = "1.0.209", features = ["derive"] } strum = { version = "0.26.3", features = ["derive"] } -toml = "0.8.10" +toml = "0.8.19" [features] ## Save and restore float registers for exceptions diff --git a/xtensa-lx-rt/procmacros/src/lib.rs b/xtensa-lx-rt/procmacros/src/lib.rs index 1b601717bd4..7d2aad39c11 100644 --- a/xtensa-lx-rt/procmacros/src/lib.rs +++ b/xtensa-lx-rt/procmacros/src/lib.rs @@ -128,6 +128,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { ) } + #[doc(hidden)] #[allow(clippy::inline_always)] #[inline(always)] #f @@ -398,6 +399,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { ) } + #[doc(hidden)] #[allow(clippy::inline_always)] #[inline(always)] #f diff --git a/xtensa-lx-rt/src/exception.rs b/xtensa-lx-rt/src/exception.rs index a088e4f3fe3..c24efaa887e 100644 --- a/xtensa-lx-rt/src/exception.rs +++ b/xtensa-lx-rt/src/exception.rs @@ -33,7 +33,7 @@ pub use context::Context; /// general exceptions, which vector to the user, kernel, or double-exception /// vectors). #[allow(unused)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] #[repr(C)] pub enum ExceptionCause { /// Illegal Instruction diff --git a/xtensa-lx-rt/src/exception/asm.rs b/xtensa-lx-rt/src/exception/asm.rs index d4b475774fb..9b92d4e354d 100644 --- a/xtensa-lx-rt/src/exception/asm.rs +++ b/xtensa-lx-rt/src/exception/asm.rs @@ -11,25 +11,25 @@ use crate::cfg_asm; // Additionally there is a chunk of memory reserved for spilled registers. global_asm!( " - .set XT_STK_PC, 0 - .set XT_STK_PS, 4 - .set XT_STK_A0, 8 - .equ XT_STK_A1, 12 - .set XT_STK_A2, 16 - .set XT_STK_A3, 20 - .set XT_STK_A4, 24 - .set XT_STK_A5, 28 - .set XT_STK_A6, 32 - .set XT_STK_A7, 36 - .set XT_STK_A8, 40 - .set XT_STK_A9, 44 - .set XT_STK_A10, 48 - .set XT_STK_A11, 52 - .set XT_STK_A12, 56 - .set XT_STK_A13, 60 - .set XT_STK_A14, 64 - .set XT_STK_A15, 68 - .set XT_STK_SAR, 72 + .set XT_STK_PC, 0 + .set XT_STK_PS, 4 + .set XT_STK_A0, 8 + .equ XT_STK_A1, 12 + .set XT_STK_A2, 16 + .set XT_STK_A3, 20 + .set XT_STK_A4, 24 + .set XT_STK_A5, 28 + .set XT_STK_A6, 32 + .set XT_STK_A7, 36 + .set XT_STK_A8, 40 + .set XT_STK_A9, 44 + .set XT_STK_A10, 48 + .set XT_STK_A11, 52 + .set XT_STK_A12, 56 + .set XT_STK_A13, 60 + .set XT_STK_A14, 64 + .set XT_STK_A15, 68 + .set XT_STK_SAR, 72 .set XT_STK_EXCCAUSE, 76 .set XT_STK_EXCVADDR, 80 .set XT_STK_LBEG, 84 // Registers for Loop Option @@ -66,6 +66,7 @@ global_asm!( .set XT_STK_F14, 208 .set XT_STK_F15, 212 .set XT_STK_TMP, 216 + .set XT_STK_CPENABLE, 220 .set XT_STK_FRMSZ, 256 // needs to be multiple of 16 and enough additional free space // for the registers spilled to the stack (max 8 registers / 0x20 bytes) @@ -126,6 +127,8 @@ unsafe extern "C" fn save_context() { #[cfg(all(XCHAL_HAVE_CP, not(feature = "float-save-restore")))] " /* Disable coprocessor, any use of floats in ISRs will cause an exception unless float-save-restore feature is enabled */ + rsr a3, CPENABLE + s32i a3, sp, +XT_STK_CPENABLE movi a3, 0 wsr a3, CPENABLE rsync @@ -177,11 +180,11 @@ unsafe extern "C" fn save_context() { #[cfg(all(feature = "float-save-restore", XCHAL_HAVE_DFP_ACCEL))] " // Double Precision Accelerator Option - rur a3, f64r_lo + rur a3, f64r_lo s32i a3, sp, +XT_STK_F64R_LO rur a3, f64r_hi s32i a3, sp, +XT_STK_F64R_HI - rur a3, f64s + rur a3, f64s s32i a3, sp, +XT_STK_F64S ", #[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))] @@ -226,7 +229,7 @@ unsafe extern "C" fn save_context() { // used as a temporary by this code, the temporary value would get stored // onto the stack, instead of the real value. // - + rsr a2, PS // to be restored after SPILL_REGISTERS movi a0, PS_INTLEVEL_MASK and a3, a2, a0 // get the current INTLEVEL @@ -237,11 +240,11 @@ unsafe extern "C" fn save_context() { or a3, a3, a0 wsr a3, ps rsr a0, EPC1 - + addmi sp, sp, +XT_STK_FRMSZ // go back to spill register region SPILL_REGISTERS addmi sp, sp, -XT_STK_FRMSZ // return the current stack pointer - + wsr a2, PS // restore to the value at entry rsync wsr a0, EPC1 @@ -288,9 +291,9 @@ global_asm!( // file on an LX6 core (ESP-32) I'm measuring 145 cycles to spill // registers with this vs. 279 (!) to do it with // xthal_spill_windows(). - + .macro SPILL_REGISTERS - and a12, a12, a12 + and a12, a12, a12 rotw 3 and a12, a12, a12 rotw 3 @@ -311,10 +314,10 @@ global_asm!( addmi sp, sp, -XT_STK_FRMSZ // only allow multiple of 256 s32i a0, sp, +XT_STK_A1 // save interruptee's A1/SP - s32e a0, sp, -12 // for debug backtrace + s32e a0, sp, -12 // for debug backtrace .ifc \level,1 - rsr a0, PS + rsr a0, PS s32i a0, sp, +XT_STK_PS // save interruptee's PS rsr a0, EXCCAUSE @@ -323,15 +326,15 @@ global_asm!( s32i a0, sp, +XT_STK_EXCVADDR .else rsr a0, EPS\level - s32i a0, sp, +XT_STK_PS // save interruptee's PS + s32i a0, sp, +XT_STK_PS // save interruptee's PS .endif - rsr a0, EPC\level - s32i a0, sp, +XT_STK_PC // save interruptee's PC - s32e a0, sp, -16 // for debug backtrace + rsr a0, EPC\level + s32i a0, sp, +XT_STK_PC // save interruptee's PC + s32e a0, sp, -16 // for debug backtrace - rsr a0, EXCSAVE\level - s32i a0, sp, +XT_STK_A0 // save interruptee's A0 + rsr a0, EXCSAVE\level + s32i a0, sp, +XT_STK_A0 // save interruptee's A0 call0 save_context @@ -429,8 +432,8 @@ unsafe extern "C" fn restore_context() { ", #[cfg(all(XCHAL_HAVE_CP, not(feature = "float-save-restore")))] " - /* Re-enable coprocessor(s) after ISR */ - movi a3, 8 /* XCHAL_CP_MAXCFG */ + /* Restore coprocessor state after ISR */ + l32i a3, sp, +XT_STK_CPENABLE wsr a3, CPENABLE rsync ", @@ -458,26 +461,26 @@ unsafe extern "C" fn restore_context() { global_asm!( r#" .macro RESTORE_CONTEXT level:req - - // Restore context and return + + // Restore context and return call0 restore_context .ifc \level,1 - l32i a0, sp, +XT_STK_PS // retrieve interruptee's PS + l32i a0, sp, +XT_STK_PS // retrieve interruptee's PS wsr a0, PS - l32i a0, sp, +XT_STK_PC // retrieve interruptee's PC + l32i a0, sp, +XT_STK_PC // retrieve interruptee's PC wsr a0, EPC\level .else - l32i a0, sp, +XT_STK_PS // retrieve interruptee's PS + l32i a0, sp, +XT_STK_PS // retrieve interruptee's PS wsr a0, EPS\level - l32i a0, sp, +XT_STK_PC // retrieve interruptee's PC - wsr a0, EPC\level - .endif - - l32i a0, sp, +XT_STK_A0 // retrieve interruptee's A0 - l32i sp, sp, +XT_STK_A1 // remove exception frame - rsync // ensure PS and EPC written - + l32i a0, sp, +XT_STK_PC // retrieve interruptee's PC + wsr a0, EPC\level + .endif + + l32i a0, sp, +XT_STK_A0 // retrieve interruptee's A0 + l32i sp, sp, +XT_STK_A1 // remove exception frame + rsync // ensure PS and EPC written + .endm "# ); @@ -518,8 +521,8 @@ unsafe extern "C" fn __default_naked_exception() { .RestoreContext: RESTORE_CONTEXT 1 - - rfe // PS.EXCM is cleared + + rfe // PS.EXCM is cleared ", options(noreturn) ) @@ -539,44 +542,44 @@ unsafe extern "C" fn __default_naked_double_exception() { " mov a0, a1 // save a1/sp addmi sp, sp, -XT_STK_FRMSZ // only allow multiple of 256 - + s32i a0, sp, +XT_STK_A1 // save interruptee's A1/SP - s32e a0, sp, -12 // for debug backtrace - - rsr a0, PS + s32e a0, sp, -12 // for debug backtrace + + rsr a0, PS s32i a0, sp, +XT_STK_PS // save interruptee's PS - + rsr a0, EXCCAUSE s32i a0, sp, +XT_STK_EXCCAUSE rsr a0, EXCVADDR s32i a0, sp, +XT_STK_EXCVADDR - - rsr a0, DEPC - s32i a0, sp, +XT_STK_PC // save interruptee's PC - s32e a0, sp, -16 // for debug backtrace - + + rsr a0, DEPC + s32i a0, sp, +XT_STK_PC // save interruptee's PC + s32e a0, sp, -16 // for debug backtrace + rsr a0, EXCSAVE7 // ok to reuse EXCSAVE7 for double exception as long as // double exception is not in first couple of instructions // of level 7 handler - s32i a0, sp, +XT_STK_A0 // save interruptee's A0 - + s32i a0, sp, +XT_STK_A0 // save interruptee's A0 + call0 save_context l32i a6, sp, +XT_STK_EXCCAUSE // put cause in a6 = a2 in callee mov a7, sp // put address of save frame in a7=a3 in callee call4 __exception // call handler <= actual call! - // Restore context and return + // Restore context and return call0 restore_context - l32i a0, sp, +XT_STK_PS // retrieve interruptee's PS + l32i a0, sp, +XT_STK_PS // retrieve interruptee's PS wsr a0, PS - l32i a0, sp, +XT_STK_PC // retrieve interruptee's PC + l32i a0, sp, +XT_STK_PC // retrieve interruptee's PC wsr a0, EPC1 - l32i a0, sp, +XT_STK_A0 // retrieve interruptee's A0 - l32i sp, sp, +XT_STK_A1 // remove exception frame - rsync // ensure PS and EPC written + l32i a0, sp, +XT_STK_A0 // retrieve interruptee's A0 + l32i sp, sp, +XT_STK_A1 // remove exception frame + rsync // ensure PS and EPC written rfde ", diff --git a/xtensa-lx-rt/src/exception/context.rs b/xtensa-lx-rt/src/exception/context.rs index 4c4099dcddb..93964a5cce2 100644 --- a/xtensa-lx-rt/src/exception/context.rs +++ b/xtensa-lx-rt/src/exception/context.rs @@ -87,6 +87,19 @@ pub struct Context { pub F15: u32, } +impl Default for Context { + fn default() -> Self { + Self::new() + } +} + +impl Context { + /// Creates a new, zeroed out context. + pub const fn new() -> Self { + unsafe { core::mem::zeroed() } + } +} + extern "Rust" { /// The exception assembly jumps here once registers have been spilled fn __exception(cause: ExceptionCause, save_frame: &mut Context); @@ -121,6 +134,14 @@ unsafe extern "C" fn __default_exception(cause: ExceptionCause, save_frame: &mut #[no_mangle] #[link_section = ".rwtext"] extern "C" fn __default_user_exception(cause: ExceptionCause, save_frame: &Context) { + #[cfg(any(feature = "esp32", feature = "esp32s3"))] + if cause == ExceptionCause::Cp0Disabled { + panic!( + "Access to the floating point coprocessor is not allowed. You may want to enable the `float-save-restore` feature of the `xtensa-lx-rt` crate. {:08x?}", + save_frame + ) + } + panic!("Exception: {:?}, {:08x?}", cause, save_frame) } diff --git a/xtensa-lx/src/lib.rs b/xtensa-lx/src/lib.rs index 353870e6db1..d334dae215c 100644 --- a/xtensa-lx/src/lib.rs +++ b/xtensa-lx/src/lib.rs @@ -80,9 +80,9 @@ pub fn get_program_counter() -> *const u32 { unsafe { asm!(" mov {1}, {2} - call0 1f + call0 2f .align 4 - 1: + 2: mov {0}, {2} mov {2}, {1} ", out(reg) x, out(reg) _, out(reg) _, options(nostack))