Merge branch 'embassy-rs:main' into update_doc_comment_for_adc_read
This commit is contained in:
commit
b17da5b794
8
.github/ci/test.sh
vendored
8
.github/ci/test.sh
vendored
@ -29,8 +29,10 @@ cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --feat
|
||||
cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp2040,_test
|
||||
cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp235xa,_test
|
||||
|
||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,exti,time-driver-any,exti
|
||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f732ze,exti,time-driver-any,exti
|
||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f769ni,exti,time-driver-any,exti
|
||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,time-driver-any,exti,single-bank
|
||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,time-driver-any,exti,dual-bank
|
||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f732ze,time-driver-any,exti
|
||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f769ni,time-driver-any,exti,single-bank
|
||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f769ni,time-driver-any,exti,dual-bank
|
||||
|
||||
cargo test --manifest-path ./embassy-net-adin1110/Cargo.toml
|
||||
|
||||
85
README.md
85
README.md
@ -1,52 +1,55 @@
|
||||
# Embassy
|
||||
|
||||
Embassy is the next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries.
|
||||
Embassy is the next-generation framework for embedded applications. Write safe, correct, and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries.
|
||||
|
||||
## [Documentation](https://embassy.dev/book/index.html) - [API reference](https://docs.embassy.dev/) - [Website](https://embassy.dev/) - [Chat](https://matrix.to/#/#embassy-rs:matrix.org)
|
||||
|
||||
## <a href="https://embassy.dev/book/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
|
||||
## Rust + async ❤️ embedded
|
||||
|
||||
The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system.
|
||||
The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector, or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system.
|
||||
|
||||
Rust's <a href="https://rust-lang.github.io/async-book/">async/await</a> allows for unprecedentedly easy and efficient multitasking in embedded systems. Tasks get transformed at compile time into state machines that get run cooperatively. It requires no dynamic memory allocation, and runs on a single stack, so no per-task stack size tuning is required. It obsoletes the need for a traditional RTOS with kernel context switching, and is <a href="https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown">faster and smaller than one!</a>
|
||||
Rust's [async/await](https://rust-lang.github.io/async-book/) allows for unprecedentedly easy and efficient multitasking in embedded systems. Tasks get transformed at compile time into state machines that get run cooperatively. It requires no dynamic memory allocation and runs on a single stack, so no per-task stack size tuning is required. It obsoletes the need for a traditional RTOS with kernel context switching, and is [faster and smaller than one!](https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown)
|
||||
|
||||
## Batteries included
|
||||
|
||||
- **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
|
||||
- <a href="https://docs.embassy.dev/embassy-stm32/">embassy-stm32</a>, for all STM32 microcontroller families.
|
||||
- <a href="https://docs.embassy.dev/embassy-nrf/">embassy-nrf</a>, for the Nordic Semiconductor nRF52, nRF53, nRF54 and nRF91 series.
|
||||
- <a href="https://docs.embassy.dev/embassy-rp/">embassy-rp</a>, for the Raspberry Pi RP2040 and RP23xx microcontrollers.
|
||||
- <a href="https://docs.embassy.dev/embassy-mspm0/">embassy-mspm0</a>, for the Texas Instruments MSPM0 microcontrollers.
|
||||
- <a href="https://github.com/esp-rs">esp-rs</a>, for the Espressif Systems ESP32 series of chips.
|
||||
- Embassy HAL support for Espressif chips, as well as Async WiFi, Bluetooth and ESP-NOW, is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository.
|
||||
- <a href="https://github.com/ch32-rs/ch32-hal">ch32-hal</a>, for the WCH 32-bit RISC-V(CH32V) series of chips.
|
||||
- <a href="https://github.com/AlexCharlton/mpfs-hal">mpfs-hal</a>, for the Microchip PolarFire SoC.
|
||||
- <a href="https://github.com/py32-rs/py32-hal">py32-hal</a>, for the Puya Semiconductor PY32 series of microcontrollers.
|
||||
- **Hardware Abstraction Layers
|
||||
** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
|
||||
- [embassy-stm32](https://docs.embassy.dev/embassy-stm32/), for all STM32 microcontroller families.
|
||||
- [embassy-nrf](https://docs.embassy.dev/embassy-nrf/), for the Nordic Semiconductor nRF52, nRF53, nRF54 and nRF91 series.
|
||||
- [embassy-rp](https://docs.embassy.dev/embassy-rp/), for the Raspberry Pi RP2040 and RP23xx microcontrollers.
|
||||
- [embassy-mspm0](https://docs.embassy.dev/embassy-mspm0/), for the Texas Instruments MSPM0 microcontrollers.
|
||||
- [esp-rs](https://github.com/esp-rs), for the Espressif Systems ESP32 series of chips.
|
||||
- Embassy HAL support for Espressif chips, as well as Async Wi-Fi, Bluetooth, and ESP-NOW, is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository.
|
||||
- [ch32-hal](https://github.com/ch32-rs/ch32-hal), for the WCH 32-bit RISC-V(CH32V) series of chips.
|
||||
- [mpfs-hal](https://github.com/AlexCharlton/mpfs-hal), for the Microchip PolarFire SoC.
|
||||
- [py32-hal](https://github.com/py32-rs/py32-hal), for the Puya Semiconductor PY32 series of microcontrollers.
|
||||
|
||||
- **Time that Just Works** -
|
||||
No more messing with hardware timers. <a href="https://docs.embassy.dev/embassy-time">embassy_time</a> provides Instant, Duration and Timer types that are globally available and never overflow.
|
||||
- **Time that Just Works** -
|
||||
No more messing with hardware timers. [embassy_time](https://docs.embassy.dev/embassy-time) provides Instant, Duration, and Timer types that are globally available and never overflow.
|
||||
|
||||
- **Real-time ready** -
|
||||
Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities, so that higher priority tasks preempt lower priority ones. See the <a href="https://github.com/embassy-rs/embassy/blob/master/examples/nrf52840/src/bin/multiprio.rs">example</a>.
|
||||
- **Real-time ready** -
|
||||
Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities so that higher priority tasks preempt lower priority ones. See the [example](https://github.com/embassy-rs/embassy/blob/master/examples/nrf52840/src/bin/multiprio.rs).
|
||||
|
||||
- **Low-power ready** -
|
||||
Easily build devices with years of battery life. The async executor automatically puts the core to sleep when there's no work to do. Tasks are woken by interrupts, there is no busy-loop polling while waiting.
|
||||
|
||||
- **Networking** -
|
||||
The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently.
|
||||
- **Low-power ready** -
|
||||
Easily build devices with years of battery life. The async executor automatically puts the core to sleep when there's no work to do. Tasks are woken by interrupts, there is no busy-loop polling while waiting.
|
||||
|
||||
- **Networking** -
|
||||
The [embassy-net](https://docs.embassy.dev/embassy-net/) network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP, and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently.
|
||||
|
||||
- **Bluetooth**
|
||||
- The <a href="https://github.com/embassy-rs/trouble">trouble</a> crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the <a href="https://github.com/embassy-rs/bt-hci">bt-hci</a> traits (currently `nRF52`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported).
|
||||
- The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
||||
- The <a href="https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan">embassy-stm32-wpan</a> crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers.
|
||||
- The [trouble](https://github.com/embassy-rs/trouble) crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the [bt-hci](https://github.com/embassy-rs/bt-hci) traits (currently
|
||||
`nRF52`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported).
|
||||
- The [nrf-softdevice](https://github.com/embassy-rs/nrf-softdevice) crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
||||
- The [embassy-stm32-wpan](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan) crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers.
|
||||
|
||||
- **LoRa** - The <a href="https://github.com/lora-rs/lora-rs">lora-rs</a> project provides an async LoRa and LoRaWAN stack that works well on Embassy.
|
||||
- **LoRa** -
|
||||
The [lora-rs](https://github.com/lora-rs/lora-rs) project provides an async LoRa and LoRaWAN stack that works well on Embassy.
|
||||
|
||||
- **USB** -
|
||||
<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
||||
|
||||
- **Bootloader and DFU** -
|
||||
<a href="https://github.com/embassy-rs/embassy/tree/master/embassy-boot">embassy-boot</a> is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
|
||||
- **USB** -
|
||||
[embassy-usb](https://docs.embassy.dev/embassy-usb/) implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
||||
|
||||
- **Bootloader and DFU** -
|
||||
[embassy-boot](https://github.com/embassy-rs/embassy/tree/master/embassy-boot) is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
|
||||
|
||||
## Sneak peek
|
||||
|
||||
@ -93,13 +96,15 @@ async fn main(spawner: Spawner) {
|
||||
|
||||
## Examples
|
||||
|
||||
Examples are found in the `examples/` folder separated by the chip manufacturer they are designed to run on. For example:
|
||||
Examples are found in the
|
||||
`examples/` folder separated by the chip manufacturer they are designed to run on. For example:
|
||||
|
||||
* `examples/nrf52840` run on the `nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards.
|
||||
* `examples/nrf5340` run on the `nrf5340-dk` board (PCA10095).
|
||||
* `examples/stm32xx` for the various STM32 families.
|
||||
* `examples/rp` are for the RP2040 chip.
|
||||
* `examples/std` are designed to run locally on your PC.
|
||||
* `examples/nrf52840` run on the
|
||||
`nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards.
|
||||
* `examples/nrf5340` run on the `nrf5340-dk` board (PCA10095).
|
||||
* `examples/stm32xx` for the various STM32 families.
|
||||
* `examples/rp` are for the RP2040 chip.
|
||||
* `examples/std` are designed to run locally on your PC.
|
||||
|
||||
### Running examples
|
||||
|
||||
@ -126,7 +131,7 @@ cargo run --release --bin blinky
|
||||
|
||||
For more help getting started, see [Getting Started][1] and [Running the Examples][2].
|
||||
|
||||
## Developing Embassy with Rust Analyzer based editors
|
||||
## Developing Embassy with Rust Analyzer-based editors
|
||||
|
||||
The [Rust Analyzer](https://rust-analyzer.github.io/) is used by [Visual Studio Code](https://code.visualstudio.com/)
|
||||
and others. Given the multiple targets that Embassy serves, there is no Cargo workspace file. Instead, the Rust Analyzer
|
||||
@ -136,7 +141,7 @@ please refer to the `.vscode/settings.json` file's `rust-analyzer.linkedProjects
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
Embassy is guaranteed to compile on stable Rust 1.75 and up. It *might*
|
||||
compile with older versions but that may change in any new patch release.
|
||||
compile with older versions, but that may change in any new patch release.
|
||||
|
||||
## Why the name?
|
||||
|
||||
|
||||
27
ci.sh
27
ci.sh
@ -19,7 +19,7 @@ fi
|
||||
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
||||
|
||||
BUILD_EXTRA=""
|
||||
if [ $TARGET = "x86_64-unknown-linux-gnu" ]; then
|
||||
if [ $TARGET = "x86_64-unknown-linux-gnu" ] || [ $TARGET = "aarch64-unknown-linux-gnu" ]; then
|
||||
BUILD_EXTRA="--- build --release --manifest-path examples/std/Cargo.toml --target $TARGET --artifact-dir out/examples/std"
|
||||
fi
|
||||
|
||||
@ -88,16 +88,17 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,defmt,rp235xa \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,log,rp235xa \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,rp235xa,binary-info \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,single-bank,defmt \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \
|
||||
@ -153,10 +154,10 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0c1ve,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,low-power,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,low-power,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32wl54jc-cm0p,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wle5jb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,dual-bank,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103re,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f100c4,defmt,exti,time-driver-any,time \
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
# replace nRF82840_xxAA with your chip as listed in `probe-run --list-chips`
|
||||
runner = "probe-run --chip nRF52840_xxAA"
|
||||
# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
|
||||
runner = "probe-rs run --chip nRF52840_xxAA"
|
||||
|
||||
[build]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
runner = "probe-run --chip STM32L475VG"
|
||||
# replace your chip as listed in `probe-rs chip list`
|
||||
runner = "probe-rs run --chip STM32L475VG"
|
||||
|
||||
rustflags = [
|
||||
"-C", "link-arg=--nmagic",
|
||||
|
||||
@ -268,7 +268,7 @@ General steps:
|
||||
1. Find out which memory region BDMA has access to. You can get this information from the bus matrix and the memory mapping table in the STM32 datasheet.
|
||||
2. Add the memory region to `memory.x`, you can modify the generated one from https://github.com/embassy-rs/stm32-data-generated/tree/main/data/chips.
|
||||
3. You might need to modify `build.rs` to make cargo pick up the modified `memory.x`.
|
||||
4. In your code, access the defined memory region using `#[link_section = ".xxx"]`
|
||||
4. In your code, access the defined memory region using `#[unsafe(link_section = ".xxx")]`
|
||||
5. Copy data to that region before using BDMA.
|
||||
|
||||
See link:https://github.com/embassy-rs/embassy/blob/main/examples/stm32h7/src/bin/spi_bdma.rs[SMT32H7 SPI BDMA example] for more details.
|
||||
|
||||
@ -66,7 +66,7 @@ If everything worked correctly, you should see a blinking LED on your board, and
|
||||
[source]
|
||||
----
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 1m 56s
|
||||
Running `probe-run --chip STM32F407VGTx target/thumbv7em-none-eabi/debug/blinky`
|
||||
Running `probe-rs run --chip STM32F407VGTx target/thumbv7em-none-eabi/debug/blinky`
|
||||
(HOST) INFO flashing program (71.36 KiB)
|
||||
(HOST) INFO success!
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@ -9,5 +9,6 @@ The link: link:https://github.com/embassy-rs/embassy/tree/main/embassy-imxrt[Emb
|
||||
|
||||
The following peripherals have a HAL implementation at present
|
||||
|
||||
* CRC
|
||||
* GPIO
|
||||
|
||||
* RNG
|
||||
|
||||
@ -150,7 +150,7 @@ stm32g474-example
|
||||
# Before upgrading check that everything is available on all tier1 targets here:
|
||||
# https://rust-lang.github.io/rustup-components-history
|
||||
[toolchain]
|
||||
channel = "nightly-2023-11-01"
|
||||
channel = "1.85"
|
||||
components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ]
|
||||
targets = ["thumbv7em-none-eabi"]
|
||||
----
|
||||
|
||||
@ -85,9 +85,9 @@ A minimal example:
|
||||
[source,toml]
|
||||
----
|
||||
[toolchain]
|
||||
channel = "nightly-2023-08-19" # <- as of writing, this is the exact rust version embassy uses
|
||||
channel = "1.85" # <- as of writing, this is the exact rust version embassy uses
|
||||
components = [ "rust-src", "rustfmt" ] # <- optionally add "llvm-tools-preview" for some extra features like "cargo size"
|
||||
targets = [
|
||||
"thumbv6m-none-eabi" # <-change for your platform
|
||||
"thumbv6m-none-eabi" # <- change for your platform
|
||||
]
|
||||
----
|
||||
|
||||
@ -12,7 +12,7 @@ documentation = "https://docs.embassy.dev/embassy-imxrt"
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-imxrt-v$VERSION/embassy-imxrt/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-imxrt/src/"
|
||||
features = ["defmt", "unstable-pac", "time", "time-driver"]
|
||||
features = ["defmt", "unstable-pac", "time", "time-driver-os-timer"]
|
||||
flavors = [
|
||||
{ regex_feature = "mimxrt6.*", target = "thumbv8m.main-none-eabihf" }
|
||||
]
|
||||
@ -37,9 +37,12 @@ defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt", "mimxr
|
||||
time = ["dep:embassy-time", "embassy-embedded-hal/time"]
|
||||
|
||||
## Enable custom embassy time-driver implementation, using 32KHz RTC
|
||||
time-driver-rtc = ["_time-driver"]
|
||||
time-driver-rtc = ["_time-driver", "embassy-time-driver?/tick-hz-1_000"]
|
||||
|
||||
_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"]
|
||||
## Enable custom embassy time-driver implementation, using 1MHz OS Timer
|
||||
time-driver-os-timer = ["_time-driver", "embassy-time-driver?/tick-hz-1_000_000"]
|
||||
|
||||
_time-driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"]
|
||||
|
||||
## Reexport the PAC for the currently enabled chip at `embassy_imxrt::pac` (unstable)
|
||||
unstable-pac = []
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
//! Clock configuration for the `RT6xx`
|
||||
use core::sync::atomic::{AtomicU32, AtomicU8, Ordering};
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
use defmt;
|
||||
use paste::paste;
|
||||
|
||||
use crate::pac;
|
||||
@ -503,7 +501,6 @@ impl ConfigurableClock for LposcConfig {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("failed to convert desired clock rate, {:#}, to LPOSC Freq", freq);
|
||||
Err(ClockError::InvalidFrequency)
|
||||
}
|
||||
}
|
||||
@ -549,7 +546,6 @@ impl ConfigurableClock for FfroConfig {
|
||||
Ok(())
|
||||
}
|
||||
fn get_clock_rate(&self) -> Result<u32, ClockError> {
|
||||
trace!("getting ffro clock rate");
|
||||
Ok(self.freq.load(Ordering::Relaxed))
|
||||
}
|
||||
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
||||
@ -616,7 +612,6 @@ impl ConfigurableClock for SfroConfig {
|
||||
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
||||
if self.state == State::Enabled {
|
||||
if freq == SFRO_FREQ {
|
||||
trace!("Sfro frequency is already set at 16MHz");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ClockError::InvalidFrequency)
|
||||
@ -677,7 +672,6 @@ impl MultiSourceClock for MainPllClkConfig {
|
||||
}
|
||||
MainPllClkSrc::SFRO => {
|
||||
if !clock_src_config.is_enabled() {
|
||||
error!("Can't set SFRO as source for MainPll as it's not enabled");
|
||||
return Err(ClockError::ClockNotEnabled);
|
||||
}
|
||||
// check if desired frequency is a valid multiple of 16m SFRO clock
|
||||
@ -703,7 +697,6 @@ impl ConfigurableClock for MainPllClkConfig {
|
||||
}
|
||||
fn disable(&self) -> Result<(), ClockError> {
|
||||
if self.is_enabled() {
|
||||
error!("Attempting to reset the Main Pll Clock, should be resetting its source");
|
||||
Err(ClockError::ClockNotSupported)
|
||||
} else {
|
||||
Err(ClockError::ClockNotEnabled)
|
||||
@ -719,7 +712,6 @@ impl ConfigurableClock for MainPllClkConfig {
|
||||
}
|
||||
fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError> {
|
||||
if self.is_enabled() {
|
||||
trace!("attempting to set main pll clock rate");
|
||||
// SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0
|
||||
let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };
|
||||
let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };
|
||||
@ -741,15 +733,12 @@ impl ConfigurableClock for MainPllClkConfig {
|
||||
base_rate = r;
|
||||
}
|
||||
MainPllClkSrc::FFRO => {
|
||||
trace!("found FFRO as source, wait a bit");
|
||||
delay_loop_clocks(1000, desired_freq);
|
||||
match clkctl0.ffroctl0().read().trim_range().is_ffro_48mhz() {
|
||||
true => base_rate = Into::into(FfroFreq::Ffro48m),
|
||||
false => base_rate = Into::into(FfroFreq::Ffro60m),
|
||||
}
|
||||
trace!("found ffro rate to be: {:#}", base_rate);
|
||||
if div == 2 {
|
||||
trace!("dividing FFRO rate by 2");
|
||||
clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2());
|
||||
delay_loop_clocks(150, desired_freq);
|
||||
base_rate /= 2;
|
||||
@ -763,10 +752,8 @@ impl ConfigurableClock for MainPllClkConfig {
|
||||
}
|
||||
};
|
||||
base_rate *= u32::from(mult);
|
||||
trace!("calculated base rate at: {:#}", base_rate);
|
||||
if base_rate != freq {
|
||||
// make sure to power syspll back up before returning the error
|
||||
error!("invalid frequency found, powering syspll back up before returning error. Check div and mult");
|
||||
// Clear System PLL reset
|
||||
clkctl0.syspll0ctl0().write(|w| w.reset().normal());
|
||||
// Power up SYSPLL
|
||||
@ -775,13 +762,11 @@ impl ConfigurableClock for MainPllClkConfig {
|
||||
.write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0());
|
||||
return Err(ClockError::InvalidFrequency);
|
||||
}
|
||||
trace!("setting default num and denom");
|
||||
// SAFETY: unsafe needed to write the bits for the num and demon fields
|
||||
clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0b0) });
|
||||
clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0b1) });
|
||||
delay_loop_clocks(30, desired_freq);
|
||||
self.mult.store(mult, Ordering::Relaxed);
|
||||
trace!("setting self.mult as: {:#}", mult);
|
||||
match mult {
|
||||
16 => {
|
||||
clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_16());
|
||||
@ -803,7 +788,6 @@ impl ConfigurableClock for MainPllClkConfig {
|
||||
}
|
||||
_ => return Err(ClockError::InvalidMult),
|
||||
}
|
||||
trace!("clear syspll reset");
|
||||
// Clear System PLL reset
|
||||
clkctl0.syspll0ctl0().modify(|_r, w| w.reset().normal());
|
||||
// Power up SYSPLL
|
||||
@ -819,7 +803,6 @@ impl ConfigurableClock for MainPllClkConfig {
|
||||
clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable());
|
||||
delay_loop_clocks(15, desired_freq);
|
||||
|
||||
trace!("setting new PFD0 bits");
|
||||
// gate the output and clear bits.
|
||||
// SAFETY: unsafe needed to write the bits for pfd0
|
||||
clkctl0
|
||||
@ -833,7 +816,6 @@ impl ConfigurableClock for MainPllClkConfig {
|
||||
.modify(|_r, w| unsafe { w.pfd0_clkgate().not_gated().pfd0().bits(0x12) });
|
||||
// wait for ready bit to be set
|
||||
delay_loop_clocks(50, desired_freq);
|
||||
trace!("waiting for mainpll clock to be ready");
|
||||
while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {}
|
||||
// clear by writing a 1
|
||||
clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().set_bit());
|
||||
@ -854,11 +836,9 @@ impl ConfigurableClock for MainPllClkConfig {
|
||||
impl MainPllClkConfig {
|
||||
/// Calculate the mult value of a desired frequency, return error if invalid
|
||||
pub(self) fn calc_mult(rate: u32, base_freq: u32) -> Result<u8, ClockError> {
|
||||
trace!("calculating mult for {:#} / {:#}", rate, base_freq);
|
||||
const VALIDMULTS: [u8; 6] = [16, 17, 20, 22, 27, 33];
|
||||
if rate > base_freq && rate % base_freq == 0 {
|
||||
let mult = (rate / base_freq) as u8;
|
||||
trace!("verifying that calculated mult {:#} is a valid one", mult);
|
||||
if VALIDMULTS.into_iter().any(|i| i == mult) {
|
||||
Ok(mult)
|
||||
} else {
|
||||
@ -1112,7 +1092,6 @@ impl ConfigurableClock for MainClkConfig {
|
||||
Ok(())
|
||||
}
|
||||
fn disable(&self) -> Result<(), ClockError> {
|
||||
error!("Attempting to reset the main clock, should NOT happen during runtime");
|
||||
Err(ClockError::ClockNotSupported)
|
||||
}
|
||||
fn get_clock_rate(&self) -> Result<u32, ClockError> {
|
||||
@ -1120,7 +1099,6 @@ impl ConfigurableClock for MainClkConfig {
|
||||
Ok(rate)
|
||||
}
|
||||
fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> {
|
||||
error!("The multi-source set_clock_rate_and_source method should be used instead of set_clock_rate");
|
||||
Err(ClockError::ClockNotSupported)
|
||||
}
|
||||
fn is_enabled(&self) -> bool {
|
||||
@ -1145,7 +1123,6 @@ impl ConfigurableClock for ClkInConfig {
|
||||
}
|
||||
}
|
||||
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
||||
trace!("Setting value of clk in config, this won't change the clock itself");
|
||||
self.freq.as_ref().unwrap().store(freq, Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
@ -1188,7 +1165,6 @@ impl ConfigurableClock for RtcClkConfig {
|
||||
Ok(())
|
||||
}
|
||||
fn disable(&self) -> Result<(), ClockError> {
|
||||
error!("Resetting the RTC clock, this should NOT happen during runtime");
|
||||
Err(ClockError::ClockNotSupported)
|
||||
}
|
||||
fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {
|
||||
@ -1199,7 +1175,6 @@ impl ConfigurableClock for RtcClkConfig {
|
||||
match r {
|
||||
RtcFreq::Default1Hz => {
|
||||
if rtc.ctrl().read().rtc_en().is_enable() {
|
||||
trace!("Attempting to enable an already enabled clock, RTC 1Hz");
|
||||
} else {
|
||||
rtc.ctrl().modify(|_r, w| w.rtc_en().enable());
|
||||
}
|
||||
@ -1207,7 +1182,6 @@ impl ConfigurableClock for RtcClkConfig {
|
||||
}
|
||||
RtcFreq::HighResolution1khz => {
|
||||
if rtc.ctrl().read().rtc1khz_en().is_enable() {
|
||||
trace!("Attempting to enable an already enabled clock, RTC 1Hz");
|
||||
} else {
|
||||
rtc.ctrl().modify(|_r, w| w.rtc1khz_en().enable());
|
||||
}
|
||||
@ -1215,7 +1189,6 @@ impl ConfigurableClock for RtcClkConfig {
|
||||
}
|
||||
RtcFreq::SubSecond32kHz => {
|
||||
if rtc.ctrl().read().rtc_subsec_ena().is_enable() {
|
||||
trace!("Attempting to enable an already enabled clock, RTC 1Hz");
|
||||
} else {
|
||||
rtc.ctrl().modify(|_r, w| w.rtc_subsec_ena().enable());
|
||||
}
|
||||
@ -1245,18 +1218,12 @@ impl ConfigurableClock for RtcClkConfig {
|
||||
|
||||
impl SysClkConfig {
|
||||
/// Updates the system core clock frequency, SW concept used for systick
|
||||
fn update_sys_core_clock(&self) {
|
||||
trace!(
|
||||
"System core clock has been updated to {:?}, this involves no HW reg writes",
|
||||
self.sysclkfreq.load(Ordering::Relaxed)
|
||||
);
|
||||
}
|
||||
fn update_sys_core_clock(&self) {}
|
||||
}
|
||||
|
||||
impl ConfigurableClock for SysOscConfig {
|
||||
fn enable_and_reset(&self) -> Result<(), ClockError> {
|
||||
if self.state == State::Enabled {
|
||||
trace!("SysOsc was already enabled");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -1498,32 +1465,26 @@ impl ClockOutConfig {
|
||||
/// Using the config, enables all desired clocks to desired clock rates
|
||||
fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> {
|
||||
if let Err(e) = config.rtc.enable_and_reset() {
|
||||
error!("couldn't Power on OSC for RTC, result: {:?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
if let Err(e) = config.lposc.enable_and_reset() {
|
||||
error!("couldn't Power on LPOSC, result: {:?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
if let Err(e) = config.ffro.enable_and_reset() {
|
||||
error!("couldn't Power on FFRO, result: {:?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
if let Err(e) = config.sfro.enable_and_reset() {
|
||||
error!("couldn't Power on SFRO, result: {:?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
if let Err(e) = config.sys_osc.enable_and_reset() {
|
||||
error!("Couldn't enable sys oscillator {:?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
if let Err(e) = config.main_pll_clk.enable_and_reset() {
|
||||
error!("Couldn't enable main pll clock {:?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
@ -1542,7 +1503,6 @@ fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> {
|
||||
init_syscpuahb_clk();
|
||||
|
||||
if let Err(e) = config.main_clk.enable_and_reset() {
|
||||
error!("Couldn't enable main clock {:?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
@ -1561,7 +1521,8 @@ pub(crate) unsafe fn init(config: ClockConfig) -> Result<(), ClockError> {
|
||||
|
||||
///Trait to expose perph clocks
|
||||
trait SealedSysconPeripheral {
|
||||
fn enable_and_reset_perph_clock();
|
||||
fn enable_perph_clock();
|
||||
fn reset_perph();
|
||||
fn disable_perph_clock();
|
||||
}
|
||||
|
||||
@ -1574,7 +1535,18 @@ pub trait SysconPeripheral: SealedSysconPeripheral + 'static {}
|
||||
///
|
||||
/// Peripheral must not be in use.
|
||||
pub fn enable_and_reset<T: SysconPeripheral>() {
|
||||
T::enable_and_reset_perph_clock();
|
||||
T::enable_perph_clock();
|
||||
T::reset_perph();
|
||||
}
|
||||
|
||||
/// Enables peripheral `T`.
|
||||
pub fn enable<T: SysconPeripheral>() {
|
||||
T::enable_perph_clock();
|
||||
}
|
||||
|
||||
/// Reset peripheral `T`.
|
||||
pub fn reset<T: SysconPeripheral>() {
|
||||
T::reset_perph();
|
||||
}
|
||||
|
||||
/// Disables peripheral `T`.
|
||||
@ -1588,15 +1560,21 @@ pub fn disable<T: SysconPeripheral>() {
|
||||
macro_rules! impl_perph_clk {
|
||||
($peripheral:ident, $clkctl:ident, $clkreg:ident, $rstctl:ident, $rstreg:ident, $bit:expr) => {
|
||||
impl SealedSysconPeripheral for crate::peripherals::$peripheral {
|
||||
fn enable_and_reset_perph_clock() {
|
||||
fn enable_perph_clock() {
|
||||
// SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1
|
||||
let cc1 = unsafe { pac::$clkctl::steal() };
|
||||
let rc1 = unsafe { pac::$rstctl::steal() };
|
||||
|
||||
paste! {
|
||||
// SAFETY: unsafe due to the use of bits()
|
||||
cc1.[<$clkreg _set>]().write(|w| unsafe { w.bits(1 << $bit) });
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_perph() {
|
||||
// SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1
|
||||
let rc1 = unsafe { pac::$rstctl::steal() };
|
||||
|
||||
paste! {
|
||||
// SAFETY: unsafe due to the use of bits()
|
||||
rc1.[<$rstreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) });
|
||||
}
|
||||
@ -1605,12 +1583,8 @@ macro_rules! impl_perph_clk {
|
||||
fn disable_perph_clock() {
|
||||
// SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1
|
||||
let cc1 = unsafe { pac::$clkctl::steal() };
|
||||
let rc1 = unsafe { pac::$rstctl::steal() };
|
||||
|
||||
paste! {
|
||||
// SAFETY: unsafe due to the use of bits()
|
||||
rc1.[<$rstreg _set>]().write(|w| unsafe { w.bits(1 << $bit) });
|
||||
|
||||
// SAFETY: unsafe due to the use of bits()
|
||||
cc1.[<$clkreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) });
|
||||
}
|
||||
|
||||
190
embassy-imxrt/src/crc.rs
Normal file
190
embassy-imxrt/src/crc.rs
Normal file
@ -0,0 +1,190 @@
|
||||
//! Cyclic Redundancy Check (CRC)
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::clocks::{enable_and_reset, SysconPeripheral};
|
||||
pub use crate::pac::crc_engine::mode::CrcPolynomial as Polynomial;
|
||||
use crate::{peripherals, Peri, PeripheralType};
|
||||
|
||||
/// CRC driver.
|
||||
pub struct Crc<'d> {
|
||||
info: Info,
|
||||
_config: Config,
|
||||
_lifetime: PhantomData<&'d ()>,
|
||||
}
|
||||
|
||||
/// CRC configuration
|
||||
pub struct Config {
|
||||
/// Polynomial to be used
|
||||
pub polynomial: Polynomial,
|
||||
|
||||
/// Reverse bit order of input?
|
||||
pub reverse_in: bool,
|
||||
|
||||
/// 1's complement input?
|
||||
pub complement_in: bool,
|
||||
|
||||
/// Reverse CRC bit order?
|
||||
pub reverse_out: bool,
|
||||
|
||||
/// 1's complement CRC?
|
||||
pub complement_out: bool,
|
||||
|
||||
/// CRC Seed
|
||||
pub seed: u32,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Create a new CRC config.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
polynomial: Polynomial,
|
||||
reverse_in: bool,
|
||||
complement_in: bool,
|
||||
reverse_out: bool,
|
||||
complement_out: bool,
|
||||
seed: u32,
|
||||
) -> Self {
|
||||
Config {
|
||||
polynomial,
|
||||
reverse_in,
|
||||
complement_in,
|
||||
reverse_out,
|
||||
complement_out,
|
||||
seed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
polynomial: Polynomial::CrcCcitt,
|
||||
reverse_in: false,
|
||||
complement_in: false,
|
||||
reverse_out: false,
|
||||
complement_out: false,
|
||||
seed: 0xffff,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Crc<'d> {
|
||||
/// Instantiates new CRC peripheral and initializes to default values.
|
||||
pub fn new<T: Instance>(_peripheral: Peri<'d, T>, config: Config) -> Self {
|
||||
// enable CRC clock
|
||||
enable_and_reset::<T>();
|
||||
|
||||
let mut instance = Self {
|
||||
info: T::info(),
|
||||
_config: config,
|
||||
_lifetime: PhantomData,
|
||||
};
|
||||
|
||||
instance.reconfigure();
|
||||
instance
|
||||
}
|
||||
|
||||
/// Reconfigured the CRC peripheral.
|
||||
fn reconfigure(&mut self) {
|
||||
self.info.regs.mode().write(|w| {
|
||||
w.crc_poly()
|
||||
.variant(self._config.polynomial)
|
||||
.bit_rvs_wr()
|
||||
.variant(self._config.reverse_in)
|
||||
.cmpl_wr()
|
||||
.variant(self._config.complement_in)
|
||||
.bit_rvs_sum()
|
||||
.variant(self._config.reverse_out)
|
||||
.cmpl_sum()
|
||||
.variant(self._config.complement_out)
|
||||
});
|
||||
|
||||
// Init CRC value
|
||||
self.info
|
||||
.regs
|
||||
.seed()
|
||||
.write(|w| unsafe { w.crc_seed().bits(self._config.seed) });
|
||||
}
|
||||
|
||||
/// Feeds a byte into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_byte(&mut self, byte: u8) -> u32 {
|
||||
self.info.regs.wr_data8().write(|w| unsafe { w.bits(byte) });
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
|
||||
/// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 {
|
||||
let (prefix, data, suffix) = unsafe { bytes.align_to::<u32>() };
|
||||
|
||||
for b in prefix {
|
||||
self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) });
|
||||
}
|
||||
|
||||
for d in data {
|
||||
self.info.regs.wr_data32().write(|w| unsafe { w.bits(*d) });
|
||||
}
|
||||
|
||||
for b in suffix {
|
||||
self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) });
|
||||
}
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
|
||||
/// Feeds a halfword into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_halfword(&mut self, halfword: u16) -> u32 {
|
||||
self.info.regs.wr_data16().write(|w| unsafe { w.bits(halfword) });
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
|
||||
/// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 {
|
||||
for halfword in halfwords {
|
||||
self.info.regs.wr_data16().write(|w| unsafe { w.bits(*halfword) });
|
||||
}
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
|
||||
/// Feeds a words into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_word(&mut self, word: u32) -> u32 {
|
||||
self.info.regs.wr_data32().write(|w| unsafe { w.bits(word) });
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
|
||||
/// Feeds an slice of words into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_words(&mut self, words: &[u32]) -> u32 {
|
||||
for word in words {
|
||||
self.info.regs.wr_data32().write(|w| unsafe { w.bits(*word) });
|
||||
}
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
}
|
||||
|
||||
struct Info {
|
||||
regs: crate::pac::CrcEngine,
|
||||
}
|
||||
|
||||
trait SealedInstance {
|
||||
fn info() -> Info;
|
||||
}
|
||||
|
||||
/// CRC instance trait.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + PeripheralType + SysconPeripheral + 'static + Send {}
|
||||
|
||||
impl Instance for peripherals::CRC {}
|
||||
|
||||
impl SealedInstance for peripherals::CRC {
|
||||
fn info() -> Info {
|
||||
// SAFETY: safe from single executor
|
||||
Info {
|
||||
regs: unsafe { crate::pac::CrcEngine::steal() },
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -18,11 +18,13 @@ compile_error!(
|
||||
pub(crate) mod fmt;
|
||||
|
||||
pub mod clocks;
|
||||
pub mod crc;
|
||||
pub mod gpio;
|
||||
pub mod iopctl;
|
||||
pub mod rng;
|
||||
|
||||
#[cfg(feature = "_time-driver")]
|
||||
pub mod rtc;
|
||||
pub mod time_driver;
|
||||
|
||||
// This mod MUST go last, so that it sees all the `impl_foo!' macros
|
||||
#[cfg_attr(feature = "mimxrt633s", path = "chips/mimxrt633s.rs")]
|
||||
@ -132,12 +134,10 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||
error!("unable to initialize Clocks for reason: {:?}", e);
|
||||
// Panic here?
|
||||
}
|
||||
gpio::init();
|
||||
}
|
||||
|
||||
// init RTC time driver
|
||||
#[cfg(feature = "_time-driver")]
|
||||
rtc::init(config.time_interrupt_priority);
|
||||
time_driver::init(config.time_interrupt_priority);
|
||||
gpio::init();
|
||||
|
||||
peripherals
|
||||
}
|
||||
|
||||
257
embassy-imxrt/src/rng.rs
Normal file
257
embassy-imxrt/src/rng.rs
Normal file
@ -0,0 +1,257 @@
|
||||
//! True Random Number Generator (TRNG)
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_futures::block_on;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use crate::clocks::{enable_and_reset, SysconPeripheral};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::{interrupt, peripherals, Peri, PeripheralType};
|
||||
|
||||
static RNG_WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
/// RNG ;error
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// Seed error.
|
||||
SeedError,
|
||||
|
||||
/// HW Error.
|
||||
HwError,
|
||||
|
||||
/// Frequency Count Fail
|
||||
FreqCountFail,
|
||||
}
|
||||
|
||||
/// RNG interrupt handler.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
let regs = T::info().regs;
|
||||
let int_status = regs.int_status().read();
|
||||
|
||||
if int_status.ent_val().bit_is_set()
|
||||
|| int_status.hw_err().bit_is_set()
|
||||
|| int_status.frq_ct_fail().bit_is_set()
|
||||
{
|
||||
regs.int_ctrl().modify(|_, w| {
|
||||
w.ent_val()
|
||||
.ent_val_0()
|
||||
.hw_err()
|
||||
.hw_err_0()
|
||||
.frq_ct_fail()
|
||||
.frq_ct_fail_0()
|
||||
});
|
||||
RNG_WAKER.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RNG driver.
|
||||
pub struct Rng<'d> {
|
||||
info: Info,
|
||||
_lifetime: PhantomData<&'d ()>,
|
||||
}
|
||||
|
||||
impl<'d> Rng<'d> {
|
||||
/// Create a new RNG driver.
|
||||
pub fn new<T: Instance>(
|
||||
_inner: Peri<'d, T>,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
) -> Self {
|
||||
enable_and_reset::<T>();
|
||||
|
||||
let mut random = Self {
|
||||
info: T::info(),
|
||||
_lifetime: PhantomData,
|
||||
};
|
||||
random.init();
|
||||
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
random
|
||||
}
|
||||
|
||||
/// Reset the RNG.
|
||||
pub fn reset(&mut self) {
|
||||
self.info.regs.mctl().write(|w| w.rst_def().set_bit().prgm().set_bit());
|
||||
}
|
||||
|
||||
/// Fill the given slice with random values.
|
||||
pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
||||
// We have a total of 16 words (512 bits) of entropy at our
|
||||
// disposal. The idea here is to read all bits and copy the
|
||||
// necessary bytes to the slice.
|
||||
for chunk in dest.chunks_mut(64) {
|
||||
self.async_fill_chunk(chunk).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn async_fill_chunk(&mut self, chunk: &mut [u8]) -> Result<(), Error> {
|
||||
// wait for interrupt
|
||||
let res = poll_fn(|cx| {
|
||||
// Check if already ready.
|
||||
// TODO: Is this necessary? Could we just check once after
|
||||
// the waker has been registered?
|
||||
if self.info.regs.int_status().read().ent_val().bit_is_set() {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
RNG_WAKER.register(cx.waker());
|
||||
|
||||
self.unmask_interrupts();
|
||||
|
||||
let mctl = self.info.regs.mctl().read();
|
||||
|
||||
// Check again if interrupt fired
|
||||
if mctl.ent_val().bit_is_set() {
|
||||
Poll::Ready(Ok(()))
|
||||
} else if mctl.err().bit_is_set() {
|
||||
Poll::Ready(Err(Error::HwError))
|
||||
} else if mctl.fct_fail().bit_is_set() {
|
||||
Poll::Ready(Err(Error::FreqCountFail))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let bits = self.info.regs.mctl().read();
|
||||
|
||||
if bits.ent_val().bit_is_set() {
|
||||
let mut entropy = [0; 16];
|
||||
|
||||
for (i, item) in entropy.iter_mut().enumerate() {
|
||||
*item = self.info.regs.ent(i).read().bits();
|
||||
}
|
||||
|
||||
// Read MCTL after reading ENT15
|
||||
let _ = self.info.regs.mctl().read();
|
||||
|
||||
if entropy.iter().any(|e| *e == 0) {
|
||||
return Err(Error::SeedError);
|
||||
}
|
||||
|
||||
// SAFETY: entropy is the same for input and output types in
|
||||
// native endianness.
|
||||
let entropy: [u8; 64] = unsafe { core::mem::transmute(entropy) };
|
||||
|
||||
// write bytes to chunk
|
||||
chunk.copy_from_slice(&entropy[..chunk.len()]);
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn mask_interrupts(&mut self) {
|
||||
self.info.regs.int_mask().write(|w| {
|
||||
w.ent_val()
|
||||
.ent_val_0()
|
||||
.hw_err()
|
||||
.hw_err_0()
|
||||
.frq_ct_fail()
|
||||
.frq_ct_fail_0()
|
||||
});
|
||||
}
|
||||
|
||||
fn unmask_interrupts(&mut self) {
|
||||
self.info.regs.int_mask().modify(|_, w| {
|
||||
w.ent_val()
|
||||
.ent_val_1()
|
||||
.hw_err()
|
||||
.hw_err_1()
|
||||
.frq_ct_fail()
|
||||
.frq_ct_fail_1()
|
||||
});
|
||||
}
|
||||
|
||||
fn enable_interrupts(&mut self) {
|
||||
self.info.regs.int_ctrl().write(|w| {
|
||||
w.ent_val()
|
||||
.ent_val_1()
|
||||
.hw_err()
|
||||
.hw_err_1()
|
||||
.frq_ct_fail()
|
||||
.frq_ct_fail_1()
|
||||
});
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
self.mask_interrupts();
|
||||
|
||||
// Switch TRNG to programming mode
|
||||
self.info.regs.mctl().modify(|_, w| w.prgm().set_bit());
|
||||
|
||||
self.enable_interrupts();
|
||||
|
||||
// Switch TRNG to Run Mode
|
||||
self.info
|
||||
.regs
|
||||
.mctl()
|
||||
.modify(|_, w| w.trng_acc().set_bit().prgm().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
impl RngCore for Rng<'_> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
let mut bytes = [0u8; 4];
|
||||
block_on(self.async_fill_bytes(&mut bytes)).unwrap();
|
||||
u32::from_ne_bytes(bytes)
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
let mut bytes = [0u8; 8];
|
||||
block_on(self.async_fill_bytes(&mut bytes)).unwrap();
|
||||
u64::from_ne_bytes(bytes)
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
block_on(self.async_fill_bytes(dest)).unwrap();
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||
self.fill_bytes(dest);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptoRng for Rng<'_> {}
|
||||
|
||||
struct Info {
|
||||
regs: crate::pac::Trng,
|
||||
}
|
||||
|
||||
trait SealedInstance {
|
||||
fn info() -> Info;
|
||||
}
|
||||
|
||||
/// RNG instance trait.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + PeripheralType + SysconPeripheral + 'static + Send {
|
||||
/// Interrupt for this RNG instance.
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
impl Instance for peripherals::RNG {
|
||||
type Interrupt = crate::interrupt::typelevel::RNG;
|
||||
}
|
||||
|
||||
impl SealedInstance for peripherals::RNG {
|
||||
fn info() -> Info {
|
||||
// SAFETY: safe from single executor
|
||||
Info {
|
||||
regs: unsafe { crate::pac::Trng::steal() },
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
//! RTC Time Driver.
|
||||
//! Time Driver.
|
||||
use core::cell::{Cell, RefCell};
|
||||
#[cfg(feature = "time-driver-rtc")]
|
||||
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
|
||||
|
||||
use critical_section::CriticalSection;
|
||||
@ -8,27 +9,11 @@ use embassy_sync::blocking_mutex::Mutex;
|
||||
use embassy_time_driver::Driver;
|
||||
use embassy_time_queue_utils::Queue;
|
||||
|
||||
#[cfg(feature = "time-driver-os-timer")]
|
||||
use crate::clocks::enable;
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::{interrupt, pac};
|
||||
|
||||
fn rtc() -> &'static pac::rtc::RegisterBlock {
|
||||
unsafe { &*pac::Rtc::ptr() }
|
||||
}
|
||||
|
||||
/// Calculate the timestamp from the period count and the tick count.
|
||||
///
|
||||
/// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches
|
||||
/// the expected range for the `period` parity, we're done. If it doesn't, this means that
|
||||
/// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value
|
||||
/// corresponds to the next period.
|
||||
///
|
||||
/// the 1kHz RTC counter is 16 bits and RTC doesn't have separate compare channels,
|
||||
/// so using a 32 bit GPREG0-2 as counter, compare, and int_en
|
||||
/// `period` is a 32bit integer, gpreg 'counter' is 31 bits plus the parity bit for overflow detection
|
||||
fn calc_now(period: u32, counter: u32) -> u64 {
|
||||
((period as u64) << 31) + ((counter ^ ((period & 1) << 31)) as u64)
|
||||
}
|
||||
|
||||
struct AlarmState {
|
||||
timestamp: Cell<u64>,
|
||||
}
|
||||
@ -43,6 +28,34 @@ impl AlarmState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time-driver-rtc")]
|
||||
fn rtc() -> &'static pac::rtc::RegisterBlock {
|
||||
unsafe { &*pac::Rtc::ptr() }
|
||||
}
|
||||
|
||||
/// Calculate the timestamp from the period count and the tick count.
|
||||
///
|
||||
/// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches
|
||||
/// the expected range for the `period` parity, we're done. If it doesn't, this means that
|
||||
/// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value
|
||||
/// corresponds to the next period.
|
||||
///
|
||||
/// the 1kHz RTC counter is 16 bits and RTC doesn't have separate compare channels,
|
||||
/// so using a 32 bit GPREG0-2 as counter, compare, and int_en
|
||||
/// `period` is a 32bit integer, gpreg 'counter' is 31 bits plus the parity bit for overflow detection
|
||||
#[cfg(feature = "time-driver-rtc")]
|
||||
fn calc_now(period: u32, counter: u32) -> u64 {
|
||||
((period as u64) << 31) + ((counter ^ ((period & 1) << 31)) as u64)
|
||||
}
|
||||
|
||||
#[cfg(feature = "time-driver-rtc")]
|
||||
embassy_time_driver::time_driver_impl!(static DRIVER: Rtc = Rtc {
|
||||
period: AtomicU32::new(0),
|
||||
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
|
||||
queue: Mutex::new(RefCell::new(Queue::new())),
|
||||
});
|
||||
|
||||
#[cfg(feature = "time-driver-rtc")]
|
||||
struct Rtc {
|
||||
/// Number of 2^31 periods elapsed since boot.
|
||||
period: AtomicU32,
|
||||
@ -51,12 +64,7 @@ struct Rtc {
|
||||
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
|
||||
}
|
||||
|
||||
embassy_time_driver::time_driver_impl!(static DRIVER: Rtc = Rtc {
|
||||
period: AtomicU32::new(0),
|
||||
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
|
||||
queue: Mutex::new(RefCell::new(Queue::new())),
|
||||
});
|
||||
|
||||
#[cfg(feature = "time-driver-rtc")]
|
||||
impl Rtc {
|
||||
/// Access the GPREG0 register to use it as a 31-bit counter.
|
||||
#[inline]
|
||||
@ -219,6 +227,7 @@ impl Rtc {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time-driver-rtc")]
|
||||
impl Driver for Rtc {
|
||||
fn now(&self) -> u64 {
|
||||
// `period` MUST be read before `counter`, see comment at the top for details.
|
||||
@ -242,13 +251,158 @@ impl Driver for Rtc {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg(all(feature = "rt", feature = "time-driver-rtc"))]
|
||||
#[allow(non_snake_case)]
|
||||
#[interrupt]
|
||||
fn RTC() {
|
||||
DRIVER.on_interrupt()
|
||||
}
|
||||
|
||||
#[cfg(feature = "time-driver-os-timer")]
|
||||
fn os() -> &'static pac::ostimer0::RegisterBlock {
|
||||
unsafe { &*pac::Ostimer0::ptr() }
|
||||
}
|
||||
|
||||
/// Convert gray to decimal
|
||||
///
|
||||
/// Os Event provides a 64-bit timestamp gray-encoded. All we have to
|
||||
/// do here is read both 32-bit halves of the register and convert
|
||||
/// from gray to regular binary.
|
||||
#[cfg(feature = "time-driver-os-timer")]
|
||||
fn gray_to_dec(gray: u64) -> u64 {
|
||||
let mut dec = gray;
|
||||
|
||||
dec ^= dec >> 1;
|
||||
dec ^= dec >> 2;
|
||||
dec ^= dec >> 4;
|
||||
dec ^= dec >> 8;
|
||||
dec ^= dec >> 16;
|
||||
dec ^= dec >> 32;
|
||||
|
||||
dec
|
||||
}
|
||||
|
||||
/// Convert decimal to gray
|
||||
///
|
||||
/// Before writing match value to the target register, we must convert
|
||||
/// it back into gray code.
|
||||
#[cfg(feature = "time-driver-os-timer")]
|
||||
fn dec_to_gray(dec: u64) -> u64 {
|
||||
let gray = dec;
|
||||
gray ^ (gray >> 1)
|
||||
}
|
||||
|
||||
#[cfg(feature = "time-driver-os-timer")]
|
||||
embassy_time_driver::time_driver_impl!(static DRIVER: OsTimer = OsTimer {
|
||||
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
|
||||
queue: Mutex::new(RefCell::new(Queue::new())),
|
||||
});
|
||||
|
||||
#[cfg(feature = "time-driver-os-timer")]
|
||||
struct OsTimer {
|
||||
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
||||
alarms: Mutex<CriticalSectionRawMutex, AlarmState>,
|
||||
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "time-driver-os-timer")]
|
||||
impl OsTimer {
|
||||
fn init(&'static self, irq_prio: crate::interrupt::Priority) {
|
||||
// init alarms
|
||||
critical_section::with(|cs| {
|
||||
let alarm = DRIVER.alarms.borrow(cs);
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
});
|
||||
|
||||
// Enable clocks. Documentation advises AGAINST resetting this
|
||||
// peripheral.
|
||||
enable::<crate::peripherals::OS_EVENT>();
|
||||
|
||||
interrupt::OS_EVENT.disable();
|
||||
|
||||
// Make sure interrupt is masked
|
||||
os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit());
|
||||
|
||||
// Default to the end of time
|
||||
os().match_l().write(|w| unsafe { w.bits(0xffff_ffff) });
|
||||
os().match_h().write(|w| unsafe { w.bits(0xffff_ffff) });
|
||||
|
||||
interrupt::OS_EVENT.unpend();
|
||||
interrupt::OS_EVENT.set_priority(irq_prio);
|
||||
unsafe { interrupt::OS_EVENT.enable() };
|
||||
}
|
||||
|
||||
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
|
||||
let alarm = self.alarms.borrow(cs);
|
||||
alarm.timestamp.set(timestamp);
|
||||
|
||||
let t = self.now();
|
||||
if timestamp <= t {
|
||||
os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit());
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
return false;
|
||||
}
|
||||
|
||||
let gray_timestamp = dec_to_gray(timestamp);
|
||||
|
||||
os().match_l()
|
||||
.write(|w| unsafe { w.bits(gray_timestamp as u32 & 0xffff_ffff) });
|
||||
os().match_h()
|
||||
.write(|w| unsafe { w.bits((gray_timestamp >> 32) as u32) });
|
||||
os().osevent_ctrl().modify(|_, w| w.ostimer_intena().set_bit());
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
fn trigger_alarm(&self, cs: CriticalSection) {
|
||||
let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
|
||||
while !self.set_alarm(cs, next) {
|
||||
next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
fn on_interrupt(&self) {
|
||||
critical_section::with(|cs| {
|
||||
if os().osevent_ctrl().read().ostimer_intrflag().bit_is_set() {
|
||||
os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit());
|
||||
self.trigger_alarm(cs);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time-driver-os-timer")]
|
||||
impl Driver for OsTimer {
|
||||
fn now(&self) -> u64 {
|
||||
let mut t = os().evtimerh().read().bits() as u64;
|
||||
t <<= 32;
|
||||
t |= os().evtimerl().read().bits() as u64;
|
||||
gray_to_dec(t)
|
||||
}
|
||||
|
||||
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
|
||||
critical_section::with(|cs| {
|
||||
let mut queue = self.queue.borrow(cs).borrow_mut();
|
||||
|
||||
if queue.schedule_wake(at, waker) {
|
||||
let mut next = queue.next_expiration(self.now());
|
||||
while !self.set_alarm(cs, next) {
|
||||
next = queue.next_expiration(self.now());
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "time-driver-os-timer"))]
|
||||
#[allow(non_snake_case)]
|
||||
#[interrupt]
|
||||
fn OS_EVENT() {
|
||||
DRIVER.on_interrupt()
|
||||
}
|
||||
|
||||
pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
|
||||
DRIVER.init(irq_prio)
|
||||
}
|
||||
@ -9,10 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
No unreleased changes yet... Quick, go send a PR!
|
||||
|
||||
## 0.7 - 2025-02-14
|
||||
## 0.7 - 2025-05-06
|
||||
|
||||
- don't infinite loop if udp::send methods receive a buffer too large to ever be sent
|
||||
- add ICMP sockets and a ping utility
|
||||
- configurable rate_limit for the ping utility
|
||||
- Feature match udp sockets
|
||||
|
||||
## 0.6 - 2025-01-05
|
||||
|
||||
|
||||
@ -262,6 +262,9 @@ embassy_hal_internal::peripherals! {
|
||||
PPI_GROUP4,
|
||||
PPI_GROUP5,
|
||||
|
||||
// IPC
|
||||
IPC,
|
||||
|
||||
// GPIO port 0
|
||||
#[cfg(feature = "lfxo-pins-as-gpio")]
|
||||
P0_00,
|
||||
@ -327,6 +330,8 @@ embassy_hal_internal::peripherals! {
|
||||
EGU5,
|
||||
}
|
||||
|
||||
impl_ipc!(IPC, IPC, IPC);
|
||||
|
||||
impl_usb!(USBD, USBD, USBD);
|
||||
|
||||
impl_uarte!(SERIAL0, UARTE0, SERIAL0);
|
||||
|
||||
@ -141,6 +141,9 @@ embassy_hal_internal::peripherals! {
|
||||
PPI_GROUP4,
|
||||
PPI_GROUP5,
|
||||
|
||||
// IPC
|
||||
IPC,
|
||||
|
||||
// GPIO port 0
|
||||
P0_00,
|
||||
P0_01,
|
||||
@ -200,6 +203,8 @@ embassy_hal_internal::peripherals! {
|
||||
EGU0,
|
||||
}
|
||||
|
||||
impl_ipc!(IPC, IPC, IPC);
|
||||
|
||||
impl_uarte!(SERIAL0, UARTE0, SERIAL0);
|
||||
impl_spim!(SERIAL0, SPIM0, SERIAL0);
|
||||
impl_spis!(SERIAL0, SPIS0, SERIAL0);
|
||||
|
||||
363
embassy-nrf/src/ipc.rs
Normal file
363
embassy-nrf/src/ipc.rs
Normal file
@ -0,0 +1,363 @@
|
||||
//! InterProcessor Communication (IPC)
|
||||
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::{Peri, PeripheralType};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::{interrupt, pac, ppi};
|
||||
|
||||
const EVENT_COUNT: usize = 16;
|
||||
|
||||
/// IPC Event
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum EventNumber {
|
||||
/// IPC Event 0
|
||||
Event0 = 0,
|
||||
/// IPC Event 1
|
||||
Event1 = 1,
|
||||
/// IPC Event 2
|
||||
Event2 = 2,
|
||||
/// IPC Event 3
|
||||
Event3 = 3,
|
||||
/// IPC Event 4
|
||||
Event4 = 4,
|
||||
/// IPC Event 5
|
||||
Event5 = 5,
|
||||
/// IPC Event 6
|
||||
Event6 = 6,
|
||||
/// IPC Event 7
|
||||
Event7 = 7,
|
||||
/// IPC Event 8
|
||||
Event8 = 8,
|
||||
/// IPC Event 9
|
||||
Event9 = 9,
|
||||
/// IPC Event 10
|
||||
Event10 = 10,
|
||||
/// IPC Event 11
|
||||
Event11 = 11,
|
||||
/// IPC Event 12
|
||||
Event12 = 12,
|
||||
/// IPC Event 13
|
||||
Event13 = 13,
|
||||
/// IPC Event 14
|
||||
Event14 = 14,
|
||||
/// IPC Event 15
|
||||
Event15 = 15,
|
||||
}
|
||||
|
||||
const EVENTS: [EventNumber; EVENT_COUNT] = [
|
||||
EventNumber::Event0,
|
||||
EventNumber::Event1,
|
||||
EventNumber::Event2,
|
||||
EventNumber::Event3,
|
||||
EventNumber::Event4,
|
||||
EventNumber::Event5,
|
||||
EventNumber::Event6,
|
||||
EventNumber::Event7,
|
||||
EventNumber::Event8,
|
||||
EventNumber::Event9,
|
||||
EventNumber::Event10,
|
||||
EventNumber::Event11,
|
||||
EventNumber::Event12,
|
||||
EventNumber::Event13,
|
||||
EventNumber::Event14,
|
||||
EventNumber::Event15,
|
||||
];
|
||||
|
||||
/// IPC Channel
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum IpcChannel {
|
||||
/// IPC Channel 0
|
||||
Channel0,
|
||||
/// IPC Channel 1
|
||||
Channel1,
|
||||
/// IPC Channel 2
|
||||
Channel2,
|
||||
/// IPC Channel 3
|
||||
Channel3,
|
||||
/// IPC Channel 4
|
||||
Channel4,
|
||||
/// IPC Channel 5
|
||||
Channel5,
|
||||
/// IPC Channel 6
|
||||
Channel6,
|
||||
/// IPC Channel 7
|
||||
Channel7,
|
||||
/// IPC Channel 8
|
||||
Channel8,
|
||||
/// IPC Channel 9
|
||||
Channel9,
|
||||
/// IPC Channel 10
|
||||
Channel10,
|
||||
/// IPC Channel 11
|
||||
Channel11,
|
||||
/// IPC Channel 12
|
||||
Channel12,
|
||||
/// IPC Channel 13
|
||||
Channel13,
|
||||
/// IPC Channel 14
|
||||
Channel14,
|
||||
/// IPC Channel 15
|
||||
Channel15,
|
||||
}
|
||||
|
||||
impl IpcChannel {
|
||||
fn mask(self) -> u32 {
|
||||
1 << (self as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt Handler
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
let regs = T::regs();
|
||||
|
||||
// Check if an event was generated, and if it was, trigger the corresponding waker
|
||||
for event in EVENTS {
|
||||
if regs.events_receive(event as usize).read() & 0x01 == 0x01 {
|
||||
regs.intenclr().write(|w| w.0 = 0x01 << event as u32);
|
||||
T::state().wakers[event as usize].wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// IPC driver
|
||||
#[non_exhaustive]
|
||||
pub struct Ipc<'d, T: Instance> {
|
||||
/// Event 0
|
||||
pub event0: Event<'d, T>,
|
||||
/// Event 1
|
||||
pub event1: Event<'d, T>,
|
||||
/// Event 2
|
||||
pub event2: Event<'d, T>,
|
||||
/// Event 3
|
||||
pub event3: Event<'d, T>,
|
||||
/// Event 4
|
||||
pub event4: Event<'d, T>,
|
||||
/// Event 5
|
||||
pub event5: Event<'d, T>,
|
||||
/// Event 6
|
||||
pub event6: Event<'d, T>,
|
||||
/// Event 7
|
||||
pub event7: Event<'d, T>,
|
||||
/// Event 8
|
||||
pub event8: Event<'d, T>,
|
||||
/// Event 9
|
||||
pub event9: Event<'d, T>,
|
||||
/// Event 10
|
||||
pub event10: Event<'d, T>,
|
||||
/// Event 11
|
||||
pub event11: Event<'d, T>,
|
||||
/// Event 12
|
||||
pub event12: Event<'d, T>,
|
||||
/// Event 13
|
||||
pub event13: Event<'d, T>,
|
||||
/// Event 14
|
||||
pub event14: Event<'d, T>,
|
||||
/// Event 15
|
||||
pub event15: Event<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Ipc<'d, T> {
|
||||
/// Create a new IPC driver.
|
||||
pub fn new(
|
||||
_p: Peri<'d, T>,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
) -> Self {
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
let _phantom = PhantomData;
|
||||
#[rustfmt::skip]
|
||||
let r = Self { // attributes on expressions are experimental
|
||||
event0: Event { number: EventNumber::Event0, _phantom },
|
||||
event1: Event { number: EventNumber::Event1, _phantom },
|
||||
event2: Event { number: EventNumber::Event2, _phantom },
|
||||
event3: Event { number: EventNumber::Event3, _phantom },
|
||||
event4: Event { number: EventNumber::Event4, _phantom },
|
||||
event5: Event { number: EventNumber::Event5, _phantom },
|
||||
event6: Event { number: EventNumber::Event6, _phantom },
|
||||
event7: Event { number: EventNumber::Event7, _phantom },
|
||||
event8: Event { number: EventNumber::Event8, _phantom },
|
||||
event9: Event { number: EventNumber::Event9, _phantom },
|
||||
event10: Event { number: EventNumber::Event10, _phantom },
|
||||
event11: Event { number: EventNumber::Event11, _phantom },
|
||||
event12: Event { number: EventNumber::Event12, _phantom },
|
||||
event13: Event { number: EventNumber::Event13, _phantom },
|
||||
event14: Event { number: EventNumber::Event14, _phantom },
|
||||
event15: Event { number: EventNumber::Event15, _phantom },
|
||||
};
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
/// IPC event
|
||||
pub struct Event<'d, T: Instance> {
|
||||
number: EventNumber,
|
||||
_phantom: PhantomData<&'d T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Event<'d, T> {
|
||||
/// Trigger the event.
|
||||
pub fn trigger(&self) {
|
||||
let nr = self.number;
|
||||
T::regs().tasks_send(nr as usize).write_value(1);
|
||||
}
|
||||
|
||||
/// Wait for the event to be triggered.
|
||||
pub async fn wait(&mut self) {
|
||||
let regs = T::regs();
|
||||
let nr = self.number as usize;
|
||||
regs.intenset().write(|w| w.0 = 1 << nr);
|
||||
poll_fn(|cx| {
|
||||
T::state().wakers[nr].register(cx.waker());
|
||||
|
||||
if regs.events_receive(nr).read() == 1 {
|
||||
regs.events_receive(nr).write_value(0x00);
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Returns the [`EventNumber`] of the event.
|
||||
pub fn number(&self) -> EventNumber {
|
||||
self.number
|
||||
}
|
||||
|
||||
/// Create a handle that can trigger the event.
|
||||
pub fn trigger_handle(&self) -> EventTrigger<'d, T> {
|
||||
EventTrigger {
|
||||
number: self.number,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure the channels the event will broadcast to
|
||||
pub fn configure_trigger<I: IntoIterator<Item = IpcChannel>>(&mut self, channels: I) {
|
||||
T::regs().send_cnf(self.number as usize).write(|w| {
|
||||
for channel in channels {
|
||||
w.0 |= channel.mask();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Configure the channels the event will listen on
|
||||
pub fn configure_wait<I: IntoIterator<Item = IpcChannel>>(&mut self, channels: I) {
|
||||
T::regs().receive_cnf(self.number as usize).write(|w| {
|
||||
for channel in channels {
|
||||
w.0 |= channel.mask();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the task for the IPC event to use with PPI.
|
||||
pub fn task(&self) -> ppi::Task<'d> {
|
||||
let nr = self.number as usize;
|
||||
let regs = T::regs();
|
||||
ppi::Task::from_reg(regs.tasks_send(nr))
|
||||
}
|
||||
|
||||
/// Get the event for the IPC event to use with PPI.
|
||||
pub fn event(&self) -> ppi::Event<'d> {
|
||||
let nr = self.number as usize;
|
||||
let regs = T::regs();
|
||||
ppi::Event::from_reg(regs.events_receive(nr))
|
||||
}
|
||||
|
||||
/// Reborrow into a "child" Event.
|
||||
///
|
||||
/// `self` will stay borrowed until the child Event is dropped.
|
||||
pub fn reborrow(&mut self) -> Event<'_, T> {
|
||||
Self { ..*self }
|
||||
}
|
||||
|
||||
/// Steal an IPC event by number.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The event number must not be in use by another [`Event`].
|
||||
pub unsafe fn steal(number: EventNumber) -> Self {
|
||||
Self {
|
||||
number,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle that can trigger an IPC event.
|
||||
///
|
||||
/// This `struct` is returned by [`Event::trigger_handle`].
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EventTrigger<'d, T: Instance> {
|
||||
number: EventNumber,
|
||||
_phantom: PhantomData<&'d T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> EventTrigger<'_, T> {
|
||||
/// Trigger the event.
|
||||
pub fn trigger(&self) {
|
||||
let nr = self.number;
|
||||
T::regs().tasks_send(nr as usize).write_value(1);
|
||||
}
|
||||
|
||||
/// Returns the [`EventNumber`] of the event.
|
||||
pub fn number(&self) -> EventNumber {
|
||||
self.number
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct State {
|
||||
wakers: [AtomicWaker; EVENT_COUNT],
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
wakers: [const { AtomicWaker::new() }; EVENT_COUNT],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait SealedInstance {
|
||||
fn regs() -> pac::ipc::Ipc;
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
|
||||
/// IPC peripheral instance.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: PeripheralType + SealedInstance + 'static + Send {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
macro_rules! impl_ipc {
|
||||
($type:ident, $pac_type:ident, $irq:ident) => {
|
||||
impl crate::ipc::SealedInstance for peripherals::$type {
|
||||
fn regs() -> pac::ipc::Ipc {
|
||||
pac::$pac_type
|
||||
}
|
||||
|
||||
fn state() -> &'static crate::ipc::State {
|
||||
static STATE: crate::ipc::State = crate::ipc::State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
impl crate::ipc::Instance for peripherals::$type {
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -88,6 +88,8 @@ pub mod gpiote;
|
||||
#[cfg(not(feature = "_nrf54l"))] // TODO
|
||||
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
|
||||
pub mod i2s;
|
||||
#[cfg(feature = "_nrf5340")]
|
||||
pub mod ipc;
|
||||
#[cfg(not(feature = "_nrf54l"))] // TODO
|
||||
#[cfg(any(
|
||||
feature = "nrf52832",
|
||||
|
||||
@ -1,394 +0,0 @@
|
||||
//! Radio driver implementation focused on Bluetooth Low-Energy transmission.
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::drop::OnDrop;
|
||||
pub use pac::radio::vals::Mode;
|
||||
#[cfg(not(feature = "_nrf51"))]
|
||||
use pac::radio::vals::Plen as PreambleLength;
|
||||
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::pac::radio::vals;
|
||||
use crate::radio::*;
|
||||
pub use crate::radio::{Error, TxPower};
|
||||
use crate::util::slice_in_ram_or;
|
||||
use crate::Peri;
|
||||
|
||||
/// Radio driver.
|
||||
pub struct Radio<'d, T: Instance> {
|
||||
_p: Peri<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Radio<'d, T> {
|
||||
/// Create a new radio driver.
|
||||
pub fn new(
|
||||
radio: Peri<'d, T>,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
) -> Self {
|
||||
let r = T::regs();
|
||||
|
||||
r.pcnf1().write(|w| {
|
||||
// It is 0 bytes long in a standard BLE packet
|
||||
w.set_statlen(0);
|
||||
// MaxLen configures the maximum packet payload plus add-on size in
|
||||
// number of bytes that can be transmitted or received by the RADIO. This feature can be used to ensure
|
||||
// that the RADIO does not overwrite, or read beyond, the RAM assigned to the packet payload. This means
|
||||
// that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a
|
||||
// packet larger than MAXLEN, the payload will be truncated at MAXLEN
|
||||
//
|
||||
// To simplify the implementation, It is setted as the maximum value
|
||||
// and the length of the packet is controlled only by the LENGTH field in the packet
|
||||
w.set_maxlen(255);
|
||||
// Configure the length of the address field in the packet
|
||||
// The prefix after the address fields is always appended, so is always 1 byte less than the size of the address
|
||||
// The base address is truncated from the least significant byte if the BALEN is less than 4
|
||||
//
|
||||
// BLE address is always 4 bytes long
|
||||
w.set_balen(3); // 3 bytes base address (+ 1 prefix);
|
||||
// Configure the endianess
|
||||
// For BLE is always little endian (LSB first)
|
||||
w.set_endian(vals::Endian::LITTLE);
|
||||
// Data whitening is used to avoid long sequences of zeros or
|
||||
// ones, e.g., 0b0000000 or 0b1111111, in the data bit stream.
|
||||
// The whitener and de-whitener are defined the same way,
|
||||
// using a 7-bit linear feedback shift register with the
|
||||
// polynomial x7 + x4 + 1.
|
||||
//
|
||||
// In BLE Whitening shall be applied on the PDU and CRC of all
|
||||
// Link Layer packets and is performed after the CRC generation
|
||||
// in the transmitter. No other parts of the packets are whitened.
|
||||
// De-whitening is performed before the CRC checking in the receiver
|
||||
// Before whitening or de-whitening, the shift register should be
|
||||
// initialized based on the channel index.
|
||||
w.set_whiteen(true);
|
||||
});
|
||||
|
||||
// Configure CRC
|
||||
r.crccnf().write(|w| {
|
||||
// In BLE the CRC shall be calculated on the PDU of all Link Layer
|
||||
// packets (even if the packet is encrypted).
|
||||
// It skips the address field
|
||||
w.set_skipaddr(vals::Skipaddr::SKIP);
|
||||
// In BLE 24-bit CRC = 3 bytes
|
||||
w.set_len(vals::Len::THREE);
|
||||
});
|
||||
|
||||
// Ch map between 2400 MHZ .. 2500 MHz
|
||||
// All modes use this range
|
||||
#[cfg(not(feature = "_nrf51"))]
|
||||
r.frequency().write(|w| w.set_map(vals::Map::DEFAULT));
|
||||
|
||||
// Configure shortcuts to simplify and speed up sending and receiving packets.
|
||||
r.shorts().write(|w| {
|
||||
// start transmission/recv immediately after ramp-up
|
||||
// disable radio when transmission/recv is done
|
||||
w.set_ready_start(true);
|
||||
w.set_end_disable(true);
|
||||
});
|
||||
|
||||
// Enable NVIC interrupt
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
Self { _p: radio }
|
||||
}
|
||||
|
||||
fn state(&self) -> RadioState {
|
||||
super::state(T::regs())
|
||||
}
|
||||
|
||||
/// Set the radio mode
|
||||
///
|
||||
/// The radio must be disabled before calling this function
|
||||
pub fn set_mode(&mut self, mode: Mode) {
|
||||
assert!(self.state() == RadioState::DISABLED);
|
||||
|
||||
let r = T::regs();
|
||||
r.mode().write(|w| w.set_mode(mode));
|
||||
|
||||
#[cfg(not(feature = "_nrf51"))]
|
||||
r.pcnf0().write(|w| {
|
||||
w.set_plen(match mode {
|
||||
Mode::BLE_1MBIT => PreambleLength::_8BIT,
|
||||
Mode::BLE_2MBIT => PreambleLength::_16BIT,
|
||||
#[cfg(any(
|
||||
feature = "nrf52811",
|
||||
feature = "nrf52820",
|
||||
feature = "nrf52833",
|
||||
feature = "nrf52840",
|
||||
feature = "_nrf5340-net"
|
||||
))]
|
||||
Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE,
|
||||
_ => unimplemented!(),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Set the header size changing the S1's len field
|
||||
///
|
||||
/// The radio must be disabled before calling this function
|
||||
pub fn set_header_expansion(&mut self, use_s1_field: bool) {
|
||||
assert!(self.state() == RadioState::DISABLED);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// s1 len in bits
|
||||
let s1len: u8 = match use_s1_field {
|
||||
false => 0,
|
||||
true => 8,
|
||||
};
|
||||
|
||||
r.pcnf0().write(|w| {
|
||||
// Configure S0 to 1 byte length, this will represent the Data/Adv header flags
|
||||
w.set_s0len(true);
|
||||
// Configure the length (in bits) field to 1 byte length, this will represent the length of the payload
|
||||
// and also be used to know how many bytes to read/write from/to the buffer
|
||||
w.set_lflen(0);
|
||||
// Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE.
|
||||
w.set_s1len(s1len);
|
||||
});
|
||||
}
|
||||
|
||||
/// Set initial data whitening value
|
||||
/// Data whitening is used to avoid long sequences of zeros or ones, e.g., 0b0000000 or 0b1111111, in the data bit stream
|
||||
/// On BLE the initial value is the channel index | 0x40
|
||||
///
|
||||
/// The radio must be disabled before calling this function
|
||||
pub fn set_whitening_init(&mut self, whitening_init: u8) {
|
||||
assert!(self.state() == RadioState::DISABLED);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
r.datawhiteiv().write(|w| w.set_datawhiteiv(whitening_init));
|
||||
}
|
||||
|
||||
/// Set the central frequency to be used
|
||||
/// It should be in the range 2400..2500
|
||||
///
|
||||
/// [The radio must be disabled before calling this function](https://devzone.nordicsemi.com/f/nordic-q-a/15829/radio-frequency-change)
|
||||
pub fn set_frequency(&mut self, frequency: u32) {
|
||||
assert!(self.state() == RadioState::DISABLED);
|
||||
assert!((2400..=2500).contains(&frequency));
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
r.frequency().write(|w| w.set_frequency((frequency - 2400) as u8));
|
||||
}
|
||||
|
||||
/// Set the acess address
|
||||
/// This address is always constants for advertising
|
||||
/// And a random value generate on each connection
|
||||
/// It is used to filter the packages
|
||||
///
|
||||
/// The radio must be disabled before calling this function
|
||||
pub fn set_access_address(&mut self, access_address: u32) {
|
||||
assert!(self.state() == RadioState::DISABLED);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// Configure logical address
|
||||
// The byte ordering on air is always least significant byte first for the address
|
||||
// So for the address 0xAA_BB_CC_DD, the address on air will be DD CC BB AA
|
||||
// The package order is BASE, PREFIX so BASE=0xBB_CC_DD and PREFIX=0xAA
|
||||
r.prefix0().write(|w| w.set_ap0((access_address >> 24) as u8));
|
||||
|
||||
// The base address is truncated from the least significant byte (because the BALEN is less than 4)
|
||||
// So it shifts the address to the right
|
||||
r.base0().write_value(access_address << 8);
|
||||
|
||||
// Don't match tx address
|
||||
r.txaddress().write(|w| w.set_txaddress(0));
|
||||
|
||||
// Match on logical address
|
||||
// This config only filter the packets by the address,
|
||||
// so only packages send to the previous address
|
||||
// will finish the reception (TODO: check the explanation)
|
||||
r.rxaddresses().write(|w| {
|
||||
w.set_addr0(true);
|
||||
w.set_addr1(true);
|
||||
w.set_addr2(true);
|
||||
w.set_addr3(true);
|
||||
w.set_addr4(true);
|
||||
});
|
||||
}
|
||||
|
||||
/// Set the CRC polynomial
|
||||
/// It only uses the 24 least significant bits
|
||||
///
|
||||
/// The radio must be disabled before calling this function
|
||||
pub fn set_crc_poly(&mut self, crc_poly: u32) {
|
||||
assert!(self.state() == RadioState::DISABLED);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
r.crcpoly().write(|w| {
|
||||
// Configure the CRC polynomial
|
||||
// Each term in the CRC polynomial is mapped to a bit in this
|
||||
// register which index corresponds to the term's exponent.
|
||||
// The least significant term/bit is hard-wired internally to
|
||||
// 1, and bit number 0 of the register content is ignored by
|
||||
// the hardware. The following example is for an 8 bit CRC
|
||||
// polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 .
|
||||
w.set_crcpoly(crc_poly & 0xFFFFFF)
|
||||
});
|
||||
}
|
||||
|
||||
/// Set the CRC init value
|
||||
/// It only uses the 24 least significant bits
|
||||
/// The CRC initial value varies depending of the PDU type
|
||||
///
|
||||
/// The radio must be disabled before calling this function
|
||||
pub fn set_crc_init(&mut self, crc_init: u32) {
|
||||
assert!(self.state() == RadioState::DISABLED);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
r.crcinit().write(|w| w.set_crcinit(crc_init & 0xFFFFFF));
|
||||
}
|
||||
|
||||
/// Set the radio tx power
|
||||
///
|
||||
/// The radio must be disabled before calling this function
|
||||
pub fn set_tx_power(&mut self, tx_power: TxPower) {
|
||||
assert!(self.state() == RadioState::DISABLED);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
r.txpower().write(|w| w.set_txpower(tx_power));
|
||||
}
|
||||
|
||||
/// Set buffer to read/write
|
||||
///
|
||||
/// This method is unsound. You should guarantee that the buffer will live
|
||||
/// for the life time of the transmission or if the buffer will be modified.
|
||||
/// Also if the buffer is smaller than the packet length, the radio will
|
||||
/// read/write memory out of the buffer bounds.
|
||||
fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// Here it consider that the length of the packet is
|
||||
// correctly set in the buffer, otherwise it will send
|
||||
// unowned regions of memory
|
||||
let ptr = buffer.as_ptr();
|
||||
|
||||
// Configure the payload
|
||||
r.packetptr().write_value(ptr as u32);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send packet
|
||||
/// If the length byte in the package is greater than the buffer length
|
||||
/// the radio will read memory out of the buffer bounds
|
||||
pub async fn transmit(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
self.set_buffer(buffer)?;
|
||||
|
||||
let r = T::regs();
|
||||
self.trigger_and_wait_end(move || {
|
||||
// Initialize the transmission
|
||||
// trace!("txen");
|
||||
|
||||
r.tasks_txen().write_value(1);
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receive packet
|
||||
/// If the length byte in the received package is greater than the buffer length
|
||||
/// the radio will write memory out of the buffer bounds
|
||||
pub async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.set_buffer(buffer)?;
|
||||
|
||||
let r = T::regs();
|
||||
self.trigger_and_wait_end(move || {
|
||||
// Initialize the transmission
|
||||
// trace!("rxen");
|
||||
r.tasks_rxen().write_value(1);
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) {
|
||||
let r = T::regs();
|
||||
let s = T::state();
|
||||
|
||||
// If the Future is dropped before the end of the transmission
|
||||
// it disable the interrupt and stop the transmission
|
||||
// to keep the state consistent
|
||||
let drop = OnDrop::new(|| {
|
||||
trace!("radio drop: stopping");
|
||||
|
||||
r.intenclr().write(|w| w.set_end(true));
|
||||
|
||||
r.tasks_stop().write_value(1);
|
||||
|
||||
r.events_end().write_value(0);
|
||||
|
||||
trace!("radio drop: stopped");
|
||||
});
|
||||
|
||||
// trace!("radio:enable interrupt");
|
||||
// Clear some remnant side-effects (TODO: check if this is necessary)
|
||||
r.events_end().write_value(0);
|
||||
|
||||
// Enable interrupt
|
||||
r.intenset().write(|w| w.set_end(true));
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
// Trigger the transmission
|
||||
trigger();
|
||||
|
||||
// On poll check if interrupt happen
|
||||
poll_fn(|cx| {
|
||||
s.event_waker.register(cx.waker());
|
||||
if r.events_end().read() == 1 {
|
||||
// trace!("radio:end");
|
||||
return core::task::Poll::Ready(());
|
||||
}
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
r.events_end().write_value(0); // ACK
|
||||
|
||||
// Everthing ends fine, so it disable the drop
|
||||
drop.defuse();
|
||||
}
|
||||
|
||||
/// Disable the radio
|
||||
fn disable(&mut self) {
|
||||
let r = T::regs();
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
// If it is already disabled, do nothing
|
||||
if self.state() != RadioState::DISABLED {
|
||||
trace!("radio:disable");
|
||||
// Trigger the disable task
|
||||
r.tasks_disable().write_value(1);
|
||||
|
||||
// Wait until the radio is disabled
|
||||
while r.events_disabled().read() == 0 {}
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
// Acknowledge it
|
||||
r.events_disabled().write_value(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Radio<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
self.disable();
|
||||
}
|
||||
}
|
||||
@ -5,10 +5,11 @@ use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::drop::OnDrop;
|
||||
|
||||
use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower};
|
||||
use super::{Error, Instance, InterruptHandler, TxPower};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::interrupt::{self};
|
||||
use crate::pac::radio::vals;
|
||||
pub use crate::pac::radio::vals::State as RadioState;
|
||||
use crate::Peri;
|
||||
|
||||
/// Default (IEEE compliant) Start of Frame Delimiter
|
||||
@ -200,7 +201,7 @@ impl<'d, T: Instance> Radio<'d, T> {
|
||||
|
||||
/// Get the current radio state
|
||||
fn state(&self) -> RadioState {
|
||||
state(T::regs())
|
||||
T::regs().state().read().state()
|
||||
}
|
||||
|
||||
/// Moves the radio from any state to the DISABLED state
|
||||
@ -293,7 +294,7 @@ impl<'d, T: Instance> Radio<'d, T> {
|
||||
r.shorts().write(|_| {});
|
||||
r.tasks_stop().write_value(1);
|
||||
loop {
|
||||
match state(r) {
|
||||
match r.state().read().state() {
|
||||
RadioState::DISABLED | RadioState::RX_IDLE => break,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
#![macro_use]
|
||||
|
||||
/// Bluetooth Low Energy Radio driver.
|
||||
pub mod ble;
|
||||
#[cfg(any(
|
||||
feature = "nrf52811",
|
||||
feature = "nrf52820",
|
||||
@ -21,7 +20,6 @@ use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_internal::PeripheralType;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use pac::radio::vals::State as RadioState;
|
||||
pub use pac::radio::vals::Txpower as TxPower;
|
||||
|
||||
use crate::{interrupt, pac};
|
||||
@ -82,6 +80,7 @@ macro_rules! impl_radio {
|
||||
pac::$pac_type
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn state() -> &'static crate::radio::State {
|
||||
static STATE: crate::radio::State = crate::radio::State::new();
|
||||
&STATE
|
||||
@ -99,8 +98,3 @@ pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
/// Get the state of the radio
|
||||
pub(crate) fn state(radio: pac::radio::Radio) -> RadioState {
|
||||
radio.state().read().state()
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::sync::atomic::compiler_fence;
|
||||
use core::sync::atomic::Ordering::SeqCst;
|
||||
use core::task::Poll;
|
||||
@ -17,7 +16,7 @@ use embassy_time::{Duration, Instant};
|
||||
use embedded_hal_1::i2c::Operation;
|
||||
pub use pac::twim::vals::Frequency;
|
||||
|
||||
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
||||
use crate::chip::EASY_DMA_SIZE;
|
||||
use crate::gpio::Pin as GpioPin;
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::pac::gpio::vals as gpiovals;
|
||||
@ -75,8 +74,8 @@ pub enum Error {
|
||||
Transmit,
|
||||
/// Data reception failed.
|
||||
Receive,
|
||||
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
|
||||
BufferNotInRAM,
|
||||
/// The buffer is not in data RAM and is larger than the RAM buffer. It's most likely in flash, and nRF's DMA cannot access flash.
|
||||
RAMBufferTooSmall,
|
||||
/// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly.
|
||||
AddressNack,
|
||||
/// Didn't receive an ACK bit after a data byte.
|
||||
@ -115,16 +114,24 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
/// TWI driver.
|
||||
pub struct Twim<'d, T: Instance> {
|
||||
_p: Peri<'d, T>,
|
||||
tx_ram_buffer: &'d mut [u8],
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Twim<'d, T> {
|
||||
/// Create a new TWI driver.
|
||||
///
|
||||
/// `tx_ram_buffer` is required if any write operations will be performed with data that is not in RAM.
|
||||
/// Usually this is static data that the compiler locates in flash instead of RAM. The `tx_ram_buffer`
|
||||
/// needs to be at least as large as the largest write operation that will be executed with a buffer
|
||||
/// that is not in RAM. If all write operations will be performed from RAM, an empty buffer (`&[]`) may
|
||||
/// be used.
|
||||
pub fn new(
|
||||
twim: Peri<'d, T>,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
sda: Peri<'d, impl GpioPin>,
|
||||
scl: Peri<'d, impl GpioPin>,
|
||||
config: Config,
|
||||
tx_ram_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
let r = T::regs();
|
||||
|
||||
@ -159,7 +166,10 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
// Enable TWIM instance.
|
||||
r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));
|
||||
|
||||
let mut twim = Self { _p: twim };
|
||||
let mut twim = Self {
|
||||
_p: twim,
|
||||
tx_ram_buffer,
|
||||
};
|
||||
|
||||
// Apply runtime peripheral configuration
|
||||
Self::set_config(&mut twim, &config).unwrap();
|
||||
@ -174,21 +184,17 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
}
|
||||
|
||||
/// Set TX buffer, checking that it is in RAM and has suitable length.
|
||||
unsafe fn set_tx_buffer(
|
||||
&mut self,
|
||||
buffer: &[u8],
|
||||
ram_buffer: Option<&mut [MaybeUninit<u8>; FORCE_COPY_BUFFER_SIZE]>,
|
||||
) -> Result<(), Error> {
|
||||
unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
let buffer = if slice_in_ram(buffer) {
|
||||
buffer
|
||||
} else {
|
||||
let ram_buffer = ram_buffer.ok_or(Error::BufferNotInRAM)?;
|
||||
if buffer.len() > self.tx_ram_buffer.len() {
|
||||
return Err(Error::RAMBufferTooSmall);
|
||||
}
|
||||
trace!("Copying TWIM tx buffer into RAM for DMA");
|
||||
let ram_buffer = &mut ram_buffer[..buffer.len()];
|
||||
// Inline implementation of the nightly API MaybeUninit::copy_from_slice(ram_buffer, buffer)
|
||||
let uninit_src: &[MaybeUninit<u8>] = unsafe { core::mem::transmute(buffer) };
|
||||
ram_buffer.copy_from_slice(uninit_src);
|
||||
unsafe { &*(ram_buffer as *const [MaybeUninit<u8>] as *const [u8]) }
|
||||
let ram_buffer = &mut self.tx_ram_buffer[..buffer.len()];
|
||||
ram_buffer.copy_from_slice(buffer);
|
||||
&*ram_buffer
|
||||
};
|
||||
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
@ -358,7 +364,6 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
&mut self,
|
||||
address: u8,
|
||||
operations: &mut [Operation<'_>],
|
||||
tx_ram_buffer: Option<&mut [MaybeUninit<u8>; FORCE_COPY_BUFFER_SIZE]>,
|
||||
last_op: Option<&Operation<'_>>,
|
||||
inten: bool,
|
||||
) -> Result<usize, Error> {
|
||||
@ -397,7 +402,7 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
|
||||
// Set up DMA buffers.
|
||||
unsafe {
|
||||
self.set_tx_buffer(wr_buffer, tx_ram_buffer)?;
|
||||
self.set_tx_buffer(wr_buffer)?;
|
||||
self.set_rx_buffer(rd_buffer)?;
|
||||
}
|
||||
|
||||
@ -450,7 +455,7 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
{
|
||||
// Set up DMA buffers.
|
||||
unsafe {
|
||||
self.set_tx_buffer(wr_buffer, tx_ram_buffer)?;
|
||||
self.set_tx_buffer(wr_buffer)?;
|
||||
self.set_rx_buffer(rd_buffer)?;
|
||||
}
|
||||
|
||||
@ -472,7 +477,7 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
|
||||
// Set up DMA buffers.
|
||||
unsafe {
|
||||
self.set_tx_buffer(buffer, tx_ram_buffer)?;
|
||||
self.set_tx_buffer(buffer)?;
|
||||
}
|
||||
|
||||
// Start write operation.
|
||||
@ -539,28 +544,9 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
/// An `Operation::Write` following an `Operation::Read` must have a
|
||||
/// non-empty buffer.
|
||||
pub fn blocking_transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> {
|
||||
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
|
||||
let mut last_op = None;
|
||||
while !operations.is_empty() {
|
||||
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?;
|
||||
let (in_progress, rest) = operations.split_at_mut(ops);
|
||||
self.blocking_wait();
|
||||
self.check_operations(in_progress)?;
|
||||
last_op = in_progress.last();
|
||||
operations = rest;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Same as [`blocking_transaction`](Twim::blocking_transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||
pub fn blocking_transaction_from_ram(
|
||||
&mut self,
|
||||
address: u8,
|
||||
mut operations: &mut [Operation<'_>],
|
||||
) -> Result<(), Error> {
|
||||
let mut last_op = None;
|
||||
while !operations.is_empty() {
|
||||
let ops = self.setup_operations(address, operations, None, last_op, false)?;
|
||||
let ops = self.setup_operations(address, operations, last_op, false)?;
|
||||
let (in_progress, rest) = operations.split_at_mut(ops);
|
||||
self.blocking_wait();
|
||||
self.check_operations(in_progress)?;
|
||||
@ -580,30 +566,9 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
mut operations: &mut [Operation<'_>],
|
||||
timeout: Duration,
|
||||
) -> Result<(), Error> {
|
||||
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
|
||||
let mut last_op = None;
|
||||
while !operations.is_empty() {
|
||||
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?;
|
||||
let (in_progress, rest) = operations.split_at_mut(ops);
|
||||
self.blocking_wait_timeout(timeout)?;
|
||||
self.check_operations(in_progress)?;
|
||||
last_op = in_progress.last();
|
||||
operations = rest;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Same as [`blocking_transaction_timeout`](Twim::blocking_transaction_timeout) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||
#[cfg(feature = "time")]
|
||||
pub fn blocking_transaction_from_ram_timeout(
|
||||
&mut self,
|
||||
address: u8,
|
||||
mut operations: &mut [Operation<'_>],
|
||||
timeout: Duration,
|
||||
) -> Result<(), Error> {
|
||||
let mut last_op = None;
|
||||
while !operations.is_empty() {
|
||||
let ops = self.setup_operations(address, operations, None, last_op, false)?;
|
||||
let ops = self.setup_operations(address, operations, last_op, false)?;
|
||||
let (in_progress, rest) = operations.split_at_mut(ops);
|
||||
self.blocking_wait_timeout(timeout)?;
|
||||
self.check_operations(in_progress)?;
|
||||
@ -624,28 +589,9 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
/// An `Operation::Write` following an `Operation::Read` must have a
|
||||
/// non-empty buffer.
|
||||
pub async fn transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> {
|
||||
let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE];
|
||||
let mut last_op = None;
|
||||
while !operations.is_empty() {
|
||||
let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, true)?;
|
||||
let (in_progress, rest) = operations.split_at_mut(ops);
|
||||
self.async_wait().await?;
|
||||
self.check_operations(in_progress)?;
|
||||
last_op = in_progress.last();
|
||||
operations = rest;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Same as [`transaction`](Twim::transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||
pub async fn transaction_from_ram(
|
||||
&mut self,
|
||||
address: u8,
|
||||
mut operations: &mut [Operation<'_>],
|
||||
) -> Result<(), Error> {
|
||||
let mut last_op = None;
|
||||
while !operations.is_empty() {
|
||||
let ops = self.setup_operations(address, operations, None, last_op, true)?;
|
||||
let ops = self.setup_operations(address, operations, last_op, true)?;
|
||||
let (in_progress, rest) = operations.split_at_mut(ops);
|
||||
self.async_wait().await?;
|
||||
self.check_operations(in_progress)?;
|
||||
@ -665,11 +611,6 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
self.blocking_transaction(address, &mut [Operation::Write(buffer)])
|
||||
}
|
||||
|
||||
/// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||
pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {
|
||||
self.blocking_transaction_from_ram(address, &mut [Operation::Write(buffer)])
|
||||
}
|
||||
|
||||
/// Read from an I2C slave.
|
||||
///
|
||||
/// The buffer must have a length of at most 255 bytes on the nRF52832
|
||||
@ -687,16 +628,6 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
self.blocking_transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
|
||||
}
|
||||
|
||||
/// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||
pub fn blocking_write_read_from_ram(
|
||||
&mut self,
|
||||
address: u8,
|
||||
wr_buffer: &[u8],
|
||||
rd_buffer: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
self.blocking_transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
|
||||
}
|
||||
|
||||
// ===========================================
|
||||
|
||||
/// Write to an I2C slave with timeout.
|
||||
@ -707,17 +638,6 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], timeout)
|
||||
}
|
||||
|
||||
/// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||
#[cfg(feature = "time")]
|
||||
pub fn blocking_write_from_ram_timeout(
|
||||
&mut self,
|
||||
address: u8,
|
||||
buffer: &[u8],
|
||||
timeout: Duration,
|
||||
) -> Result<(), Error> {
|
||||
self.blocking_transaction_from_ram_timeout(address, &mut [Operation::Write(buffer)], timeout)
|
||||
}
|
||||
|
||||
/// Read from an I2C slave.
|
||||
///
|
||||
/// The buffer must have a length of at most 255 bytes on the nRF52832
|
||||
@ -747,22 +667,6 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||
#[cfg(feature = "time")]
|
||||
pub fn blocking_write_read_from_ram_timeout(
|
||||
&mut self,
|
||||
address: u8,
|
||||
wr_buffer: &[u8],
|
||||
rd_buffer: &mut [u8],
|
||||
timeout: Duration,
|
||||
) -> Result<(), Error> {
|
||||
self.blocking_transaction_from_ram_timeout(
|
||||
address,
|
||||
&mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)],
|
||||
timeout,
|
||||
)
|
||||
}
|
||||
|
||||
// ===========================================
|
||||
|
||||
/// Read from an I2C slave.
|
||||
@ -781,12 +685,6 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
self.transaction(address, &mut [Operation::Write(buffer)]).await
|
||||
}
|
||||
|
||||
/// Same as [`write`](Twim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||
pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {
|
||||
self.transaction_from_ram(address, &mut [Operation::Write(buffer)])
|
||||
.await
|
||||
}
|
||||
|
||||
/// Write data to an I2C slave, then read data from the slave without
|
||||
/// triggering a stop condition between the two.
|
||||
///
|
||||
@ -796,17 +694,6 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
self.transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
|
||||
.await
|
||||
}
|
||||
|
||||
/// Same as [`write_read`](Twim::write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||
pub async fn write_read_from_ram(
|
||||
&mut self,
|
||||
address: u8,
|
||||
wr_buffer: &[u8],
|
||||
rd_buffer: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
self.transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Instance> Drop for Twim<'a, T> {
|
||||
@ -904,7 +791,7 @@ impl embedded_hal_1::i2c::Error for Error {
|
||||
Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::Receive => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::BufferNotInRAM => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::RAMBufferTooSmall => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::AddressNack => {
|
||||
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
|
||||
}
|
||||
|
||||
@ -26,7 +26,10 @@ features = ["defmt", "unstable-pac", "time-driver", "rp2040"]
|
||||
|
||||
[features]
|
||||
default = [ "rt" ]
|
||||
## Enable the rt feature of [`rp-pac`](https://docs.rs/rp-pac). This brings in the [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds startup code and minimal runtime initialization.
|
||||
|
||||
## Enable the `rt` feature of [`rp-pac`](https://docs.rs/rp-pac).
|
||||
## With `rt` enabled the PAC provides interrupt vectors instead of letting [`cortex-m-rt`](https://docs.rs/cortex-m-rt) do that.
|
||||
## See <https://docs.rs/cortex-m-rt/latest/cortex_m_rt/#device> for more info.
|
||||
rt = [ "rp-pac/rt" ]
|
||||
|
||||
## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
25
embassy-rp/src/pio_programs/clock_divider.rs
Normal file
25
embassy-rp/src/pio_programs/clock_divider.rs
Normal file
@ -0,0 +1,25 @@
|
||||
//! Helper functions for calculating PIO clock dividers
|
||||
|
||||
use fixed::traits::ToFixed;
|
||||
use fixed::types::extra::U8;
|
||||
|
||||
use crate::clocks::clk_sys_freq;
|
||||
|
||||
/// Calculate a PIO clock divider value based on the desired target frequency.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `target_hz` - The desired PIO clock frequency in Hz
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A fixed-point divider value suitable for use in a PIO state machine configuration
|
||||
#[inline]
|
||||
pub fn calculate_pio_clock_divider(target_hz: u32) -> fixed::FixedU32<U8> {
|
||||
// Requires a non-zero frequency
|
||||
assert!(target_hz > 0, "PIO clock frequency cannot be zero");
|
||||
|
||||
// Calculate the divider
|
||||
let divider = (clk_sys_freq() + target_hz / 2) / target_hz;
|
||||
divider.to_fixed()
|
||||
}
|
||||
@ -5,6 +5,7 @@ use crate::pio::{
|
||||
Common, Config, Direction, FifoJoin, Instance, Irq, LoadedProgram, PioPin, ShiftConfig, ShiftDirection,
|
||||
StateMachine,
|
||||
};
|
||||
use crate::pio_programs::clock_divider::calculate_pio_clock_divider;
|
||||
use crate::Peri;
|
||||
|
||||
/// This struct represents a HD44780 program that takes command words (<wait:24> <command:4> <0:4>)
|
||||
@ -134,7 +135,10 @@ impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> {
|
||||
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(&word_prg.prg, &[&e]);
|
||||
cfg.clock_divider = 125u8.into();
|
||||
|
||||
// Target 1 MHz PIO clock (each cycle is 1µs)
|
||||
cfg.clock_divider = calculate_pio_clock_divider(1_000_000);
|
||||
|
||||
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
||||
cfg.shift_out = ShiftConfig {
|
||||
auto_fill: true,
|
||||
@ -160,7 +164,10 @@ impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> {
|
||||
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(&seq_prg.prg, &[&e]);
|
||||
cfg.clock_divider = 8u8.into(); // ~64ns/insn
|
||||
|
||||
// Target ~15.6 MHz PIO clock (~64ns/insn)
|
||||
cfg.clock_divider = calculate_pio_clock_divider(15_600_000);
|
||||
|
||||
cfg.set_jmp_pin(&db7);
|
||||
cfg.set_set_pins(&[&rs, &rw]);
|
||||
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
//! Pre-built pio programs for common interfaces
|
||||
|
||||
pub mod clock_divider;
|
||||
pub mod hd44780;
|
||||
pub mod i2s;
|
||||
pub mod onewire;
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
//! PIO backed quadrature encoder
|
||||
|
||||
use fixed::traits::ToFixed;
|
||||
|
||||
use crate::gpio::Pull;
|
||||
use crate::pio::{
|
||||
Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine,
|
||||
};
|
||||
use crate::pio_programs::clock_divider::calculate_pio_clock_divider;
|
||||
use crate::Peri;
|
||||
|
||||
/// This struct represents an Encoder program loaded into pio instruction memory.
|
||||
@ -48,7 +47,10 @@ impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {
|
||||
cfg.set_in_pins(&[&pin_a, &pin_b]);
|
||||
cfg.fifo_join = FifoJoin::RxOnly;
|
||||
cfg.shift_in.direction = ShiftDirection::Left;
|
||||
cfg.clock_divider = 10_000.to_fixed();
|
||||
|
||||
// Target 12.5 KHz PIO clock
|
||||
cfg.clock_divider = calculate_pio_clock_divider(12_500);
|
||||
|
||||
cfg.use_program(&program.prg, &[]);
|
||||
sm.set_config(&cfg);
|
||||
sm.set_enable(true);
|
||||
|
||||
@ -2,11 +2,8 @@
|
||||
|
||||
use core::mem::{self, MaybeUninit};
|
||||
|
||||
use fixed::traits::ToFixed;
|
||||
use fixed::types::extra::U8;
|
||||
use fixed::FixedU32;
|
||||
|
||||
use crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine};
|
||||
use crate::pio_programs::clock_divider::calculate_pio_clock_divider;
|
||||
use crate::Peri;
|
||||
|
||||
/// This struct represents a Stepper driver program loaded into pio instruction memory.
|
||||
@ -64,7 +61,9 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
|
||||
sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
|
||||
let mut cfg = Config::default();
|
||||
cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
|
||||
cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
|
||||
|
||||
cfg.clock_divider = calculate_pio_clock_divider(100 * 136);
|
||||
|
||||
cfg.use_program(&program.prg, &[]);
|
||||
sm.set_config(&cfg);
|
||||
sm.set_enable(true);
|
||||
@ -73,9 +72,11 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
|
||||
|
||||
/// Set pulse frequency
|
||||
pub fn set_frequency(&mut self, freq: u32) {
|
||||
let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
|
||||
assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
|
||||
assert!(clock_divider >= 1, "clkdiv must be >= 1");
|
||||
let clock_divider = calculate_pio_clock_divider(freq * 136);
|
||||
let divider_f32 = clock_divider.to_num::<f32>();
|
||||
assert!(divider_f32 <= 65536.0, "clkdiv must be <= 65536");
|
||||
assert!(divider_f32 >= 1.0, "clkdiv must be >= 1");
|
||||
|
||||
self.sm.set_clock_divider(clock_divider);
|
||||
self.sm.clkdiv_restart();
|
||||
}
|
||||
|
||||
@ -34,28 +34,29 @@ impl State {
|
||||
}
|
||||
|
||||
/// Buffered UART driver.
|
||||
pub struct BufferedUart<'d, T: Instance> {
|
||||
pub(crate) rx: BufferedUartRx<'d, T>,
|
||||
pub(crate) tx: BufferedUartTx<'d, T>,
|
||||
pub struct BufferedUart {
|
||||
pub(super) rx: BufferedUartRx,
|
||||
pub(super) tx: BufferedUartTx,
|
||||
}
|
||||
|
||||
/// Buffered UART RX handle.
|
||||
pub struct BufferedUartRx<'d, T: Instance> {
|
||||
pub(crate) phantom: PhantomData<&'d mut T>,
|
||||
pub struct BufferedUartRx {
|
||||
pub(super) info: &'static Info,
|
||||
pub(super) state: &'static State,
|
||||
}
|
||||
|
||||
/// Buffered UART TX handle.
|
||||
pub struct BufferedUartTx<'d, T: Instance> {
|
||||
pub(crate) phantom: PhantomData<&'d mut T>,
|
||||
pub struct BufferedUartTx {
|
||||
pub(super) info: &'static Info,
|
||||
pub(super) state: &'static State,
|
||||
}
|
||||
|
||||
pub(crate) fn init_buffers<'d, T: Instance + 'd>(
|
||||
_irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
pub(super) fn init_buffers<'d>(
|
||||
info: &Info,
|
||||
state: &State,
|
||||
tx_buffer: Option<&'d mut [u8]>,
|
||||
rx_buffer: Option<&'d mut [u8]>,
|
||||
) {
|
||||
let state = T::buffered_state();
|
||||
|
||||
if let Some(tx_buffer) = tx_buffer {
|
||||
let len = tx_buffer.len();
|
||||
unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
|
||||
@ -76,61 +77,73 @@ pub(crate) fn init_buffers<'d, T: Instance + 'd>(
|
||||
// This means we can leave the interrupt enabled the whole time as long as
|
||||
// we clear it after it happens. The downside is that the we manually have
|
||||
// to pend the ISR when we want data transmission to start.
|
||||
let regs = T::regs();
|
||||
regs.uartimsc().write(|w| {
|
||||
info.regs.uartimsc().write(|w| {
|
||||
w.set_rxim(true);
|
||||
w.set_rtim(true);
|
||||
w.set_txim(true);
|
||||
});
|
||||
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
info.interrupt.unpend();
|
||||
unsafe { info.interrupt.enable() };
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> BufferedUart<'d, T> {
|
||||
impl BufferedUart {
|
||||
/// Create a buffered UART instance.
|
||||
pub fn new(
|
||||
pub fn new<'d, T: Instance>(
|
||||
_uart: Peri<'d, T>,
|
||||
tx: Peri<'d, impl TxPin<T>>,
|
||||
rx: Peri<'d, impl RxPin<T>>,
|
||||
irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
_irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
tx_buffer: &'d mut [u8],
|
||||
rx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> Self {
|
||||
super::Uart::<'d, T, Async>::init(Some(tx.into()), Some(rx.into()), None, None, config);
|
||||
init_buffers::<T>(irq, Some(tx_buffer), Some(rx_buffer));
|
||||
super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), Some(rx.into()), None, None, config);
|
||||
init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer));
|
||||
|
||||
Self {
|
||||
rx: BufferedUartRx { phantom: PhantomData },
|
||||
tx: BufferedUartTx { phantom: PhantomData },
|
||||
rx: BufferedUartRx {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
},
|
||||
tx: BufferedUartTx {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a buffered UART instance with flow control.
|
||||
pub fn new_with_rtscts(
|
||||
pub fn new_with_rtscts<'d, T: Instance>(
|
||||
_uart: Peri<'d, T>,
|
||||
tx: Peri<'d, impl TxPin<T>>,
|
||||
rx: Peri<'d, impl RxPin<T>>,
|
||||
rts: Peri<'d, impl RtsPin<T>>,
|
||||
cts: Peri<'d, impl CtsPin<T>>,
|
||||
irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
_irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
tx_buffer: &'d mut [u8],
|
||||
rx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> Self {
|
||||
super::Uart::<'d, T, Async>::init(
|
||||
super::Uart::<'d, Async>::init(
|
||||
T::info(),
|
||||
Some(tx.into()),
|
||||
Some(rx.into()),
|
||||
Some(rts.into()),
|
||||
Some(cts.into()),
|
||||
config,
|
||||
);
|
||||
init_buffers::<T>(irq, Some(tx_buffer), Some(rx_buffer));
|
||||
init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer));
|
||||
|
||||
Self {
|
||||
rx: BufferedUartRx { phantom: PhantomData },
|
||||
tx: BufferedUartTx { phantom: PhantomData },
|
||||
rx: BufferedUartRx {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
},
|
||||
tx: BufferedUartTx {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,68 +173,75 @@ impl<'d, T: Instance> BufferedUart<'d, T> {
|
||||
}
|
||||
|
||||
/// sets baudrate on runtime
|
||||
pub fn set_baudrate(&mut self, baudrate: u32) {
|
||||
super::Uart::<'d, T, Async>::set_baudrate_inner(baudrate);
|
||||
pub fn set_baudrate<'d>(&mut self, baudrate: u32) {
|
||||
super::Uart::<'d, Async>::set_baudrate_inner(self.rx.info, baudrate);
|
||||
}
|
||||
|
||||
/// Split into separate RX and TX handles.
|
||||
pub fn split(self) -> (BufferedUartTx<'d, T>, BufferedUartRx<'d, T>) {
|
||||
pub fn split(self) -> (BufferedUartTx, BufferedUartRx) {
|
||||
(self.tx, self.rx)
|
||||
}
|
||||
|
||||
/// Split the Uart into a transmitter and receiver by mutable reference,
|
||||
/// which is particularly useful when having two tasks correlating to
|
||||
/// transmitting and receiving.
|
||||
pub fn split_ref(&mut self) -> (&mut BufferedUartTx<'d, T>, &mut BufferedUartRx<'d, T>) {
|
||||
pub fn split_ref(&mut self) -> (&mut BufferedUartTx, &mut BufferedUartRx) {
|
||||
(&mut self.tx, &mut self.rx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> BufferedUartRx<'d, T> {
|
||||
impl BufferedUartRx {
|
||||
/// Create a new buffered UART RX.
|
||||
pub fn new(
|
||||
pub fn new<'d, T: Instance>(
|
||||
_uart: Peri<'d, T>,
|
||||
irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
_irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
rx: Peri<'d, impl RxPin<T>>,
|
||||
rx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> Self {
|
||||
super::Uart::<'d, T, Async>::init(None, Some(rx.into()), None, None, config);
|
||||
init_buffers::<T>(irq, None, Some(rx_buffer));
|
||||
super::Uart::<'d, Async>::init(T::info(), None, Some(rx.into()), None, None, config);
|
||||
init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer));
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
Self {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new buffered UART RX with flow control.
|
||||
pub fn new_with_rts(
|
||||
pub fn new_with_rts<'d, T: Instance>(
|
||||
_uart: Peri<'d, T>,
|
||||
irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
_irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
rx: Peri<'d, impl RxPin<T>>,
|
||||
rts: Peri<'d, impl RtsPin<T>>,
|
||||
rx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> Self {
|
||||
super::Uart::<'d, T, Async>::init(None, Some(rx.into()), Some(rts.into()), None, config);
|
||||
init_buffers::<T>(irq, None, Some(rx_buffer));
|
||||
super::Uart::<'d, Async>::init(T::info(), None, Some(rx.into()), Some(rts.into()), None, config);
|
||||
init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer));
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
Self {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
}
|
||||
}
|
||||
|
||||
fn read<'a>(buf: &'a mut [u8]) -> impl Future<Output = Result<usize, Error>> + 'a
|
||||
where
|
||||
T: 'd,
|
||||
{
|
||||
fn read<'a>(
|
||||
info: &'static Info,
|
||||
state: &'static State,
|
||||
buf: &'a mut [u8],
|
||||
) -> impl Future<Output = Result<usize, Error>> + 'a {
|
||||
poll_fn(move |cx| {
|
||||
if let Poll::Ready(r) = Self::try_read(buf) {
|
||||
if let Poll::Ready(r) = Self::try_read(info, state, buf) {
|
||||
return Poll::Ready(r);
|
||||
}
|
||||
T::buffered_state().rx_waker.register(cx.waker());
|
||||
state.rx_waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
})
|
||||
}
|
||||
|
||||
fn get_rx_error() -> Option<Error> {
|
||||
let errs = T::buffered_state().rx_error.swap(0, Ordering::Relaxed);
|
||||
fn get_rx_error(state: &State) -> Option<Error> {
|
||||
let errs = state.rx_error.swap(0, Ordering::Relaxed);
|
||||
if errs & RXE_OVERRUN != 0 {
|
||||
Some(Error::Overrun)
|
||||
} else if errs & RXE_BREAK != 0 {
|
||||
@ -235,15 +255,11 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn try_read(buf: &mut [u8]) -> Poll<Result<usize, Error>>
|
||||
where
|
||||
T: 'd,
|
||||
{
|
||||
fn try_read(info: &Info, state: &State, buf: &mut [u8]) -> Poll<Result<usize, Error>> {
|
||||
if buf.is_empty() {
|
||||
return Poll::Ready(Ok(0));
|
||||
}
|
||||
|
||||
let state = T::buffered_state();
|
||||
let mut rx_reader = unsafe { state.rx_buf.reader() };
|
||||
let n = rx_reader.pop(|data| {
|
||||
let n = data.len().min(buf.len());
|
||||
@ -252,7 +268,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
|
||||
});
|
||||
|
||||
let result = if n == 0 {
|
||||
match Self::get_rx_error() {
|
||||
match Self::get_rx_error(state) {
|
||||
None => return Poll::Pending,
|
||||
Some(e) => Err(e),
|
||||
}
|
||||
@ -262,8 +278,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
|
||||
|
||||
// (Re-)Enable the interrupt to receive more data in case it was
|
||||
// disabled because the buffer was full or errors were detected.
|
||||
let regs = T::regs();
|
||||
regs.uartimsc().write_set(|w| {
|
||||
info.regs.uartimsc().write_set(|w| {
|
||||
w.set_rxim(true);
|
||||
w.set_rtim(true);
|
||||
});
|
||||
@ -274,23 +289,19 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
|
||||
/// Read from UART RX buffer blocking execution until done.
|
||||
pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
loop {
|
||||
match Self::try_read(buf) {
|
||||
match Self::try_read(self.info, self.state, buf) {
|
||||
Poll::Ready(res) => return res,
|
||||
Poll::Pending => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_buf<'a>() -> impl Future<Output = Result<&'a [u8], Error>>
|
||||
where
|
||||
T: 'd,
|
||||
{
|
||||
fn fill_buf<'a>(state: &'static State) -> impl Future<Output = Result<&'a [u8], Error>> {
|
||||
poll_fn(move |cx| {
|
||||
let state = T::buffered_state();
|
||||
let mut rx_reader = unsafe { state.rx_buf.reader() };
|
||||
let (p, n) = rx_reader.pop_buf();
|
||||
let result = if n == 0 {
|
||||
match Self::get_rx_error() {
|
||||
match Self::get_rx_error(state) {
|
||||
None => {
|
||||
state.rx_waker.register(cx.waker());
|
||||
return Poll::Pending;
|
||||
@ -306,64 +317,70 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
|
||||
})
|
||||
}
|
||||
|
||||
fn consume(amt: usize) {
|
||||
let state = T::buffered_state();
|
||||
fn consume(info: &Info, state: &State, amt: usize) {
|
||||
let mut rx_reader = unsafe { state.rx_buf.reader() };
|
||||
rx_reader.pop_done(amt);
|
||||
|
||||
// (Re-)Enable the interrupt to receive more data in case it was
|
||||
// disabled because the buffer was full or errors were detected.
|
||||
let regs = T::regs();
|
||||
regs.uartimsc().write_set(|w| {
|
||||
info.regs.uartimsc().write_set(|w| {
|
||||
w.set_rxim(true);
|
||||
w.set_rtim(true);
|
||||
});
|
||||
}
|
||||
|
||||
/// we are ready to read if there is data in the buffer
|
||||
fn read_ready() -> Result<bool, Error> {
|
||||
let state = T::buffered_state();
|
||||
fn read_ready(state: &State) -> Result<bool, Error> {
|
||||
Ok(!state.rx_buf.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
||||
impl BufferedUartTx {
|
||||
/// Create a new buffered UART TX.
|
||||
pub fn new(
|
||||
pub fn new<'d, T: Instance>(
|
||||
_uart: Peri<'d, T>,
|
||||
irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
_irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
tx: Peri<'d, impl TxPin<T>>,
|
||||
tx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> Self {
|
||||
super::Uart::<'d, T, Async>::init(Some(tx.into()), None, None, None, config);
|
||||
init_buffers::<T>(irq, Some(tx_buffer), None);
|
||||
super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), None, None, None, config);
|
||||
init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None);
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
Self {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new buffered UART TX with flow control.
|
||||
pub fn new_with_cts(
|
||||
pub fn new_with_cts<'d, T: Instance>(
|
||||
_uart: Peri<'d, T>,
|
||||
irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
_irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
tx: Peri<'d, impl TxPin<T>>,
|
||||
cts: Peri<'d, impl CtsPin<T>>,
|
||||
tx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> Self {
|
||||
super::Uart::<'d, T, Async>::init(Some(tx.into()), None, None, Some(cts.into()), config);
|
||||
init_buffers::<T>(irq, Some(tx_buffer), None);
|
||||
super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), None, None, Some(cts.into()), config);
|
||||
init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None);
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
Self {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write(buf: &[u8]) -> impl Future<Output = Result<usize, Error>> + '_ {
|
||||
fn write<'d>(
|
||||
info: &'static Info,
|
||||
state: &'static State,
|
||||
buf: &'d [u8],
|
||||
) -> impl Future<Output = Result<usize, Error>> + 'd {
|
||||
poll_fn(move |cx| {
|
||||
if buf.is_empty() {
|
||||
return Poll::Ready(Ok(0));
|
||||
}
|
||||
|
||||
let state = T::buffered_state();
|
||||
let mut tx_writer = unsafe { state.tx_buf.writer() };
|
||||
let n = tx_writer.push(|data| {
|
||||
let n = data.len().min(buf.len());
|
||||
@ -379,14 +396,13 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
||||
// FIFO and the number of bytes drops below a threshold. When the
|
||||
// FIFO was empty we have to manually pend the interrupt to shovel
|
||||
// TX data from the buffer into the FIFO.
|
||||
T::Interrupt::pend();
|
||||
info.interrupt.pend();
|
||||
Poll::Ready(Ok(n))
|
||||
})
|
||||
}
|
||||
|
||||
fn flush() -> impl Future<Output = Result<(), Error>> {
|
||||
fn flush(state: &'static State) -> impl Future<Output = Result<(), Error>> {
|
||||
poll_fn(move |cx| {
|
||||
let state = T::buffered_state();
|
||||
if !state.tx_buf.is_empty() {
|
||||
state.tx_waker.register(cx.waker());
|
||||
return Poll::Pending;
|
||||
@ -403,8 +419,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
||||
}
|
||||
|
||||
loop {
|
||||
let state = T::buffered_state();
|
||||
let mut tx_writer = unsafe { state.tx_buf.writer() };
|
||||
let mut tx_writer = unsafe { self.state.tx_buf.writer() };
|
||||
let n = tx_writer.push(|data| {
|
||||
let n = data.len().min(buf.len());
|
||||
data[..n].copy_from_slice(&buf[..n]);
|
||||
@ -416,7 +431,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
||||
// FIFO and the number of bytes drops below a threshold. When the
|
||||
// FIFO was empty we have to manually pend the interrupt to shovel
|
||||
// TX data from the buffer into the FIFO.
|
||||
T::Interrupt::pend();
|
||||
self.info.interrupt.pend();
|
||||
return Ok(n);
|
||||
}
|
||||
}
|
||||
@ -425,8 +440,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
||||
/// Flush UART TX blocking execution until done.
|
||||
pub fn blocking_flush(&mut self) -> Result<(), Error> {
|
||||
loop {
|
||||
let state = T::buffered_state();
|
||||
if state.tx_buf.is_empty() {
|
||||
if self.state.tx_buf.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@ -434,7 +448,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
||||
|
||||
/// Check if UART is busy.
|
||||
pub fn busy(&self) -> bool {
|
||||
T::regs().uartfr().read().busy()
|
||||
self.info.regs.uartfr().read().busy()
|
||||
}
|
||||
|
||||
/// Assert a break condition after waiting for the transmit buffers to empty,
|
||||
@ -445,7 +459,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
||||
/// This method may block for a long amount of time since it has to wait
|
||||
/// for the transmit fifo to empty, which may take a while on slow links.
|
||||
pub async fn send_break(&mut self, bits: u32) {
|
||||
let regs = T::regs();
|
||||
let regs = self.info.regs;
|
||||
let bits = bits.max({
|
||||
let lcr = regs.uartlcr_h().read();
|
||||
let width = lcr.wlen() as u32 + 5;
|
||||
@ -458,7 +472,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
||||
let div_clk = clk_peri_freq() as u64 * 64;
|
||||
let wait_usecs = (1_000_000 * bits as u64 * divx64 * 16 + div_clk - 1) / div_clk;
|
||||
|
||||
Self::flush().await.unwrap();
|
||||
Self::flush(self.state).await.unwrap();
|
||||
while self.busy() {}
|
||||
regs.uartlcr_h().write_set(|w| w.set_brk(true));
|
||||
Timer::after_micros(wait_usecs).await;
|
||||
@ -466,28 +480,26 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> {
|
||||
impl Drop for BufferedUartRx {
|
||||
fn drop(&mut self) {
|
||||
let state = T::buffered_state();
|
||||
unsafe { state.rx_buf.deinit() }
|
||||
unsafe { self.state.rx_buf.deinit() }
|
||||
|
||||
// TX is inactive if the buffer is not available.
|
||||
// We can now unregister the interrupt handler
|
||||
if !state.tx_buf.is_available() {
|
||||
T::Interrupt::disable();
|
||||
if !self.state.tx_buf.is_available() {
|
||||
self.info.interrupt.disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> {
|
||||
impl Drop for BufferedUartTx {
|
||||
fn drop(&mut self) {
|
||||
let state = T::buffered_state();
|
||||
unsafe { state.tx_buf.deinit() }
|
||||
unsafe { self.state.tx_buf.deinit() }
|
||||
|
||||
// RX is inactive if the buffer is not available.
|
||||
// We can now unregister the interrupt handler
|
||||
if !state.rx_buf.is_available() {
|
||||
T::Interrupt::disable();
|
||||
if !self.state.rx_buf.is_available() {
|
||||
self.info.interrupt.disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -499,7 +511,7 @@ pub struct BufferedInterruptHandler<T: Instance> {
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
let r = T::regs();
|
||||
let r = T::info().regs;
|
||||
if r.uartdmacr().read().rxdmae() {
|
||||
return;
|
||||
}
|
||||
@ -603,95 +615,95 @@ impl embedded_io::Error for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUart<'d, T> {
|
||||
impl embedded_io_async::ErrorType for BufferedUart {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUartRx<'d, T> {
|
||||
impl embedded_io_async::ErrorType for BufferedUartRx {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUartTx<'d, T> {
|
||||
impl embedded_io_async::ErrorType for BufferedUartTx {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io_async::Read for BufferedUart<'d, T> {
|
||||
impl embedded_io_async::Read for BufferedUart {
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
BufferedUartRx::<'d, T>::read(buf).await
|
||||
BufferedUartRx::read(self.rx.info, self.rx.state, buf).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io_async::Read for BufferedUartRx<'d, T> {
|
||||
impl embedded_io_async::Read for BufferedUartRx {
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
Self::read(buf).await
|
||||
Self::read(self.info, self.state, buf).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io_async::ReadReady for BufferedUart<'d, T> {
|
||||
impl embedded_io_async::ReadReady for BufferedUart {
|
||||
fn read_ready(&mut self) -> Result<bool, Self::Error> {
|
||||
BufferedUartRx::<'d, T>::read_ready()
|
||||
BufferedUartRx::read_ready(self.rx.state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io_async::ReadReady for BufferedUartRx<'d, T> {
|
||||
impl embedded_io_async::ReadReady for BufferedUartRx {
|
||||
fn read_ready(&mut self) -> Result<bool, Self::Error> {
|
||||
Self::read_ready()
|
||||
Self::read_ready(self.state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUart<'d, T> {
|
||||
impl embedded_io_async::BufRead for BufferedUart {
|
||||
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
|
||||
BufferedUartRx::<'d, T>::fill_buf().await
|
||||
BufferedUartRx::fill_buf(self.rx.state).await
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
BufferedUartRx::<'d, T>::consume(amt)
|
||||
BufferedUartRx::consume(self.rx.info, self.rx.state, amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUartRx<'d, T> {
|
||||
impl embedded_io_async::BufRead for BufferedUartRx {
|
||||
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
|
||||
Self::fill_buf().await
|
||||
Self::fill_buf(self.state).await
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
Self::consume(amt)
|
||||
Self::consume(self.info, self.state, amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io_async::Write for BufferedUart<'d, T> {
|
||||
impl embedded_io_async::Write for BufferedUart {
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
BufferedUartTx::<'d, T>::write(buf).await
|
||||
BufferedUartTx::write(self.tx.info, self.tx.state, buf).await
|
||||
}
|
||||
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
BufferedUartTx::<'d, T>::flush().await
|
||||
BufferedUartTx::flush(self.tx.state).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io_async::Write for BufferedUartTx<'d, T> {
|
||||
impl embedded_io_async::Write for BufferedUartTx {
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
Self::write(buf).await
|
||||
Self::write(self.info, self.state, buf).await
|
||||
}
|
||||
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Self::flush().await
|
||||
Self::flush(self.state).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::Read for BufferedUart<'d, T> {
|
||||
impl embedded_io::Read for BufferedUart {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.rx.blocking_read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::Read for BufferedUartRx<'d, T> {
|
||||
impl embedded_io::Read for BufferedUartRx {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.blocking_read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUart<'d, T> {
|
||||
impl embedded_io::Write for BufferedUart {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.tx.blocking_write(buf)
|
||||
}
|
||||
@ -701,7 +713,7 @@ impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUart<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUartTx<'d, T> {
|
||||
impl embedded_io::Write for BufferedUartTx {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.blocking_write(buf)
|
||||
}
|
||||
@ -711,11 +723,11 @@ impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUartTx<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::serial::Read<u8> for BufferedUartRx<'d, T> {
|
||||
impl embedded_hal_02::serial::Read<u8> for BufferedUartRx {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
let r = T::regs();
|
||||
let r = self.info.regs;
|
||||
if r.uartfr().read().rxfe() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
@ -736,7 +748,7 @@ impl<'d, T: Instance> embedded_hal_02::serial::Read<u8> for BufferedUartRx<'d, T
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUartTx<'d, T> {
|
||||
impl embedded_hal_02::blocking::serial::Write<u8> for BufferedUartTx {
|
||||
type Error = Error;
|
||||
|
||||
fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
@ -755,7 +767,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write<u8> for BufferedU
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::serial::Read<u8> for BufferedUart<'d, T> {
|
||||
impl embedded_hal_02::serial::Read<u8> for BufferedUart {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
@ -763,7 +775,7 @@ impl<'d, T: Instance> embedded_hal_02::serial::Read<u8> for BufferedUart<'d, T>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUart<'d, T> {
|
||||
impl embedded_hal_02::blocking::serial::Write<u8> for BufferedUart {
|
||||
type Error = Error;
|
||||
|
||||
fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
@ -782,25 +794,25 @@ impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write<u8> for BufferedU
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d, T> {
|
||||
impl embedded_hal_nb::serial::ErrorType for BufferedUartRx {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d, T> {
|
||||
impl embedded_hal_nb::serial::ErrorType for BufferedUartTx {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUart<'d, T> {
|
||||
impl embedded_hal_nb::serial::ErrorType for BufferedUart {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_nb::serial::Read for BufferedUartRx<'d, T> {
|
||||
impl embedded_hal_nb::serial::Read for BufferedUartRx {
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
embedded_hal_02::serial::Read::read(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> {
|
||||
impl embedded_hal_nb::serial::Write for BufferedUartTx {
|
||||
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)
|
||||
}
|
||||
@ -810,13 +822,13 @@ impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_nb::serial::Read for BufferedUart<'d, T> {
|
||||
impl embedded_hal_nb::serial::Read for BufferedUart {
|
||||
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
embedded_hal_02::serial::Read::read(&mut self.rx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> {
|
||||
impl embedded_hal_nb::serial::Write for BufferedUart {
|
||||
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)
|
||||
}
|
||||
|
||||
@ -13,7 +13,8 @@ use pac::uart::regs::Uartris;
|
||||
use crate::clocks::clk_peri_freq;
|
||||
use crate::dma::{AnyChannel, Channel};
|
||||
use crate::gpio::{AnyPin, SealedPin};
|
||||
use crate::interrupt::typelevel::{Binding, Interrupt};
|
||||
use crate::interrupt::typelevel::{Binding, Interrupt as _};
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::pac::io::vals::{Inover, Outover};
|
||||
use crate::{interrupt, pac, peripherals, RegExt};
|
||||
|
||||
@ -135,37 +136,41 @@ pub struct DmaState {
|
||||
}
|
||||
|
||||
/// UART driver.
|
||||
pub struct Uart<'d, T: Instance, M: Mode> {
|
||||
tx: UartTx<'d, T, M>,
|
||||
rx: UartRx<'d, T, M>,
|
||||
pub struct Uart<'d, M: Mode> {
|
||||
tx: UartTx<'d, M>,
|
||||
rx: UartRx<'d, M>,
|
||||
}
|
||||
|
||||
/// UART TX driver.
|
||||
pub struct UartTx<'d, T: Instance, M: Mode> {
|
||||
pub struct UartTx<'d, M: Mode> {
|
||||
info: &'static Info,
|
||||
tx_dma: Option<Peri<'d, AnyChannel>>,
|
||||
phantom: PhantomData<(&'d mut T, M)>,
|
||||
phantom: PhantomData<M>,
|
||||
}
|
||||
|
||||
/// UART RX driver.
|
||||
pub struct UartRx<'d, T: Instance, M: Mode> {
|
||||
pub struct UartRx<'d, M: Mode> {
|
||||
info: &'static Info,
|
||||
dma_state: &'static DmaState,
|
||||
rx_dma: Option<Peri<'d, AnyChannel>>,
|
||||
phantom: PhantomData<(&'d mut T, M)>,
|
||||
phantom: PhantomData<M>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> {
|
||||
impl<'d, M: Mode> UartTx<'d, M> {
|
||||
/// Create a new DMA-enabled UART which can only send data
|
||||
pub fn new(
|
||||
pub fn new<T: Instance>(
|
||||
_uart: Peri<'d, T>,
|
||||
tx: Peri<'d, impl TxPin<T>>,
|
||||
tx_dma: Peri<'d, impl Channel>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
Uart::<T, M>::init(Some(tx.into()), None, None, None, config);
|
||||
Self::new_inner(Some(tx_dma.into()))
|
||||
Uart::<M>::init(T::info(), Some(tx.into()), None, None, None, config);
|
||||
Self::new_inner(T::info(), Some(tx_dma.into()))
|
||||
}
|
||||
|
||||
fn new_inner(tx_dma: Option<Peri<'d, AnyChannel>>) -> Self {
|
||||
fn new_inner(info: &'static Info, tx_dma: Option<Peri<'d, AnyChannel>>) -> Self {
|
||||
Self {
|
||||
info,
|
||||
tx_dma,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
@ -173,7 +178,7 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> {
|
||||
|
||||
/// Transmit the provided buffer blocking execution until done.
|
||||
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
let r = T::regs();
|
||||
let r = self.info.regs;
|
||||
for &b in buffer {
|
||||
while r.uartfr().read().txff() {}
|
||||
r.uartdr().write(|w| w.set_data(b));
|
||||
@ -183,14 +188,13 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> {
|
||||
|
||||
/// Flush UART TX blocking execution until done.
|
||||
pub fn blocking_flush(&mut self) -> Result<(), Error> {
|
||||
let r = T::regs();
|
||||
while !r.uartfr().read().txfe() {}
|
||||
while !self.info.regs.uartfr().read().txfe() {}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if UART is busy transmitting.
|
||||
pub fn busy(&self) -> bool {
|
||||
T::regs().uartfr().read().busy()
|
||||
self.info.regs.uartfr().read().busy()
|
||||
}
|
||||
|
||||
/// Assert a break condition after waiting for the transmit buffers to empty,
|
||||
@ -201,7 +205,7 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> {
|
||||
/// This method may block for a long amount of time since it has to wait
|
||||
/// for the transmit fifo to empty, which may take a while on slow links.
|
||||
pub async fn send_break(&mut self, bits: u32) {
|
||||
let regs = T::regs();
|
||||
let regs = self.info.regs;
|
||||
let bits = bits.max({
|
||||
let lcr = regs.uartlcr_h().read();
|
||||
let width = lcr.wlen() as u32 + 5;
|
||||
@ -222,65 +226,80 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> UartTx<'d, T, Blocking> {
|
||||
impl<'d> UartTx<'d, Blocking> {
|
||||
/// Create a new UART TX instance for blocking mode operations.
|
||||
pub fn new_blocking(_uart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self {
|
||||
Uart::<T, Blocking>::init(Some(tx.into()), None, None, None, config);
|
||||
Self::new_inner(None)
|
||||
pub fn new_blocking<T: Instance>(_uart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self {
|
||||
Uart::<Blocking>::init(T::info(), Some(tx.into()), None, None, None, config);
|
||||
Self::new_inner(T::info(), None)
|
||||
}
|
||||
|
||||
/// Convert this uart TX instance into a buffered uart using the provided
|
||||
/// irq and transmit buffer.
|
||||
pub fn into_buffered(
|
||||
pub fn into_buffered<T: Instance>(
|
||||
self,
|
||||
irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
_irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
tx_buffer: &'d mut [u8],
|
||||
) -> BufferedUartTx<'d, T> {
|
||||
buffered::init_buffers::<T>(irq, Some(tx_buffer), None);
|
||||
) -> BufferedUartTx {
|
||||
buffered::init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None);
|
||||
|
||||
BufferedUartTx { phantom: PhantomData }
|
||||
BufferedUartTx {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> UartTx<'d, T, Async> {
|
||||
impl<'d> UartTx<'d, Async> {
|
||||
/// Write to UART TX from the provided buffer using DMA.
|
||||
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
let ch = self.tx_dma.as_mut().unwrap().reborrow();
|
||||
let transfer = unsafe {
|
||||
T::regs().uartdmacr().write_set(|reg| {
|
||||
self.info.regs.uartdmacr().write_set(|reg| {
|
||||
reg.set_txdmae(true);
|
||||
});
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::write(ch, buffer, T::regs().uartdr().as_ptr() as *mut _, T::TX_DREQ.into())
|
||||
crate::dma::write(
|
||||
ch,
|
||||
buffer,
|
||||
self.info.regs.uartdr().as_ptr() as *mut _,
|
||||
self.info.tx_dreq.into(),
|
||||
)
|
||||
};
|
||||
transfer.await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
|
||||
impl<'d, M: Mode> UartRx<'d, M> {
|
||||
/// Create a new DMA-enabled UART which can only receive data
|
||||
pub fn new(
|
||||
pub fn new<T: Instance>(
|
||||
_uart: Peri<'d, T>,
|
||||
rx: Peri<'d, impl RxPin<T>>,
|
||||
_irq: impl Binding<T::Interrupt, InterruptHandler<T>>,
|
||||
rx_dma: Peri<'d, impl Channel>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
Uart::<T, M>::init(None, Some(rx.into()), None, None, config);
|
||||
Self::new_inner(true, Some(rx_dma.into()))
|
||||
Uart::<M>::init(T::info(), None, Some(rx.into()), None, None, config);
|
||||
Self::new_inner(T::info(), T::dma_state(), true, Some(rx_dma.into()))
|
||||
}
|
||||
|
||||
fn new_inner(has_irq: bool, rx_dma: Option<Peri<'d, AnyChannel>>) -> Self {
|
||||
fn new_inner(
|
||||
info: &'static Info,
|
||||
dma_state: &'static DmaState,
|
||||
has_irq: bool,
|
||||
rx_dma: Option<Peri<'d, AnyChannel>>,
|
||||
) -> Self {
|
||||
debug_assert_eq!(has_irq, rx_dma.is_some());
|
||||
if has_irq {
|
||||
// disable all error interrupts initially
|
||||
T::regs().uartimsc().write(|w| w.0 = 0);
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
info.regs.uartimsc().write(|w| w.0 = 0);
|
||||
info.interrupt.unpend();
|
||||
unsafe { info.interrupt.enable() };
|
||||
}
|
||||
Self {
|
||||
info,
|
||||
dma_state,
|
||||
rx_dma,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
@ -299,7 +318,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
|
||||
/// encountered. in both cases, `len` is the number of *good* bytes copied into
|
||||
/// `buffer`.
|
||||
fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> {
|
||||
let r = T::regs();
|
||||
let r = self.info.regs;
|
||||
for (i, b) in buffer.iter_mut().enumerate() {
|
||||
if r.uartfr().read().rxfe() {
|
||||
return Ok(i);
|
||||
@ -323,12 +342,12 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> {
|
||||
impl<'d, M: Mode> Drop for UartRx<'d, M> {
|
||||
fn drop(&mut self) {
|
||||
if self.rx_dma.is_some() {
|
||||
T::Interrupt::disable();
|
||||
self.info.interrupt.disable();
|
||||
// clear dma flags. irq handlers use these to disambiguate among themselves.
|
||||
T::regs().uartdmacr().write_clear(|reg| {
|
||||
self.info.regs.uartdmacr().write_clear(|reg| {
|
||||
reg.set_rxdmae(true);
|
||||
reg.set_txdmae(true);
|
||||
reg.set_dmaonerr(true);
|
||||
@ -337,23 +356,26 @@ impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> UartRx<'d, T, Blocking> {
|
||||
impl<'d> UartRx<'d, Blocking> {
|
||||
/// Create a new UART RX instance for blocking mode operations.
|
||||
pub fn new_blocking(_uart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self {
|
||||
Uart::<T, Blocking>::init(None, Some(rx.into()), None, None, config);
|
||||
Self::new_inner(false, None)
|
||||
pub fn new_blocking<T: Instance>(_uart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self {
|
||||
Uart::<Blocking>::init(T::info(), None, Some(rx.into()), None, None, config);
|
||||
Self::new_inner(T::info(), T::dma_state(), false, None)
|
||||
}
|
||||
|
||||
/// Convert this uart RX instance into a buffered uart using the provided
|
||||
/// irq and receive buffer.
|
||||
pub fn into_buffered(
|
||||
pub fn into_buffered<T: Instance>(
|
||||
self,
|
||||
irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
_irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
rx_buffer: &'d mut [u8],
|
||||
) -> BufferedUartRx<'d, T> {
|
||||
buffered::init_buffers::<T>(irq, None, Some(rx_buffer));
|
||||
) -> BufferedUartRx {
|
||||
buffered::init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer));
|
||||
|
||||
BufferedUartRx { phantom: PhantomData }
|
||||
BufferedUartRx {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,7 +386,7 @@ pub struct InterruptHandler<T: Instance> {
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
let uart = T::regs();
|
||||
let uart = T::info().regs;
|
||||
if !uart.uartdmacr().read().rxdmae() {
|
||||
return;
|
||||
}
|
||||
@ -380,13 +402,13 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
impl<'d> UartRx<'d, Async> {
|
||||
/// Read from UART RX into the provided buffer.
|
||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
// clear error flags before we drain the fifo. errors that have accumulated
|
||||
// in the flags will also be present in the fifo.
|
||||
T::dma_state().rx_errs.store(0, Ordering::Relaxed);
|
||||
T::regs().uarticr().write(|w| {
|
||||
self.dma_state.rx_errs.store(0, Ordering::Relaxed);
|
||||
self.info.regs.uarticr().write(|w| {
|
||||
w.set_oeic(true);
|
||||
w.set_beic(true);
|
||||
w.set_peic(true);
|
||||
@ -408,28 +430,33 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
// interrupt flags will have been raised, and those will be picked up immediately
|
||||
// by the interrupt handler.
|
||||
let ch = self.rx_dma.as_mut().unwrap().reborrow();
|
||||
T::regs().uartimsc().write_set(|w| {
|
||||
self.info.regs.uartimsc().write_set(|w| {
|
||||
w.set_oeim(true);
|
||||
w.set_beim(true);
|
||||
w.set_peim(true);
|
||||
w.set_feim(true);
|
||||
});
|
||||
T::regs().uartdmacr().write_set(|reg| {
|
||||
self.info.regs.uartdmacr().write_set(|reg| {
|
||||
reg.set_rxdmae(true);
|
||||
reg.set_dmaonerr(true);
|
||||
});
|
||||
let transfer = unsafe {
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::read(ch, T::regs().uartdr().as_ptr() as *const _, buffer, T::RX_DREQ.into())
|
||||
crate::dma::read(
|
||||
ch,
|
||||
self.info.regs.uartdr().as_ptr() as *const _,
|
||||
buffer,
|
||||
self.info.rx_dreq.into(),
|
||||
)
|
||||
};
|
||||
|
||||
// wait for either the transfer to complete or an error to happen.
|
||||
let transfer_result = select(
|
||||
transfer,
|
||||
poll_fn(|cx| {
|
||||
T::dma_state().rx_err_waker.register(cx.waker());
|
||||
match T::dma_state().rx_errs.swap(0, Ordering::Relaxed) {
|
||||
self.dma_state.rx_err_waker.register(cx.waker());
|
||||
match self.dma_state.rx_errs.swap(0, Ordering::Relaxed) {
|
||||
0 => Poll::Pending,
|
||||
e => Poll::Ready(Uartris(e as u32)),
|
||||
}
|
||||
@ -441,7 +468,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
Either::First(()) => {
|
||||
// We're here because the DMA finished, BUT if an error occurred on the LAST
|
||||
// byte, then we may still need to grab the error state!
|
||||
Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32)
|
||||
Uartris(self.dma_state.rx_errs.swap(0, Ordering::Relaxed) as u32)
|
||||
}
|
||||
Either::Second(e) => {
|
||||
// We're here because we errored, which means this is the error that
|
||||
@ -521,8 +548,8 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
) -> Result<usize, ReadToBreakError> {
|
||||
// clear error flags before we drain the fifo. errors that have accumulated
|
||||
// in the flags will also be present in the fifo.
|
||||
T::dma_state().rx_errs.store(0, Ordering::Relaxed);
|
||||
T::regs().uarticr().write(|w| {
|
||||
self.dma_state.rx_errs.store(0, Ordering::Relaxed);
|
||||
self.info.regs.uarticr().write(|w| {
|
||||
w.set_oeic(true);
|
||||
w.set_beic(true);
|
||||
w.set_peic(true);
|
||||
@ -555,13 +582,13 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
// interrupt flags will have been raised, and those will be picked up immediately
|
||||
// by the interrupt handler.
|
||||
let ch = self.rx_dma.as_mut().unwrap();
|
||||
T::regs().uartimsc().write_set(|w| {
|
||||
self.info.regs.uartimsc().write_set(|w| {
|
||||
w.set_oeim(true);
|
||||
w.set_beim(true);
|
||||
w.set_peim(true);
|
||||
w.set_feim(true);
|
||||
});
|
||||
T::regs().uartdmacr().write_set(|reg| {
|
||||
self.info.regs.uartdmacr().write_set(|reg| {
|
||||
reg.set_rxdmae(true);
|
||||
reg.set_dmaonerr(true);
|
||||
});
|
||||
@ -572,9 +599,9 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::read(
|
||||
ch.reborrow(),
|
||||
T::regs().uartdr().as_ptr() as *const _,
|
||||
self.info.regs.uartdr().as_ptr() as *const _,
|
||||
sbuffer,
|
||||
T::RX_DREQ.into(),
|
||||
self.info.rx_dreq.into(),
|
||||
)
|
||||
};
|
||||
|
||||
@ -582,8 +609,8 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
let transfer_result = select(
|
||||
transfer,
|
||||
poll_fn(|cx| {
|
||||
T::dma_state().rx_err_waker.register(cx.waker());
|
||||
match T::dma_state().rx_errs.swap(0, Ordering::Relaxed) {
|
||||
self.dma_state.rx_err_waker.register(cx.waker());
|
||||
match self.dma_state.rx_errs.swap(0, Ordering::Relaxed) {
|
||||
0 => Poll::Pending,
|
||||
e => Poll::Ready(Uartris(e as u32)),
|
||||
}
|
||||
@ -596,7 +623,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
Either::First(()) => {
|
||||
// We're here because the DMA finished, BUT if an error occurred on the LAST
|
||||
// byte, then we may still need to grab the error state!
|
||||
Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32)
|
||||
Uartris(self.dma_state.rx_errs.swap(0, Ordering::Relaxed) as u32)
|
||||
}
|
||||
Either::Second(e) => {
|
||||
// We're here because we errored, which means this is the error that
|
||||
@ -635,7 +662,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let regs = T::regs();
|
||||
let regs = self.info.regs;
|
||||
let all_full = next_addr == eval;
|
||||
|
||||
// NOTE: This is off label usage of RSR! See the issue below for
|
||||
@ -685,9 +712,9 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Uart<'d, T, Blocking> {
|
||||
impl<'d> Uart<'d, Blocking> {
|
||||
/// Create a new UART without hardware flow control
|
||||
pub fn new_blocking(
|
||||
pub fn new_blocking<T: Instance>(
|
||||
uart: Peri<'d, T>,
|
||||
tx: Peri<'d, impl TxPin<T>>,
|
||||
rx: Peri<'d, impl RxPin<T>>,
|
||||
@ -697,7 +724,7 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> {
|
||||
}
|
||||
|
||||
/// Create a new UART with hardware flow control (RTS/CTS)
|
||||
pub fn new_with_rtscts_blocking(
|
||||
pub fn new_with_rtscts_blocking<T: Instance>(
|
||||
uart: Peri<'d, T>,
|
||||
tx: Peri<'d, impl TxPin<T>>,
|
||||
rx: Peri<'d, impl RxPin<T>>,
|
||||
@ -720,24 +747,30 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> {
|
||||
|
||||
/// Convert this uart instance into a buffered uart using the provided
|
||||
/// irq, transmit and receive buffers.
|
||||
pub fn into_buffered(
|
||||
pub fn into_buffered<T: Instance>(
|
||||
self,
|
||||
irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
_irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,
|
||||
tx_buffer: &'d mut [u8],
|
||||
rx_buffer: &'d mut [u8],
|
||||
) -> BufferedUart<'d, T> {
|
||||
buffered::init_buffers::<T>(irq, Some(tx_buffer), Some(rx_buffer));
|
||||
) -> BufferedUart {
|
||||
buffered::init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer));
|
||||
|
||||
BufferedUart {
|
||||
rx: BufferedUartRx { phantom: PhantomData },
|
||||
tx: BufferedUartTx { phantom: PhantomData },
|
||||
rx: BufferedUartRx {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
},
|
||||
tx: BufferedUartTx {
|
||||
info: T::info(),
|
||||
state: T::buffered_state(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Uart<'d, T, Async> {
|
||||
impl<'d> Uart<'d, Async> {
|
||||
/// Create a new DMA enabled UART without hardware flow control
|
||||
pub fn new(
|
||||
pub fn new<T: Instance>(
|
||||
uart: Peri<'d, T>,
|
||||
tx: Peri<'d, impl TxPin<T>>,
|
||||
rx: Peri<'d, impl RxPin<T>>,
|
||||
@ -760,7 +793,7 @@ impl<'d, T: Instance> Uart<'d, T, Async> {
|
||||
}
|
||||
|
||||
/// Create a new DMA enabled UART with hardware flow control (RTS/CTS)
|
||||
pub fn new_with_rtscts(
|
||||
pub fn new_with_rtscts<T: Instance>(
|
||||
uart: Peri<'d, T>,
|
||||
tx: Peri<'d, impl TxPin<T>>,
|
||||
rx: Peri<'d, impl RxPin<T>>,
|
||||
@ -785,8 +818,8 @@ impl<'d, T: Instance> Uart<'d, T, Async> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
|
||||
fn new_inner(
|
||||
impl<'d, M: Mode> Uart<'d, M> {
|
||||
fn new_inner<T: Instance>(
|
||||
_uart: Peri<'d, T>,
|
||||
mut tx: Peri<'d, AnyPin>,
|
||||
mut rx: Peri<'d, AnyPin>,
|
||||
@ -798,6 +831,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
|
||||
config: Config,
|
||||
) -> Self {
|
||||
Self::init(
|
||||
T::info(),
|
||||
Some(tx.reborrow()),
|
||||
Some(rx.reborrow()),
|
||||
rts.as_mut().map(|x| x.reborrow()),
|
||||
@ -806,19 +840,20 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
|
||||
);
|
||||
|
||||
Self {
|
||||
tx: UartTx::new_inner(tx_dma),
|
||||
rx: UartRx::new_inner(has_irq, rx_dma),
|
||||
tx: UartTx::new_inner(T::info(), tx_dma),
|
||||
rx: UartRx::new_inner(T::info(), T::dma_state(), has_irq, rx_dma),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
info: &Info,
|
||||
tx: Option<Peri<'_, AnyPin>>,
|
||||
rx: Option<Peri<'_, AnyPin>>,
|
||||
rts: Option<Peri<'_, AnyPin>>,
|
||||
cts: Option<Peri<'_, AnyPin>>,
|
||||
config: Config,
|
||||
) {
|
||||
let r = T::regs();
|
||||
let r = info.regs;
|
||||
if let Some(pin) = &tx {
|
||||
let funcsel = {
|
||||
let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8;
|
||||
@ -896,7 +931,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
|
||||
});
|
||||
}
|
||||
|
||||
Self::set_baudrate_inner(config.baudrate);
|
||||
Self::set_baudrate_inner(info, config.baudrate);
|
||||
|
||||
let (pen, eps) = match config.parity {
|
||||
Parity::ParityNone => (false, false),
|
||||
@ -926,8 +961,8 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
|
||||
});
|
||||
}
|
||||
|
||||
fn lcr_modify<R>(f: impl FnOnce(&mut crate::pac::uart::regs::UartlcrH) -> R) -> R {
|
||||
let r = T::regs();
|
||||
fn lcr_modify<R>(info: &Info, f: impl FnOnce(&mut crate::pac::uart::regs::UartlcrH) -> R) -> R {
|
||||
let r = info.regs;
|
||||
|
||||
// Notes from PL011 reference manual:
|
||||
//
|
||||
@ -978,11 +1013,11 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
|
||||
|
||||
/// sets baudrate on runtime
|
||||
pub fn set_baudrate(&mut self, baudrate: u32) {
|
||||
Self::set_baudrate_inner(baudrate);
|
||||
Self::set_baudrate_inner(self.tx.info, baudrate);
|
||||
}
|
||||
|
||||
fn set_baudrate_inner(baudrate: u32) {
|
||||
let r = T::regs();
|
||||
fn set_baudrate_inner(info: &Info, baudrate: u32) {
|
||||
let r = info.regs;
|
||||
|
||||
let clk_base = crate::clocks::clk_peri_freq();
|
||||
|
||||
@ -1002,11 +1037,11 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
|
||||
r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd));
|
||||
r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd));
|
||||
|
||||
Self::lcr_modify(|_| {});
|
||||
Self::lcr_modify(info, |_| {});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> Uart<'d, T, M> {
|
||||
impl<'d, M: Mode> Uart<'d, M> {
|
||||
/// Transmit the provided buffer blocking execution until done.
|
||||
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
self.tx.blocking_write(buffer)
|
||||
@ -1034,19 +1069,19 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> {
|
||||
|
||||
/// Split the Uart into a transmitter and receiver, which is particularly
|
||||
/// useful when having two tasks correlating to transmitting and receiving.
|
||||
pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) {
|
||||
pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) {
|
||||
(self.tx, self.rx)
|
||||
}
|
||||
|
||||
/// Split the Uart into a transmitter and receiver by mutable reference,
|
||||
/// which is particularly useful when having two tasks correlating to
|
||||
/// transmitting and receiving.
|
||||
pub fn split_ref(&mut self) -> (&mut UartTx<'d, T, M>, &mut UartRx<'d, T, M>) {
|
||||
pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) {
|
||||
(&mut self.tx, &mut self.rx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Uart<'d, T, Async> {
|
||||
impl<'d> Uart<'d, Async> {
|
||||
/// Write to UART TX from the provided buffer.
|
||||
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
self.tx.write(buffer).await
|
||||
@ -1076,10 +1111,10 @@ impl<'d, T: Instance> Uart<'d, T, Async> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for UartRx<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_02::serial::Read<u8> for UartRx<'d, M> {
|
||||
type Error = Error;
|
||||
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
let r = T::regs();
|
||||
let r = self.info.regs;
|
||||
if r.uartfr().read().rxfe() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
@ -1100,11 +1135,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for UartRx<'d,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write<u8> for UartTx<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_02::serial::Write<u8> for UartTx<'d, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, word: u8) -> Result<(), nb::Error<Self::Error>> {
|
||||
let r = T::regs();
|
||||
let r = self.info.regs;
|
||||
if r.uartfr().read().txff() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
@ -1114,7 +1149,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write<u8> for UartTx<'d,
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), nb::Error<Self::Error>> {
|
||||
let r = T::regs();
|
||||
let r = self.info.regs;
|
||||
if !r.uartfr().read().txfe() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
@ -1122,7 +1157,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write<u8> for UartTx<'d,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UartTx<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UartTx<'d, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
@ -1134,7 +1169,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for Uart<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_02::serial::Read<u8> for Uart<'d, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
@ -1142,7 +1177,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for Uart<'d, T,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write<u8> for Uart<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_02::serial::Write<u8> for Uart<'d, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, word: u8) -> Result<(), nb::Error<Self::Error>> {
|
||||
@ -1154,7 +1189,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write<u8> for Uart<'d, T
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Uart<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Uart<'d, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
@ -1177,21 +1212,21 @@ impl embedded_hal_nb::serial::Error for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, M> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, M> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, M> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, M> {
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
let r = T::regs();
|
||||
let r = self.info.regs;
|
||||
if r.uartfr().read().rxfe() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
@ -1212,7 +1247,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, M> {
|
||||
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_write(&[char]).map_err(nb::Error::Other)
|
||||
}
|
||||
@ -1222,11 +1257,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io::ErrorType for UartTx<'d, T, Blocking> {
|
||||
impl<'d> embedded_io::ErrorType for UartTx<'d, Blocking> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io::Write for UartTx<'d, T, Blocking> {
|
||||
impl<'d> embedded_io::Write for UartTx<'d, Blocking> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.blocking_write(buf).map(|_| buf.len())
|
||||
}
|
||||
@ -1236,13 +1271,13 @@ impl<'d, T: Instance> embedded_io::Write for UartTx<'d, T, Blocking> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, M> {
|
||||
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
embedded_hal_02::serial::Read::read(&mut self.rx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> {
|
||||
impl<'d, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, M> {
|
||||
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_write(&[char]).map_err(nb::Error::Other)
|
||||
}
|
||||
@ -1252,11 +1287,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io::ErrorType for Uart<'d, T, Blocking> {
|
||||
impl<'d> embedded_io::ErrorType for Uart<'d, Blocking> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io::Write for Uart<'d, T, Blocking> {
|
||||
impl<'d> embedded_io::Write for Uart<'d, Blocking> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.blocking_write(buf).map(|_| buf.len())
|
||||
}
|
||||
@ -1266,13 +1301,17 @@ impl<'d, T: Instance> embedded_io::Write for Uart<'d, T, Blocking> {
|
||||
}
|
||||
}
|
||||
|
||||
struct Info {
|
||||
regs: pac::uart::Uart,
|
||||
tx_dreq: pac::dma::vals::TreqSel,
|
||||
rx_dreq: pac::dma::vals::TreqSel,
|
||||
interrupt: Interrupt,
|
||||
}
|
||||
|
||||
trait SealedMode {}
|
||||
|
||||
trait SealedInstance {
|
||||
const TX_DREQ: pac::dma::vals::TreqSel;
|
||||
const RX_DREQ: pac::dma::vals::TreqSel;
|
||||
|
||||
fn regs() -> pac::uart::Uart;
|
||||
fn info() -> &'static Info;
|
||||
|
||||
fn buffered_state() -> &'static buffered::State;
|
||||
|
||||
@ -1308,11 +1347,14 @@ pub trait Instance: SealedInstance + PeripheralType {
|
||||
macro_rules! impl_instance {
|
||||
($inst:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {
|
||||
impl SealedInstance for peripherals::$inst {
|
||||
const TX_DREQ: pac::dma::vals::TreqSel = $tx_dreq;
|
||||
const RX_DREQ: pac::dma::vals::TreqSel = $rx_dreq;
|
||||
|
||||
fn regs() -> pac::uart::Uart {
|
||||
pac::$inst
|
||||
fn info() -> &'static Info {
|
||||
static INFO: Info = Info {
|
||||
regs: pac::$inst,
|
||||
tx_dreq: $tx_dreq,
|
||||
rx_dreq: $rx_dreq,
|
||||
interrupt: crate::interrupt::typelevel::$irq::IRQ,
|
||||
};
|
||||
&INFO
|
||||
}
|
||||
|
||||
fn buffered_state() -> &'static buffered::State {
|
||||
|
||||
@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## Unreleased
|
||||
- Modify BufferedUart initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983))
|
||||
- Added a 'single-bank' and a 'dual-bank' feature so chips with configurable flash bank setups are be supported in embassy ([#4125](https://github.com/embassy-rs/embassy/pull/4125))
|
||||
|
||||
## 0.2.0 - 2025-01-10
|
||||
|
||||
|
||||
@ -73,7 +73,7 @@ rand_core = "0.6.3"
|
||||
sdio-host = "0.9.0"
|
||||
critical-section = "1.1"
|
||||
#stm32-metapac = { version = "16" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a821bf5dd8d283c1e8de88fc7699235777a07e78" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7251801e3273011ce28a89e8f2e45eec2e419e26" }
|
||||
|
||||
vcell = "0.1.3"
|
||||
nb = "1.0.0"
|
||||
@ -102,7 +102,7 @@ proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
|
||||
#stm32-metapac = { version = "16", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a821bf5dd8d283c1e8de88fc7699235777a07e78", default-features = false, features = ["metadata"] }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7251801e3273011ce28a89e8f2e45eec2e419e26", default-features = false, features = ["metadata"] }
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
@ -197,6 +197,9 @@ split-pc2 = ["_split-pins-enabled"]
|
||||
## Split PC3
|
||||
split-pc3 = ["_split-pins-enabled"]
|
||||
|
||||
dual-bank = []
|
||||
single-bank = []
|
||||
|
||||
## internal use only
|
||||
_split-pins-enabled = []
|
||||
|
||||
|
||||
@ -49,6 +49,44 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// ========
|
||||
// Select the memory variant to use
|
||||
let memory = {
|
||||
let single_bank_selected = env::var("CARGO_FEATURE_SINGLE_BANK").is_ok();
|
||||
let dual_bank_selected = env::var("CARGO_FEATURE_DUAL_BANK").is_ok();
|
||||
|
||||
let single_bank_memory = METADATA.memory.iter().find(|mem| {
|
||||
mem.iter().any(|region| region.name.contains("BANK_1"))
|
||||
&& !mem.iter().any(|region| region.name.contains("BANK_2"))
|
||||
});
|
||||
|
||||
let dual_bank_memory = METADATA.memory.iter().find(|mem| {
|
||||
mem.iter().any(|region| region.name.contains("BANK_1"))
|
||||
&& mem.iter().any(|region| region.name.contains("BANK_2"))
|
||||
});
|
||||
|
||||
cfgs.set(
|
||||
"bank_setup_configurable",
|
||||
single_bank_memory.is_some() && dual_bank_memory.is_some(),
|
||||
);
|
||||
|
||||
match (single_bank_selected, dual_bank_selected) {
|
||||
(true, true) => panic!("Both 'single-bank' and 'dual-bank' features enabled"),
|
||||
(true, false) => {
|
||||
single_bank_memory.expect("The 'single-bank' feature is not supported on this dual bank chip")
|
||||
}
|
||||
(false, true) => {
|
||||
dual_bank_memory.expect("The 'dual-bank' feature is not supported on this single bank chip")
|
||||
}
|
||||
(false, false) => {
|
||||
if METADATA.memory.len() != 1 {
|
||||
panic!("Chip supports single and dual bank configuration. No Cargo feature to select one is enabled. Use the 'single-bank' or 'dual-bank' feature to make your selection")
|
||||
}
|
||||
METADATA.memory[0]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ========
|
||||
// Generate singletons
|
||||
|
||||
@ -290,8 +328,7 @@ fn main() {
|
||||
// ========
|
||||
// Generate FLASH regions
|
||||
let mut flash_regions = TokenStream::new();
|
||||
let flash_memory_regions: Vec<_> = METADATA
|
||||
.memory
|
||||
let flash_memory_regions: Vec<_> = memory
|
||||
.iter()
|
||||
.filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some())
|
||||
.collect();
|
||||
@ -647,6 +684,8 @@ fn main() {
|
||||
PeripheralRccKernelClock::Clock(clock) => clock_gen.gen_clock(p.name, clock),
|
||||
};
|
||||
|
||||
let bus_clock_frequency = clock_gen.gen_clock(p.name, &rcc.bus_clock);
|
||||
|
||||
// A refcount leak can result if the same field is shared by peripherals with different stop modes
|
||||
// This condition should be checked in stm32-data
|
||||
let stop_mode = match rcc.stop_mode {
|
||||
@ -660,6 +699,9 @@ fn main() {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
#clock_frequency
|
||||
}
|
||||
fn bus_frequency() -> crate::time::Hertz {
|
||||
#bus_clock_frequency
|
||||
}
|
||||
|
||||
const RCC_INFO: crate::rcc::RccInfo = unsafe {
|
||||
crate::rcc::RccInfo::new(
|
||||
@ -1616,8 +1658,7 @@ fn main() {
|
||||
let mut pins_table: Vec<Vec<String>> = Vec::new();
|
||||
let mut adc_table: Vec<Vec<String>> = Vec::new();
|
||||
|
||||
for m in METADATA
|
||||
.memory
|
||||
for m in memory
|
||||
.iter()
|
||||
.filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some())
|
||||
{
|
||||
@ -1855,8 +1896,7 @@ fn main() {
|
||||
// ========
|
||||
// Generate flash constants
|
||||
|
||||
let flash_regions: Vec<&MemoryRegion> = METADATA
|
||||
.memory
|
||||
let flash_regions: Vec<&MemoryRegion> = memory
|
||||
.iter()
|
||||
.filter(|x| x.kind == MemoryRegionKind::Flash && x.name.starts_with("BANK_"))
|
||||
.collect();
|
||||
@ -1981,7 +2021,7 @@ fn main() {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
if cfg!(feature = "memory-x") {
|
||||
gen_memory_x(out_dir);
|
||||
gen_memory_x(memory, out_dir);
|
||||
println!("cargo:rustc-link-search={}", out_dir.display());
|
||||
}
|
||||
}
|
||||
@ -2070,11 +2110,11 @@ fn rustfmt(path: impl AsRef<Path>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_memory_x(out_dir: &Path) {
|
||||
fn gen_memory_x(memory: &[MemoryRegion], out_dir: &Path) {
|
||||
let mut memory_x = String::new();
|
||||
|
||||
let flash = get_memory_range(MemoryRegionKind::Flash);
|
||||
let ram = get_memory_range(MemoryRegionKind::Ram);
|
||||
let flash = get_memory_range(memory, MemoryRegionKind::Flash);
|
||||
let ram = get_memory_range(memory, MemoryRegionKind::Ram);
|
||||
|
||||
write!(memory_x, "MEMORY\n{{\n").unwrap();
|
||||
writeln!(
|
||||
@ -2098,12 +2138,8 @@ fn gen_memory_x(out_dir: &Path) {
|
||||
std::fs::write(out_dir.join("memory.x"), memory_x.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn get_memory_range(kind: MemoryRegionKind) -> (u32, u32, String) {
|
||||
let mut mems: Vec<_> = METADATA
|
||||
.memory
|
||||
.iter()
|
||||
.filter(|m| m.kind == kind && m.size != 0)
|
||||
.collect();
|
||||
fn get_memory_range(memory: &[MemoryRegion], kind: MemoryRegionKind) -> (u32, u32, String) {
|
||||
let mut mems: Vec<_> = memory.iter().filter(|m| m.kind == kind && m.size != 0).collect();
|
||||
mems.sort_by_key(|m| m.address);
|
||||
|
||||
let mut start = u32::MAX;
|
||||
|
||||
@ -6,8 +6,8 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
|
||||
use super::{
|
||||
blocking_read, ensure_sector_aligned, family, get_sector, Async, Error, Flash, FlashLayout, FLASH_BASE, FLASH_SIZE,
|
||||
WRITE_SIZE,
|
||||
blocking_read, ensure_sector_aligned, family, get_flash_regions, get_sector, Async, Error, Flash, FlashLayout,
|
||||
FLASH_BASE, FLASH_SIZE, WRITE_SIZE,
|
||||
};
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::peripherals::FLASH;
|
||||
@ -34,7 +34,6 @@ impl<'d> Flash<'d, Async> {
|
||||
///
|
||||
/// See module-level documentation for details on how memory regions work.
|
||||
pub fn into_regions(self) -> FlashLayout<'d, Async> {
|
||||
assert!(family::is_default_layout());
|
||||
FlashLayout::new(self.inner)
|
||||
}
|
||||
|
||||
@ -123,7 +122,7 @@ pub(super) async unsafe fn write_chunked(base: u32, size: u32, offset: u32, byte
|
||||
pub(super) async unsafe fn erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> {
|
||||
let start_address = base + from;
|
||||
let end_address = base + to;
|
||||
let regions = family::get_flash_regions();
|
||||
let regions = get_flash_regions();
|
||||
|
||||
ensure_sector_aligned(start_address, end_address, regions)?;
|
||||
|
||||
|
||||
@ -4,8 +4,8 @@ use core::sync::atomic::{fence, Ordering};
|
||||
use embassy_hal_internal::drop::OnDrop;
|
||||
|
||||
use super::{
|
||||
family, Async, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, MAX_ERASE_SIZE,
|
||||
READ_SIZE, WRITE_SIZE,
|
||||
family, get_flash_regions, Async, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE,
|
||||
MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE,
|
||||
};
|
||||
use crate::Peri;
|
||||
use crate::_generated::FLASH_BASE;
|
||||
@ -20,6 +20,10 @@ pub struct Flash<'d, MODE = Async> {
|
||||
impl<'d> Flash<'d, Blocking> {
|
||||
/// Create a new flash driver, usable in blocking mode.
|
||||
pub fn new_blocking(p: Peri<'d, FLASH>) -> Self {
|
||||
#[cfg(bank_setup_configurable)]
|
||||
// Check if the configuration matches the embassy setup
|
||||
super::check_bank_setup();
|
||||
|
||||
Self {
|
||||
inner: p,
|
||||
_mode: PhantomData,
|
||||
@ -32,7 +36,6 @@ impl<'d, MODE> Flash<'d, MODE> {
|
||||
///
|
||||
/// See module-level documentation for details on how memory regions work.
|
||||
pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> {
|
||||
assert!(family::is_default_layout());
|
||||
FlashLayout::new(self.inner)
|
||||
}
|
||||
|
||||
@ -137,7 +140,7 @@ pub(super) unsafe fn blocking_erase(
|
||||
) -> Result<(), Error> {
|
||||
let start_address = base + from;
|
||||
let end_address = base + to;
|
||||
let regions = family::get_flash_regions();
|
||||
let regions = get_flash_regions();
|
||||
|
||||
ensure_sector_aligned(start_address, end_address, regions)?;
|
||||
|
||||
|
||||
@ -1,18 +1,10 @@
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{FlashSector, WRITE_SIZE};
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
pac::FLASH.cr().modify(|w| w.set_lock(true));
|
||||
}
|
||||
|
||||
@ -1,18 +1,10 @@
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{FlashSector, WRITE_SIZE};
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
pac::FLASH.cr().modify(|w| w.set_lock(true));
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ use core::sync::atomic::{fence, AtomicBool, Ordering};
|
||||
|
||||
use pac::flash::regs::Sr;
|
||||
|
||||
use super::{FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE};
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
|
||||
@ -15,14 +15,6 @@ impl FlashSector {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
pac::FLASH.cr().modify(|w| w.set_lock(true));
|
||||
}
|
||||
|
||||
@ -4,177 +4,10 @@ use core::sync::atomic::{fence, AtomicBool, Ordering};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use pac::flash::regs::Sr;
|
||||
|
||||
use super::{FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE};
|
||||
use crate::_generated::FLASH_SIZE;
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
#[allow(missing_docs)] // TODO
|
||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
|
||||
mod alt_regions {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::Peri;
|
||||
use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION};
|
||||
use crate::_generated::FLASH_SIZE;
|
||||
use crate::flash::{asynch, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion};
|
||||
use crate::peripherals::FLASH;
|
||||
|
||||
pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion {
|
||||
size: 3 * BANK1_REGION3.erase_size,
|
||||
..BANK1_REGION3
|
||||
};
|
||||
pub const ALT_BANK2_REGION1: FlashRegion = FlashRegion {
|
||||
bank: FlashBank::Bank2,
|
||||
base: BANK1_REGION1.base + FLASH_SIZE as u32 / 2,
|
||||
..BANK1_REGION1
|
||||
};
|
||||
pub const ALT_BANK2_REGION2: FlashRegion = FlashRegion {
|
||||
bank: FlashBank::Bank2,
|
||||
base: BANK1_REGION2.base + FLASH_SIZE as u32 / 2,
|
||||
..BANK1_REGION2
|
||||
};
|
||||
pub const ALT_BANK2_REGION3: FlashRegion = FlashRegion {
|
||||
bank: FlashBank::Bank2,
|
||||
base: BANK1_REGION3.base + FLASH_SIZE as u32 / 2,
|
||||
size: 3 * BANK1_REGION3.erase_size,
|
||||
..BANK1_REGION3
|
||||
};
|
||||
|
||||
pub const ALT_FLASH_REGIONS: [&FlashRegion; 6] = [
|
||||
&BANK1_REGION1,
|
||||
&BANK1_REGION2,
|
||||
&ALT_BANK1_REGION3,
|
||||
&ALT_BANK2_REGION1,
|
||||
&ALT_BANK2_REGION2,
|
||||
&ALT_BANK2_REGION3,
|
||||
];
|
||||
|
||||
pub struct AltBank1Region3<'d, MODE = Async>(pub &'static FlashRegion, Peri<'d, FLASH>, PhantomData<MODE>);
|
||||
pub struct AltBank2Region1<'d, MODE = Async>(pub &'static FlashRegion, Peri<'d, FLASH>, PhantomData<MODE>);
|
||||
pub struct AltBank2Region2<'d, MODE = Async>(pub &'static FlashRegion, Peri<'d, FLASH>, PhantomData<MODE>);
|
||||
pub struct AltBank2Region3<'d, MODE = Async>(pub &'static FlashRegion, Peri<'d, FLASH>, PhantomData<MODE>);
|
||||
|
||||
pub struct AltFlashLayout<'d, MODE = Async> {
|
||||
pub bank1_region1: Bank1Region1<'d, MODE>,
|
||||
pub bank1_region2: Bank1Region2<'d, MODE>,
|
||||
pub bank1_region3: AltBank1Region3<'d, MODE>,
|
||||
pub bank2_region1: AltBank2Region1<'d, MODE>,
|
||||
pub bank2_region2: AltBank2Region2<'d, MODE>,
|
||||
pub bank2_region3: AltBank2Region3<'d, MODE>,
|
||||
pub otp_region: OTPRegion<'d, MODE>,
|
||||
}
|
||||
|
||||
impl<'d> Flash<'d> {
|
||||
pub fn into_alt_regions(self) -> AltFlashLayout<'d, Async> {
|
||||
assert!(!super::is_default_layout());
|
||||
|
||||
// SAFETY: We never expose the cloned peripheral references, and their instance is not public.
|
||||
// Also, all async flash region operations are protected with a mutex.
|
||||
let p = self.inner;
|
||||
AltFlashLayout {
|
||||
bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_alt_blocking_regions(self) -> AltFlashLayout<'d, Blocking> {
|
||||
assert!(!super::is_default_layout());
|
||||
|
||||
// SAFETY: We never expose the cloned peripheral references, and their instance is not public.
|
||||
// Also, all blocking flash region operations are protected with a cs.
|
||||
let p = self.inner;
|
||||
AltFlashLayout {
|
||||
bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! foreach_altflash_region {
|
||||
($type_name:ident, $region:ident) => {
|
||||
impl<MODE> $type_name<'_, MODE> {
|
||||
pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
|
||||
crate::flash::common::blocking_read(self.0.base, self.0.size, offset, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl $type_name<'_, Async> {
|
||||
pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
|
||||
self.blocking_read(offset, bytes)
|
||||
}
|
||||
|
||||
pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
|
||||
let _guard = asynch::REGION_ACCESS.lock().await;
|
||||
unsafe { asynch::write_chunked(self.0.base, self.0.size, offset, bytes).await }
|
||||
}
|
||||
|
||||
pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
|
||||
let _guard = asynch::REGION_ACCESS.lock().await;
|
||||
unsafe { asynch::erase_sectored(self.0.base, from, to).await }
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> embedded_storage::nor_flash::ErrorType for $type_name<'_, MODE> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<MODE> embedded_storage::nor_flash::ReadNorFlash for $type_name<'_, MODE> {
|
||||
const READ_SIZE: usize = crate::flash::READ_SIZE;
|
||||
|
||||
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(offset, bytes)
|
||||
}
|
||||
|
||||
fn capacity(&self) -> usize {
|
||||
self.0.size as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_, Async> {
|
||||
const READ_SIZE: usize = crate::flash::READ_SIZE;
|
||||
|
||||
async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.read(offset, bytes).await
|
||||
}
|
||||
|
||||
fn capacity(&self) -> usize {
|
||||
self.0.size as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_, Async> {
|
||||
const WRITE_SIZE: usize = $region.write_size as usize;
|
||||
const ERASE_SIZE: usize = $region.erase_size as usize;
|
||||
|
||||
async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
self.write(offset, bytes).await
|
||||
}
|
||||
|
||||
async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
||||
self.erase(from, to).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
foreach_altflash_region!(AltBank1Region3, ALT_BANK1_REGION3);
|
||||
foreach_altflash_region!(AltBank2Region1, ALT_BANK2_REGION1);
|
||||
foreach_altflash_region!(AltBank2Region2, ALT_BANK2_REGION2);
|
||||
foreach_altflash_region!(AltBank2Region3, ALT_BANK2_REGION3);
|
||||
}
|
||||
|
||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
|
||||
pub use alt_regions::*;
|
||||
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
static DATA_CACHE_WAS_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
@ -185,30 +18,6 @@ impl FlashSector {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
|
||||
pub(crate) fn is_default_layout() -> bool {
|
||||
!pac::FLASH.optcr().read().db1m()
|
||||
}
|
||||
|
||||
#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))]
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
|
||||
pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
if is_default_layout() {
|
||||
&FLASH_REGIONS
|
||||
} else {
|
||||
&ALT_FLASH_REGIONS
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))]
|
||||
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn on_interrupt() {
|
||||
// Clear IRQ flags
|
||||
pac::FLASH.sr().write(|w| {
|
||||
@ -487,71 +296,83 @@ mod tests {
|
||||
const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024;
|
||||
const LARGE_SECTOR_SIZE: u32 = 128 * 1024;
|
||||
|
||||
let assert_sector = |snb: u8, index_in_bank: u8, start: u32, size: u32, address: u32| {
|
||||
let sector = get_sector(address, &FLASH_REGIONS);
|
||||
assert_eq!(snb, sector.snb());
|
||||
assert_eq!(
|
||||
FlashSector {
|
||||
bank: FlashBank::Bank1,
|
||||
index_in_bank,
|
||||
start,
|
||||
size
|
||||
},
|
||||
sector
|
||||
);
|
||||
};
|
||||
if !cfg!(feature = "dual-bank") {
|
||||
let assert_sector = |snb: u8, index_in_bank: u8, start: u32, size: u32, address: u32| {
|
||||
let sector = get_sector(address, crate::flash::get_flash_regions());
|
||||
assert_eq!(snb, sector.snb());
|
||||
assert_eq!(
|
||||
FlashSector {
|
||||
bank: sector.bank,
|
||||
index_in_bank,
|
||||
start,
|
||||
size
|
||||
},
|
||||
sector
|
||||
);
|
||||
};
|
||||
|
||||
assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
|
||||
assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);
|
||||
assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);
|
||||
assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);
|
||||
assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
|
||||
assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);
|
||||
assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);
|
||||
assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);
|
||||
|
||||
assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);
|
||||
assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);
|
||||
assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);
|
||||
assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);
|
||||
|
||||
assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);
|
||||
assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);
|
||||
assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000);
|
||||
assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);
|
||||
assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);
|
||||
assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);
|
||||
assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000);
|
||||
assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);
|
||||
} else {
|
||||
let assert_sector = |snb: u8, bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| {
|
||||
let sector = get_sector(address, crate::flash::get_flash_regions());
|
||||
assert_eq!(snb, sector.snb());
|
||||
assert_eq!(
|
||||
FlashSector {
|
||||
bank,
|
||||
index_in_bank,
|
||||
start,
|
||||
size
|
||||
},
|
||||
sector
|
||||
)
|
||||
};
|
||||
|
||||
let assert_sector = |snb: u8, bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| {
|
||||
let sector = get_sector(address, &ALT_FLASH_REGIONS);
|
||||
assert_eq!(snb, sector.snb());
|
||||
assert_eq!(
|
||||
FlashSector {
|
||||
bank,
|
||||
index_in_bank,
|
||||
start,
|
||||
size
|
||||
},
|
||||
sector
|
||||
)
|
||||
};
|
||||
assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
|
||||
assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);
|
||||
assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);
|
||||
assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);
|
||||
|
||||
assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
|
||||
assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);
|
||||
assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);
|
||||
assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);
|
||||
assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);
|
||||
assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);
|
||||
|
||||
assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);
|
||||
assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);
|
||||
assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);
|
||||
assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);
|
||||
assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000);
|
||||
assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF);
|
||||
|
||||
assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);
|
||||
assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);
|
||||
assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000);
|
||||
assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF);
|
||||
assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000);
|
||||
assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF);
|
||||
assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000);
|
||||
assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF);
|
||||
|
||||
assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000);
|
||||
assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF);
|
||||
assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000);
|
||||
assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF);
|
||||
assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000);
|
||||
assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF);
|
||||
|
||||
assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000);
|
||||
assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF);
|
||||
|
||||
assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000);
|
||||
assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF);
|
||||
assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000);
|
||||
assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);
|
||||
assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000);
|
||||
assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF);
|
||||
assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000);
|
||||
assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(bank_setup_configurable))]
|
||||
pub(crate) fn check_bank_setup() {
|
||||
if cfg!(feature = "single-bank") && pac::FLASH.optcr().read().db1m() {
|
||||
panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use dual-bank config");
|
||||
}
|
||||
if cfg!(feature = "dual-bank") && !pac::FLASH.optcr().read().db1m() {
|
||||
panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use single-bank config");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,14 @@
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{FlashSector, WRITE_SIZE};
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
impl FlashSector {
|
||||
const fn snb(&self) -> u8 {
|
||||
((self.bank as u8) << 4) + self.index_in_bank
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
@ -53,7 +51,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE])
|
||||
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
|
||||
pac::FLASH.cr().modify(|w| {
|
||||
w.set_ser(true);
|
||||
w.set_snb(sector.index_in_bank)
|
||||
w.set_snb(sector.snb())
|
||||
});
|
||||
|
||||
pac::FLASH.cr().modify(|w| {
|
||||
@ -118,7 +116,7 @@ mod tests {
|
||||
start,
|
||||
size
|
||||
},
|
||||
get_sector(address, &FLASH_REGIONS)
|
||||
get_sector(address, crate::flash::get_flash_regions())
|
||||
)
|
||||
};
|
||||
|
||||
@ -137,7 +135,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(stm32f769)]
|
||||
#[cfg(all(stm32f769, feature = "single-bank"))]
|
||||
fn can_get_sector() {
|
||||
const SMALL_SECTOR_SIZE: u32 = 32 * 1024;
|
||||
const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024;
|
||||
@ -151,7 +149,7 @@ mod tests {
|
||||
start,
|
||||
size
|
||||
},
|
||||
get_sector(address, &FLASH_REGIONS)
|
||||
get_sector(address, crate::flash::get_flash_regions())
|
||||
)
|
||||
};
|
||||
|
||||
@ -168,4 +166,61 @@ mod tests {
|
||||
assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000);
|
||||
assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(stm32f769, feature = "dual-bank"))]
|
||||
fn can_get_sector() {
|
||||
const SMALL_SECTOR_SIZE: u32 = 16 * 1024;
|
||||
const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024;
|
||||
const LARGE_SECTOR_SIZE: u32 = 128 * 1024;
|
||||
|
||||
let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32, snb: u8, bank: FlashBank| {
|
||||
assert_eq!(
|
||||
FlashSector {
|
||||
bank: bank,
|
||||
index_in_bank,
|
||||
start,
|
||||
size
|
||||
},
|
||||
get_sector(address, crate::flash::get_flash_regions())
|
||||
);
|
||||
assert_eq!(get_sector(address, crate::flash::get_flash_regions()).snb(), snb);
|
||||
};
|
||||
|
||||
assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000, 0x00, FlashBank::Bank1);
|
||||
assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF, 0x00, FlashBank::Bank1);
|
||||
assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000, 0x03, FlashBank::Bank1);
|
||||
assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF, 0x03, FlashBank::Bank1);
|
||||
|
||||
assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000, 0x04, FlashBank::Bank1);
|
||||
assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF, 0x04, FlashBank::Bank1);
|
||||
|
||||
assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000, 0x05, FlashBank::Bank1);
|
||||
assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF, 0x05, FlashBank::Bank1);
|
||||
assert_sector(10, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000, 0x0A, FlashBank::Bank1);
|
||||
assert_sector(10, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080D_FFFF, 0x0A, FlashBank::Bank1);
|
||||
|
||||
assert_sector(0, 0x0810_0000, SMALL_SECTOR_SIZE, 0x0810_0000, 0x10, FlashBank::Bank2);
|
||||
assert_sector(0, 0x0810_0000, SMALL_SECTOR_SIZE, 0x0810_3FFF, 0x10, FlashBank::Bank2);
|
||||
assert_sector(3, 0x0810_C000, SMALL_SECTOR_SIZE, 0x0810_C000, 0x13, FlashBank::Bank2);
|
||||
assert_sector(3, 0x0810_C000, SMALL_SECTOR_SIZE, 0x0810_FFFF, 0x13, FlashBank::Bank2);
|
||||
|
||||
assert_sector(4, 0x0811_0000, MEDIUM_SECTOR_SIZE, 0x0811_0000, 0x14, FlashBank::Bank2);
|
||||
assert_sector(4, 0x0811_0000, MEDIUM_SECTOR_SIZE, 0x0811_FFFF, 0x14, FlashBank::Bank2);
|
||||
|
||||
assert_sector(5, 0x0812_0000, LARGE_SECTOR_SIZE, 0x0812_0000, 0x15, FlashBank::Bank2);
|
||||
assert_sector(5, 0x0812_0000, LARGE_SECTOR_SIZE, 0x0813_FFFF, 0x15, FlashBank::Bank2);
|
||||
assert_sector(10, 0x081C_0000, LARGE_SECTOR_SIZE, 0x081C_0000, 0x1A, FlashBank::Bank2);
|
||||
assert_sector(10, 0x081C_0000, LARGE_SECTOR_SIZE, 0x081D_FFFF, 0x1A, FlashBank::Bank2);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(bank_setup_configurable))]
|
||||
pub(crate) fn check_bank_setup() {
|
||||
if cfg!(feature = "single-bank") && !pac::FLASH.optcr().read().n_dbank() {
|
||||
panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use dual-bank config");
|
||||
}
|
||||
if cfg!(feature = "dual-bank") && pac::FLASH.optcr().read().n_dbank() {
|
||||
panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use single-bank config");
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,18 +3,10 @@ use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use cortex_m::interrupt;
|
||||
|
||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{FlashSector, WRITE_SIZE};
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
pac::FLASH.cr().modify(|w| w.set_lock(true));
|
||||
}
|
||||
@ -109,3 +101,23 @@ fn wait_busy() {
|
||||
fn wait_busy() {
|
||||
while pac::FLASH.sr().read().bsy() {}
|
||||
}
|
||||
|
||||
#[cfg(all(bank_setup_configurable, any(flash_g4c2, flash_g4c3, flash_g4c4)))]
|
||||
pub(crate) fn check_bank_setup() {
|
||||
if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() {
|
||||
panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config");
|
||||
}
|
||||
if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() {
|
||||
panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(bank_setup_configurable, flash_g0x1))]
|
||||
pub(crate) fn check_bank_setup() {
|
||||
if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dual_bank() {
|
||||
panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use dual-bank config");
|
||||
}
|
||||
if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dual_bank() {
|
||||
panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use single-bank config");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,22 +1,10 @@
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{FlashSector, WRITE_SIZE};
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// const fn is_dual_bank() -> bool {
|
||||
// FLASH_REGIONS.len() >= 2
|
||||
// }
|
||||
|
||||
pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
if !pac::FLASH.nscr().read().lock() {
|
||||
pac::FLASH.nscr().modify(|r| {
|
||||
|
||||
@ -8,17 +8,9 @@ use cortex_m::interrupt;
|
||||
use pac::flash::regs::Nssr;
|
||||
use pac::flash::vals::Bksel;
|
||||
|
||||
use super::{Error, FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{Error, FlashBank, FlashSector, WRITE_SIZE};
|
||||
use crate::pac;
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
pac::FLASH.nscr().modify(|w| w.set_lock(true));
|
||||
}
|
||||
|
||||
@ -1,22 +1,14 @@
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE};
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
const fn is_dual_bank() -> bool {
|
||||
FLASH_REGIONS.len() >= 2
|
||||
}
|
||||
|
||||
pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true));
|
||||
if is_dual_bank() {
|
||||
|
||||
@ -1,18 +1,10 @@
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{FlashSector, WRITE_SIZE};
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||
pac::FLASH.cr().modify(|w| w.set_lock(true));
|
||||
@ -238,3 +230,23 @@ unsafe fn wait_ready_blocking() -> Result<(), Error> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(bank_setup_configurable, flash_l5))]
|
||||
pub(crate) fn check_bank_setup() {
|
||||
if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() {
|
||||
panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config");
|
||||
}
|
||||
if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() {
|
||||
panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(bank_setup_configurable, flash_l4))]
|
||||
pub(crate) fn check_bank_setup() {
|
||||
if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dualbank() {
|
||||
panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use dual-bank config");
|
||||
}
|
||||
if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dualbank() {
|
||||
panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use single-bank config");
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,17 +14,9 @@ pub use common::*;
|
||||
pub use crate::_generated::flash_regions::*;
|
||||
pub use crate::_generated::{FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE};
|
||||
|
||||
/// Get whether the default flash layout is being used.
|
||||
///
|
||||
/// In some chips, dual-bank is not default. This will then return `false`
|
||||
/// when dual-bank is enabled.
|
||||
pub fn is_default_layout() -> bool {
|
||||
family::is_default_layout()
|
||||
}
|
||||
|
||||
/// Get all flash regions.
|
||||
pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
family::get_flash_regions()
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
/// Read size (always 1)
|
||||
|
||||
@ -1,14 +1,6 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
use super::{Error, FlashSector, WRITE_SIZE};
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
unimplemented!();
|
||||
|
||||
@ -3,18 +3,10 @@ use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use cortex_m::interrupt;
|
||||
|
||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{FlashSector, WRITE_SIZE};
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
pac::FLASH.cr().modify(|w| w.set_lock(true));
|
||||
}
|
||||
|
||||
@ -1,18 +1,10 @@
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use super::{FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use super::{FlashBank, FlashSector, WRITE_SIZE};
|
||||
use crate::flash::Error;
|
||||
use crate::pac;
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
#[cfg(feature = "trustzone-secure")]
|
||||
pac::FLASH.seccr().modify(|w| w.set_lock(true));
|
||||
|
||||
@ -173,8 +173,9 @@ pub use crate::_generated::interrupt;
|
||||
// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
|
||||
#[macro_export]
|
||||
macro_rules! bind_interrupts {
|
||||
($vis:vis struct $name:ident {
|
||||
($(#[$outer:meta])* $vis:vis struct $name:ident {
|
||||
$(
|
||||
$(#[$inner:meta])*
|
||||
$(#[cfg($cond_irq:meta)])?
|
||||
$irq:ident => $(
|
||||
$(#[cfg($cond_handler:meta)])?
|
||||
@ -183,12 +184,14 @@ macro_rules! bind_interrupts {
|
||||
)*
|
||||
}) => {
|
||||
#[derive(Copy, Clone)]
|
||||
$(#[$outer])*
|
||||
$vis struct $name;
|
||||
|
||||
$(
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
$(#[cfg($cond_irq)])?
|
||||
$(#[$inner])*
|
||||
unsafe extern "C" fn $irq() {
|
||||
$(
|
||||
$(#[cfg($cond_handler)])?
|
||||
@ -600,17 +603,7 @@ fn init_hw(config: Config) -> Peripherals {
|
||||
#[cfg(feature = "exti")]
|
||||
exti::init(cs);
|
||||
|
||||
rcc::init(config.rcc);
|
||||
|
||||
// must be after rcc init
|
||||
#[cfg(feature = "_time-driver")]
|
||||
time_driver::init(cs);
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
{
|
||||
crate::rcc::REFCOUNT_STOP2 = 0;
|
||||
crate::rcc::REFCOUNT_STOP1 = 0;
|
||||
}
|
||||
rcc::init_rcc(cs, config.rcc);
|
||||
}
|
||||
|
||||
p
|
||||
|
||||
@ -239,7 +239,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
|
||||
|
||||
T::regs().csr().modify(|w| {
|
||||
w.set_vp_sel(VpSel::from_bits(pin.channel()));
|
||||
w.set_vm_sel(VmSel::OUTPUT);
|
||||
w.set_vm_sel(VmSel::PGA);
|
||||
w.set_pga_gain(pga_gain);
|
||||
w.set_opaintoen(true);
|
||||
w.set_opampen(true);
|
||||
|
||||
@ -97,6 +97,8 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks {
|
||||
|
||||
pub(crate) trait SealedRccPeripheral {
|
||||
fn frequency() -> Hertz;
|
||||
#[allow(dead_code)]
|
||||
fn bus_frequency() -> Hertz;
|
||||
const RCC_INFO: RccInfo;
|
||||
}
|
||||
|
||||
@ -369,3 +371,32 @@ pub fn enable_and_reset<T: RccPeripheral>() {
|
||||
pub fn disable<T: RccPeripheral>() {
|
||||
T::RCC_INFO.disable();
|
||||
}
|
||||
|
||||
/// Re-initialize the `embassy-stm32` clock configuration with the provided configuration.
|
||||
///
|
||||
/// This is useful when you need to alter the CPU clock after configuring peripherals.
|
||||
/// For instance, configure an external clock via spi or i2c.
|
||||
///
|
||||
/// Please not this only re-configures the rcc and the time driver (not GPIO, EXTI, etc).
|
||||
///
|
||||
/// This should only be called after `init`.
|
||||
#[cfg(not(feature = "_dual-core"))]
|
||||
pub fn reinit(config: Config) {
|
||||
critical_section::with(|cs| init_rcc(cs, config))
|
||||
}
|
||||
|
||||
pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) {
|
||||
unsafe {
|
||||
init(config);
|
||||
|
||||
// must be after rcc init
|
||||
#[cfg(feature = "_time-driver")]
|
||||
crate::time_driver::init(_cs);
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
{
|
||||
REFCOUNT_STOP2 = 0;
|
||||
REFCOUNT_STOP1 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,7 +223,7 @@ impl<'d, T: Instance> Spdifrx<'d, T> {
|
||||
};
|
||||
|
||||
for sample in data.as_mut() {
|
||||
if (*sample & (0x0002_u32)) == 0x0001 {
|
||||
if (*sample & (0x0002_u32)) != 0 {
|
||||
// Discard invalid samples, setting them to mute level.
|
||||
*sample = 0;
|
||||
} else {
|
||||
|
||||
@ -31,6 +31,8 @@ pub struct PwmPin<'d, T, C> {
|
||||
/// PWM pin config
|
||||
///
|
||||
/// This configures the pwm pin settings
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PwmPinConfig {
|
||||
/// PWM Pin output type
|
||||
pub output_type: OutputType,
|
||||
|
||||
@ -549,7 +549,7 @@ foreach_interrupt!(
|
||||
);
|
||||
|
||||
fn calculate_trdt<T: Instance>(speed: Dspd) -> u8 {
|
||||
let ahb_freq = T::frequency().0;
|
||||
let ahb_freq = T::bus_frequency().0;
|
||||
match speed {
|
||||
Dspd::HIGH_SPEED => {
|
||||
// From RM0431 (F72xx), RM0090 (F429), RM0390 (F446)
|
||||
|
||||
@ -6,7 +6,7 @@ use core::task::{Context, Poll, Waker};
|
||||
use crate::blocking_mutex::raw::RawMutex;
|
||||
use crate::blocking_mutex::Mutex;
|
||||
|
||||
/// Single-slot signaling primitive.
|
||||
/// Single-slot signaling primitive for a _single_ consumer.
|
||||
///
|
||||
/// This is similar to a [`Channel`](crate::channel::Channel) with a buffer size of 1, except
|
||||
/// "sending" to it (calling [`Signal::signal`]) when full will overwrite the previous value instead
|
||||
@ -17,6 +17,7 @@ use crate::blocking_mutex::Mutex;
|
||||
/// updates.
|
||||
///
|
||||
/// For more advanced use cases, you might want to use [`Channel`](crate::channel::Channel) instead.
|
||||
/// For multiple consumers, use [`Watch`](crate::watch::Watch) instead.
|
||||
///
|
||||
/// Signals are generally declared as `static`s and then borrowed as required.
|
||||
///
|
||||
@ -106,7 +107,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// Future that completes when this Signal has been signaled.
|
||||
/// Future that completes when this Signal has been signaled, taking the value out of the signal.
|
||||
pub fn wait(&self) -> impl Future<Output = T> + '_ {
|
||||
poll_fn(move |cx| self.poll_wait(cx))
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ use crate::blocking_mutex::raw::RawMutex;
|
||||
use crate::blocking_mutex::Mutex;
|
||||
use crate::waitqueue::MultiWakerRegistration;
|
||||
|
||||
/// The `Watch` is a single-slot signaling primitive that allows multiple receivers to concurrently await
|
||||
/// The `Watch` is a single-slot signaling primitive that allows _multiple_ (`N`) receivers to concurrently await
|
||||
/// changes to the value. Unlike a [`Signal`](crate::signal::Signal), `Watch` supports multiple receivers,
|
||||
/// and unlike a [`PubSubChannel`](crate::pubsub::PubSubChannel), `Watch` immediately overwrites the previous
|
||||
/// value when a new one is sent, without waiting for all receivers to read the previous value.
|
||||
@ -298,7 +298,7 @@ impl<M: RawMutex, T: Clone, const N: usize> WatchBehavior<T> for Watch<M, T, N>
|
||||
}
|
||||
|
||||
impl<M: RawMutex, T: Clone, const N: usize> Watch<M, T, N> {
|
||||
/// Create a new `Watch` channel.
|
||||
/// Create a new `Watch` channel for `N` receivers.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
mutex: Mutex::new(RefCell::new(WatchState {
|
||||
|
||||
@ -226,6 +226,8 @@ tick-hz-128_000_000 = []
|
||||
tick-hz-130_000_000 = []
|
||||
## 131.072MHz Tick Rate
|
||||
tick-hz-131_072_000 = []
|
||||
## 133.0MHz Tick Rate
|
||||
tick-hz-133_000_000 = []
|
||||
## 140.0MHz Tick Rate
|
||||
tick-hz-140_000_000 = []
|
||||
## 144.0MHz Tick Rate
|
||||
|
||||
@ -22,6 +22,7 @@ for i in range(1, 30):
|
||||
ticks.append(10 * i * 1_000_000)
|
||||
for i in range(15, 50):
|
||||
ticks.append(20 * i * 1_000_000)
|
||||
ticks.append(133 * 1_000_000)
|
||||
|
||||
seen = set()
|
||||
ticks = sorted([x for x in ticks if not (x in seen or seen.add(x))])
|
||||
|
||||
@ -182,6 +182,8 @@ pub const TICK_HZ: u64 = 128_000_000;
|
||||
pub const TICK_HZ: u64 = 130_000_000;
|
||||
#[cfg(feature = "tick-hz-131_072_000")]
|
||||
pub const TICK_HZ: u64 = 131_072_000;
|
||||
#[cfg(feature = "tick-hz-133_000_000")]
|
||||
pub const TICK_HZ: u64 = 133_000_000;
|
||||
#[cfg(feature = "tick-hz-140_000_000")]
|
||||
pub const TICK_HZ: u64 = 140_000_000;
|
||||
#[cfg(feature = "tick-hz-144_000_000")]
|
||||
@ -410,6 +412,7 @@ pub const TICK_HZ: u64 = 5_242_880_000;
|
||||
feature = "tick-hz-128_000_000",
|
||||
feature = "tick-hz-130_000_000",
|
||||
feature = "tick-hz-131_072_000",
|
||||
feature = "tick-hz-133_000_000",
|
||||
feature = "tick-hz-140_000_000",
|
||||
feature = "tick-hz-144_000_000",
|
||||
feature = "tick-hz-150_000_000",
|
||||
|
||||
@ -274,6 +274,8 @@ tick-hz-128_000_000 = ["embassy-time-driver/tick-hz-128_000_000"]
|
||||
tick-hz-130_000_000 = ["embassy-time-driver/tick-hz-130_000_000"]
|
||||
## 131.072MHz Tick Rate
|
||||
tick-hz-131_072_000 = ["embassy-time-driver/tick-hz-131_072_000"]
|
||||
## 133.0MHz Tick Rate
|
||||
tick-hz-133_000_000 = ["embassy-time-driver/tick-hz-133_000_000"]
|
||||
## 140.0MHz Tick Rate
|
||||
tick-hz-140_000_000 = ["embassy-time-driver/tick-hz-140_000_000"]
|
||||
## 144.0MHz Tick Rate
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_boot::BlockingFirmwareState;
|
||||
use embassy_time::{Duration, Instant};
|
||||
use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
|
||||
@ -13,31 +11,47 @@ use crate::consts::{
|
||||
};
|
||||
use crate::Reset;
|
||||
|
||||
/// Generic interface for a system that can signal to the bootloader that USB DFU mode is needed on the next boot.
|
||||
///
|
||||
/// By default this trait is implemented for `BlockingFirmwareState<'d, STATE>` but you could also implement this generic
|
||||
/// interface yourself instead in more complex situations. This could for instance be when you cannot hand ownership of a
|
||||
/// `BlockingFirmwareState` instance over directly to the DFU `Control` instance and need to use a more complex mechanism.
|
||||
pub trait DfuMarker {
|
||||
/// Signal to the bootloader that DFU mode should be used on the next boot.
|
||||
fn mark_dfu(&mut self);
|
||||
}
|
||||
|
||||
impl<'d, STATE: NorFlash> DfuMarker for BlockingFirmwareState<'d, STATE> {
|
||||
fn mark_dfu(&mut self) {
|
||||
self.mark_dfu().expect("Failed to mark DFU mode in bootloader")
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal state for the DFU class
|
||||
pub struct Control<'d, STATE: NorFlash, RST: Reset> {
|
||||
firmware_state: BlockingFirmwareState<'d, STATE>,
|
||||
pub struct Control<MARK: DfuMarker, RST: Reset> {
|
||||
dfu_marker: MARK,
|
||||
attrs: DfuAttributes,
|
||||
state: State,
|
||||
timeout: Option<Duration>,
|
||||
detach_start: Option<Instant>,
|
||||
_rst: PhantomData<RST>,
|
||||
reset: RST,
|
||||
}
|
||||
|
||||
impl<'d, STATE: NorFlash, RST: Reset> Control<'d, STATE, RST> {
|
||||
impl<MARK: DfuMarker, RST: Reset> Control<MARK, RST> {
|
||||
/// Create a new DFU instance to expose a DFU interface.
|
||||
pub fn new(firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes) -> Self {
|
||||
pub fn new(dfu_marker: MARK, attrs: DfuAttributes, reset: RST) -> Self {
|
||||
Control {
|
||||
firmware_state,
|
||||
dfu_marker,
|
||||
attrs,
|
||||
state: State::AppIdle,
|
||||
detach_start: None,
|
||||
timeout: None,
|
||||
_rst: PhantomData,
|
||||
reset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> {
|
||||
impl<MARK: DfuMarker, RST: Reset> Handler for Control<MARK, RST> {
|
||||
fn reset(&mut self) {
|
||||
if let Some(start) = self.detach_start {
|
||||
let delta = Instant::now() - start;
|
||||
@ -48,10 +62,8 @@ impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> {
|
||||
timeout.as_millis()
|
||||
);
|
||||
if delta < timeout {
|
||||
self.firmware_state
|
||||
.mark_dfu()
|
||||
.expect("Failed to mark DFU mode in bootloader");
|
||||
RST::sys_reset()
|
||||
self.dfu_marker.mark_dfu();
|
||||
self.reset.sys_reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,10 +81,15 @@ impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> {
|
||||
|
||||
match Request::try_from(req.request) {
|
||||
Ok(Request::Detach) => {
|
||||
trace!("Received DETACH, awaiting USB reset");
|
||||
self.detach_start = Some(Instant::now());
|
||||
self.timeout = Some(Duration::from_millis(req.value as u64));
|
||||
self.state = State::AppDetach;
|
||||
if self.attrs.contains(DfuAttributes::WILL_DETACH) {
|
||||
trace!("Received DETACH, performing reset");
|
||||
self.reset();
|
||||
} else {
|
||||
trace!("Received DETACH, awaiting USB reset");
|
||||
}
|
||||
Some(OutResponse::Accepted)
|
||||
}
|
||||
_ => None,
|
||||
@ -109,9 +126,9 @@ impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> {
|
||||
/// it should expose a DFU device, and a software reset will be issued.
|
||||
///
|
||||
/// To apply USB DFU updates, the bootloader must be capable of recognizing the DFU magic and exposing a device to handle the full DFU transaction with the host.
|
||||
pub fn usb_dfu<'d, D: Driver<'d>, STATE: NorFlash, RST: Reset>(
|
||||
pub fn usb_dfu<'d, D: Driver<'d>, MARK: DfuMarker, RST: Reset>(
|
||||
builder: &mut Builder<'d, D>,
|
||||
handler: &'d mut Control<'d, STATE, RST>,
|
||||
handler: &'d mut Control<MARK, RST>,
|
||||
timeout: Duration,
|
||||
) {
|
||||
let mut func = builder.function(0x00, 0x00, 0x00);
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError};
|
||||
use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
|
||||
use embassy_usb::driver::Driver;
|
||||
@ -20,12 +18,12 @@ pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_S
|
||||
status: Status,
|
||||
offset: usize,
|
||||
buf: AlignedBuffer<BLOCK_SIZE>,
|
||||
_rst: PhantomData<RST>,
|
||||
reset: RST,
|
||||
}
|
||||
|
||||
impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Control<'d, DFU, STATE, RST, BLOCK_SIZE> {
|
||||
/// Create a new DFU instance to handle DFU transfers.
|
||||
pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self {
|
||||
pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes, reset: RST) -> Self {
|
||||
Self {
|
||||
updater,
|
||||
attrs,
|
||||
@ -33,7 +31,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Co
|
||||
status: Status::Ok,
|
||||
offset: 0,
|
||||
buf: AlignedBuffer([0; BLOCK_SIZE]),
|
||||
_rst: PhantomData,
|
||||
reset,
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,14 +153,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha
|
||||
}
|
||||
match Request::try_from(req.request) {
|
||||
Ok(Request::GetStatus) => {
|
||||
//TODO: Configurable poll timeout, ability to add string for Vendor error
|
||||
buf[0..6].copy_from_slice(&[self.status as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]);
|
||||
match self.state {
|
||||
State::DlSync => self.state = State::Download,
|
||||
State::ManifestSync => RST::sys_reset(),
|
||||
State::ManifestSync => self.reset.sys_reset(),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
//TODO: Configurable poll timeout, ability to add string for Vendor error
|
||||
buf[0..6].copy_from_slice(&[self.status as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]);
|
||||
Some(InResponse::Accepted(&buf[0..6]))
|
||||
}
|
||||
Ok(Request::GetState) => {
|
||||
|
||||
@ -26,10 +26,10 @@ compile_error!("usb-dfu must be compiled with exactly one of `dfu`, or `applicat
|
||||
/// This crate exposes `ResetImmediate` when compiled with cortex-m or esp32c3 support, which immediately issues a
|
||||
/// reset request without interfacing with any other peripherals.
|
||||
///
|
||||
/// If alternate behaviour is desired, a custom implementation of Reset can be provided as a type argument to the usb_dfu function.
|
||||
/// If alternate behaviour is desired, a custom implementation of Reset can be provided as an argument to the usb_dfu function.
|
||||
pub trait Reset {
|
||||
/// Reset the device.
|
||||
fn sys_reset() -> !;
|
||||
fn sys_reset(&self);
|
||||
}
|
||||
|
||||
/// Reset immediately.
|
||||
@ -38,7 +38,7 @@ pub struct ResetImmediate;
|
||||
|
||||
#[cfg(feature = "esp32c3-hal")]
|
||||
impl Reset for ResetImmediate {
|
||||
fn sys_reset() -> ! {
|
||||
fn sys_reset(&self) {
|
||||
esp32c3_hal::reset::software_reset();
|
||||
loop {}
|
||||
}
|
||||
@ -50,7 +50,7 @@ pub struct ResetImmediate;
|
||||
|
||||
#[cfg(feature = "cortex-m")]
|
||||
impl Reset for ResetImmediate {
|
||||
fn sys_reset() -> ! {
|
||||
fn sys_reset(&self) {
|
||||
cortex_m::peripheral::SCB::sys_reset()
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,3 +20,4 @@ features = ["defmt"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
embedded-io-async = "0.6.1"
|
||||
|
||||
@ -395,3 +395,12 @@ pub enum EndpointError {
|
||||
/// The endpoint is disabled.
|
||||
Disabled,
|
||||
}
|
||||
|
||||
impl embedded_io_async::Error for EndpointError {
|
||||
fn kind(&self) -> embedded_io_async::ErrorKind {
|
||||
match self {
|
||||
Self::BufferOverflow => embedded_io_async::ErrorKind::OutOfMemory,
|
||||
Self::Disabled => embedded_io_async::ErrorKind::NotConnected,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,6 +52,7 @@ embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
|
||||
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
embedded-io-async = "0.6.1"
|
||||
log = { version = "0.4.14", optional = true }
|
||||
heapless = "0.8"
|
||||
|
||||
|
||||
@ -410,6 +410,18 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>> embedded_io_async::ErrorType for Sender<'d, D> {
|
||||
type Error = EndpointError;
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>> embedded_io_async::Write for Sender<'d, D> {
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
let len = core::cmp::min(buf.len(), self.max_packet_size() as usize);
|
||||
self.write_packet(&buf[..len]).await?;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
/// CDC ACM class packet receiver.
|
||||
///
|
||||
/// You can obtain a `Receiver` with [`CdcAcmClass::split`]
|
||||
@ -451,6 +463,93 @@ impl<'d, D: Driver<'d>> Receiver<'d, D> {
|
||||
pub async fn wait_connection(&mut self) {
|
||||
self.read_ep.wait_enabled().await;
|
||||
}
|
||||
|
||||
/// Turn the `Receiver` into a [`BufferedReceiver`].
|
||||
///
|
||||
/// The supplied buffer must be large enough to hold max_packet_size bytes.
|
||||
pub fn into_buffered(self, buf: &'d mut [u8]) -> BufferedReceiver<'d, D> {
|
||||
BufferedReceiver {
|
||||
receiver: self,
|
||||
buffer: buf,
|
||||
start: 0,
|
||||
end: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CDC ACM class buffered receiver.
|
||||
///
|
||||
/// It is a requirement of the [`embedded_io_async::Read`] trait that arbitrarily small lengths of
|
||||
/// data can be read from the stream. The [`Receiver`] can only read full packets at a time. The
|
||||
/// `BufferedReceiver` instead buffers a single packet if the caller does not read all of the data,
|
||||
/// so that the remaining data can be returned in subsequent calls.
|
||||
///
|
||||
/// If you have no requirement to use the [`embedded_io_async::Read`] trait or to read a data length
|
||||
/// less than the packet length, then it is more efficient to use the [`Receiver`] directly.
|
||||
///
|
||||
/// You can obtain a `BufferedReceiver` with [`Receiver::into_buffered`].
|
||||
///
|
||||
/// [`embedded_io_async::Read`]: https://docs.rs/embedded-io-async/latest/embedded_io_async/trait.Read.html
|
||||
pub struct BufferedReceiver<'d, D: Driver<'d>> {
|
||||
receiver: Receiver<'d, D>,
|
||||
buffer: &'d mut [u8],
|
||||
start: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>> BufferedReceiver<'d, D> {
|
||||
fn read_from_buffer(&mut self, buf: &mut [u8]) -> usize {
|
||||
let available = &self.buffer[self.start..self.end];
|
||||
let len = core::cmp::min(available.len(), buf.len());
|
||||
buf[..len].copy_from_slice(&self.buffer[..len]);
|
||||
self.start += len;
|
||||
len
|
||||
}
|
||||
|
||||
/// Gets the current line coding. The line coding contains information that's mainly relevant
|
||||
/// for USB to UART serial port emulators, and can be ignored if not relevant.
|
||||
pub fn line_coding(&self) -> LineCoding {
|
||||
self.receiver.line_coding()
|
||||
}
|
||||
|
||||
/// Gets the DTR (data terminal ready) state
|
||||
pub fn dtr(&self) -> bool {
|
||||
self.receiver.dtr()
|
||||
}
|
||||
|
||||
/// Gets the RTS (request to send) state
|
||||
pub fn rts(&self) -> bool {
|
||||
self.receiver.rts()
|
||||
}
|
||||
|
||||
/// Waits for the USB host to enable this interface
|
||||
pub async fn wait_connection(&mut self) {
|
||||
self.receiver.wait_connection().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>> embedded_io_async::ErrorType for BufferedReceiver<'d, D> {
|
||||
type Error = EndpointError;
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>> embedded_io_async::Read for BufferedReceiver<'d, D> {
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
// If there is a buffered packet, return data from that first
|
||||
if self.start != self.end {
|
||||
return Ok(self.read_from_buffer(buf));
|
||||
}
|
||||
|
||||
// If the caller's buffer is large enough to contain an entire packet, read directly into
|
||||
// that instead of buffering the packet internally.
|
||||
if buf.len() > self.receiver.max_packet_size() as usize {
|
||||
return self.receiver.read_packet(buf).await;
|
||||
}
|
||||
|
||||
// Otherwise read a packet into the internal buffer, and return some of it to the caller
|
||||
self.start = 0;
|
||||
self.end = self.receiver.read_packet(&mut self.buffer).await?;
|
||||
return Ok(self.read_from_buffer(buf));
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of stop bits for LineCoding
|
||||
|
||||
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
embassy-sync = { version = "0.6.2", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] }
|
||||
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti", "single-bank"] }
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) {
|
||||
let mut config_descriptor = [0; 256];
|
||||
let mut bos_descriptor = [0; 256];
|
||||
let mut control_buf = [0; 64];
|
||||
let mut state = Control::new(firmware_state, DfuAttributes::CAN_DOWNLOAD);
|
||||
let mut state = Control::new(firmware_state, DfuAttributes::CAN_DOWNLOAD, ResetImmediate);
|
||||
let mut builder = Builder::new(
|
||||
driver,
|
||||
config,
|
||||
@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) {
|
||||
&mut control_buf,
|
||||
);
|
||||
|
||||
usb_dfu::<_, _, ResetImmediate>(&mut builder, &mut state, Duration::from_millis(2500));
|
||||
usb_dfu(&mut builder, &mut state, Duration::from_millis(2500));
|
||||
|
||||
let mut dev = builder.build();
|
||||
dev.run().await
|
||||
|
||||
@ -20,7 +20,7 @@ static APP_B: &[u8] = &[0, 1, 2, 3];
|
||||
#[cfg(not(feature = "skip-include"))]
|
||||
static APP_B: &[u8] = include_bytes!("../../b.bin");
|
||||
|
||||
#[link_section = ".shared_data"]
|
||||
#[unsafe(link_section = ".shared_data")]
|
||||
static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
||||
@ -11,7 +11,7 @@ use embassy_stm32::SharedData;
|
||||
use embassy_time::Timer;
|
||||
use panic_reset as _;
|
||||
|
||||
#[link_section = ".shared_data"]
|
||||
#[unsafe(link_section = ".shared_data")]
|
||||
static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
||||
@ -55,7 +55,7 @@ fn main() -> ! {
|
||||
let mut config_descriptor = [0; 256];
|
||||
let mut bos_descriptor = [0; 256];
|
||||
let mut control_buf = [0; 4096];
|
||||
let mut state = Control::new(updater, DfuAttributes::CAN_DOWNLOAD);
|
||||
let mut state = Control::new(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate);
|
||||
let mut builder = Builder::new(
|
||||
driver,
|
||||
config,
|
||||
@ -77,7 +77,7 @@ fn main() -> ! {
|
||||
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
|
||||
));
|
||||
|
||||
usb_dfu::<_, _, _, ResetImmediate, 4096>(&mut builder, &mut state);
|
||||
usb_dfu::<_, _, _, _, 4096>(&mut builder, &mut state);
|
||||
|
||||
let mut dev = builder.build();
|
||||
embassy_futures::block_on(dev.run());
|
||||
|
||||
@ -12,8 +12,8 @@ defmt-rtt = "1.0"
|
||||
|
||||
embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-futures = { version = "0.1.1", path = "../../embassy-futures" }
|
||||
embassy-imxrt = { version = "0.1.0", path = "../../embassy-imxrt", features = ["defmt", "mimxrt685s", "unstable-pac", "time", "time-driver-rtc"] }
|
||||
embassy-time = { version = "0.4", path = "../../embassy-time" }
|
||||
embassy-imxrt = { version = "0.1.0", path = "../../embassy-imxrt", features = ["defmt", "mimxrt685s", "unstable-pac", "time", "time-driver-os-timer"] }
|
||||
embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
||||
embedded-hal-async = "1.0.0"
|
||||
|
||||
@ -25,7 +25,7 @@ fn main() {
|
||||
.write_all(
|
||||
format!(
|
||||
r##"
|
||||
#[link_section = ".biv"]
|
||||
#[unsafe(link_section = ".biv")]
|
||||
#[used]
|
||||
static BOOT_IMAGE_VERSION: u32 = 0x{:02x}{:02x}{:02x}00;
|
||||
"##,
|
||||
|
||||
175
examples/mimxrt6/src/bin/crc.rs
Normal file
175
examples/mimxrt6/src/bin/crc.rs
Normal file
@ -0,0 +1,175 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate embassy_imxrt_examples;
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_imxrt::crc::{Config, Crc, Polynomial};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut p = embassy_imxrt::init(Default::default());
|
||||
let data = b"123456789";
|
||||
|
||||
info!("Initializing CRC");
|
||||
|
||||
// CRC-CCITT
|
||||
let mut crc = Crc::new(p.CRC.reborrow(), Default::default());
|
||||
let output = crc.feed_bytes(data);
|
||||
defmt::assert_eq!(output, 0x29b1);
|
||||
|
||||
// CRC16-ARC
|
||||
let mut crc = Crc::new(
|
||||
p.CRC.reborrow(),
|
||||
Config {
|
||||
polynomial: Polynomial::Crc16,
|
||||
reverse_in: true,
|
||||
reverse_out: true,
|
||||
complement_out: false,
|
||||
seed: 0,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let output = crc.feed_bytes(data);
|
||||
defmt::assert_eq!(output, 0xbb3d);
|
||||
|
||||
// CRC16-CMS
|
||||
let mut crc = Crc::new(
|
||||
p.CRC.reborrow(),
|
||||
Config {
|
||||
polynomial: Polynomial::Crc16,
|
||||
reverse_in: false,
|
||||
reverse_out: false,
|
||||
complement_out: false,
|
||||
seed: 0xffff,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let output = crc.feed_bytes(data);
|
||||
defmt::assert_eq!(output, 0xaee7);
|
||||
|
||||
// CRC16-DDS-110
|
||||
let mut crc = Crc::new(
|
||||
p.CRC.reborrow(),
|
||||
Config {
|
||||
polynomial: Polynomial::Crc16,
|
||||
reverse_in: false,
|
||||
reverse_out: false,
|
||||
complement_out: false,
|
||||
seed: 0x800d,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let output = crc.feed_bytes(data);
|
||||
defmt::assert_eq!(output, 0x9ecf);
|
||||
|
||||
// CRC16-MAXIM-DOW
|
||||
let mut crc = Crc::new(
|
||||
p.CRC.reborrow(),
|
||||
Config {
|
||||
polynomial: Polynomial::Crc16,
|
||||
reverse_in: true,
|
||||
reverse_out: true,
|
||||
complement_out: true,
|
||||
seed: 0,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let output = crc.feed_bytes(data);
|
||||
defmt::assert_eq!(output, 0x44c2);
|
||||
|
||||
// CRC16-MODBUS
|
||||
let mut crc = Crc::new(
|
||||
p.CRC.reborrow(),
|
||||
Config {
|
||||
polynomial: Polynomial::Crc16,
|
||||
reverse_in: true,
|
||||
reverse_out: true,
|
||||
complement_out: false,
|
||||
seed: 0xffff,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let output = crc.feed_bytes(data);
|
||||
defmt::assert_eq!(output, 0x4b37);
|
||||
|
||||
// CRC32-BZIP2
|
||||
let mut crc = Crc::new(
|
||||
p.CRC.reborrow(),
|
||||
Config {
|
||||
polynomial: Polynomial::Crc32,
|
||||
reverse_in: false,
|
||||
reverse_out: false,
|
||||
complement_out: true,
|
||||
seed: 0xffff_ffff,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let output = crc.feed_bytes(data);
|
||||
defmt::assert_eq!(output, 0xfc89_1918);
|
||||
|
||||
// CRC32-CKSUM
|
||||
let mut crc = Crc::new(
|
||||
p.CRC.reborrow(),
|
||||
Config {
|
||||
polynomial: Polynomial::Crc32,
|
||||
reverse_in: false,
|
||||
reverse_out: false,
|
||||
complement_out: true,
|
||||
seed: 0,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let output = crc.feed_bytes(data);
|
||||
defmt::assert_eq!(output, 0x765e_7680);
|
||||
|
||||
// CRC32-ISO-HDLC
|
||||
let mut crc = Crc::new(
|
||||
p.CRC.reborrow(),
|
||||
Config {
|
||||
polynomial: Polynomial::Crc32,
|
||||
reverse_in: true,
|
||||
reverse_out: true,
|
||||
complement_out: true,
|
||||
seed: 0xffff_ffff,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let output = crc.feed_bytes(data);
|
||||
defmt::assert_eq!(output, 0xcbf4_3926);
|
||||
|
||||
// CRC32-JAMCRC
|
||||
let mut crc = Crc::new(
|
||||
p.CRC.reborrow(),
|
||||
Config {
|
||||
polynomial: Polynomial::Crc32,
|
||||
reverse_in: true,
|
||||
reverse_out: true,
|
||||
complement_out: false,
|
||||
seed: 0xffff_ffff,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let output = crc.feed_bytes(data);
|
||||
defmt::assert_eq!(output, 0x340b_c6d9);
|
||||
|
||||
// CRC32-MPEG-2
|
||||
let mut crc = Crc::new(
|
||||
p.CRC.reborrow(),
|
||||
Config {
|
||||
polynomial: Polynomial::Crc32,
|
||||
reverse_in: false,
|
||||
reverse_out: false,
|
||||
complement_out: false,
|
||||
seed: 0xffff_ffff,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let output = crc.feed_bytes(data);
|
||||
defmt::assert_eq!(output, 0x0376_e6e7);
|
||||
|
||||
info!("end program");
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
||||
40
examples/mimxrt6/src/bin/rng.rs
Normal file
40
examples/mimxrt6/src/bin/rng.rs
Normal file
@ -0,0 +1,40 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate embassy_imxrt_examples;
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_imxrt::rng::Rng;
|
||||
use embassy_imxrt::{bind_interrupts, peripherals, rng};
|
||||
use rand::RngCore;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
RNG => rng::InterruptHandler<peripherals::RNG>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_imxrt::init(Default::default());
|
||||
|
||||
info!("Initializing RNG");
|
||||
let mut rng = Rng::new(p.RNG, Irqs);
|
||||
let mut buf = [0u8; 65];
|
||||
|
||||
// Async interface
|
||||
unwrap!(rng.async_fill_bytes(&mut buf).await);
|
||||
info!("random bytes: {:02x}", buf);
|
||||
|
||||
// RngCore interface
|
||||
let mut random_bytes = [0; 16];
|
||||
|
||||
let random_u32 = rng.next_u32();
|
||||
let random_u64 = rng.next_u64();
|
||||
|
||||
rng.fill_bytes(&mut random_bytes);
|
||||
|
||||
info!("random_u32 {}", random_u32);
|
||||
info!("random_u64 {}", random_u64);
|
||||
info!("random_bytes {}", random_bytes);
|
||||
}
|
||||
@ -6,15 +6,15 @@ use {defmt_rtt as _, panic_probe as _};
|
||||
// auto-generated version information from Cargo.toml
|
||||
include!(concat!(env!("OUT_DIR"), "/biv.rs"));
|
||||
|
||||
#[link_section = ".otfad"]
|
||||
#[unsafe(link_section = ".otfad")]
|
||||
#[used]
|
||||
static OTFAD: [u8; 256] = [0; 256];
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[link_section = ".fcb"]
|
||||
#[unsafe(link_section = ".fcb")]
|
||||
#[used]
|
||||
static FCB: FlexSPIFlashConfigurationBlock = FlexSPIFlashConfigurationBlock::build();
|
||||
|
||||
#[link_section = ".keystore"]
|
||||
#[unsafe(link_section = ".keystore")]
|
||||
#[used]
|
||||
static KEYSTORE: [u8; 2048] = [0; 2048];
|
||||
|
||||
@ -9,6 +9,7 @@ use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_nrf::twim::{self, Twim};
|
||||
use embassy_nrf::{bind_interrupts, peripherals};
|
||||
use static_cell::ConstStaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const ADDRESS: u8 = 0x50;
|
||||
@ -22,7 +23,8 @@ async fn main(_spawner: Spawner) {
|
||||
let p = embassy_nrf::init(Default::default());
|
||||
info!("Initializing TWI...");
|
||||
let config = twim::Config::default();
|
||||
let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config);
|
||||
static RAM_BUFFER: ConstStaticCell<[u8; 16]> = ConstStaticCell::new([0; 16]);
|
||||
let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config, RAM_BUFFER.take());
|
||||
|
||||
info!("Reading...");
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@ async fn main(_p: Spawner) {
|
||||
loop {
|
||||
info!("Initializing TWI...");
|
||||
let config = twim::Config::default();
|
||||
let mut ram_buffer = [0u8; 16];
|
||||
|
||||
// Create the TWIM instance with borrowed singletons, so they're not consumed.
|
||||
let mut twi = Twim::new(
|
||||
@ -38,6 +39,7 @@ async fn main(_p: Spawner) {
|
||||
p.P0_03.reborrow(),
|
||||
p.P0_04.reborrow(),
|
||||
config,
|
||||
&mut ram_buffer,
|
||||
);
|
||||
|
||||
info!("Reading...");
|
||||
|
||||
64
examples/rp/src/bin/overclock.rs
Normal file
64
examples/rp/src/bin/overclock.rs
Normal file
@ -0,0 +1,64 @@
|
||||
//! # Overclocking the RP2040 to 200 MHz
|
||||
//!
|
||||
//! This example demonstrates how to configure the RP2040 to run at 200 MHz.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig};
|
||||
use embassy_rp::config::Config;
|
||||
use embassy_rp::gpio::{Level, Output};
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const COUNT_TO: i64 = 10_000_000;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
// Set up for clock frequency of 200 MHz, setting all necessary defaults.
|
||||
let config = Config::new(ClockConfig::system_freq(200_000_000).unwrap());
|
||||
|
||||
// Initialize the peripherals
|
||||
let p = embassy_rp::init(config);
|
||||
|
||||
// Show CPU frequency for verification
|
||||
let sys_freq = clk_sys_freq();
|
||||
info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
|
||||
// Show core voltage for verification
|
||||
let core_voltage = core_voltage().unwrap();
|
||||
info!("Core voltage: {}", core_voltage);
|
||||
|
||||
// LED to indicate the system is running
|
||||
let mut led = Output::new(p.PIN_25, Level::Low);
|
||||
|
||||
loop {
|
||||
// Reset the counter at the start of measurement period
|
||||
let mut counter = 0;
|
||||
|
||||
// Turn LED on while counting
|
||||
led.set_high();
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
// This is a busy loop that will take some time to complete
|
||||
while counter < COUNT_TO {
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
let elapsed = Instant::now() - start;
|
||||
|
||||
// Report the elapsed time
|
||||
led.set_low();
|
||||
info!(
|
||||
"At {}Mhz: Elapsed time to count to {}: {}ms",
|
||||
sys_freq / 1_000_000,
|
||||
counter,
|
||||
elapsed.as_millis()
|
||||
);
|
||||
|
||||
// Wait 2 seconds before starting the next measurement
|
||||
Timer::after(Duration::from_secs(2)).await;
|
||||
}
|
||||
}
|
||||
81
examples/rp/src/bin/overclock_manual.rs
Normal file
81
examples/rp/src/bin/overclock_manual.rs
Normal file
@ -0,0 +1,81 @@
|
||||
//! # Overclocking the RP2040 to 200 MHz manually
|
||||
//!
|
||||
//! This example demonstrates how to manually configure the RP2040 to run at 200 MHz.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage, PllConfig};
|
||||
use embassy_rp::config::Config;
|
||||
use embassy_rp::gpio::{Level, Output};
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const COUNT_TO: i64 = 10_000_000;
|
||||
|
||||
/// Configure the RP2040 for 200 MHz operation by manually specifying the PLL settings.
|
||||
fn configure_manual_overclock() -> Config {
|
||||
// Set the PLL configuration manually, starting from default values
|
||||
let mut config = Config::default();
|
||||
|
||||
// Set the system clock to 200 MHz
|
||||
config.clocks = ClockConfig::manual_pll(
|
||||
12_000_000, // Crystal frequency, 12 MHz is common. If using custom, set to your value.
|
||||
PllConfig {
|
||||
refdiv: 1, // Reference divider
|
||||
fbdiv: 100, // Feedback divider
|
||||
post_div1: 3, // Post divider 1
|
||||
post_div2: 2, // Post divider 2
|
||||
},
|
||||
CoreVoltage::V1_15, // Core voltage, should be set to V1_15 for 200 MHz
|
||||
);
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
// Initialize with our manual overclock configuration
|
||||
let p = embassy_rp::init(configure_manual_overclock());
|
||||
|
||||
// Show CPU frequency for verification
|
||||
let sys_freq = clk_sys_freq();
|
||||
info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
|
||||
// Show core voltage for verification
|
||||
let core_voltage = core_voltage().unwrap();
|
||||
info!("Core voltage: {}", core_voltage);
|
||||
|
||||
// LED to indicate the system is running
|
||||
let mut led = Output::new(p.PIN_25, Level::Low);
|
||||
|
||||
loop {
|
||||
// Reset the counter at the start of measurement period
|
||||
let mut counter = 0;
|
||||
|
||||
// Turn LED on while counting
|
||||
led.set_high();
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
// This is a busy loop that will take some time to complete
|
||||
while counter < COUNT_TO {
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
let elapsed = Instant::now() - start;
|
||||
|
||||
// Report the elapsed time
|
||||
led.set_low();
|
||||
info!(
|
||||
"At {}Mhz: Elapsed time to count to {}: {}ms",
|
||||
sys_freq / 1_000_000,
|
||||
counter,
|
||||
elapsed.as_millis()
|
||||
);
|
||||
|
||||
// Wait 2 seconds before starting the next measurement
|
||||
Timer::after(Duration::from_secs(2)).await;
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,7 @@ use rand::RngCore;
|
||||
use static_cell::{ConstStaticCell, StaticCell};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>;
|
||||
type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, uart::Async>>;
|
||||
|
||||
struct MyType {
|
||||
inner: u32,
|
||||
|
||||
@ -48,7 +48,7 @@ async fn main(spawner: Spawner) {
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn reader(mut rx: BufferedUartRx<'static, UART0>) {
|
||||
async fn reader(mut rx: BufferedUartRx) {
|
||||
info!("Reading...");
|
||||
loop {
|
||||
let mut buf = [0; 31];
|
||||
|
||||
@ -39,7 +39,7 @@ async fn main(spawner: Spawner) {
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn reader(mut rx: UartRx<'static, UART1, Async>) {
|
||||
async fn reader(mut rx: UartRx<'static, Async>) {
|
||||
info!("Reading...");
|
||||
loop {
|
||||
// read a total of 4 transmissions (32 / 8) and then print the result
|
||||
|
||||
@ -14,7 +14,7 @@ use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
// Program metadata for `picotool info`.
|
||||
// This isn't needed, but it's recomended to have these minimal entries.
|
||||
#[link_section = ".bi_entries"]
|
||||
#[unsafe(link_section = ".bi_entries")]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info::rp_program_name!(c"Blinky Example"),
|
||||
|
||||
@ -18,7 +18,7 @@ use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
// Program metadata for `picotool info`.
|
||||
// This isn't needed, but it's recommended to have these minimal entries.
|
||||
#[link_section = ".bi_entries"]
|
||||
#[unsafe(link_section = ".bi_entries")]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info::rp_program_name!(c"Blinky Example"),
|
||||
|
||||
@ -18,7 +18,7 @@ use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
// Program metadata for `picotool info`.
|
||||
// This isn't needed, but it's recomended to have these minimal entries.
|
||||
#[link_section = ".bi_entries"]
|
||||
#[unsafe(link_section = ".bi_entries")]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info::rp_program_name!(c"Blinky Example"),
|
||||
|
||||
74
examples/rp235x/src/bin/overclock.rs
Normal file
74
examples/rp235x/src/bin/overclock.rs
Normal file
@ -0,0 +1,74 @@
|
||||
//! # Overclocking the RP2350 to 200 MHz
|
||||
//!
|
||||
//! This example demonstrates how to configure the RP2350 to run at 200 MHz instead of the default 150 MHz.
|
||||
//!
|
||||
//! ## Note
|
||||
//!
|
||||
//! As of yet there is no official support for running the RP235x at higher clock frequencies and/or other core voltages than the default.
|
||||
//! Doing so may cause unexpected behavior and/or damage the chip.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage};
|
||||
use embassy_rp::config::Config;
|
||||
use embassy_rp::gpio::{Level, Output};
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const COUNT_TO: i64 = 10_000_000;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
// Set up for clock frequency of 200 MHz, setting all necessary defaults.
|
||||
let mut config = Config::new(ClockConfig::system_freq(200_000_000).unwrap());
|
||||
|
||||
// since for the rp235x there is no official support for higher clock frequencies, `system_freq()` will not set a voltage for us.
|
||||
// We need to guess the core voltage, that is needed for the higher clock frequency. Going with a small increase from the default 1.1V here, based on
|
||||
// what we know about the RP2040. This is not guaranteed to be correct.
|
||||
config.clocks.core_voltage = CoreVoltage::V1_15;
|
||||
|
||||
// Initialize the peripherals
|
||||
let p = embassy_rp::init(config);
|
||||
|
||||
// Show CPU frequency for verification
|
||||
let sys_freq = clk_sys_freq();
|
||||
info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
|
||||
// Show core voltage for verification
|
||||
let core_voltage = core_voltage().unwrap();
|
||||
info!("Core voltage: {}", core_voltage);
|
||||
|
||||
// LED to indicate the system is running
|
||||
let mut led = Output::new(p.PIN_25, Level::Low);
|
||||
|
||||
loop {
|
||||
// Reset the counter at the start of measurement period
|
||||
let mut counter = 0;
|
||||
|
||||
// Turn LED on while counting
|
||||
led.set_high();
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
// This is a busy loop that will take some time to complete
|
||||
while counter < COUNT_TO {
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
let elapsed = Instant::now() - start;
|
||||
|
||||
// Report the elapsed time
|
||||
led.set_low();
|
||||
info!(
|
||||
"At {}Mhz: Elapsed time to count to {}: {}ms",
|
||||
sys_freq / 1_000_000,
|
||||
counter,
|
||||
elapsed.as_millis()
|
||||
);
|
||||
|
||||
// Wait 2 seconds before starting the next measurement
|
||||
Timer::after(Duration::from_secs(2)).await;
|
||||
}
|
||||
}
|
||||
@ -16,7 +16,7 @@ use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, Shi
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[unsafe(link_section = ".bi_entries")]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info::rp_program_name!(c"example_pio_rotary_encoder_rxf"),
|
||||
@ -88,8 +88,8 @@ impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {
|
||||
Self { sm }
|
||||
}
|
||||
|
||||
pub async fn read(&mut self) -> u32 {
|
||||
self.sm.get_rxf_entry(0)
|
||||
pub async fn read(&mut self) -> i32 {
|
||||
self.sm.get_rxf_entry(0) as i32
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ use rand::RngCore;
|
||||
use static_cell::{ConstStaticCell, StaticCell};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>;
|
||||
type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, uart::Async>>;
|
||||
|
||||
struct MyType {
|
||||
inner: u32,
|
||||
|
||||
@ -48,7 +48,7 @@ async fn main(spawner: Spawner) {
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn reader(mut rx: BufferedUartRx<'static, UART0>) {
|
||||
async fn reader(mut rx: BufferedUartRx) {
|
||||
info!("Reading...");
|
||||
loop {
|
||||
let mut buf = [0; 31];
|
||||
|
||||
@ -39,7 +39,7 @@ async fn main(spawner: Spawner) {
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn reader(mut rx: UartRx<'static, UART1, Async>) {
|
||||
async fn reader(mut rx: UartRx<'static, Async>) {
|
||||
info!("Reading...");
|
||||
loop {
|
||||
// read a total of 4 transmissions (32 / 8) and then print the result
|
||||
|
||||
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
# Change stm32f777zi to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
|
||||
embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti", "single-bank"] }
|
||||
embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
|
||||
@ -8,7 +8,7 @@ use embassy_stm32::Config;
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[link_section = ".ram_d3"]
|
||||
#[unsafe(link_section = ".ram_d3")]
|
||||
static mut DMA_BUF: [u16; 2] = [0; 2];
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
||||
@ -16,9 +16,9 @@ const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks
|
||||
const SAMPLE_RATE: u32 = 48000;
|
||||
|
||||
//DMA buffer must be in special region. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions
|
||||
#[link_section = ".sram1_bss"]
|
||||
#[unsafe(link_section = ".sram1_bss")]
|
||||
static mut TX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();
|
||||
#[link_section = ".sram1_bss"]
|
||||
#[unsafe(link_section = ".sram1_bss")]
|
||||
static mut RX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();
|
||||
|
||||
#[embassy_executor::main]
|
||||
@ -112,8 +112,10 @@ async fn main(_spawner: Spawner) {
|
||||
let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH];
|
||||
|
||||
loop {
|
||||
sai_receiver.read(&mut buf).await.unwrap();
|
||||
// write() must be called before read() to start the master (transmitter)
|
||||
// clock used by the receiver
|
||||
sai_transmitter.write(&buf).await.unwrap();
|
||||
sai_receiver.read(&mut buf).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user