aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xci.sh5
-rw-r--r--docs/modules/ROOT/examples/basic/Cargo.toml2
-rw-r--r--docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml2
-rw-r--r--embassy-boot/boot/Cargo.toml4
-rw-r--r--embassy-boot/boot/src/boot_loader.rs533
-rw-r--r--embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs30
-rw-r--r--embassy-boot/boot/src/digest_adapters/mod.rs5
-rw-r--r--embassy-boot/boot/src/digest_adapters/salty.rs29
-rw-r--r--embassy-boot/boot/src/firmware_updater.rs534
-rw-r--r--embassy-boot/boot/src/lib.rs1427
-rw-r--r--embassy-boot/boot/src/mem_flash.rs164
-rw-r--r--embassy-boot/boot/src/partition.rs139
-rw-r--r--embassy-boot/nrf/src/lib.rs26
-rw-r--r--embassy-boot/rp/src/lib.rs28
-rw-r--r--embassy-boot/stm32/src/lib.rs26
-rw-r--r--embassy-cortex-m/src/executor.rs116
-rw-r--r--embassy-cortex-m/src/lib.rs2
-rw-r--r--embassy-embedded-hal/Cargo.toml4
-rw-r--r--embassy-embedded-hal/src/adapter.rs23
-rw-r--r--embassy-embedded-hal/src/lib.rs2
-rw-r--r--embassy-embedded-hal/src/shared_bus/asynch/i2c.rs42
-rw-r--r--embassy-embedded-hal/src/shared_bus/asynch/spi.rs171
-rw-r--r--embassy-embedded-hal/src/shared_bus/blocking/i2c.rs56
-rw-r--r--embassy-embedded-hal/src/shared_bus/blocking/spi.rs153
-rw-r--r--embassy-executor/Cargo.toml46
-rw-r--r--embassy-executor/src/arch/cortex_m.rs238
-rw-r--r--embassy-executor/src/arch/riscv32.rs133
-rw-r--r--embassy-executor/src/arch/std.rs150
-rw-r--r--embassy-executor/src/arch/wasm.rs134
-rw-r--r--embassy-executor/src/arch/xtensa.rs135
-rw-r--r--embassy-executor/src/lib.rs83
-rw-r--r--embassy-executor/src/raw/mod.rs126
-rw-r--r--embassy-executor/src/raw/run_queue.rs18
-rw-r--r--embassy-executor/src/raw/waker_turbo.rs34
-rw-r--r--embassy-lora/Cargo.toml12
-rw-r--r--embassy-macros/src/macros/cortex_m_interrupt_take.rs4
-rw-r--r--embassy-nrf/Cargo.toml4
-rw-r--r--embassy-nrf/src/twim.rs47
-rw-r--r--embassy-rp/Cargo.toml6
-rw-r--r--embassy-rp/src/gpio.rs59
-rw-r--r--embassy-rp/src/i2c.rs107
-rw-r--r--embassy-rp/src/spi.rs1
-rw-r--r--embassy-rp/src/uart/buffered.rs16
-rw-r--r--embassy-rp/src/uart/mod.rs8
-rw-r--r--embassy-stm32/Cargo.toml94
-rw-r--r--embassy-stm32/build.rs143
-rw-r--r--embassy-stm32/src/adc/mod.rs11
-rw-r--r--embassy-stm32/src/adc/resolution.rs8
-rw-r--r--embassy-stm32/src/adc/sample_time.rs2
-rw-r--r--embassy-stm32/src/adc/v1.rs170
-rw-r--r--embassy-stm32/src/dma/gpdma.rs11
-rw-r--r--embassy-stm32/src/eth/v2/mod.rs25
-rw-r--r--embassy-stm32/src/exti.rs18
-rw-r--r--embassy-stm32/src/flash/common.rs211
-rw-r--r--embassy-stm32/src/flash/f3.rs82
-rw-r--r--embassy-stm32/src/flash/f4.rs277
-rw-r--r--embassy-stm32/src/flash/f7.rs139
-rw-r--r--embassy-stm32/src/flash/h7.rs99
-rw-r--r--embassy-stm32/src/flash/l.rs125
-rw-r--r--embassy-stm32/src/flash/mod.rs155
-rw-r--r--embassy-stm32/src/flash/other.rs29
-rw-r--r--embassy-stm32/src/i2c/timeout.rs75
-rw-r--r--embassy-stm32/src/i2c/v1.rs73
-rw-r--r--embassy-stm32/src/i2c/v2.rs160
-rw-r--r--embassy-stm32/src/lib.rs8
-rw-r--r--embassy-stm32/src/pwm/complementary_pwm.rs124
-rw-r--r--embassy-stm32/src/pwm/mod.rs40
-rw-r--r--embassy-stm32/src/rcc/f4.rs166
-rw-r--r--embassy-stm32/src/rcc/h5.rs606
-rw-r--r--embassy-stm32/src/rcc/l4.rs133
-rw-r--r--embassy-stm32/src/rcc/mod.rs13
-rw-r--r--embassy-stm32/src/spi/mod.rs72
-rw-r--r--embassy-stm32/src/time.rs46
-rw-r--r--embassy-stm32/src/usart/buffered.rs696
-rw-r--r--embassy-stm32/src/usart/mod.rs9
-rw-r--r--embassy-stm32/src/usb/usb.rs134
-rw-r--r--embassy-stm32/src/usb_otg/mod.rs6
-rw-r--r--embassy-sync/Cargo.toml1
-rw-r--r--embassy-sync/src/waitqueue/atomic_waker.rs41
-rw-r--r--embassy-sync/src/waitqueue/atomic_waker_turbo.rs30
-rw-r--r--embassy-sync/src/waitqueue/mod.rs8
-rw-r--r--embassy-sync/src/waitqueue/waker_registration.rs (renamed from embassy-sync/src/waitqueue/waker.rs)40
-rw-r--r--embassy-time/Cargo.toml4
-rw-r--r--embassy-time/src/delay.rs20
-rw-r--r--examples/boot/application/nrf/Cargo.toml2
-rw-r--r--examples/boot/application/rp/Cargo.toml2
-rw-r--r--examples/boot/application/stm32f3/Cargo.toml2
-rw-r--r--examples/boot/application/stm32f7/Cargo.toml2
-rw-r--r--examples/boot/application/stm32h7/Cargo.toml2
-rw-r--r--examples/boot/application/stm32l0/Cargo.toml2
-rw-r--r--examples/boot/application/stm32l1/Cargo.toml2
-rw-r--r--examples/boot/application/stm32l4/Cargo.toml2
-rw-r--r--examples/boot/application/stm32wl/Cargo.toml2
-rw-r--r--examples/boot/bootloader/nrf/src/main.rs8
-rw-r--r--examples/boot/bootloader/rp/src/main.rs3
-rw-r--r--examples/boot/bootloader/stm32/src/main.rs7
-rw-r--r--examples/nrf-rtos-trace/Cargo.toml2
-rw-r--r--examples/nrf52840/Cargo.toml2
-rw-r--r--examples/nrf5340/Cargo.toml2
-rw-r--r--examples/rp/.cargo/config.toml2
-rw-r--r--examples/rp/Cargo.toml7
-rw-r--r--examples/rp/src/bin/spi_display.rs158
-rw-r--r--examples/std/Cargo.toml2
-rw-r--r--examples/stm32c0/Cargo.toml2
-rw-r--r--examples/stm32f0/Cargo.toml2
-rw-r--r--examples/stm32f0/src/bin/adc.rs35
-rw-r--r--examples/stm32f0/src/bin/multiprio.rs2
-rw-r--r--examples/stm32f1/Cargo.toml4
-rw-r--r--examples/stm32f2/Cargo.toml2
-rw-r--r--examples/stm32f3/Cargo.toml2
-rw-r--r--examples/stm32f3/src/bin/flash.rs2
-rw-r--r--examples/stm32f3/src/bin/multiprio.rs2
-rw-r--r--examples/stm32f4/Cargo.toml2
-rw-r--r--examples/stm32f4/src/bin/flash.rs13
-rw-r--r--examples/stm32f4/src/bin/mco.rs30
-rw-r--r--examples/stm32f4/src/bin/multiprio.rs2
-rw-r--r--examples/stm32f4/src/bin/pwm_complementary.rs77
-rw-r--r--examples/stm32f4/src/bin/usart_buffered.rs14
-rw-r--r--examples/stm32f7/Cargo.toml2
-rw-r--r--examples/stm32f7/src/bin/flash.rs4
-rw-r--r--examples/stm32g0/Cargo.toml2
-rw-r--r--examples/stm32g4/Cargo.toml2
-rw-r--r--examples/stm32h5/.cargo/config.toml8
-rw-r--r--examples/stm32h5/Cargo.toml71
-rw-r--r--examples/stm32h5/build.rs5
-rw-r--r--examples/stm32h5/memory.x5
-rw-r--r--examples/stm32h5/src/bin/blinky.rs27
-rw-r--r--examples/stm32h5/src/bin/button_exti.rs27
-rw-r--r--examples/stm32h5/src/bin/eth.rs133
-rw-r--r--examples/stm32h5/src/bin/i2c.rs44
-rw-r--r--examples/stm32h5/src/bin/rng.rs20
-rw-r--r--examples/stm32h5/src/bin/usart.rs43
-rw-r--r--examples/stm32h5/src/bin/usart_dma.rs46
-rw-r--r--examples/stm32h5/src/bin/usart_split.rs58
-rw-r--r--examples/stm32h5/src/bin/usb_serial.rs128
-rw-r--r--examples/stm32h7/Cargo.toml6
-rw-r--r--examples/stm32h7/src/bin/flash.rs4
-rw-r--r--examples/stm32l0/Cargo.toml2
-rw-r--r--examples/stm32l0/src/bin/flash.rs2
-rw-r--r--examples/stm32l0/src/bin/usart_irq.rs16
-rw-r--r--examples/stm32l1/Cargo.toml2
-rw-r--r--examples/stm32l1/src/bin/flash.rs2
-rw-r--r--examples/stm32l4/Cargo.toml6
-rw-r--r--examples/stm32l4/src/bin/mco.rs27
-rw-r--r--examples/stm32l5/Cargo.toml2
-rw-r--r--examples/stm32u5/Cargo.toml2
-rw-r--r--examples/stm32wb/Cargo.toml2
-rw-r--r--examples/stm32wl/Cargo.toml4
-rw-r--r--examples/stm32wl/src/bin/flash.rs2
-rw-r--r--examples/wasm/Cargo.toml2
-rw-r--r--rust-toolchain.toml3
-rw-r--r--tests/nrf/Cargo.toml2
-rw-r--r--tests/nrf/src/bin/timer.rs25
-rw-r--r--tests/rp/Cargo.toml6
-rw-r--r--tests/stm32/Cargo.toml7
-rw-r--r--tests/stm32/src/bin/gpio.rs2
-rw-r--r--tests/stm32/src/bin/spi.rs19
-rw-r--r--tests/stm32/src/bin/spi_dma.rs18
-rw-r--r--tests/stm32/src/bin/usart.rs2
-rw-r--r--tests/stm32/src/bin/usart_dma.rs9
160 files changed, 6694 insertions, 3495 deletions
diff --git a/ci.sh b/ci.sh
index d86c93520..47bf5d660 100755
--- a/ci.sh
+++ b/ci.sh
@@ -49,6 +49,7 @@ cargo batch \
49 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \ 49 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \
50 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits \ 50 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits \
51 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc \ 51 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc \
52 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f730i8,defmt,exti,time-driver-any,unstable-traits \
52 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \ 53 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \
53 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \ 54 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \
54 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \ 55 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \
@@ -65,6 +66,8 @@ cargo batch \
65 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \ 66 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \
66 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \ 67 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \
67 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ 68 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \
69 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits \
70 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits \
68 --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ 71 --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
69 --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ 72 --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
70 --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \ 73 --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \
@@ -86,6 +89,7 @@ cargo batch \
86 --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ 89 --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \
87 --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \ 90 --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \
88 --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \ 91 --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \
92 --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h5 \
89 --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \ 93 --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \
90 --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \ 94 --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \
91 --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \ 95 --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \
@@ -115,6 +119,7 @@ cargo batch \
115 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/nucleo-stm32g071rb \ 119 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/nucleo-stm32g071rb \
116 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ 120 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \
117 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ 121 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \
122 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/nucleo-stm32h563zi \
118 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ 123 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \
119 --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ 124 --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
120 --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \ 125 --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml
index d9f8a285a..e3e446e63 100644
--- a/docs/modules/ROOT/examples/basic/Cargo.toml
+++ b/docs/modules/ROOT/examples/basic/Cargo.toml
@@ -6,7 +6,7 @@ version = "0.1.0"
6license = "MIT OR Apache-2.0" 6license = "MIT OR Apache-2.0"
7 7
8[dependencies] 8[dependencies]
9embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } 10embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
11embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } 11embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
12 12
diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml
index c9a963d4d..a11a7e0ba 100644
--- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml
+++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
8cortex-m = "0.7" 8cortex-m = "0.7"
9cortex-m-rt = "0.7" 9cortex-m-rt = "0.7"
10embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false } 10embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false }
11embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly"] } 11embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] }
12 12
13defmt = "0.3.0" 13defmt = "0.3.0"
14defmt-rtt = "0.3.0" 14defmt-rtt = "0.3.0"
diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml
index 04409cdc7..39f501570 100644
--- a/embassy-boot/boot/Cargo.toml
+++ b/embassy-boot/boot/Cargo.toml
@@ -24,6 +24,7 @@ features = ["defmt"]
24 24
25[dependencies] 25[dependencies]
26defmt = { version = "0.3", optional = true } 26defmt = { version = "0.3", optional = true }
27digest = "0.10"
27log = { version = "0.4", optional = true } 28log = { version = "0.4", optional = true }
28ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } 29ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
29embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } 30embassy-sync = { version = "0.1.0", path = "../../embassy-sync" }
@@ -37,6 +38,7 @@ log = "0.4"
37env_logger = "0.9" 38env_logger = "0.9"
38rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version 39rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version
39futures = { version = "0.3", features = ["executor"] } 40futures = { version = "0.3", features = ["executor"] }
41sha1 = "0.10.5"
40 42
41[dev-dependencies.ed25519-dalek] 43[dev-dependencies.ed25519-dalek]
42default_features = false 44default_features = false
@@ -50,4 +52,4 @@ ed25519-salty = ["dep:salty", "_verify"]
50nightly = ["dep:embedded-storage-async"] 52nightly = ["dep:embedded-storage-async"]
51 53
52#Internal features 54#Internal features
53_verify = [] \ No newline at end of file 55_verify = []
diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs
new file mode 100644
index 000000000..b959de2c4
--- /dev/null
+++ b/embassy-boot/boot/src/boot_loader.rs
@@ -0,0 +1,533 @@
1use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
2
3use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC};
4
5/// Errors returned by bootloader
6#[derive(PartialEq, Eq, Debug)]
7pub enum BootError {
8 /// Error from flash.
9 Flash(NorFlashErrorKind),
10 /// Invalid bootloader magic
11 BadMagic,
12}
13
14#[cfg(feature = "defmt")]
15impl defmt::Format for BootError {
16 fn format(&self, fmt: defmt::Formatter) {
17 match self {
18 BootError::Flash(_) => defmt::write!(fmt, "BootError::Flash(_)"),
19 BootError::BadMagic => defmt::write!(fmt, "BootError::BadMagic"),
20 }
21 }
22}
23
24impl<E> From<E> for BootError
25where
26 E: NorFlashError,
27{
28 fn from(error: E) -> Self {
29 BootError::Flash(error.kind())
30 }
31}
32
33/// Trait defining the flash handles used for active and DFU partition.
34pub trait FlashConfig {
35 /// The erase value of the state flash. Typically the default of 0xFF is used, but some flashes use a different value.
36 const STATE_ERASE_VALUE: u8 = 0xFF;
37 /// Flash type used for the state partition.
38 type STATE: NorFlash;
39 /// Flash type used for the active partition.
40 type ACTIVE: NorFlash;
41 /// Flash type used for the dfu partition.
42 type DFU: NorFlash;
43
44 /// Return flash instance used to write/read to/from active partition.
45 fn active(&mut self) -> &mut Self::ACTIVE;
46 /// Return flash instance used to write/read to/from dfu partition.
47 fn dfu(&mut self) -> &mut Self::DFU;
48 /// Return flash instance used to write/read to/from bootloader state.
49 fn state(&mut self) -> &mut Self::STATE;
50}
51
52trait FlashConfigEx {
53 fn page_size() -> u32;
54}
55
56impl<T: FlashConfig> FlashConfigEx for T {
57 /// Get the page size which is the "unit of operation" within the bootloader.
58 fn page_size() -> u32 {
59 core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) as u32
60 }
61}
62
63/// BootLoader works with any flash implementing embedded_storage.
64pub struct BootLoader {
65 // Page with current state of bootloader. The state partition has the following format:
66 // All ranges are in multiples of WRITE_SIZE bytes.
67 // | Range | Description |
68 // | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
69 // | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. |
70 // | 2..2 + N | Progress index used while swapping or reverting |
71 state: Partition,
72 // Location of the partition which will be booted from
73 active: Partition,
74 // Location of the partition which will be swapped in when requested
75 dfu: Partition,
76}
77
78impl BootLoader {
79 /// Create a new instance of a bootloader with the given partitions.
80 ///
81 /// - All partitions must be aligned with the PAGE_SIZE const generic parameter.
82 /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition.
83 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
84 Self { active, dfu, state }
85 }
86
87 /// Return the offset of the active partition into the active flash.
88 pub fn boot_address(&self) -> usize {
89 self.active.from as usize
90 }
91
92 /// Perform necessary boot preparations like swapping images.
93 ///
94 /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap
95 /// algorithm to work correctly.
96 ///
97 /// The provided aligned_buf argument must satisfy any alignment requirements
98 /// given by the partition flashes. All flash operations will use this buffer.
99 ///
100 /// SWAPPING
101 ///
102 /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition.
103 /// The swap index contains the copy progress, as to allow continuation of the copy process on
104 /// power failure. The index counter is represented within 1 or more pages (depending on total
105 /// flash size), where a page X is considered swapped if index at location (X + WRITE_SIZE)
106 /// contains a zero value. This ensures that index updates can be performed atomically and
107 /// avoid a situation where the wrong index value is set (page write size is "atomic").
108 ///
109 /// +-----------+------------+--------+--------+--------+--------+
110 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
111 /// +-----------+------------+--------+--------+--------+--------+
112 /// | Active | 0 | 1 | 2 | 3 | - |
113 /// | DFU | 0 | 3 | 2 | 1 | X |
114 /// +-----------+------------+--------+--------+--------+--------+
115 ///
116 /// The algorithm starts by copying 'backwards', and after the first step, the layout is
117 /// as follows:
118 ///
119 /// +-----------+------------+--------+--------+--------+--------+
120 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
121 /// +-----------+------------+--------+--------+--------+--------+
122 /// | Active | 1 | 1 | 2 | 1 | - |
123 /// | DFU | 1 | 3 | 2 | 1 | 3 |
124 /// +-----------+------------+--------+--------+--------+--------+
125 ///
126 /// The next iteration performs the same steps
127 ///
128 /// +-----------+------------+--------+--------+--------+--------+
129 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
130 /// +-----------+------------+--------+--------+--------+--------+
131 /// | Active | 2 | 1 | 2 | 1 | - |
132 /// | DFU | 2 | 3 | 2 | 2 | 3 |
133 /// +-----------+------------+--------+--------+--------+--------+
134 ///
135 /// And again until we're done
136 ///
137 /// +-----------+------------+--------+--------+--------+--------+
138 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
139 /// +-----------+------------+--------+--------+--------+--------+
140 /// | Active | 3 | 3 | 2 | 1 | - |
141 /// | DFU | 3 | 3 | 1 | 2 | 3 |
142 /// +-----------+------------+--------+--------+--------+--------+
143 ///
144 /// REVERTING
145 ///
146 /// The reverting algorithm uses the swap index to discover that images were swapped, but that
147 /// the application failed to mark the boot successful. In this case, the revert algorithm will
148 /// run.
149 ///
150 /// The revert index is located separately from the swap index, to ensure that revert can continue
151 /// on power failure.
152 ///
153 /// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start.
154 ///
155 /// +-----------+--------------+--------+--------+--------+--------+
156 /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
157 //*/
158 /// +-----------+--------------+--------+--------+--------+--------+
159 /// | Active | 3 | 1 | 2 | 1 | - |
160 /// | DFU | 3 | 3 | 1 | 2 | 3 |
161 /// +-----------+--------------+--------+--------+--------+--------+
162 ///
163 ///
164 /// +-----------+--------------+--------+--------+--------+--------+
165 /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
166 /// +-----------+--------------+--------+--------+--------+--------+
167 /// | Active | 3 | 1 | 2 | 1 | - |
168 /// | DFU | 3 | 3 | 2 | 2 | 3 |
169 /// +-----------+--------------+--------+--------+--------+--------+
170 ///
171 /// +-----------+--------------+--------+--------+--------+--------+
172 /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
173 /// +-----------+--------------+--------+--------+--------+--------+
174 /// | Active | 3 | 1 | 2 | 3 | - |
175 /// | DFU | 3 | 3 | 2 | 1 | 3 |
176 /// +-----------+--------------+--------+--------+--------+--------+
177 ///
178 pub fn prepare_boot<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<State, BootError> {
179 // Ensure we have enough progress pages to store copy progress
180 assert_eq!(0, P::page_size() % aligned_buf.len() as u32);
181 assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE as u32);
182 assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE as u32);
183 assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE as u32);
184 assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE as u32);
185 assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE);
186 assert_eq!(0, aligned_buf.len() % P::ACTIVE::WRITE_SIZE);
187 assert_eq!(0, aligned_buf.len() % P::DFU::WRITE_SIZE);
188 assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE);
189
190 // Copy contents from partition N to active
191 let state = self.read_state(p, aligned_buf)?;
192 if state == State::Swap {
193 //
194 // Check if we already swapped. If we're in the swap state, this means we should revert
195 // since the app has failed to mark boot as successful
196 //
197 if !self.is_swapped(p, aligned_buf)? {
198 trace!("Swapping");
199 self.swap(p, aligned_buf)?;
200 trace!("Swapping done");
201 } else {
202 trace!("Reverting");
203 self.revert(p, aligned_buf)?;
204
205 let state_flash = p.state();
206 let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE];
207
208 // Invalidate progress
209 state_word.fill(!P::STATE_ERASE_VALUE);
210 self.state
211 .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?;
212
213 // Clear magic and progress
214 self.state.wipe_blocking(state_flash)?;
215
216 // Set magic
217 state_word.fill(BOOT_MAGIC);
218 self.state.write_blocking(state_flash, 0, state_word)?;
219 }
220 }
221 Ok(state)
222 }
223
224 fn is_swapped<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<bool, BootError> {
225 let page_count = (self.active.size() / P::page_size()) as usize;
226 let progress = self.current_progress(p, aligned_buf)?;
227
228 Ok(progress >= page_count * 2)
229 }
230
231 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result<usize, BootError> {
232 let write_size = P::STATE::WRITE_SIZE as u32;
233 let max_index = (((self.state.size() - write_size) / write_size) - 2) as usize;
234 let state_flash = config.state();
235 let state_word = &mut aligned_buf[..write_size as usize];
236
237 self.state.read_blocking(state_flash, write_size, state_word)?;
238 if state_word.iter().any(|&b| b != P::STATE_ERASE_VALUE) {
239 // Progress is invalid
240 return Ok(max_index);
241 }
242
243 for index in 0..max_index {
244 self.state
245 .read_blocking(state_flash, (2 + index) as u32 * write_size, state_word)?;
246
247 if state_word.iter().any(|&b| b == P::STATE_ERASE_VALUE) {
248 return Ok(index);
249 }
250 }
251 Ok(max_index)
252 }
253
254 fn update_progress<P: FlashConfig>(
255 &mut self,
256 progress_index: usize,
257 p: &mut P,
258 aligned_buf: &mut [u8],
259 ) -> Result<(), BootError> {
260 let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE];
261 state_word.fill(!P::STATE_ERASE_VALUE);
262 self.state.write_blocking(
263 p.state(),
264 (2 + progress_index) as u32 * P::STATE::WRITE_SIZE as u32,
265 state_word,
266 )?;
267 Ok(())
268 }
269
270 fn copy_page_once_to_active<P: FlashConfig>(
271 &mut self,
272 progress_index: usize,
273 from_offset: u32,
274 to_offset: u32,
275 p: &mut P,
276 aligned_buf: &mut [u8],
277 ) -> Result<(), BootError> {
278 if self.current_progress(p, aligned_buf)? <= progress_index {
279 let page_size = P::page_size() as u32;
280
281 self.active
282 .erase_blocking(p.active(), to_offset, to_offset + page_size)?;
283
284 for offset_in_page in (0..page_size).step_by(aligned_buf.len()) {
285 self.dfu
286 .read_blocking(p.dfu(), from_offset + offset_in_page as u32, aligned_buf)?;
287 self.active
288 .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?;
289 }
290
291 self.update_progress(progress_index, p, aligned_buf)?;
292 }
293 Ok(())
294 }
295
296 fn copy_page_once_to_dfu<P: FlashConfig>(
297 &mut self,
298 progress_index: usize,
299 from_offset: u32,
300 to_offset: u32,
301 p: &mut P,
302 aligned_buf: &mut [u8],
303 ) -> Result<(), BootError> {
304 if self.current_progress(p, aligned_buf)? <= progress_index {
305 let page_size = P::page_size() as u32;
306
307 self.dfu
308 .erase_blocking(p.dfu(), to_offset as u32, to_offset + page_size)?;
309
310 for offset_in_page in (0..page_size).step_by(aligned_buf.len()) {
311 self.active
312 .read_blocking(p.active(), from_offset + offset_in_page as u32, aligned_buf)?;
313 self.dfu
314 .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?;
315 }
316
317 self.update_progress(progress_index, p, aligned_buf)?;
318 }
319 Ok(())
320 }
321
322 fn swap<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> {
323 let page_size = P::page_size();
324 let page_count = self.active.size() / page_size;
325 for page_num in 0..page_count {
326 let progress_index = (page_num * 2) as usize;
327
328 // Copy active page to the 'next' DFU page.
329 let active_from_offset = (page_count - 1 - page_num) * page_size;
330 let dfu_to_offset = (page_count - page_num) * page_size;
331 //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset);
332 self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?;
333
334 // Copy DFU page to the active page
335 let active_to_offset = (page_count - 1 - page_num) * page_size;
336 let dfu_from_offset = (page_count - 1 - page_num) * page_size;
337 //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset);
338 self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?;
339 }
340
341 Ok(())
342 }
343
344 fn revert<P: FlashConfig>(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> {
345 let page_size = P::page_size();
346 let page_count = self.active.size() / page_size;
347 for page_num in 0..page_count {
348 let progress_index = (page_count * 2 + page_num * 2) as usize;
349
350 // Copy the bad active page to the DFU page
351 let active_from_offset = page_num * page_size;
352 let dfu_to_offset = page_num * page_size;
353 self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?;
354
355 // Copy the DFU page back to the active page
356 let active_to_offset = page_num * page_size;
357 let dfu_from_offset = (page_num + 1) * page_size;
358 self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?;
359 }
360
361 Ok(())
362 }
363
364 fn read_state<P: FlashConfig>(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result<State, BootError> {
365 let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE];
366 self.state.read_blocking(config.state(), 0, state_word)?;
367
368 if !state_word.iter().any(|&b| b != SWAP_MAGIC) {
369 Ok(State::Swap)
370 } else {
371 Ok(State::Boot)
372 }
373 }
374}
375
376fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: u32, state_write_size: usize) {
377 assert_eq!(active.size() % page_size, 0);
378 assert_eq!(dfu.size() % page_size, 0);
379 assert!(dfu.size() - active.size() >= page_size);
380 assert!(2 + 2 * (active.size() / page_size) <= state.size() / state_write_size as u32);
381}
382
383/// A flash wrapper implementing the Flash and embedded_storage traits.
384pub struct BootFlash<F>
385where
386 F: NorFlash,
387{
388 flash: F,
389}
390
391impl<F> BootFlash<F>
392where
393 F: NorFlash,
394{
395 /// Create a new instance of a bootable flash
396 pub fn new(flash: F) -> Self {
397 Self { flash }
398 }
399}
400
401impl<F> ErrorType for BootFlash<F>
402where
403 F: NorFlash,
404{
405 type Error = F::Error;
406}
407
408impl<F> NorFlash for BootFlash<F>
409where
410 F: NorFlash,
411{
412 const WRITE_SIZE: usize = F::WRITE_SIZE;
413 const ERASE_SIZE: usize = F::ERASE_SIZE;
414
415 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
416 F::erase(&mut self.flash, from, to)
417 }
418
419 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
420 F::write(&mut self.flash, offset, bytes)
421 }
422}
423
424impl<F> ReadNorFlash for BootFlash<F>
425where
426 F: NorFlash,
427{
428 const READ_SIZE: usize = F::READ_SIZE;
429
430 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
431 F::read(&mut self.flash, offset, bytes)
432 }
433
434 fn capacity(&self) -> usize {
435 F::capacity(&self.flash)
436 }
437}
438
439/// Convenience provider that uses a single flash for all partitions.
440pub struct SingleFlashConfig<'a, F>
441where
442 F: NorFlash,
443{
444 flash: &'a mut F,
445}
446
447impl<'a, F> SingleFlashConfig<'a, F>
448where
449 F: NorFlash,
450{
451 /// Create a provider for a single flash.
452 pub fn new(flash: &'a mut F) -> Self {
453 Self { flash }
454 }
455}
456
457impl<'a, F> FlashConfig for SingleFlashConfig<'a, F>
458where
459 F: NorFlash,
460{
461 type STATE = F;
462 type ACTIVE = F;
463 type DFU = F;
464
465 fn active(&mut self) -> &mut Self::STATE {
466 self.flash
467 }
468 fn dfu(&mut self) -> &mut Self::ACTIVE {
469 self.flash
470 }
471 fn state(&mut self) -> &mut Self::DFU {
472 self.flash
473 }
474}
475
476/// Convenience flash provider that uses separate flash instances for each partition.
477pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU>
478where
479 ACTIVE: NorFlash,
480 STATE: NorFlash,
481 DFU: NorFlash,
482{
483 active: &'a mut ACTIVE,
484 state: &'a mut STATE,
485 dfu: &'a mut DFU,
486}
487
488impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU>
489where
490 ACTIVE: NorFlash,
491 STATE: NorFlash,
492 DFU: NorFlash,
493{
494 /// Create a new flash provider with separate configuration for all three partitions.
495 pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self {
496 Self { active, state, dfu }
497 }
498}
499
500impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU>
501where
502 ACTIVE: NorFlash,
503 STATE: NorFlash,
504 DFU: NorFlash,
505{
506 type STATE = STATE;
507 type ACTIVE = ACTIVE;
508 type DFU = DFU;
509
510 fn active(&mut self) -> &mut Self::ACTIVE {
511 self.active
512 }
513 fn dfu(&mut self) -> &mut Self::DFU {
514 self.dfu
515 }
516 fn state(&mut self) -> &mut Self::STATE {
517 self.state
518 }
519}
520
521#[cfg(test)]
522mod tests {
523 use super::*;
524
525 #[test]
526 #[should_panic]
527 fn test_range_asserts() {
528 const ACTIVE: Partition = Partition::new(4096, 4194304);
529 const DFU: Partition = Partition::new(4194304, 2 * 4194304);
530 const STATE: Partition = Partition::new(0, 4096);
531 assert_partitions(ACTIVE, DFU, STATE, 4096, 4);
532 }
533}
diff --git a/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs b/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs
new file mode 100644
index 000000000..a184d1c51
--- /dev/null
+++ b/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs
@@ -0,0 +1,30 @@
1use digest::typenum::U64;
2use digest::{FixedOutput, HashMarker, OutputSizeUser, Update};
3use ed25519_dalek::Digest as _;
4
5pub struct Sha512(ed25519_dalek::Sha512);
6
7impl Default for Sha512 {
8 fn default() -> Self {
9 Self(ed25519_dalek::Sha512::new())
10 }
11}
12
13impl Update for Sha512 {
14 fn update(&mut self, data: &[u8]) {
15 self.0.update(data)
16 }
17}
18
19impl FixedOutput for Sha512 {
20 fn finalize_into(self, out: &mut digest::Output<Self>) {
21 let result = self.0.finalize();
22 out.as_mut_slice().copy_from_slice(result.as_slice())
23 }
24}
25
26impl OutputSizeUser for Sha512 {
27 type OutputSize = U64;
28}
29
30impl HashMarker for Sha512 {}
diff --git a/embassy-boot/boot/src/digest_adapters/mod.rs b/embassy-boot/boot/src/digest_adapters/mod.rs
new file mode 100644
index 000000000..9b4b4b60c
--- /dev/null
+++ b/embassy-boot/boot/src/digest_adapters/mod.rs
@@ -0,0 +1,5 @@
1#[cfg(feature = "ed25519-dalek")]
2pub(crate) mod ed25519_dalek;
3
4#[cfg(feature = "ed25519-salty")]
5pub(crate) mod salty;
diff --git a/embassy-boot/boot/src/digest_adapters/salty.rs b/embassy-boot/boot/src/digest_adapters/salty.rs
new file mode 100644
index 000000000..2b5dcf3af
--- /dev/null
+++ b/embassy-boot/boot/src/digest_adapters/salty.rs
@@ -0,0 +1,29 @@
1use digest::typenum::U64;
2use digest::{FixedOutput, HashMarker, OutputSizeUser, Update};
3
4pub struct Sha512(salty::Sha512);
5
6impl Default for Sha512 {
7 fn default() -> Self {
8 Self(salty::Sha512::new())
9 }
10}
11
12impl Update for Sha512 {
13 fn update(&mut self, data: &[u8]) {
14 self.0.update(data)
15 }
16}
17
18impl FixedOutput for Sha512 {
19 fn finalize_into(self, out: &mut digest::Output<Self>) {
20 let result = self.0.finalize();
21 out.as_mut_slice().copy_from_slice(result.as_slice())
22 }
23}
24
25impl OutputSizeUser for Sha512 {
26 type OutputSize = U64;
27}
28
29impl HashMarker for Sha512 {}
diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs
new file mode 100644
index 000000000..a2f822f4a
--- /dev/null
+++ b/embassy-boot/boot/src/firmware_updater.rs
@@ -0,0 +1,534 @@
1use digest::Digest;
2use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
3use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
4
5use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC};
6
7/// Errors returned by FirmwareUpdater
8#[derive(Debug)]
9pub enum FirmwareUpdaterError {
10 /// Error from flash.
11 Flash(NorFlashErrorKind),
12 /// Signature errors.
13 Signature(signature::Error),
14}
15
16#[cfg(feature = "defmt")]
17impl defmt::Format for FirmwareUpdaterError {
18 fn format(&self, fmt: defmt::Formatter) {
19 match self {
20 FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"),
21 FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"),
22 }
23 }
24}
25
26impl<E> From<E> for FirmwareUpdaterError
27where
28 E: NorFlashError,
29{
30 fn from(error: E) -> Self {
31 FirmwareUpdaterError::Flash(error.kind())
32 }
33}
34
35/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
36/// 'mess up' the internal bootloader state
37pub struct FirmwareUpdater {
38 state: Partition,
39 dfu: Partition,
40}
41
42impl Default for FirmwareUpdater {
43 fn default() -> Self {
44 extern "C" {
45 static __bootloader_state_start: u32;
46 static __bootloader_state_end: u32;
47 static __bootloader_dfu_start: u32;
48 static __bootloader_dfu_end: u32;
49 }
50
51 let dfu = unsafe {
52 Partition::new(
53 &__bootloader_dfu_start as *const u32 as u32,
54 &__bootloader_dfu_end as *const u32 as u32,
55 )
56 };
57 let state = unsafe {
58 Partition::new(
59 &__bootloader_state_start as *const u32 as u32,
60 &__bootloader_state_end as *const u32 as u32,
61 )
62 };
63
64 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
65 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
66 FirmwareUpdater::new(dfu, state)
67 }
68}
69
70impl FirmwareUpdater {
71 /// Create a firmware updater instance with partition ranges for the update and state partitions.
72 pub const fn new(dfu: Partition, state: Partition) -> Self {
73 Self { dfu, state }
74 }
75
76 /// Obtain the current state.
77 ///
78 /// This is useful to check if the bootloader has just done a swap, in order
79 /// to do verifications and self-tests of the new image before calling
80 /// `mark_booted`.
81 pub async fn get_state<F: AsyncNorFlash>(
82 &mut self,
83 state_flash: &mut F,
84 aligned: &mut [u8],
85 ) -> Result<State, FirmwareUpdaterError> {
86 self.state.read(state_flash, 0, aligned).await?;
87
88 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
89 Ok(State::Swap)
90 } else {
91 Ok(State::Boot)
92 }
93 }
94
95 /// Verify the DFU given a public key. If there is an error then DO NOT
96 /// proceed with updating the firmware as it must be signed with a
97 /// corresponding private key (otherwise it could be malicious firmware).
98 ///
99 /// Mark to trigger firmware swap on next boot if verify suceeds.
100 ///
101 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
102 /// been generated from a SHA-512 digest of the firmware bytes.
103 ///
104 /// If no signature feature is set then this method will always return a
105 /// signature error.
106 ///
107 /// # Safety
108 ///
109 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from
110 /// and written to.
111 #[cfg(feature = "_verify")]
112 pub async fn verify_and_mark_updated<F: AsyncNorFlash>(
113 &mut self,
114 _state_and_dfu_flash: &mut F,
115 _public_key: &[u8],
116 _signature: &[u8],
117 _update_len: u32,
118 _aligned: &mut [u8],
119 ) -> Result<(), FirmwareUpdaterError> {
120 assert_eq!(_aligned.len(), F::WRITE_SIZE);
121 assert!(_update_len <= self.dfu.size());
122
123 #[cfg(feature = "ed25519-dalek")]
124 {
125 use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
126
127 use crate::digest_adapters::ed25519_dalek::Sha512;
128
129 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
130
131 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
132 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
133
134 let mut message = [0; 64];
135 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)
136 .await?;
137
138 public_key.verify(&message, &signature).map_err(into_signature_error)?
139 }
140 #[cfg(feature = "ed25519-salty")]
141 {
142 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
143 use salty::{PublicKey, Signature};
144
145 use crate::digest_adapters::salty::Sha512;
146
147 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
148 FirmwareUpdaterError::Signature(signature::Error::default())
149 }
150
151 let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
152 let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
153 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
154 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
155
156 let mut message = [0; 64];
157 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)
158 .await?;
159
160 let r = public_key.verify(&message, &signature);
161 trace!(
162 "Verifying with public key {}, signature {} and message {} yields ok: {}",
163 public_key.to_bytes(),
164 signature.to_bytes(),
165 message,
166 r.is_ok()
167 );
168 r.map_err(into_signature_error)?
169 }
170
171 self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await
172 }
173
174 /// Verify the update in DFU with any digest.
175 pub async fn hash<F: AsyncNorFlash, D: Digest>(
176 &mut self,
177 dfu_flash: &mut F,
178 update_len: u32,
179 chunk_buf: &mut [u8],
180 output: &mut [u8],
181 ) -> Result<(), FirmwareUpdaterError> {
182 let mut digest = D::new();
183 for offset in (0..update_len).step_by(chunk_buf.len()) {
184 self.dfu.read(dfu_flash, offset, chunk_buf).await?;
185 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
186 digest.update(&chunk_buf[..len]);
187 }
188 output.copy_from_slice(digest.finalize().as_slice());
189 Ok(())
190 }
191
192 /// Mark to trigger firmware swap on next boot.
193 ///
194 /// # Safety
195 ///
196 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
197 #[cfg(not(feature = "_verify"))]
198 pub async fn mark_updated<F: AsyncNorFlash>(
199 &mut self,
200 state_flash: &mut F,
201 aligned: &mut [u8],
202 ) -> Result<(), FirmwareUpdaterError> {
203 assert_eq!(aligned.len(), F::WRITE_SIZE);
204 self.set_magic(aligned, SWAP_MAGIC, state_flash).await
205 }
206
207 /// Mark firmware boot successful and stop rollback on reset.
208 ///
209 /// # Safety
210 ///
211 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
212 pub async fn mark_booted<F: AsyncNorFlash>(
213 &mut self,
214 state_flash: &mut F,
215 aligned: &mut [u8],
216 ) -> Result<(), FirmwareUpdaterError> {
217 assert_eq!(aligned.len(), F::WRITE_SIZE);
218 self.set_magic(aligned, BOOT_MAGIC, state_flash).await
219 }
220
221 async fn set_magic<F: AsyncNorFlash>(
222 &mut self,
223 aligned: &mut [u8],
224 magic: u8,
225 state_flash: &mut F,
226 ) -> Result<(), FirmwareUpdaterError> {
227 self.state.read(state_flash, 0, aligned).await?;
228
229 if aligned.iter().any(|&b| b != magic) {
230 // Read progress validity
231 self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?;
232
233 // FIXME: Do not make this assumption.
234 const STATE_ERASE_VALUE: u8 = 0xFF;
235
236 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
237 // The current progress validity marker is invalid
238 } else {
239 // Invalidate progress
240 aligned.fill(!STATE_ERASE_VALUE);
241 self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?;
242 }
243
244 // Clear magic and progress
245 self.state.wipe(state_flash).await?;
246
247 // Set magic
248 aligned.fill(magic);
249 self.state.write(state_flash, 0, aligned).await?;
250 }
251 Ok(())
252 }
253
254 /// Write data to a flash page.
255 ///
256 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
257 ///
258 /// # Safety
259 ///
260 /// Failing to meet alignment and size requirements may result in a panic.
261 pub async fn write_firmware<F: AsyncNorFlash>(
262 &mut self,
263 offset: usize,
264 data: &[u8],
265 dfu_flash: &mut F,
266 ) -> Result<(), FirmwareUpdaterError> {
267 assert!(data.len() >= F::ERASE_SIZE);
268
269 self.dfu
270 .erase(dfu_flash, offset as u32, (offset + data.len()) as u32)
271 .await?;
272
273 self.dfu.write(dfu_flash, offset as u32, data).await?;
274
275 Ok(())
276 }
277
278 /// Prepare for an incoming DFU update by erasing the entire DFU area and
279 /// returning its `Partition`.
280 ///
281 /// Using this instead of `write_firmware` allows for an optimized API in
282 /// exchange for added complexity.
283 pub async fn prepare_update<F: AsyncNorFlash>(
284 &mut self,
285 dfu_flash: &mut F,
286 ) -> Result<Partition, FirmwareUpdaterError> {
287 self.dfu.wipe(dfu_flash).await?;
288
289 Ok(self.dfu)
290 }
291
292 //
293 // Blocking API
294 //
295
296 /// Obtain the current state.
297 ///
298 /// This is useful to check if the bootloader has just done a swap, in order
299 /// to do verifications and self-tests of the new image before calling
300 /// `mark_booted`.
301 pub fn get_state_blocking<F: NorFlash>(
302 &mut self,
303 state_flash: &mut F,
304 aligned: &mut [u8],
305 ) -> Result<State, FirmwareUpdaterError> {
306 self.state.read_blocking(state_flash, 0, aligned)?;
307
308 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
309 Ok(State::Swap)
310 } else {
311 Ok(State::Boot)
312 }
313 }
314
315 /// Verify the DFU given a public key. If there is an error then DO NOT
316 /// proceed with updating the firmware as it must be signed with a
317 /// corresponding private key (otherwise it could be malicious firmware).
318 ///
319 /// Mark to trigger firmware swap on next boot if verify suceeds.
320 ///
321 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
322 /// been generated from a SHA-512 digest of the firmware bytes.
323 ///
324 /// If no signature feature is set then this method will always return a
325 /// signature error.
326 ///
327 /// # Safety
328 ///
329 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from
330 /// and written to.
331 #[cfg(feature = "_verify")]
332 pub fn verify_and_mark_updated_blocking<F: NorFlash>(
333 &mut self,
334 _state_and_dfu_flash: &mut F,
335 _public_key: &[u8],
336 _signature: &[u8],
337 _update_len: u32,
338 _aligned: &mut [u8],
339 ) -> Result<(), FirmwareUpdaterError> {
340 assert_eq!(_aligned.len(), F::WRITE_SIZE);
341 assert!(_update_len <= self.dfu.size());
342
343 #[cfg(feature = "ed25519-dalek")]
344 {
345 use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
346
347 use crate::digest_adapters::ed25519_dalek::Sha512;
348
349 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
350
351 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
352 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
353
354 let mut message = [0; 64];
355 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?;
356
357 public_key.verify(&message, &signature).map_err(into_signature_error)?
358 }
359 #[cfg(feature = "ed25519-salty")]
360 {
361 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
362 use salty::{PublicKey, Signature};
363
364 use crate::digest_adapters::salty::Sha512;
365
366 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
367 FirmwareUpdaterError::Signature(signature::Error::default())
368 }
369
370 let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
371 let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
372 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
373 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
374
375 let mut message = [0; 64];
376 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?;
377
378 let r = public_key.verify(&message, &signature);
379 trace!(
380 "Verifying with public key {}, signature {} and message {} yields ok: {}",
381 public_key.to_bytes(),
382 signature.to_bytes(),
383 message,
384 r.is_ok()
385 );
386 r.map_err(into_signature_error)?
387 }
388
389 self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash)
390 }
391
392 /// Verify the update in DFU with any digest.
393 pub fn hash_blocking<F: NorFlash, D: Digest>(
394 &mut self,
395 dfu_flash: &mut F,
396 update_len: u32,
397 chunk_buf: &mut [u8],
398 output: &mut [u8],
399 ) -> Result<(), FirmwareUpdaterError> {
400 let mut digest = D::new();
401 for offset in (0..update_len).step_by(chunk_buf.len()) {
402 self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?;
403 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
404 digest.update(&chunk_buf[..len]);
405 }
406 output.copy_from_slice(digest.finalize().as_slice());
407 Ok(())
408 }
409
410 /// Mark to trigger firmware swap on next boot.
411 ///
412 /// # Safety
413 ///
414 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
415 #[cfg(not(feature = "_verify"))]
416 pub fn mark_updated_blocking<F: NorFlash>(
417 &mut self,
418 state_flash: &mut F,
419 aligned: &mut [u8],
420 ) -> Result<(), FirmwareUpdaterError> {
421 assert_eq!(aligned.len(), F::WRITE_SIZE);
422 self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash)
423 }
424
425 /// Mark firmware boot successful and stop rollback on reset.
426 ///
427 /// # Safety
428 ///
429 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
430 pub fn mark_booted_blocking<F: NorFlash>(
431 &mut self,
432 state_flash: &mut F,
433 aligned: &mut [u8],
434 ) -> Result<(), FirmwareUpdaterError> {
435 assert_eq!(aligned.len(), F::WRITE_SIZE);
436 self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash)
437 }
438
439 fn set_magic_blocking<F: NorFlash>(
440 &mut self,
441 aligned: &mut [u8],
442 magic: u8,
443 state_flash: &mut F,
444 ) -> Result<(), FirmwareUpdaterError> {
445 self.state.read_blocking(state_flash, 0, aligned)?;
446
447 if aligned.iter().any(|&b| b != magic) {
448 // Read progress validity
449 self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
450
451 // FIXME: Do not make this assumption.
452 const STATE_ERASE_VALUE: u8 = 0xFF;
453
454 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
455 // The current progress validity marker is invalid
456 } else {
457 // Invalidate progress
458 aligned.fill(!STATE_ERASE_VALUE);
459 self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
460 }
461
462 // Clear magic and progress
463 self.state.wipe_blocking(state_flash)?;
464
465 // Set magic
466 aligned.fill(magic);
467 self.state.write_blocking(state_flash, 0, aligned)?;
468 }
469 Ok(())
470 }
471
472 /// Write data to a flash page.
473 ///
474 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
475 ///
476 /// # Safety
477 ///
478 /// Failing to meet alignment and size requirements may result in a panic.
479 pub fn write_firmware_blocking<F: NorFlash>(
480 &mut self,
481 offset: usize,
482 data: &[u8],
483 dfu_flash: &mut F,
484 ) -> Result<(), FirmwareUpdaterError> {
485 assert!(data.len() >= F::ERASE_SIZE);
486
487 self.dfu
488 .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?;
489
490 self.dfu.write_blocking(dfu_flash, offset as u32, data)?;
491
492 Ok(())
493 }
494
495 /// Prepare for an incoming DFU update by erasing the entire DFU area and
496 /// returning its `Partition`.
497 ///
498 /// Using this instead of `write_firmware_blocking` allows for an optimized
499 /// API in exchange for added complexity.
500 pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<Partition, FirmwareUpdaterError> {
501 self.dfu.wipe_blocking(flash)?;
502
503 Ok(self.dfu)
504 }
505}
506
507#[cfg(test)]
508mod tests {
509 use futures::executor::block_on;
510 use sha1::{Digest, Sha1};
511
512 use super::*;
513 use crate::mem_flash::MemFlash;
514
515 #[test]
516 fn can_verify_sha1() {
517 const STATE: Partition = Partition::new(0, 4096);
518 const DFU: Partition = Partition::new(65536, 131072);
519
520 let mut flash = MemFlash::<131072, 4096, 8>::default();
521
522 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
523 let mut to_write = [0; 4096];
524 to_write[..7].copy_from_slice(update.as_slice());
525
526 let mut updater = FirmwareUpdater::new(DFU, STATE);
527 block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap();
528 let mut chunk_buf = [0; 2];
529 let mut hash = [0; 20];
530 block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
531
532 assert_eq!(Sha1::digest(update).as_slice(), hash);
533 }
534}
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 7ce0c664a..e268d8883 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -5,36 +5,18 @@
5#![doc = include_str!("../README.md")] 5#![doc = include_str!("../README.md")]
6mod fmt; 6mod fmt;
7 7
8use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; 8mod boot_loader;
9mod digest_adapters;
10mod firmware_updater;
11mod mem_flash;
12mod partition;
9 13
10#[cfg(feature = "nightly")] 14pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig};
11use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; 15pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError};
16pub use partition::Partition;
12 17
13const BOOT_MAGIC: u8 = 0xD0; 18pub(crate) const BOOT_MAGIC: u8 = 0xD0;
14const SWAP_MAGIC: u8 = 0xF0; 19pub(crate) const SWAP_MAGIC: u8 = 0xF0;
15
16/// A region in flash used by the bootloader.
17#[derive(Copy, Clone, Debug)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub struct Partition {
20 /// Start of the flash region.
21 pub from: usize,
22 /// End of the flash region.
23 pub to: usize,
24}
25
26impl Partition {
27 /// Create a new partition with the provided range
28 pub const fn new(from: usize, to: usize) -> Self {
29 Self { from, to }
30 }
31
32 /// Return the length of the partition
33 #[allow(clippy::len_without_is_empty)]
34 pub const fn len(&self) -> usize {
35 self.to - self.from
36 }
37}
38 20
39/// The state of the bootloader after running prepare. 21/// The state of the bootloader after running prepare.
40#[derive(PartialEq, Eq, Debug)] 22#[derive(PartialEq, Eq, Debug)]
@@ -46,34 +28,6 @@ pub enum State {
46 Swap, 28 Swap,
47} 29}
48 30
49/// Errors returned by bootloader
50#[derive(PartialEq, Eq, Debug)]
51pub enum BootError {
52 /// Error from flash.
53 Flash(NorFlashErrorKind),
54 /// Invalid bootloader magic
55 BadMagic,
56}
57
58#[cfg(feature = "defmt")]
59impl defmt::Format for BootError {
60 fn format(&self, fmt: defmt::Formatter) {
61 match self {
62 BootError::Flash(_) => defmt::write!(fmt, "BootError::Flash(_)"),
63 BootError::BadMagic => defmt::write!(fmt, "BootError::BadMagic"),
64 }
65 }
66}
67
68impl<E> From<E> for BootError
69where
70 E: NorFlashError,
71{
72 fn from(error: E) -> Self {
73 BootError::Flash(error.kind())
74 }
75}
76
77/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. 31/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
78#[repr(align(32))] 32#[repr(align(32))]
79pub struct AlignedBuffer<const N: usize>(pub [u8; N]); 33pub struct AlignedBuffer<const N: usize>(pub [u8; N]);
@@ -90,1128 +44,12 @@ impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {
90 } 44 }
91} 45}
92 46
93/// Extension of the embedded-storage flash type information with block size and erase value.
94pub trait Flash: NorFlash + ReadNorFlash {
95 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase
96 /// size of the flash, but for external QSPI flash modules, this can be lower.
97 const BLOCK_SIZE: usize;
98 /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value.
99 const ERASE_VALUE: u8 = 0xFF;
100}
101
102/// Trait defining the flash handles used for active and DFU partition
103pub trait FlashConfig {
104 /// Flash type used for the state partition.
105 type STATE: Flash;
106 /// Flash type used for the active partition.
107 type ACTIVE: Flash;
108 /// Flash type used for the dfu partition.
109 type DFU: Flash;
110
111 /// Return flash instance used to write/read to/from active partition.
112 fn active(&mut self) -> &mut Self::ACTIVE;
113 /// Return flash instance used to write/read to/from dfu partition.
114 fn dfu(&mut self) -> &mut Self::DFU;
115 /// Return flash instance used to write/read to/from bootloader state.
116 fn state(&mut self) -> &mut Self::STATE;
117}
118
119/// BootLoader works with any flash implementing embedded_storage and can also work with
120/// different page sizes and flash write sizes.
121pub struct BootLoader {
122 // Page with current state of bootloader. The state partition has the following format:
123 // | Range | Description |
124 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
125 // | WRITE_SIZE - N | Progress index used while swapping or reverting |
126 state: Partition,
127 // Location of the partition which will be booted from
128 active: Partition,
129 // Location of the partition which will be swapped in when requested
130 dfu: Partition,
131}
132
133impl BootLoader {
134 /// Create a new instance of a bootloader with the given partitions.
135 ///
136 /// - All partitions must be aligned with the PAGE_SIZE const generic parameter.
137 /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition.
138 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
139 Self { active, dfu, state }
140 }
141
142 /// Return the boot address for the active partition.
143 pub fn boot_address(&self) -> usize {
144 self.active.from
145 }
146
147 /// Perform necessary boot preparations like swapping images.
148 ///
149 /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap
150 /// algorithm to work correctly.
151 ///
152 /// SWAPPING
153 ///
154 /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition.
155 /// The swap index contains the copy progress, as to allow continuation of the copy process on
156 /// power failure. The index counter is represented within 1 or more pages (depending on total
157 /// flash size), where a page X is considered swapped if index at location (X + WRITE_SIZE)
158 /// contains a zero value. This ensures that index updates can be performed atomically and
159 /// avoid a situation where the wrong index value is set (page write size is "atomic").
160 ///
161 /// +-----------+------------+--------+--------+--------+--------+
162 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
163 /// +-----------+------------+--------+--------+--------+--------+
164 /// | Active | 0 | 1 | 2 | 3 | - |
165 /// | DFU | 0 | 3 | 2 | 1 | X |
166 /// +-----------+------------+--------+--------+--------+--------+
167 ///
168 /// The algorithm starts by copying 'backwards', and after the first step, the layout is
169 /// as follows:
170 ///
171 /// +-----------+------------+--------+--------+--------+--------+
172 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
173 /// +-----------+------------+--------+--------+--------+--------+
174 /// | Active | 1 | 1 | 2 | 1 | - |
175 /// | DFU | 1 | 3 | 2 | 1 | 3 |
176 /// +-----------+------------+--------+--------+--------+--------+
177 ///
178 /// The next iteration performs the same steps
179 ///
180 /// +-----------+------------+--------+--------+--------+--------+
181 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
182 /// +-----------+------------+--------+--------+--------+--------+
183 /// | Active | 2 | 1 | 2 | 1 | - |
184 /// | DFU | 2 | 3 | 2 | 2 | 3 |
185 /// +-----------+------------+--------+--------+--------+--------+
186 ///
187 /// And again until we're done
188 ///
189 /// +-----------+------------+--------+--------+--------+--------+
190 /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
191 /// +-----------+------------+--------+--------+--------+--------+
192 /// | Active | 3 | 3 | 2 | 1 | - |
193 /// | DFU | 3 | 3 | 1 | 2 | 3 |
194 /// +-----------+------------+--------+--------+--------+--------+
195 ///
196 /// REVERTING
197 ///
198 /// The reverting algorithm uses the swap index to discover that images were swapped, but that
199 /// the application failed to mark the boot successful. In this case, the revert algorithm will
200 /// run.
201 ///
202 /// The revert index is located separately from the swap index, to ensure that revert can continue
203 /// on power failure.
204 ///
205 /// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start.
206 ///
207 /// +-----------+--------------+--------+--------+--------+--------+
208 /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
209 //*/
210 /// +-----------+--------------+--------+--------+--------+--------+
211 /// | Active | 3 | 1 | 2 | 1 | - |
212 /// | DFU | 3 | 3 | 1 | 2 | 3 |
213 /// +-----------+--------------+--------+--------+--------+--------+
214 ///
215 ///
216 /// +-----------+--------------+--------+--------+--------+--------+
217 /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
218 /// +-----------+--------------+--------+--------+--------+--------+
219 /// | Active | 3 | 1 | 2 | 1 | - |
220 /// | DFU | 3 | 3 | 2 | 2 | 3 |
221 /// +-----------+--------------+--------+--------+--------+--------+
222 ///
223 /// +-----------+--------------+--------+--------+--------+--------+
224 /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
225 /// +-----------+--------------+--------+--------+--------+--------+
226 /// | Active | 3 | 1 | 2 | 3 | - |
227 /// | DFU | 3 | 3 | 2 | 1 | 3 |
228 /// +-----------+--------------+--------+--------+--------+--------+
229 ///
230 pub fn prepare_boot<P: FlashConfig>(
231 &mut self,
232 p: &mut P,
233 magic: &mut [u8],
234 page: &mut [u8],
235 ) -> Result<State, BootError> {
236 // Ensure we have enough progress pages to store copy progress
237 assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE);
238 assert_eq!(magic.len(), P::STATE::WRITE_SIZE);
239
240 // Copy contents from partition N to active
241 let state = self.read_state(p, magic)?;
242 if state == State::Swap {
243 //
244 // Check if we already swapped. If we're in the swap state, this means we should revert
245 // since the app has failed to mark boot as successful
246 //
247 if !self.is_swapped(p, magic, page)? {
248 trace!("Swapping");
249 self.swap(p, magic, page)?;
250 trace!("Swapping done");
251 } else {
252 trace!("Reverting");
253 self.revert(p, magic, page)?;
254
255 // Overwrite magic and reset progress
256 let fstate = p.state();
257 magic.fill(!P::STATE::ERASE_VALUE);
258 fstate.write(self.state.from as u32, magic)?;
259 fstate.erase(self.state.from as u32, self.state.to as u32)?;
260
261 magic.fill(BOOT_MAGIC);
262 fstate.write(self.state.from as u32, magic)?;
263 }
264 }
265 Ok(state)
266 }
267
268 fn is_swapped<P: FlashConfig>(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<bool, BootError> {
269 let page_size = page.len();
270 let page_count = self.active.len() / page_size;
271 let progress = self.current_progress(p, magic)?;
272
273 Ok(progress >= page_count * 2)
274 }
275
276 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> {
277 let write_size = aligned.len();
278 let max_index = ((self.state.len() - write_size) / write_size) - 1;
279 aligned.fill(!P::STATE::ERASE_VALUE);
280
281 let flash = config.state();
282 for i in 0..max_index {
283 flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?;
284
285 if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) {
286 return Ok(i);
287 }
288 }
289 Ok(max_index)
290 }
291
292 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> {
293 let flash = p.state();
294 let write_size = magic.len();
295 let w = self.state.from + write_size + idx * write_size;
296
297 let aligned = magic;
298 aligned.fill(!P::STATE::ERASE_VALUE);
299 flash.write(w as u32, aligned)?;
300 Ok(())
301 }
302
303 fn active_addr(&self, n: usize, page_size: usize) -> usize {
304 self.active.from + n * page_size
305 }
306
307 fn dfu_addr(&self, n: usize, page_size: usize) -> usize {
308 self.dfu.from + n * page_size
309 }
310
311 fn copy_page_once_to_active<P: FlashConfig>(
312 &mut self,
313 idx: usize,
314 from_page: usize,
315 to_page: usize,
316 p: &mut P,
317 magic: &mut [u8],
318 page: &mut [u8],
319 ) -> Result<(), BootError> {
320 let buf = page;
321 if self.current_progress(p, magic)? <= idx {
322 let mut offset = from_page;
323 for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) {
324 p.dfu().read(offset as u32, chunk)?;
325 offset += chunk.len();
326 }
327
328 p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?;
329
330 let mut offset = to_page;
331 for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) {
332 p.active().write(offset as u32, chunk)?;
333 offset += chunk.len();
334 }
335 self.update_progress(idx, p, magic)?;
336 }
337 Ok(())
338 }
339
340 fn copy_page_once_to_dfu<P: FlashConfig>(
341 &mut self,
342 idx: usize,
343 from_page: usize,
344 to_page: usize,
345 p: &mut P,
346 magic: &mut [u8],
347 page: &mut [u8],
348 ) -> Result<(), BootError> {
349 let buf = page;
350 if self.current_progress(p, magic)? <= idx {
351 let mut offset = from_page;
352 for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) {
353 p.active().read(offset as u32, chunk)?;
354 offset += chunk.len();
355 }
356
357 p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?;
358
359 let mut offset = to_page;
360 for chunk in buf.chunks(P::DFU::BLOCK_SIZE) {
361 p.dfu().write(offset as u32, chunk)?;
362 offset += chunk.len();
363 }
364 self.update_progress(idx, p, magic)?;
365 }
366 Ok(())
367 }
368
369 fn swap<P: FlashConfig>(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> {
370 let page_size = page.len();
371 let page_count = self.active.len() / page_size;
372 trace!("Page count: {}", page_count);
373 for page_num in 0..page_count {
374 trace!("COPY PAGE {}", page_num);
375 // Copy active page to the 'next' DFU page.
376 let active_page = self.active_addr(page_count - 1 - page_num, page_size);
377 let dfu_page = self.dfu_addr(page_count - page_num, page_size);
378 //trace!("Copy active {} to dfu {}", active_page, dfu_page);
379 self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?;
380
381 // Copy DFU page to the active page
382 let active_page = self.active_addr(page_count - 1 - page_num, page_size);
383 let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size);
384 //trace!("Copy dfy {} to active {}", dfu_page, active_page);
385 self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?;
386 }
387
388 Ok(())
389 }
390
391 fn revert<P: FlashConfig>(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> {
392 let page_size = page.len();
393 let page_count = self.active.len() / page_size;
394 for page_num in 0..page_count {
395 // Copy the bad active page to the DFU page
396 let active_page = self.active_addr(page_num, page_size);
397 let dfu_page = self.dfu_addr(page_num, page_size);
398 self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?;
399
400 // Copy the DFU page back to the active page
401 let active_page = self.active_addr(page_num, page_size);
402 let dfu_page = self.dfu_addr(page_num + 1, page_size);
403 self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?;
404 }
405
406 Ok(())
407 }
408
409 fn read_state<P: FlashConfig>(&mut self, config: &mut P, magic: &mut [u8]) -> Result<State, BootError> {
410 let flash = config.state();
411 flash.read(self.state.from as u32, magic)?;
412
413 if !magic.iter().any(|&b| b != SWAP_MAGIC) {
414 Ok(State::Swap)
415 } else {
416 Ok(State::Boot)
417 }
418 }
419}
420
421fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) {
422 assert_eq!(active.len() % page_size, 0);
423 assert_eq!(dfu.len() % page_size, 0);
424 assert!(dfu.len() - active.len() >= page_size);
425 assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size);
426}
427
428/// Convenience provider that uses a single flash for all partitions.
429pub struct SingleFlashConfig<'a, F>
430where
431 F: Flash,
432{
433 flash: &'a mut F,
434}
435
436impl<'a, F> SingleFlashConfig<'a, F>
437where
438 F: Flash,
439{
440 /// Create a provider for a single flash.
441 pub fn new(flash: &'a mut F) -> Self {
442 Self { flash }
443 }
444}
445
446impl<'a, F> FlashConfig for SingleFlashConfig<'a, F>
447where
448 F: Flash,
449{
450 type STATE = F;
451 type ACTIVE = F;
452 type DFU = F;
453
454 fn active(&mut self) -> &mut Self::STATE {
455 self.flash
456 }
457 fn dfu(&mut self) -> &mut Self::ACTIVE {
458 self.flash
459 }
460 fn state(&mut self) -> &mut Self::DFU {
461 self.flash
462 }
463}
464
465/// A flash wrapper implementing the Flash and embedded_storage traits.
466pub struct BootFlash<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF>
467where
468 F: NorFlash + ReadNorFlash,
469{
470 flash: F,
471}
472
473impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
474where
475 F: NorFlash + ReadNorFlash,
476{
477 /// Create a new instance of a bootable flash
478 pub fn new(flash: F) -> Self {
479 Self { flash }
480 }
481}
482
483impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
484where
485 F: NorFlash + ReadNorFlash,
486{
487 const BLOCK_SIZE: usize = BLOCK_SIZE;
488 const ERASE_VALUE: u8 = ERASE_VALUE;
489}
490
491impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
492where
493 F: ReadNorFlash + NorFlash,
494{
495 type Error = F::Error;
496}
497
498impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
499where
500 F: ReadNorFlash + NorFlash,
501{
502 const WRITE_SIZE: usize = F::WRITE_SIZE;
503 const ERASE_SIZE: usize = F::ERASE_SIZE;
504
505 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
506 F::erase(&mut self.flash, from, to)
507 }
508
509 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
510 F::write(&mut self.flash, offset, bytes)
511 }
512}
513
514impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
515where
516 F: ReadNorFlash + NorFlash,
517{
518 const READ_SIZE: usize = F::READ_SIZE;
519
520 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
521 F::read(&mut self.flash, offset, bytes)
522 }
523
524 fn capacity(&self) -> usize {
525 F::capacity(&self.flash)
526 }
527}
528
529/// Convenience flash provider that uses separate flash instances for each partition.
530pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU>
531where
532 ACTIVE: Flash,
533 STATE: Flash,
534 DFU: Flash,
535{
536 active: &'a mut ACTIVE,
537 state: &'a mut STATE,
538 dfu: &'a mut DFU,
539}
540
541impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU>
542where
543 ACTIVE: Flash,
544 STATE: Flash,
545 DFU: Flash,
546{
547 /// Create a new flash provider with separate configuration for all three partitions.
548 pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self {
549 Self { active, state, dfu }
550 }
551}
552
553impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU>
554where
555 ACTIVE: Flash,
556 STATE: Flash,
557 DFU: Flash,
558{
559 type STATE = STATE;
560 type ACTIVE = ACTIVE;
561 type DFU = DFU;
562
563 fn active(&mut self) -> &mut Self::ACTIVE {
564 self.active
565 }
566 fn dfu(&mut self) -> &mut Self::DFU {
567 self.dfu
568 }
569 fn state(&mut self) -> &mut Self::STATE {
570 self.state
571 }
572}
573/// Errors returned by FirmwareUpdater
574#[derive(Debug)]
575pub enum FirmwareUpdaterError {
576 /// Error from flash.
577 Flash(NorFlashErrorKind),
578 /// Signature errors.
579 Signature(signature::Error),
580}
581
582#[cfg(feature = "defmt")]
583impl defmt::Format for FirmwareUpdaterError {
584 fn format(&self, fmt: defmt::Formatter) {
585 match self {
586 FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"),
587 FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"),
588 }
589 }
590}
591
592impl<E> From<E> for FirmwareUpdaterError
593where
594 E: NorFlashError,
595{
596 fn from(error: E) -> Self {
597 FirmwareUpdaterError::Flash(error.kind())
598 }
599}
600
601/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
602/// 'mess up' the internal bootloader state
603pub struct FirmwareUpdater {
604 state: Partition,
605 dfu: Partition,
606}
607
608impl Default for FirmwareUpdater {
609 fn default() -> Self {
610 extern "C" {
611 static __bootloader_state_start: u32;
612 static __bootloader_state_end: u32;
613 static __bootloader_dfu_start: u32;
614 static __bootloader_dfu_end: u32;
615 }
616
617 let dfu = unsafe {
618 Partition::new(
619 &__bootloader_dfu_start as *const u32 as usize,
620 &__bootloader_dfu_end as *const u32 as usize,
621 )
622 };
623 let state = unsafe {
624 Partition::new(
625 &__bootloader_state_start as *const u32 as usize,
626 &__bootloader_state_end as *const u32 as usize,
627 )
628 };
629
630 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
631 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
632 FirmwareUpdater::new(dfu, state)
633 }
634}
635
636impl FirmwareUpdater {
637 /// Create a firmware updater instance with partition ranges for the update and state partitions.
638 pub const fn new(dfu: Partition, state: Partition) -> Self {
639 Self { dfu, state }
640 }
641
642 /// Return the length of the DFU area
643 pub fn firmware_len(&self) -> usize {
644 self.dfu.len()
645 }
646
647 /// Obtain the current state.
648 ///
649 /// This is useful to check if the bootloader has just done a swap, in order
650 /// to do verifications and self-tests of the new image before calling
651 /// `mark_booted`.
652 #[cfg(feature = "nightly")]
653 pub async fn get_state<F: AsyncNorFlash>(
654 &mut self,
655 flash: &mut F,
656 aligned: &mut [u8],
657 ) -> Result<State, FirmwareUpdaterError> {
658 flash.read(self.state.from as u32, aligned).await?;
659
660 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
661 Ok(State::Swap)
662 } else {
663 Ok(State::Boot)
664 }
665 }
666
667 /// Verify the DFU given a public key. If there is an error then DO NOT
668 /// proceed with updating the firmware as it must be signed with a
669 /// corresponding private key (otherwise it could be malicious firmware).
670 ///
671 /// Mark to trigger firmware swap on next boot if verify suceeds.
672 ///
673 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
674 /// been generated from a SHA-512 digest of the firmware bytes.
675 ///
676 /// If no signature feature is set then this method will always return a
677 /// signature error.
678 ///
679 /// # Safety
680 ///
681 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from
682 /// and written to.
683 #[cfg(feature = "_verify")]
684 pub async fn verify_and_mark_updated<F: AsyncNorFlash>(
685 &mut self,
686 _flash: &mut F,
687 _public_key: &[u8],
688 _signature: &[u8],
689 _update_len: usize,
690 _aligned: &mut [u8],
691 ) -> Result<(), FirmwareUpdaterError> {
692 let _end = self.dfu.from + _update_len;
693 let _read_size = _aligned.len();
694
695 assert_eq!(_aligned.len(), F::WRITE_SIZE);
696 assert!(_end <= self.dfu.to);
697
698 #[cfg(feature = "ed25519-dalek")]
699 {
700 use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier};
701
702 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
703
704 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
705 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
706
707 let mut digest = Sha512::new();
708
709 let mut offset = self.dfu.from;
710 let last_offset = _end / _read_size * _read_size;
711
712 while offset < last_offset {
713 _flash.read(offset as u32, _aligned).await?;
714 digest.update(&_aligned);
715 offset += _read_size;
716 }
717
718 let remaining = _end % _read_size;
719
720 if remaining > 0 {
721 _flash.read(last_offset as u32, _aligned).await?;
722 digest.update(&_aligned[0..remaining]);
723 }
724
725 public_key
726 .verify(&digest.finalize(), &signature)
727 .map_err(into_signature_error)?
728 }
729 #[cfg(feature = "ed25519-salty")]
730 {
731 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
732 use salty::{PublicKey, Sha512, Signature};
733
734 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
735 FirmwareUpdaterError::Signature(signature::Error::default())
736 }
737
738 let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
739 let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
740 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
741 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
742
743 let mut digest = Sha512::new();
744
745 let mut offset = self.dfu.from;
746 let last_offset = _end / _read_size * _read_size;
747
748 while offset < last_offset {
749 _flash.read(offset as u32, _aligned).await?;
750 digest.update(&_aligned);
751 offset += _read_size;
752 }
753
754 let remaining = _end % _read_size;
755
756 if remaining > 0 {
757 _flash.read(last_offset as u32, _aligned).await?;
758 digest.update(&_aligned[0..remaining]);
759 }
760
761 let message = digest.finalize();
762 let r = public_key.verify(&message, &signature);
763 trace!(
764 "Verifying with public key {}, signature {} and message {} yields ok: {}",
765 public_key.to_bytes(),
766 signature.to_bytes(),
767 message,
768 r.is_ok()
769 );
770 r.map_err(into_signature_error)?
771 }
772
773 self.set_magic(_aligned, SWAP_MAGIC, _flash).await
774 }
775
776 /// Mark to trigger firmware swap on next boot.
777 ///
778 /// # Safety
779 ///
780 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
781 #[cfg(not(feature = "_verify"))]
782 #[cfg(feature = "nightly")]
783 pub async fn mark_updated<F: AsyncNorFlash>(
784 &mut self,
785 flash: &mut F,
786 aligned: &mut [u8],
787 ) -> Result<(), FirmwareUpdaterError> {
788 assert_eq!(aligned.len(), F::WRITE_SIZE);
789 self.set_magic(aligned, SWAP_MAGIC, flash).await
790 }
791
792 /// Mark firmware boot successful and stop rollback on reset.
793 ///
794 /// # Safety
795 ///
796 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
797 #[cfg(feature = "nightly")]
798 pub async fn mark_booted<F: AsyncNorFlash>(
799 &mut self,
800 flash: &mut F,
801 aligned: &mut [u8],
802 ) -> Result<(), FirmwareUpdaterError> {
803 assert_eq!(aligned.len(), F::WRITE_SIZE);
804 self.set_magic(aligned, BOOT_MAGIC, flash).await
805 }
806
807 #[cfg(feature = "nightly")]
808 async fn set_magic<F: AsyncNorFlash>(
809 &mut self,
810 aligned: &mut [u8],
811 magic: u8,
812 flash: &mut F,
813 ) -> Result<(), FirmwareUpdaterError> {
814 flash.read(self.state.from as u32, aligned).await?;
815
816 if aligned.iter().any(|&b| b != magic) {
817 aligned.fill(0);
818
819 flash.write(self.state.from as u32, aligned).await?;
820 flash.erase(self.state.from as u32, self.state.to as u32).await?;
821
822 aligned.fill(magic);
823 flash.write(self.state.from as u32, aligned).await?;
824 }
825 Ok(())
826 }
827
828 /// Write data to a flash page.
829 ///
830 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
831 ///
832 /// # Safety
833 ///
834 /// Failing to meet alignment and size requirements may result in a panic.
835 #[cfg(feature = "nightly")]
836 pub async fn write_firmware<F: AsyncNorFlash>(
837 &mut self,
838 offset: usize,
839 data: &[u8],
840 flash: &mut F,
841 block_size: usize,
842 ) -> Result<(), FirmwareUpdaterError> {
843 assert!(data.len() >= F::ERASE_SIZE);
844
845 flash
846 .erase(
847 (self.dfu.from + offset) as u32,
848 (self.dfu.from + offset + data.len()) as u32,
849 )
850 .await?;
851
852 trace!(
853 "Erased from {} to {}",
854 self.dfu.from + offset,
855 self.dfu.from + offset + data.len()
856 );
857
858 FirmwareWriter(self.dfu)
859 .write_block(offset, data, flash, block_size)
860 .await?;
861
862 Ok(())
863 }
864
865 /// Prepare for an incoming DFU update by erasing the entire DFU area and
866 /// returning a `FirmwareWriter`.
867 ///
868 /// Using this instead of `write_firmware` allows for an optimized API in
869 /// exchange for added complexity.
870 #[cfg(feature = "nightly")]
871 pub async fn prepare_update<F: AsyncNorFlash>(
872 &mut self,
873 flash: &mut F,
874 ) -> Result<FirmwareWriter, FirmwareUpdaterError> {
875 flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?;
876
877 trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
878
879 Ok(FirmwareWriter(self.dfu))
880 }
881
882 //
883 // Blocking API
884 //
885
886 /// Obtain the current state.
887 ///
888 /// This is useful to check if the bootloader has just done a swap, in order
889 /// to do verifications and self-tests of the new image before calling
890 /// `mark_booted`.
891 pub fn get_state_blocking<F: NorFlash>(
892 &mut self,
893 flash: &mut F,
894 aligned: &mut [u8],
895 ) -> Result<State, FirmwareUpdaterError> {
896 flash.read(self.state.from as u32, aligned)?;
897
898 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
899 Ok(State::Swap)
900 } else {
901 Ok(State::Boot)
902 }
903 }
904
905 /// Verify the DFU given a public key. If there is an error then DO NOT
906 /// proceed with updating the firmware as it must be signed with a
907 /// corresponding private key (otherwise it could be malicious firmware).
908 ///
909 /// Mark to trigger firmware swap on next boot if verify suceeds.
910 ///
911 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
912 /// been generated from a SHA-512 digest of the firmware bytes.
913 ///
914 /// If no signature feature is set then this method will always return a
915 /// signature error.
916 ///
917 /// # Safety
918 ///
919 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from
920 /// and written to.
921 #[cfg(feature = "_verify")]
922 pub fn verify_and_mark_updated_blocking<F: NorFlash>(
923 &mut self,
924 _flash: &mut F,
925 _public_key: &[u8],
926 _signature: &[u8],
927 _update_len: usize,
928 _aligned: &mut [u8],
929 ) -> Result<(), FirmwareUpdaterError> {
930 let _end = self.dfu.from + _update_len;
931 let _read_size = _aligned.len();
932
933 assert_eq!(_aligned.len(), F::WRITE_SIZE);
934 assert!(_end <= self.dfu.to);
935
936 #[cfg(feature = "ed25519-dalek")]
937 {
938 use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier};
939
940 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
941
942 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
943 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
944
945 let mut digest = Sha512::new();
946
947 let mut offset = self.dfu.from;
948 let last_offset = _end / _read_size * _read_size;
949
950 while offset < last_offset {
951 _flash.read(offset as u32, _aligned)?;
952 digest.update(&_aligned);
953 offset += _read_size;
954 }
955
956 let remaining = _end % _read_size;
957
958 if remaining > 0 {
959 _flash.read(last_offset as u32, _aligned)?;
960 digest.update(&_aligned[0..remaining]);
961 }
962
963 public_key
964 .verify(&digest.finalize(), &signature)
965 .map_err(into_signature_error)?
966 }
967 #[cfg(feature = "ed25519-salty")]
968 {
969 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
970 use salty::{PublicKey, Sha512, Signature};
971
972 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
973 FirmwareUpdaterError::Signature(signature::Error::default())
974 }
975
976 let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
977 let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
978 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
979 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
980
981 let mut digest = Sha512::new();
982
983 let mut offset = self.dfu.from;
984 let last_offset = _end / _read_size * _read_size;
985
986 while offset < last_offset {
987 _flash.read(offset as u32, _aligned)?;
988 digest.update(&_aligned);
989 offset += _read_size;
990 }
991
992 let remaining = _end % _read_size;
993
994 if remaining > 0 {
995 _flash.read(last_offset as u32, _aligned)?;
996 digest.update(&_aligned[0..remaining]);
997 }
998
999 let message = digest.finalize();
1000 let r = public_key.verify(&message, &signature);
1001 trace!(
1002 "Verifying with public key {}, signature {} and message {} yields ok: {}",
1003 public_key.to_bytes(),
1004 signature.to_bytes(),
1005 message,
1006 r.is_ok()
1007 );
1008 r.map_err(into_signature_error)?
1009 }
1010
1011 self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash)
1012 }
1013
1014 /// Mark to trigger firmware swap on next boot.
1015 ///
1016 /// # Safety
1017 ///
1018 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
1019 #[cfg(not(feature = "_verify"))]
1020 pub fn mark_updated_blocking<F: NorFlash>(
1021 &mut self,
1022 flash: &mut F,
1023 aligned: &mut [u8],
1024 ) -> Result<(), FirmwareUpdaterError> {
1025 assert_eq!(aligned.len(), F::WRITE_SIZE);
1026 self.set_magic_blocking(aligned, SWAP_MAGIC, flash)
1027 }
1028
1029 /// Mark firmware boot successful and stop rollback on reset.
1030 ///
1031 /// # Safety
1032 ///
1033 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
1034 pub fn mark_booted_blocking<F: NorFlash>(
1035 &mut self,
1036 flash: &mut F,
1037 aligned: &mut [u8],
1038 ) -> Result<(), FirmwareUpdaterError> {
1039 assert_eq!(aligned.len(), F::WRITE_SIZE);
1040 self.set_magic_blocking(aligned, BOOT_MAGIC, flash)
1041 }
1042
1043 fn set_magic_blocking<F: NorFlash>(
1044 &mut self,
1045 aligned: &mut [u8],
1046 magic: u8,
1047 flash: &mut F,
1048 ) -> Result<(), FirmwareUpdaterError> {
1049 flash.read(self.state.from as u32, aligned)?;
1050
1051 if aligned.iter().any(|&b| b != magic) {
1052 aligned.fill(0);
1053
1054 flash.write(self.state.from as u32, aligned)?;
1055 flash.erase(self.state.from as u32, self.state.to as u32)?;
1056
1057 aligned.fill(magic);
1058 flash.write(self.state.from as u32, aligned)?;
1059 }
1060 Ok(())
1061 }
1062
1063 /// Write data to a flash page.
1064 ///
1065 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
1066 ///
1067 /// # Safety
1068 ///
1069 /// Failing to meet alignment and size requirements may result in a panic.
1070 pub fn write_firmware_blocking<F: NorFlash>(
1071 &mut self,
1072 offset: usize,
1073 data: &[u8],
1074 flash: &mut F,
1075 block_size: usize,
1076 ) -> Result<(), FirmwareUpdaterError> {
1077 assert!(data.len() >= F::ERASE_SIZE);
1078
1079 flash.erase(
1080 (self.dfu.from + offset) as u32,
1081 (self.dfu.from + offset + data.len()) as u32,
1082 )?;
1083
1084 trace!(
1085 "Erased from {} to {}",
1086 self.dfu.from + offset,
1087 self.dfu.from + offset + data.len()
1088 );
1089
1090 FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?;
1091
1092 Ok(())
1093 }
1094
1095 /// Prepare for an incoming DFU update by erasing the entire DFU area and
1096 /// returning a `FirmwareWriter`.
1097 ///
1098 /// Using this instead of `write_firmware_blocking` allows for an optimized
1099 /// API in exchange for added complexity.
1100 pub fn prepare_update_blocking<F: NorFlash>(
1101 &mut self,
1102 flash: &mut F,
1103 ) -> Result<FirmwareWriter, FirmwareUpdaterError> {
1104 flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?;
1105
1106 trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
1107
1108 Ok(FirmwareWriter(self.dfu))
1109 }
1110}
1111
1112/// FirmwareWriter allows writing blocks to an already erased flash.
1113pub struct FirmwareWriter(Partition);
1114
1115impl FirmwareWriter {
1116 /// Write data to a flash page.
1117 ///
1118 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
1119 ///
1120 /// # Safety
1121 ///
1122 /// Failing to meet alignment and size requirements may result in a panic.
1123 #[cfg(feature = "nightly")]
1124 pub async fn write_block<F: AsyncNorFlash>(
1125 &mut self,
1126 offset: usize,
1127 data: &[u8],
1128 flash: &mut F,
1129 block_size: usize,
1130 ) -> Result<(), F::Error> {
1131 trace!(
1132 "Writing firmware at offset 0x{:x} len {}",
1133 self.0.from + offset,
1134 data.len()
1135 );
1136
1137 let mut write_offset = self.0.from + offset;
1138 for chunk in data.chunks(block_size) {
1139 trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
1140 flash.write(write_offset as u32, chunk).await?;
1141 write_offset += chunk.len();
1142 }
1143 /*
1144 trace!("Wrote data, reading back for verification");
1145
1146 let mut buf: [u8; 4096] = [0; 4096];
1147 let mut data_offset = 0;
1148 let mut read_offset = self.dfu.from + offset;
1149 for chunk in buf.chunks_mut(block_size) {
1150 flash.read(read_offset as u32, chunk).await?;
1151 trace!("Read chunk at {}: {:?}", read_offset, chunk);
1152 assert_eq!(&data[data_offset..data_offset + block_size], chunk);
1153 read_offset += chunk.len();
1154 data_offset += chunk.len();
1155 }
1156 */
1157
1158 Ok(())
1159 }
1160
1161 /// Write data to a flash page.
1162 ///
1163 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
1164 ///
1165 /// # Safety
1166 ///
1167 /// Failing to meet alignment and size requirements may result in a panic.
1168 pub fn write_block_blocking<F: NorFlash>(
1169 &mut self,
1170 offset: usize,
1171 data: &[u8],
1172 flash: &mut F,
1173 block_size: usize,
1174 ) -> Result<(), F::Error> {
1175 trace!(
1176 "Writing firmware at offset 0x{:x} len {}",
1177 self.0.from + offset,
1178 data.len()
1179 );
1180
1181 let mut write_offset = self.0.from + offset;
1182 for chunk in data.chunks(block_size) {
1183 trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
1184 flash.write(write_offset as u32, chunk)?;
1185 write_offset += chunk.len();
1186 }
1187 /*
1188 trace!("Wrote data, reading back for verification");
1189
1190 let mut buf: [u8; 4096] = [0; 4096];
1191 let mut data_offset = 0;
1192 let mut read_offset = self.dfu.from + offset;
1193 for chunk in buf.chunks_mut(block_size) {
1194 flash.read(read_offset as u32, chunk).await?;
1195 trace!("Read chunk at {}: {:?}", read_offset, chunk);
1196 assert_eq!(&data[data_offset..data_offset + block_size], chunk);
1197 read_offset += chunk.len();
1198 data_offset += chunk.len();
1199 }
1200 */
1201
1202 Ok(())
1203 }
1204}
1205
1206#[cfg(test)] 47#[cfg(test)]
1207mod tests { 48mod tests {
1208 use core::convert::Infallible;
1209
1210 use embedded_storage::nor_flash::ErrorType;
1211 use embedded_storage_async::nor_flash::ReadNorFlash as AsyncReadNorFlash;
1212 use futures::executor::block_on; 49 use futures::executor::block_on;
1213 50
1214 use super::*; 51 use super::*;
52 use crate::mem_flash::MemFlash;
1215 53
1216 /* 54 /*
1217 #[test] 55 #[test]
@@ -1234,18 +72,14 @@ mod tests {
1234 const ACTIVE: Partition = Partition::new(4096, 61440); 72 const ACTIVE: Partition = Partition::new(4096, 61440);
1235 const DFU: Partition = Partition::new(61440, 122880); 73 const DFU: Partition = Partition::new(61440, 122880);
1236 74
1237 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); 75 let mut flash = MemFlash::<131072, 4096, 4>::default();
1238 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); 76 flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
1239 let mut flash = SingleFlashConfig::new(&mut flash); 77 let mut flash = SingleFlashConfig::new(&mut flash);
1240 78
1241 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 79 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
1242 80
1243 let mut magic = [0; 4];
1244 let mut page = [0; 4096]; 81 let mut page = [0; 4096];
1245 assert_eq!( 82 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash, &mut page).unwrap());
1246 State::Boot,
1247 bootloader.prepare_boot(&mut flash, &mut magic, &mut page).unwrap()
1248 );
1249 } 83 }
1250 84
1251 #[test] 85 #[test]
@@ -1254,66 +88,49 @@ mod tests {
1254 const STATE: Partition = Partition::new(0, 4096); 88 const STATE: Partition = Partition::new(0, 4096);
1255 const ACTIVE: Partition = Partition::new(4096, 61440); 89 const ACTIVE: Partition = Partition::new(4096, 61440);
1256 const DFU: Partition = Partition::new(61440, 122880); 90 const DFU: Partition = Partition::new(61440, 122880);
1257 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); 91 let mut flash = MemFlash::<131072, 4096, 4>::random();
1258 92
1259 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 93 let original = [rand::random::<u8>(); ACTIVE.size() as usize];
1260 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 94 let update = [rand::random::<u8>(); ACTIVE.size() as usize];
1261 let mut aligned = [0; 4]; 95 let mut aligned = [0; 4];
1262 96
1263 for i in ACTIVE.from..ACTIVE.to { 97 flash.program(ACTIVE.from, &original).unwrap();
1264 flash.0[i] = original[i - ACTIVE.from];
1265 }
1266 98
1267 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 99 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
1268 let mut updater = FirmwareUpdater::new(DFU, STATE); 100 let mut updater = FirmwareUpdater::new(DFU, STATE);
1269 let mut offset = 0; 101 block_on(updater.write_firmware(0, &update, &mut flash)).unwrap();
1270 for chunk in update.chunks(4096) {
1271 block_on(updater.write_firmware(offset, chunk, &mut flash, 4096)).unwrap();
1272 offset += chunk.len();
1273 }
1274 block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); 102 block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap();
1275 103
1276 let mut magic = [0; 4]; 104 let mut page = [0; 1024];
1277 let mut page = [0; 4096];
1278 assert_eq!( 105 assert_eq!(
1279 State::Swap, 106 State::Swap,
1280 bootloader 107 bootloader
1281 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) 108 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page)
1282 .unwrap() 109 .unwrap()
1283 ); 110 );
1284 111
1285 for i in ACTIVE.from..ACTIVE.to { 112 flash.assert_eq(ACTIVE.from, &update);
1286 assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i);
1287 }
1288
1289 // First DFU page is untouched 113 // First DFU page is untouched
1290 for i in DFU.from + 4096..DFU.to { 114 flash.assert_eq(DFU.from + 4096, &original);
1291 assert_eq!(flash.0[i], original[i - DFU.from - 4096], "Index {}", i);
1292 }
1293 115
1294 // Running again should cause a revert 116 // Running again should cause a revert
1295 assert_eq!( 117 assert_eq!(
1296 State::Swap, 118 State::Swap,
1297 bootloader 119 bootloader
1298 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) 120 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page)
1299 .unwrap() 121 .unwrap()
1300 ); 122 );
1301 123
1302 for i in ACTIVE.from..ACTIVE.to { 124 flash.assert_eq(ACTIVE.from, &original);
1303 assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i);
1304 }
1305
1306 // Last page is untouched 125 // Last page is untouched
1307 for i in DFU.from..DFU.to - 4096 { 126 flash.assert_eq(DFU.from, &update);
1308 assert_eq!(flash.0[i], update[i - DFU.from], "Index {}", i);
1309 }
1310 127
1311 // Mark as booted 128 // Mark as booted
1312 block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap(); 129 block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap();
1313 assert_eq!( 130 assert_eq!(
1314 State::Boot, 131 State::Boot,
1315 bootloader 132 bootloader
1316 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) 133 .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page)
1317 .unwrap() 134 .unwrap()
1318 ); 135 );
1319 } 136 }
@@ -1325,50 +142,34 @@ mod tests {
1325 const ACTIVE: Partition = Partition::new(4096, 16384); 142 const ACTIVE: Partition = Partition::new(4096, 16384);
1326 const DFU: Partition = Partition::new(0, 16384); 143 const DFU: Partition = Partition::new(0, 16384);
1327 144
1328 let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]); 145 let mut active = MemFlash::<16384, 4096, 8>::random();
1329 let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]); 146 let mut dfu = MemFlash::<16384, 2048, 8>::random();
1330 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 147 let mut state = MemFlash::<4096, 128, 4>::random();
1331 let mut aligned = [0; 4]; 148 let mut aligned = [0; 4];
1332 149
1333 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 150 let original = [rand::random::<u8>(); ACTIVE.size() as usize];
1334 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 151 let update = [rand::random::<u8>(); ACTIVE.size() as usize];
1335 152
1336 for i in ACTIVE.from..ACTIVE.to { 153 active.program(ACTIVE.from, &original).unwrap();
1337 active.0[i] = original[i - ACTIVE.from];
1338 }
1339 154
1340 let mut updater = FirmwareUpdater::new(DFU, STATE); 155 let mut updater = FirmwareUpdater::new(DFU, STATE);
1341 156
1342 let mut offset = 0; 157 block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap();
1343 for chunk in update.chunks(2048) {
1344 block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap();
1345 offset += chunk.len();
1346 }
1347 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); 158 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
1348 159
1349 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 160 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
1350 let mut magic = [0; 4];
1351 let mut page = [0; 4096]; 161 let mut page = [0; 4096];
1352 162
1353 assert_eq!( 163 assert_eq!(
1354 State::Swap, 164 State::Swap,
1355 bootloader 165 bootloader
1356 .prepare_boot( 166 .prepare_boot(&mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), &mut page)
1357 &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu),
1358 &mut magic,
1359 &mut page
1360 )
1361 .unwrap() 167 .unwrap()
1362 ); 168 );
1363 169
1364 for i in ACTIVE.from..ACTIVE.to { 170 active.assert_eq(ACTIVE.from, &update);
1365 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i);
1366 }
1367
1368 // First DFU page is untouched 171 // First DFU page is untouched
1369 for i in DFU.from + 4096..DFU.to { 172 dfu.assert_eq(DFU.from + 4096, &original);
1370 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i);
1371 }
1372 } 173 }
1373 174
1374 #[test] 175 #[test]
@@ -1379,57 +180,35 @@ mod tests {
1379 const DFU: Partition = Partition::new(0, 16384); 180 const DFU: Partition = Partition::new(0, 16384);
1380 181
1381 let mut aligned = [0; 4]; 182 let mut aligned = [0; 4];
1382 let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]); 183 let mut active = MemFlash::<16384, 2048, 4>::random();
1383 let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]); 184 let mut dfu = MemFlash::<16384, 4096, 8>::random();
1384 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 185 let mut state = MemFlash::<4096, 128, 4>::random();
1385 186
1386 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 187 let original = [rand::random::<u8>(); ACTIVE.size() as usize];
1387 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 188 let update = [rand::random::<u8>(); ACTIVE.size() as usize];
1388 189
1389 for i in ACTIVE.from..ACTIVE.to { 190 active.program(ACTIVE.from, &original).unwrap();
1390 active.0[i] = original[i - ACTIVE.from];
1391 }
1392 191
1393 let mut updater = FirmwareUpdater::new(DFU, STATE); 192 let mut updater = FirmwareUpdater::new(DFU, STATE);
1394 193
1395 let mut offset = 0; 194 block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap();
1396 for chunk in update.chunks(4096) {
1397 block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap();
1398 offset += chunk.len();
1399 }
1400 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); 195 block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
1401 196
1402 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 197 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
1403 let mut magic = [0; 4];
1404 let mut page = [0; 4096]; 198 let mut page = [0; 4096];
1405 assert_eq!( 199 assert_eq!(
1406 State::Swap, 200 State::Swap,
1407 bootloader 201 bootloader
1408 .prepare_boot( 202 .prepare_boot(
1409 &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,), 203 &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,),
1410 &mut magic,
1411 &mut page 204 &mut page
1412 ) 205 )
1413 .unwrap() 206 .unwrap()
1414 ); 207 );
1415 208
1416 for i in ACTIVE.from..ACTIVE.to { 209 active.assert_eq(ACTIVE.from, &update);
1417 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i);
1418 }
1419
1420 // First DFU page is untouched 210 // First DFU page is untouched
1421 for i in DFU.from + 4096..DFU.to { 211 dfu.assert_eq(DFU.from + 4096, &original);
1422 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i);
1423 }
1424 }
1425
1426 #[test]
1427 #[should_panic]
1428 fn test_range_asserts() {
1429 const ACTIVE: Partition = Partition::new(4096, 4194304);
1430 const DFU: Partition = Partition::new(4194304, 2 * 4194304);
1431 const STATE: Partition = Partition::new(0, 4096);
1432 assert_partitions(ACTIVE, DFU, STATE, 4096, 4);
1433 } 212 }
1434 213
1435 #[test] 214 #[test]
@@ -1458,13 +237,13 @@ mod tests {
1458 237
1459 const STATE: Partition = Partition::new(0, 4096); 238 const STATE: Partition = Partition::new(0, 4096);
1460 const DFU: Partition = Partition::new(4096, 8192); 239 const DFU: Partition = Partition::new(4096, 8192);
1461 let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]); 240 let mut flash = MemFlash::<8192, 4096, 4>::default();
1462 241
1463 let firmware_len = firmware.len(); 242 let firmware_len = firmware.len();
1464 243
1465 let mut write_buf = [0; 4096]; 244 let mut write_buf = [0; 4096];
1466 write_buf[0..firmware_len].copy_from_slice(firmware); 245 write_buf[0..firmware_len].copy_from_slice(firmware);
1467 NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap(); 246 DFU.write_blocking(&mut flash, 0, &write_buf).unwrap();
1468 247
1469 // On with the test 248 // On with the test
1470 249
@@ -1476,117 +255,9 @@ mod tests {
1476 &mut flash, 255 &mut flash,
1477 &public_key.to_bytes(), 256 &public_key.to_bytes(),
1478 &signature.to_bytes(), 257 &signature.to_bytes(),
1479 firmware_len, 258 firmware_len as u32,
1480 &mut aligned, 259 &mut aligned,
1481 )) 260 ))
1482 .is_ok()); 261 .is_ok());
1483 } 262 }
1484 struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>([u8; SIZE]);
1485
1486 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
1487 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1488 {
1489 const WRITE_SIZE: usize = WRITE_SIZE;
1490 const ERASE_SIZE: usize = ERASE_SIZE;
1491 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
1492 let from = from as usize;
1493 let to = to as usize;
1494 assert!(from % ERASE_SIZE == 0);
1495 assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
1496 for i in from..to {
1497 self.0[i] = 0xFF;
1498 }
1499 Ok(())
1500 }
1501
1502 fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
1503 assert!(data.len() % WRITE_SIZE == 0);
1504 assert!(offset as usize % WRITE_SIZE == 0);
1505 assert!(offset as usize + data.len() <= SIZE);
1506
1507 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
1508
1509 Ok(())
1510 }
1511 }
1512
1513 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
1514 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1515 {
1516 type Error = Infallible;
1517 }
1518
1519 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
1520 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1521 {
1522 const READ_SIZE: usize = 1;
1523
1524 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
1525 let len = buf.len();
1526 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
1527 Ok(())
1528 }
1529
1530 fn capacity(&self) -> usize {
1531 SIZE
1532 }
1533 }
1534
1535 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> super::Flash
1536 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1537 {
1538 const BLOCK_SIZE: usize = ERASE_SIZE;
1539 const ERASE_VALUE: u8 = 0xFF;
1540 }
1541
1542 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
1543 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1544 {
1545 const READ_SIZE: usize = 1;
1546
1547 async fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
1548 let len = buf.len();
1549 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
1550 Ok(())
1551 }
1552
1553 fn capacity(&self) -> usize {
1554 SIZE
1555 }
1556 }
1557
1558 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
1559 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
1560 {
1561 const WRITE_SIZE: usize = WRITE_SIZE;
1562 const ERASE_SIZE: usize = ERASE_SIZE;
1563
1564 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
1565 let from = from as usize;
1566 let to = to as usize;
1567 assert!(from % ERASE_SIZE == 0);
1568 assert!(to % ERASE_SIZE == 0);
1569 for i in from..to {
1570 self.0[i] = 0xFF;
1571 }
1572 Ok(())
1573 }
1574
1575 async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
1576 info!("Writing {} bytes to 0x{:x}", data.len(), offset);
1577 assert!(data.len() % WRITE_SIZE == 0);
1578 assert!(offset as usize % WRITE_SIZE == 0);
1579 assert!(
1580 offset as usize + data.len() <= SIZE,
1581 "OFFSET: {}, LEN: {}, FLASH SIZE: {}",
1582 offset,
1583 data.len(),
1584 SIZE
1585 );
1586
1587 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
1588
1589 Ok(())
1590 }
1591 }
1592} 263}
diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs
new file mode 100644
index 000000000..c62379b24
--- /dev/null
+++ b/embassy-boot/boot/src/mem_flash.rs
@@ -0,0 +1,164 @@
1#![allow(unused)]
2
3use core::ops::{Bound, Range, RangeBounds};
4
5use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
6use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
7
8pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> {
9 pub mem: [u8; SIZE],
10 pub pending_write_successes: Option<usize>,
11}
12
13#[derive(Debug)]
14pub struct MemFlashError;
15
16impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> {
17 pub const fn new(fill: u8) -> Self {
18 Self {
19 mem: [fill; SIZE],
20 pending_write_successes: None,
21 }
22 }
23
24 #[cfg(test)]
25 pub fn random() -> Self {
26 let mut mem = [0; SIZE];
27 for byte in mem.iter_mut() {
28 *byte = rand::random::<u8>();
29 }
30 Self {
31 mem,
32 pending_write_successes: None,
33 }
34 }
35
36 pub fn program(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> {
37 let offset = offset as usize;
38 assert!(bytes.len() % WRITE_SIZE == 0);
39 assert!(offset % WRITE_SIZE == 0);
40 assert!(offset + bytes.len() <= SIZE);
41
42 self.mem[offset..offset + bytes.len()].copy_from_slice(bytes);
43
44 Ok(())
45 }
46
47 pub fn assert_eq(&self, offset: u32, expectation: &[u8]) {
48 for i in 0..expectation.len() {
49 assert_eq!(self.mem[offset as usize + i], expectation[i], "Index {}", i);
50 }
51 }
52}
53
54impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Default
55 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
56{
57 fn default() -> Self {
58 Self::new(0xFF)
59 }
60}
61
62impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
63 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
64{
65 type Error = MemFlashError;
66}
67
68impl NorFlashError for MemFlashError {
69 fn kind(&self) -> NorFlashErrorKind {
70 NorFlashErrorKind::Other
71 }
72}
73
74impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
75 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
76{
77 const READ_SIZE: usize = 1;
78
79 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
80 let len = bytes.len();
81 bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]);
82 Ok(())
83 }
84
85 fn capacity(&self) -> usize {
86 SIZE
87 }
88}
89
90impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
91 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
92{
93 const WRITE_SIZE: usize = WRITE_SIZE;
94 const ERASE_SIZE: usize = ERASE_SIZE;
95
96 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
97 let from = from as usize;
98 let to = to as usize;
99 assert!(from % ERASE_SIZE == 0);
100 assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
101 for i in from..to {
102 self.mem[i] = 0xFF;
103 }
104 Ok(())
105 }
106
107 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
108 let offset = offset as usize;
109 assert!(bytes.len() % WRITE_SIZE == 0);
110 assert!(offset % WRITE_SIZE == 0);
111 assert!(offset + bytes.len() <= SIZE);
112
113 if let Some(pending_successes) = self.pending_write_successes {
114 if pending_successes > 0 {
115 self.pending_write_successes = Some(pending_successes - 1);
116 } else {
117 return Err(MemFlashError);
118 }
119 }
120
121 for ((offset, mem_byte), new_byte) in self
122 .mem
123 .iter_mut()
124 .enumerate()
125 .skip(offset)
126 .take(bytes.len())
127 .zip(bytes)
128 {
129 assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset);
130 *mem_byte = *new_byte;
131 }
132
133 Ok(())
134 }
135}
136
137impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
138 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
139{
140 const READ_SIZE: usize = 1;
141
142 async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
143 <Self as ReadNorFlash>::read(self, offset, bytes)
144 }
145
146 fn capacity(&self) -> usize {
147 <Self as ReadNorFlash>::capacity(self)
148 }
149}
150
151impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
152 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
153{
154 const WRITE_SIZE: usize = WRITE_SIZE;
155 const ERASE_SIZE: usize = ERASE_SIZE;
156
157 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
158 <Self as NorFlash>::erase(self, from, to)
159 }
160
161 async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
162 <Self as NorFlash>::write(self, offset, bytes)
163 }
164}
diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs
new file mode 100644
index 000000000..7529059b6
--- /dev/null
+++ b/embassy-boot/boot/src/partition.rs
@@ -0,0 +1,139 @@
1use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
2use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
3
4/// A region in flash used by the bootloader.
5#[derive(Copy, Clone, Debug)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7pub struct Partition {
8 /// The offset into the flash where the partition starts.
9 pub from: u32,
10 /// The offset into the flash where the partition ends.
11 pub to: u32,
12}
13
14impl Partition {
15 /// Create a new partition with the provided range
16 pub const fn new(from: u32, to: u32) -> Self {
17 Self { from, to }
18 }
19
20 /// Return the size of the partition
21 pub const fn size(&self) -> u32 {
22 self.to - self.from
23 }
24
25 /// Read from the partition on the provided flash
26 pub async fn read<F: AsyncReadNorFlash>(
27 &self,
28 flash: &mut F,
29 offset: u32,
30 bytes: &mut [u8],
31 ) -> Result<(), F::Error> {
32 let offset = self.from as u32 + offset;
33 flash.read(offset, bytes).await
34 }
35
36 /// Write to the partition on the provided flash
37 pub async fn write<F: AsyncNorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> {
38 let offset = self.from as u32 + offset;
39 flash.write(offset, bytes).await?;
40 trace!("Wrote from 0x{:x} len {}", offset, bytes.len());
41 Ok(())
42 }
43
44 /// Erase part of the partition on the provided flash
45 pub async fn erase<F: AsyncNorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> {
46 let from = self.from as u32 + from;
47 let to = self.from as u32 + to;
48 flash.erase(from, to).await?;
49 trace!("Erased from 0x{:x} to 0x{:x}", from, to);
50 Ok(())
51 }
52
53 /// Erase the entire partition
54 pub(crate) async fn wipe<F: AsyncNorFlash>(&self, flash: &mut F) -> Result<(), F::Error> {
55 let from = self.from as u32;
56 let to = self.to as u32;
57 flash.erase(from, to).await?;
58 trace!("Wiped from 0x{:x} to 0x{:x}", from, to);
59 Ok(())
60 }
61
62 /// Read from the partition on the provided flash
63 pub fn read_blocking<F: ReadNorFlash>(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> {
64 let offset = self.from as u32 + offset;
65 flash.read(offset, bytes)
66 }
67
68 /// Write to the partition on the provided flash
69 pub fn write_blocking<F: NorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> {
70 let offset = self.from as u32 + offset;
71 flash.write(offset, bytes)?;
72 trace!("Wrote from 0x{:x} len {}", offset, bytes.len());
73 Ok(())
74 }
75
76 /// Erase part of the partition on the provided flash
77 pub fn erase_blocking<F: NorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> {
78 let from = self.from as u32 + from;
79 let to = self.from as u32 + to;
80 flash.erase(from, to)?;
81 trace!("Erased from 0x{:x} to 0x{:x}", from, to);
82 Ok(())
83 }
84
85 /// Erase the entire partition
86 pub(crate) fn wipe_blocking<F: NorFlash>(&self, flash: &mut F) -> Result<(), F::Error> {
87 let from = self.from as u32;
88 let to = self.to as u32;
89 flash.erase(from, to)?;
90 trace!("Wiped from 0x{:x} to 0x{:x}", from, to);
91 Ok(())
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use crate::mem_flash::MemFlash;
98 use crate::Partition;
99
100 #[test]
101 fn can_erase() {
102 let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
103 let partition = Partition::new(256, 512);
104
105 partition.erase_blocking(&mut flash, 64, 192).unwrap();
106
107 for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) {
108 assert_eq!(0x00, byte, "Index {}", index);
109 }
110
111 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) {
112 assert_eq!(0xFF, byte, "Index {}", index);
113 }
114
115 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) {
116 assert_eq!(0x00, byte, "Index {}", index);
117 }
118 }
119
120 #[test]
121 fn can_wipe() {
122 let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
123 let partition = Partition::new(256, 512);
124
125 partition.wipe_blocking(&mut flash).unwrap();
126
127 for (index, byte) in flash.mem.iter().copied().enumerate().take(256) {
128 assert_eq!(0x00, byte, "Index {}", index);
129 }
130
131 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) {
132 assert_eq!(0xFF, byte, "Index {}", index);
133 }
134
135 for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) {
136 assert_eq!(0x00, byte, "Index {}", index);
137 }
138 }
139}
diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs
index 5cc6ba448..48bbd7e2a 100644
--- a/embassy-boot/nrf/src/lib.rs
+++ b/embassy-boot/nrf/src/lib.rs
@@ -11,13 +11,12 @@ use embassy_nrf::wdt;
11use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; 11use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
12 12
13/// A bootloader for nRF devices. 13/// A bootloader for nRF devices.
14pub struct BootLoader { 14pub struct BootLoader<const BUFFER_SIZE: usize = PAGE_SIZE> {
15 boot: embassy_boot::BootLoader, 15 boot: embassy_boot::BootLoader,
16 magic: AlignedBuffer<4>, 16 aligned_buf: AlignedBuffer<BUFFER_SIZE>,
17 page: AlignedBuffer<PAGE_SIZE>,
18} 17}
19 18
20impl Default for BootLoader { 19impl Default for BootLoader<PAGE_SIZE> {
21 /// Create a new bootloader instance using parameters from linker script 20 /// Create a new bootloader instance using parameters from linker script
22 fn default() -> Self { 21 fn default() -> Self {
23 extern "C" { 22 extern "C" {
@@ -31,20 +30,20 @@ impl Default for BootLoader {
31 30
32 let active = unsafe { 31 let active = unsafe {
33 Partition::new( 32 Partition::new(
34 &__bootloader_active_start as *const u32 as usize, 33 &__bootloader_active_start as *const u32 as u32,
35 &__bootloader_active_end as *const u32 as usize, 34 &__bootloader_active_end as *const u32 as u32,
36 ) 35 )
37 }; 36 };
38 let dfu = unsafe { 37 let dfu = unsafe {
39 Partition::new( 38 Partition::new(
40 &__bootloader_dfu_start as *const u32 as usize, 39 &__bootloader_dfu_start as *const u32 as u32,
41 &__bootloader_dfu_end as *const u32 as usize, 40 &__bootloader_dfu_end as *const u32 as u32,
42 ) 41 )
43 }; 42 };
44 let state = unsafe { 43 let state = unsafe {
45 Partition::new( 44 Partition::new(
46 &__bootloader_state_start as *const u32 as usize, 45 &__bootloader_state_start as *const u32 as u32,
47 &__bootloader_state_end as *const u32 as usize, 46 &__bootloader_state_end as *const u32 as u32,
48 ) 47 )
49 }; 48 };
50 49
@@ -56,20 +55,19 @@ impl Default for BootLoader {
56 } 55 }
57} 56}
58 57
59impl BootLoader { 58impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
60 /// Create a new bootloader instance using the supplied partitions for active, dfu and state. 59 /// Create a new bootloader instance using the supplied partitions for active, dfu and state.
61 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 60 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
62 Self { 61 Self {
63 boot: embassy_boot::BootLoader::new(active, dfu, state), 62 boot: embassy_boot::BootLoader::new(active, dfu, state),
64 magic: AlignedBuffer([0; 4]), 63 aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
65 page: AlignedBuffer([0; PAGE_SIZE]),
66 } 64 }
67 } 65 }
68 66
69 /// Inspect the bootloader state and perform actions required before booting, such as swapping 67 /// Inspect the bootloader state and perform actions required before booting, such as swapping
70 /// firmware. 68 /// firmware.
71 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize { 69 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize {
72 match self.boot.prepare_boot(flash, &mut self.magic.0, &mut self.page.0) { 70 match self.boot.prepare_boot(flash, &mut self.aligned_buf.0) {
73 Ok(_) => self.boot.boot_address(), 71 Ok(_) => self.boot.boot_address(),
74 Err(_) => panic!("boot prepare error!"), 72 Err(_) => panic!("boot prepare error!"),
75 } 73 }
diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs
index 6df34133e..c3cb22299 100644
--- a/embassy-boot/rp/src/lib.rs
+++ b/embassy-boot/rp/src/lib.rs
@@ -5,33 +5,31 @@
5mod fmt; 5mod fmt;
6 6
7pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; 7pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State};
8use embassy_rp::flash::{Flash, ERASE_SIZE, WRITE_SIZE}; 8use embassy_rp::flash::{Flash, ERASE_SIZE};
9use embassy_rp::peripherals::{FLASH, WATCHDOG}; 9use embassy_rp::peripherals::{FLASH, WATCHDOG};
10use embassy_rp::watchdog::Watchdog; 10use embassy_rp::watchdog::Watchdog;
11use embassy_time::Duration; 11use embassy_time::Duration;
12use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; 12use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
13 13
14/// A bootloader for RP2040 devices. 14/// A bootloader for RP2040 devices.
15pub struct BootLoader { 15pub struct BootLoader<const BUFFER_SIZE: usize = ERASE_SIZE> {
16 boot: embassy_boot::BootLoader, 16 boot: embassy_boot::BootLoader,
17 magic: AlignedBuffer<WRITE_SIZE>, 17 aligned_buf: AlignedBuffer<BUFFER_SIZE>,
18 page: AlignedBuffer<ERASE_SIZE>,
19} 18}
20 19
21impl BootLoader { 20impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
22 /// Create a new bootloader instance using the supplied partitions for active, dfu and state. 21 /// Create a new bootloader instance using the supplied partitions for active, dfu and state.
23 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 22 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
24 Self { 23 Self {
25 boot: embassy_boot::BootLoader::new(active, dfu, state), 24 boot: embassy_boot::BootLoader::new(active, dfu, state),
26 magic: AlignedBuffer([0; WRITE_SIZE]), 25 aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
27 page: AlignedBuffer([0; ERASE_SIZE]),
28 } 26 }
29 } 27 }
30 28
31 /// Inspect the bootloader state and perform actions required before booting, such as swapping 29 /// Inspect the bootloader state and perform actions required before booting, such as swapping
32 /// firmware. 30 /// firmware.
33 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize { 31 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize {
34 match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) { 32 match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) {
35 Ok(_) => embassy_rp::flash::FLASH_BASE + self.boot.boot_address(), 33 Ok(_) => embassy_rp::flash::FLASH_BASE + self.boot.boot_address(),
36 Err(_) => panic!("boot prepare error!"), 34 Err(_) => panic!("boot prepare error!"),
37 } 35 }
@@ -54,7 +52,7 @@ impl BootLoader {
54 } 52 }
55} 53}
56 54
57impl Default for BootLoader { 55impl Default for BootLoader<ERASE_SIZE> {
58 /// Create a new bootloader instance using parameters from linker script 56 /// Create a new bootloader instance using parameters from linker script
59 fn default() -> Self { 57 fn default() -> Self {
60 extern "C" { 58 extern "C" {
@@ -68,20 +66,20 @@ impl Default for BootLoader {
68 66
69 let active = unsafe { 67 let active = unsafe {
70 Partition::new( 68 Partition::new(
71 &__bootloader_active_start as *const u32 as usize, 69 &__bootloader_active_start as *const u32 as u32,
72 &__bootloader_active_end as *const u32 as usize, 70 &__bootloader_active_end as *const u32 as u32,
73 ) 71 )
74 }; 72 };
75 let dfu = unsafe { 73 let dfu = unsafe {
76 Partition::new( 74 Partition::new(
77 &__bootloader_dfu_start as *const u32 as usize, 75 &__bootloader_dfu_start as *const u32 as u32,
78 &__bootloader_dfu_end as *const u32 as usize, 76 &__bootloader_dfu_end as *const u32 as u32,
79 ) 77 )
80 }; 78 };
81 let state = unsafe { 79 let state = unsafe {
82 Partition::new( 80 Partition::new(
83 &__bootloader_state_start as *const u32 as usize, 81 &__bootloader_state_start as *const u32 as u32,
84 &__bootloader_state_end as *const u32 as usize, 82 &__bootloader_state_end as *const u32 as u32,
85 ) 83 )
86 }; 84 };
87 85
diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs
index 82f712c4d..94404697f 100644
--- a/embassy-boot/stm32/src/lib.rs
+++ b/embassy-boot/stm32/src/lib.rs
@@ -7,26 +7,24 @@ mod fmt;
7pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; 7pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State};
8 8
9/// A bootloader for STM32 devices. 9/// A bootloader for STM32 devices.
10pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize> { 10pub struct BootLoader<const BUFFER_SIZE: usize> {
11 boot: embassy_boot::BootLoader, 11 boot: embassy_boot::BootLoader,
12 magic: AlignedBuffer<WRITE_SIZE>, 12 aligned_buf: AlignedBuffer<BUFFER_SIZE>,
13 page: AlignedBuffer<PAGE_SIZE>,
14} 13}
15 14
16impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> BootLoader<PAGE_SIZE, WRITE_SIZE> { 15impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
17 /// Create a new bootloader instance using the supplied partitions for active, dfu and state. 16 /// Create a new bootloader instance using the supplied partitions for active, dfu and state.
18 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 17 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
19 Self { 18 Self {
20 boot: embassy_boot::BootLoader::new(active, dfu, state), 19 boot: embassy_boot::BootLoader::new(active, dfu, state),
21 magic: AlignedBuffer([0; WRITE_SIZE]), 20 aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
22 page: AlignedBuffer([0; PAGE_SIZE]),
23 } 21 }
24 } 22 }
25 23
26 /// Inspect the bootloader state and perform actions required before booting, such as swapping 24 /// Inspect the bootloader state and perform actions required before booting, such as swapping
27 /// firmware. 25 /// firmware.
28 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize { 26 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize {
29 match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) { 27 match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) {
30 Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(), 28 Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(),
31 Err(_) => panic!("boot prepare error!"), 29 Err(_) => panic!("boot prepare error!"),
32 } 30 }
@@ -49,7 +47,7 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> BootLoader<PAGE_SIZE, WRIT
49 } 47 }
50} 48}
51 49
52impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> Default for BootLoader<PAGE_SIZE, WRITE_SIZE> { 50impl<const BUFFER_SIZE: usize> Default for BootLoader<BUFFER_SIZE> {
53 /// Create a new bootloader instance using parameters from linker script 51 /// Create a new bootloader instance using parameters from linker script
54 fn default() -> Self { 52 fn default() -> Self {
55 extern "C" { 53 extern "C" {
@@ -63,20 +61,20 @@ impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> Default for BootLoader<PAG
63 61
64 let active = unsafe { 62 let active = unsafe {
65 Partition::new( 63 Partition::new(
66 &__bootloader_active_start as *const u32 as usize, 64 &__bootloader_active_start as *const u32 as u32,
67 &__bootloader_active_end as *const u32 as usize, 65 &__bootloader_active_end as *const u32 as u32,
68 ) 66 )
69 }; 67 };
70 let dfu = unsafe { 68 let dfu = unsafe {
71 Partition::new( 69 Partition::new(
72 &__bootloader_dfu_start as *const u32 as usize, 70 &__bootloader_dfu_start as *const u32 as u32,
73 &__bootloader_dfu_end as *const u32 as usize, 71 &__bootloader_dfu_end as *const u32 as u32,
74 ) 72 )
75 }; 73 };
76 let state = unsafe { 74 let state = unsafe {
77 Partition::new( 75 Partition::new(
78 &__bootloader_state_start as *const u32 as usize, 76 &__bootloader_state_start as *const u32 as u32,
79 &__bootloader_state_end as *const u32 as usize, 77 &__bootloader_state_end as *const u32 as u32,
80 ) 78 )
81 }; 79 };
82 80
diff --git a/embassy-cortex-m/src/executor.rs b/embassy-cortex-m/src/executor.rs
deleted file mode 100644
index 558539e73..000000000
--- a/embassy-cortex-m/src/executor.rs
+++ /dev/null
@@ -1,116 +0,0 @@
1//! Executor specific to cortex-m devices.
2
3use core::cell::UnsafeCell;
4use core::mem::MaybeUninit;
5
6use atomic_polyfill::{AtomicBool, Ordering};
7use cortex_m::interrupt::InterruptNumber;
8use cortex_m::peripheral::NVIC;
9pub use embassy_executor::*;
10
11#[derive(Clone, Copy)]
12struct N(u16);
13unsafe impl cortex_m::interrupt::InterruptNumber for N {
14 fn number(self) -> u16 {
15 self.0
16 }
17}
18
19fn pend_by_number(n: u16) {
20 cortex_m::peripheral::NVIC::pend(N(n))
21}
22
23/// Interrupt mode executor.
24///
25/// This executor runs tasks in interrupt mode. The interrupt handler is set up
26/// to poll tasks, and when a task is woken the interrupt is pended from software.
27///
28/// This allows running async tasks at a priority higher than thread mode. One
29/// use case is to leave thread mode free for non-async tasks. Another use case is
30/// to run multiple executors: one in thread mode for low priority tasks and another in
31/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
32/// priority ones.
33///
34/// It is even possible to run multiple interrupt mode executors at different priorities,
35/// by assigning different priorities to the interrupts. For an example on how to do this,
36/// See the 'multiprio' example for 'embassy-nrf'.
37///
38/// To use it, you have to pick an interrupt that won't be used by the hardware.
39/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
40/// If this is not the case, you may use an interrupt from any unused peripheral.
41///
42/// It is somewhat more complex to use, it's recommended to use the thread-mode
43/// [`Executor`] instead, if it works for your use case.
44pub struct InterruptExecutor {
45 started: AtomicBool,
46 executor: UnsafeCell<MaybeUninit<raw::Executor>>,
47}
48
49unsafe impl Send for InterruptExecutor {}
50unsafe impl Sync for InterruptExecutor {}
51
52impl InterruptExecutor {
53 /// Create a new, not started `InterruptExecutor`.
54 #[inline]
55 pub const fn new() -> Self {
56 Self {
57 started: AtomicBool::new(false),
58 executor: UnsafeCell::new(MaybeUninit::uninit()),
59 }
60 }
61
62 /// Executor interrupt callback.
63 ///
64 /// # Safety
65 ///
66 /// You MUST call this from the interrupt handler, and from nowhere else.
67 pub unsafe fn on_interrupt(&'static self) {
68 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
69 executor.poll();
70 }
71
72 /// Start the executor.
73 ///
74 /// This initializes the executor, enables the interrupt, and returns.
75 /// The executor keeps running in the background through the interrupt.
76 ///
77 /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]
78 /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a
79 /// different "thread" (the interrupt), so spawning tasks on it is effectively
80 /// sending them.
81 ///
82 /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from
83 /// a task running in it.
84 ///
85 /// # Interrupt requirements
86 ///
87 /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).
88 ///
89 /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.
90 ///
91 /// You must set the interrupt priority before calling this method. You MUST NOT
92 /// do it after.
93 ///
94 pub fn start(&'static self, irq: impl InterruptNumber) -> SendSpawner {
95 if self
96 .started
97 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
98 .is_err()
99 {
100 panic!("InterruptExecutor::start() called multiple times on the same executor.");
101 }
102
103 unsafe {
104 (&mut *self.executor.get()).as_mut_ptr().write(raw::Executor::new(
105 |ctx| pend_by_number(ctx as u16),
106 irq.number() as *mut (),
107 ))
108 }
109
110 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
111
112 unsafe { NVIC::unmask(irq) }
113
114 executor.spawner().make_send()
115 }
116}
diff --git a/embassy-cortex-m/src/lib.rs b/embassy-cortex-m/src/lib.rs
index fba23367b..e4b713a06 100644
--- a/embassy-cortex-m/src/lib.rs
+++ b/embassy-cortex-m/src/lib.rs
@@ -5,6 +5,6 @@
5// This mod MUST go first, so that the others see its macros. 5// This mod MUST go first, so that the others see its macros.
6pub(crate) mod fmt; 6pub(crate) mod fmt;
7 7
8pub mod executor; 8pub use embassy_executor as executor;
9pub mod interrupt; 9pub mod interrupt;
10pub mod peripheral; 10pub mod peripheral;
diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml
index 45eb0d43d..c509d6ee5 100644
--- a/embassy-embedded-hal/Cargo.toml
+++ b/embassy-embedded-hal/Cargo.toml
@@ -19,8 +19,8 @@ nightly = ["embedded-hal-async", "embedded-storage-async"]
19[dependencies] 19[dependencies]
20embassy-sync = { version = "0.1.0", path = "../embassy-sync" } 20embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
21embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 21embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
22embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } 22embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
23embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true } 23embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true }
24embedded-storage = "0.3.0" 24embedded-storage = "0.3.0"
25embedded-storage-async = { version = "0.4.0", optional = true } 25embedded-storage-async = { version = "0.4.0", optional = true }
26nb = "1.0.0" 26nb = "1.0.0"
diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter.rs
index a49f8df4b..ee919bd84 100644
--- a/embassy-embedded-hal/src/adapter.rs
+++ b/embassy-embedded-hal/src/adapter.rs
@@ -36,27 +36,22 @@ where
36 E: embedded_hal_1::i2c::Error + 'static, 36 E: embedded_hal_1::i2c::Error + 'static,
37 T: blocking::i2c::WriteRead<Error = E> + blocking::i2c::Read<Error = E> + blocking::i2c::Write<Error = E>, 37 T: blocking::i2c::WriteRead<Error = E> + blocking::i2c::Read<Error = E> + blocking::i2c::Write<Error = E>,
38{ 38{
39 async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), Self::Error> { 39 async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
40 self.wrapped.read(address, buffer) 40 self.wrapped.read(address, read)
41 } 41 }
42 42
43 async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), Self::Error> { 43 async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
44 self.wrapped.write(address, bytes) 44 self.wrapped.write(address, write)
45 } 45 }
46 46
47 async fn write_read<'a>( 47 async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
48 &'a mut self, 48 self.wrapped.write_read(address, write, read)
49 address: u8,
50 bytes: &'a [u8],
51 buffer: &'a mut [u8],
52 ) -> Result<(), Self::Error> {
53 self.wrapped.write_read(address, bytes, buffer)
54 } 49 }
55 50
56 async fn transaction<'a, 'b>( 51 async fn transaction(
57 &'a mut self, 52 &mut self,
58 address: u8, 53 address: u8,
59 operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], 54 operations: &mut [embedded_hal_1::i2c::Operation<'_>],
60 ) -> Result<(), Self::Error> { 55 ) -> Result<(), Self::Error> {
61 let _ = address; 56 let _ = address;
62 let _ = operations; 57 let _ = operations;
diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs
index 8da042228..a23fbdc41 100644
--- a/embassy-embedded-hal/src/lib.rs
+++ b/embassy-embedded-hal/src/lib.rs
@@ -1,7 +1,7 @@
1#![cfg_attr(not(feature = "std"), no_std)] 1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr( 2#![cfg_attr(
3 feature = "nightly", 3 feature = "nightly",
4 feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) 4 feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections, try_blocks)
5)] 5)]
6#![cfg_attr(feature = "nightly", allow(incomplete_features))] 6#![cfg_attr(feature = "nightly", allow(incomplete_features))]
7#![warn(missing_docs)] 7#![warn(missing_docs)]
diff --git a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs
index c5e1fd415..829554045 100644
--- a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs
+++ b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs
@@ -54,35 +54,35 @@ where
54 M: RawMutex + 'static, 54 M: RawMutex + 'static,
55 BUS: i2c::I2c + 'static, 55 BUS: i2c::I2c + 'static,
56{ 56{
57 async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> { 57 async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
58 let mut bus = self.bus.lock().await; 58 let mut bus = self.bus.lock().await;
59 bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; 59 bus.read(address, read).await.map_err(I2cDeviceError::I2c)?;
60 Ok(()) 60 Ok(())
61 } 61 }
62 62
63 async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), I2cDeviceError<BUS::Error>> { 63 async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
64 let mut bus = self.bus.lock().await; 64 let mut bus = self.bus.lock().await;
65 bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; 65 bus.write(address, write).await.map_err(I2cDeviceError::I2c)?;
66 Ok(()) 66 Ok(())
67 } 67 }
68 68
69 async fn write_read<'a>( 69 async fn write_read(
70 &'a mut self, 70 &mut self,
71 address: u8, 71 address: u8,
72 wr_buffer: &'a [u8], 72 write: &[u8],
73 rd_buffer: &'a mut [u8], 73 read: &mut [u8],
74 ) -> Result<(), I2cDeviceError<BUS::Error>> { 74 ) -> Result<(), I2cDeviceError<BUS::Error>> {
75 let mut bus = self.bus.lock().await; 75 let mut bus = self.bus.lock().await;
76 bus.write_read(address, wr_buffer, rd_buffer) 76 bus.write_read(address, write, read)
77 .await 77 .await
78 .map_err(I2cDeviceError::I2c)?; 78 .map_err(I2cDeviceError::I2c)?;
79 Ok(()) 79 Ok(())
80 } 80 }
81 81
82 async fn transaction<'a, 'b>( 82 async fn transaction(
83 &'a mut self, 83 &mut self,
84 address: u8, 84 address: u8,
85 operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], 85 operations: &mut [embedded_hal_async::i2c::Operation<'_>],
86 ) -> Result<(), I2cDeviceError<BUS::Error>> { 86 ) -> Result<(), I2cDeviceError<BUS::Error>> {
87 let _ = address; 87 let _ = address;
88 let _ = operations; 88 let _ = operations;
@@ -121,25 +121,25 @@ where
121 M: RawMutex + 'static, 121 M: RawMutex + 'static,
122 BUS: i2c::I2c + SetConfig + 'static, 122 BUS: i2c::I2c + SetConfig + 'static,
123{ 123{
124 async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> { 124 async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
125 let mut bus = self.bus.lock().await; 125 let mut bus = self.bus.lock().await;
126 bus.set_config(&self.config); 126 bus.set_config(&self.config);
127 bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; 127 bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?;
128 Ok(()) 128 Ok(())
129 } 129 }
130 130
131 async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), I2cDeviceError<BUS::Error>> { 131 async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
132 let mut bus = self.bus.lock().await; 132 let mut bus = self.bus.lock().await;
133 bus.set_config(&self.config); 133 bus.set_config(&self.config);
134 bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; 134 bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?;
135 Ok(()) 135 Ok(())
136 } 136 }
137 137
138 async fn write_read<'a>( 138 async fn write_read(
139 &'a mut self, 139 &mut self,
140 address: u8, 140 address: u8,
141 wr_buffer: &'a [u8], 141 wr_buffer: &[u8],
142 rd_buffer: &'a mut [u8], 142 rd_buffer: &mut [u8],
143 ) -> Result<(), I2cDeviceError<BUS::Error>> { 143 ) -> Result<(), I2cDeviceError<BUS::Error>> {
144 let mut bus = self.bus.lock().await; 144 let mut bus = self.bus.lock().await;
145 bus.set_config(&self.config); 145 bus.set_config(&self.config);
@@ -149,11 +149,7 @@ where
149 Ok(()) 149 Ok(())
150 } 150 }
151 151
152 async fn transaction<'a, 'b>( 152 async fn transaction(&mut self, address: u8, operations: &mut [i2c::Operation<'_>]) -> Result<(), Self::Error> {
153 &'a mut self,
154 address: u8,
155 operations: &'a mut [embedded_hal_async::i2c::Operation<'b>],
156 ) -> Result<(), I2cDeviceError<BUS::Error>> {
157 let _ = address; 153 let _ = address;
158 let _ = operations; 154 let _ = operations;
159 todo!() 155 todo!()
diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs
index d25716655..b5549a6cd 100644
--- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs
+++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs
@@ -25,12 +25,11 @@
25//! let spi_dev2 = SpiDevice::new(spi_bus, cs_pin2); 25//! let spi_dev2 = SpiDevice::new(spi_bus, cs_pin2);
26//! let display2 = ST7735::new(spi_dev2, dc2, rst2, Default::default(), 160, 128); 26//! let display2 = ST7735::new(spi_dev2, dc2, rst2, Default::default(), 160, 128);
27//! ``` 27//! ```
28use core::future::Future;
29 28
30use embassy_sync::blocking_mutex::raw::RawMutex; 29use embassy_sync::blocking_mutex::raw::RawMutex;
31use embassy_sync::mutex::Mutex; 30use embassy_sync::mutex::Mutex;
32use embedded_hal_1::digital::OutputPin; 31use embedded_hal_1::digital::OutputPin;
33use embedded_hal_1::spi::ErrorType; 32use embedded_hal_1::spi::Operation;
34use embedded_hal_async::spi; 33use embedded_hal_async::spi;
35 34
36use crate::shared_bus::SpiDeviceError; 35use crate::shared_bus::SpiDeviceError;
@@ -57,33 +56,92 @@ where
57 type Error = SpiDeviceError<BUS::Error, CS::Error>; 56 type Error = SpiDeviceError<BUS::Error, CS::Error>;
58} 57}
59 58
60unsafe impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS> 59impl<M, BUS, CS> spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
61where 60where
62 M: RawMutex + 'static, 61 M: RawMutex,
63 BUS: spi::SpiBusFlush + 'static, 62 BUS: spi::SpiBusRead,
64 CS: OutputPin, 63 CS: OutputPin,
65{ 64{
66 type Bus = BUS; 65 async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
66 let mut bus = self.bus.lock().await;
67 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
68
69 let op_res: Result<(), BUS::Error> = try {
70 for buf in operations {
71 bus.read(buf).await?;
72 }
73 };
74
75 // On failure, it's important to still flush and deassert CS.
76 let flush_res = bus.flush().await;
77 let cs_res = self.cs.set_high();
67 78
68 async fn transaction<R, F, Fut>(&mut self, f: F) -> Result<R, Self::Error> 79 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
69 where 80 flush_res.map_err(SpiDeviceError::Spi)?;
70 F: FnOnce(*mut Self::Bus) -> Fut, 81 cs_res.map_err(SpiDeviceError::Cs)?;
71 Fut: Future<Output = Result<R, <Self::Bus as ErrorType>::Error>>, 82
72 { 83 Ok(op_res)
84 }
85}
86
87impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
88where
89 M: RawMutex,
90 BUS: spi::SpiBusWrite,
91 CS: OutputPin,
92{
93 async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
73 let mut bus = self.bus.lock().await; 94 let mut bus = self.bus.lock().await;
74 self.cs.set_low().map_err(SpiDeviceError::Cs)?; 95 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
75 96
76 let f_res = f(&mut *bus).await; 97 let op_res: Result<(), BUS::Error> = try {
98 for buf in operations {
99 bus.write(buf).await?;
100 }
101 };
77 102
78 // On failure, it's important to still flush and deassert CS. 103 // On failure, it's important to still flush and deassert CS.
79 let flush_res = bus.flush().await; 104 let flush_res = bus.flush().await;
80 let cs_res = self.cs.set_high(); 105 let cs_res = self.cs.set_high();
81 106
82 let f_res = f_res.map_err(SpiDeviceError::Spi)?; 107 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
83 flush_res.map_err(SpiDeviceError::Spi)?; 108 flush_res.map_err(SpiDeviceError::Spi)?;
84 cs_res.map_err(SpiDeviceError::Cs)?; 109 cs_res.map_err(SpiDeviceError::Cs)?;
85 110
86 Ok(f_res) 111 Ok(op_res)
112 }
113}
114
115impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
116where
117 M: RawMutex,
118 BUS: spi::SpiBus,
119 CS: OutputPin,
120{
121 async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
122 let mut bus = self.bus.lock().await;
123 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
124
125 let op_res: Result<(), BUS::Error> = try {
126 for op in operations {
127 match op {
128 Operation::Read(buf) => bus.read(buf).await?,
129 Operation::Write(buf) => bus.write(buf).await?,
130 Operation::Transfer(read, write) => bus.transfer(read, write).await?,
131 Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
132 }
133 }
134 };
135
136 // On failure, it's important to still flush and deassert CS.
137 let flush_res = bus.flush().await;
138 let cs_res = self.cs.set_high();
139
140 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
141 flush_res.map_err(SpiDeviceError::Spi)?;
142 cs_res.map_err(SpiDeviceError::Cs)?;
143
144 Ok(op_res)
87 } 145 }
88} 146}
89 147
@@ -114,33 +172,94 @@ where
114 type Error = SpiDeviceError<BUS::Error, CS::Error>; 172 type Error = SpiDeviceError<BUS::Error, CS::Error>;
115} 173}
116 174
117unsafe impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> 175impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
176where
177 M: RawMutex,
178 BUS: spi::SpiBusWrite + SetConfig,
179 CS: OutputPin,
180{
181 async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
182 let mut bus = self.bus.lock().await;
183 bus.set_config(&self.config);
184 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
185
186 let op_res: Result<(), BUS::Error> = try {
187 for buf in operations {
188 bus.write(buf).await?;
189 }
190 };
191
192 // On failure, it's important to still flush and deassert CS.
193 let flush_res = bus.flush().await;
194 let cs_res = self.cs.set_high();
195
196 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
197 flush_res.map_err(SpiDeviceError::Spi)?;
198 cs_res.map_err(SpiDeviceError::Cs)?;
199
200 Ok(op_res)
201 }
202}
203
204impl<M, BUS, CS> spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
118where 205where
119 M: RawMutex + 'static, 206 M: RawMutex,
120 BUS: spi::SpiBusFlush + SetConfig + 'static, 207 BUS: spi::SpiBusRead + SetConfig,
121 CS: OutputPin, 208 CS: OutputPin,
122{ 209{
123 type Bus = BUS; 210 async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
211 let mut bus = self.bus.lock().await;
212 bus.set_config(&self.config);
213 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
214
215 let op_res: Result<(), BUS::Error> = try {
216 for buf in operations {
217 bus.read(buf).await?;
218 }
219 };
220
221 // On failure, it's important to still flush and deassert CS.
222 let flush_res = bus.flush().await;
223 let cs_res = self.cs.set_high();
224
225 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
226 flush_res.map_err(SpiDeviceError::Spi)?;
227 cs_res.map_err(SpiDeviceError::Cs)?;
228
229 Ok(op_res)
230 }
231}
124 232
125 async fn transaction<R, F, Fut>(&mut self, f: F) -> Result<R, Self::Error> 233impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
126 where 234where
127 F: FnOnce(*mut Self::Bus) -> Fut, 235 M: RawMutex,
128 Fut: Future<Output = Result<R, <Self::Bus as ErrorType>::Error>>, 236 BUS: spi::SpiBus + SetConfig,
129 { 237 CS: OutputPin,
238{
239 async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
130 let mut bus = self.bus.lock().await; 240 let mut bus = self.bus.lock().await;
131 bus.set_config(&self.config); 241 bus.set_config(&self.config);
132 self.cs.set_low().map_err(SpiDeviceError::Cs)?; 242 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
133 243
134 let f_res = f(&mut *bus).await; 244 let op_res: Result<(), BUS::Error> = try {
245 for op in operations {
246 match op {
247 Operation::Read(buf) => bus.read(buf).await?,
248 Operation::Write(buf) => bus.write(buf).await?,
249 Operation::Transfer(read, write) => bus.transfer(read, write).await?,
250 Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
251 }
252 }
253 };
135 254
136 // On failure, it's important to still flush and deassert CS. 255 // On failure, it's important to still flush and deassert CS.
137 let flush_res = bus.flush().await; 256 let flush_res = bus.flush().await;
138 let cs_res = self.cs.set_high(); 257 let cs_res = self.cs.set_high();
139 258
140 let f_res = f_res.map_err(SpiDeviceError::Spi)?; 259 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
141 flush_res.map_err(SpiDeviceError::Spi)?; 260 flush_res.map_err(SpiDeviceError::Spi)?;
142 cs_res.map_err(SpiDeviceError::Cs)?; 261 cs_res.map_err(SpiDeviceError::Cs)?;
143 262
144 Ok(f_res) 263 Ok(op_res)
145 } 264 }
146} 265}
diff --git a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs
index 892000b26..1fe520e6c 100644
--- a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs
+++ b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs
@@ -72,34 +72,6 @@ where
72 let _ = operations; 72 let _ = operations;
73 todo!() 73 todo!()
74 } 74 }
75
76 fn write_iter<B: IntoIterator<Item = u8>>(&mut self, addr: u8, bytes: B) -> Result<(), Self::Error> {
77 let _ = addr;
78 let _ = bytes;
79 todo!()
80 }
81
82 fn write_iter_read<B: IntoIterator<Item = u8>>(
83 &mut self,
84 addr: u8,
85 bytes: B,
86 buffer: &mut [u8],
87 ) -> Result<(), Self::Error> {
88 let _ = addr;
89 let _ = bytes;
90 let _ = buffer;
91 todo!()
92 }
93
94 fn transaction_iter<'a, O: IntoIterator<Item = Operation<'a>>>(
95 &mut self,
96 address: u8,
97 operations: O,
98 ) -> Result<(), Self::Error> {
99 let _ = address;
100 let _ = operations;
101 todo!()
102 }
103} 75}
104 76
105impl<'a, M, BUS, E> embedded_hal_02::blocking::i2c::Write for I2cDevice<'_, M, BUS> 77impl<'a, M, BUS, E> embedded_hal_02::blocking::i2c::Write for I2cDevice<'_, M, BUS>
@@ -204,32 +176,4 @@ where
204 let _ = operations; 176 let _ = operations;
205 todo!() 177 todo!()
206 } 178 }
207
208 fn write_iter<B: IntoIterator<Item = u8>>(&mut self, addr: u8, bytes: B) -> Result<(), Self::Error> {
209 let _ = addr;
210 let _ = bytes;
211 todo!()
212 }
213
214 fn write_iter_read<B: IntoIterator<Item = u8>>(
215 &mut self,
216 addr: u8,
217 bytes: B,
218 buffer: &mut [u8],
219 ) -> Result<(), Self::Error> {
220 let _ = addr;
221 let _ = bytes;
222 let _ = buffer;
223 todo!()
224 }
225
226 fn transaction_iter<'a, O: IntoIterator<Item = Operation<'a>>>(
227 &mut self,
228 address: u8,
229 operations: O,
230 ) -> Result<(), Self::Error> {
231 let _ = address;
232 let _ = operations;
233 todo!()
234 }
235} 179}
diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs
index 4a08dc36e..7982ffb6e 100644
--- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs
+++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs
@@ -23,8 +23,7 @@ use core::cell::RefCell;
23use embassy_sync::blocking_mutex::raw::RawMutex; 23use embassy_sync::blocking_mutex::raw::RawMutex;
24use embassy_sync::blocking_mutex::Mutex; 24use embassy_sync::blocking_mutex::Mutex;
25use embedded_hal_1::digital::OutputPin; 25use embedded_hal_1::digital::OutputPin;
26use embedded_hal_1::spi; 26use embedded_hal_1::spi::{self, Operation, SpiBus, SpiBusRead, SpiBusWrite};
27use embedded_hal_1::spi::SpiBusFlush;
28 27
29use crate::shared_bus::SpiDeviceError; 28use crate::shared_bus::SpiDeviceError;
30use crate::SetConfig; 29use crate::SetConfig;
@@ -50,30 +49,85 @@ where
50 type Error = SpiDeviceError<BUS::Error, CS::Error>; 49 type Error = SpiDeviceError<BUS::Error, CS::Error>;
51} 50}
52 51
53impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> 52impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
54where 53where
55 M: RawMutex, 54 M: RawMutex,
56 BUS: SpiBusFlush, 55 BUS: SpiBusRead,
57 CS: OutputPin, 56 CS: OutputPin,
58{ 57{
59 type Bus = BUS; 58 fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
59 self.bus.lock(|bus| {
60 let mut bus = bus.borrow_mut();
61 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
62
63 let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
60 64
61 fn transaction<R>(&mut self, f: impl FnOnce(&mut Self::Bus) -> Result<R, BUS::Error>) -> Result<R, Self::Error> { 65 // On failure, it's important to still flush and deassert CS.
66 let flush_res = bus.flush();
67 let cs_res = self.cs.set_high();
68
69 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
70 flush_res.map_err(SpiDeviceError::Spi)?;
71 cs_res.map_err(SpiDeviceError::Cs)?;
72
73 Ok(op_res)
74 })
75 }
76}
77
78impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
79where
80 M: RawMutex,
81 BUS: SpiBusWrite,
82 CS: OutputPin,
83{
84 fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
85 self.bus.lock(|bus| {
86 let mut bus = bus.borrow_mut();
87 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
88
89 let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
90
91 // On failure, it's important to still flush and deassert CS.
92 let flush_res = bus.flush();
93 let cs_res = self.cs.set_high();
94
95 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
96 flush_res.map_err(SpiDeviceError::Spi)?;
97 cs_res.map_err(SpiDeviceError::Cs)?;
98
99 Ok(op_res)
100 })
101 }
102}
103
104impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
105where
106 M: RawMutex,
107 BUS: SpiBus,
108 CS: OutputPin,
109{
110 fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
62 self.bus.lock(|bus| { 111 self.bus.lock(|bus| {
63 let mut bus = bus.borrow_mut(); 112 let mut bus = bus.borrow_mut();
64 self.cs.set_low().map_err(SpiDeviceError::Cs)?; 113 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
65 114
66 let f_res = f(&mut bus); 115 let op_res = operations.iter_mut().try_for_each(|op| match op {
116 Operation::Read(buf) => bus.read(buf),
117 Operation::Write(buf) => bus.write(buf),
118 Operation::Transfer(read, write) => bus.transfer(read, write),
119 Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
120 });
67 121
68 // On failure, it's important to still flush and deassert CS. 122 // On failure, it's important to still flush and deassert CS.
69 let flush_res = bus.flush(); 123 let flush_res = bus.flush();
70 let cs_res = self.cs.set_high(); 124 let cs_res = self.cs.set_high();
71 125
72 let f_res = f_res.map_err(SpiDeviceError::Spi)?; 126 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
73 flush_res.map_err(SpiDeviceError::Spi)?; 127 flush_res.map_err(SpiDeviceError::Spi)?;
74 cs_res.map_err(SpiDeviceError::Cs)?; 128 cs_res.map_err(SpiDeviceError::Cs)?;
75 129
76 Ok(f_res) 130 Ok(op_res)
77 }) 131 })
78 } 132 }
79} 133}
@@ -89,11 +143,11 @@ where
89 self.bus.lock(|bus| { 143 self.bus.lock(|bus| {
90 let mut bus = bus.borrow_mut(); 144 let mut bus = bus.borrow_mut();
91 self.cs.set_low().map_err(SpiDeviceError::Cs)?; 145 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
92 let f_res = bus.transfer(words); 146 let op_res = bus.transfer(words);
93 let cs_res = self.cs.set_high(); 147 let cs_res = self.cs.set_high();
94 let f_res = f_res.map_err(SpiDeviceError::Spi)?; 148 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
95 cs_res.map_err(SpiDeviceError::Cs)?; 149 cs_res.map_err(SpiDeviceError::Cs)?;
96 Ok(f_res) 150 Ok(op_res)
97 }) 151 })
98 } 152 }
99} 153}
@@ -110,11 +164,11 @@ where
110 self.bus.lock(|bus| { 164 self.bus.lock(|bus| {
111 let mut bus = bus.borrow_mut(); 165 let mut bus = bus.borrow_mut();
112 self.cs.set_low().map_err(SpiDeviceError::Cs)?; 166 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
113 let f_res = bus.write(words); 167 let op_res = bus.write(words);
114 let cs_res = self.cs.set_high(); 168 let cs_res = self.cs.set_high();
115 let f_res = f_res.map_err(SpiDeviceError::Spi)?; 169 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
116 cs_res.map_err(SpiDeviceError::Cs)?; 170 cs_res.map_err(SpiDeviceError::Cs)?;
117 Ok(f_res) 171 Ok(op_res)
118 }) 172 })
119 } 173 }
120} 174}
@@ -146,30 +200,85 @@ where
146 type Error = SpiDeviceError<BUS::Error, CS::Error>; 200 type Error = SpiDeviceError<BUS::Error, CS::Error>;
147} 201}
148 202
149impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> 203impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
150where 204where
151 M: RawMutex, 205 M: RawMutex,
152 BUS: SpiBusFlush + SetConfig, 206 BUS: SpiBusRead + SetConfig,
153 CS: OutputPin, 207 CS: OutputPin,
154{ 208{
155 type Bus = BUS; 209 fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
210 self.bus.lock(|bus| {
211 let mut bus = bus.borrow_mut();
212 bus.set_config(&self.config);
213 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
214
215 let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
156 216
157 fn transaction<R>(&mut self, f: impl FnOnce(&mut Self::Bus) -> Result<R, BUS::Error>) -> Result<R, Self::Error> { 217 // On failure, it's important to still flush and deassert CS.
218 let flush_res = bus.flush();
219 let cs_res = self.cs.set_high();
220
221 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
222 flush_res.map_err(SpiDeviceError::Spi)?;
223 cs_res.map_err(SpiDeviceError::Cs)?;
224 Ok(op_res)
225 })
226 }
227}
228
229impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
230where
231 M: RawMutex,
232 BUS: SpiBusWrite + SetConfig,
233 CS: OutputPin,
234{
235 fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
236 self.bus.lock(|bus| {
237 let mut bus = bus.borrow_mut();
238 bus.set_config(&self.config);
239 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
240
241 let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
242
243 // On failure, it's important to still flush and deassert CS.
244 let flush_res = bus.flush();
245 let cs_res = self.cs.set_high();
246
247 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
248 flush_res.map_err(SpiDeviceError::Spi)?;
249 cs_res.map_err(SpiDeviceError::Cs)?;
250 Ok(op_res)
251 })
252 }
253}
254
255impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
256where
257 M: RawMutex,
258 BUS: SpiBus + SetConfig,
259 CS: OutputPin,
260{
261 fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
158 self.bus.lock(|bus| { 262 self.bus.lock(|bus| {
159 let mut bus = bus.borrow_mut(); 263 let mut bus = bus.borrow_mut();
160 bus.set_config(&self.config); 264 bus.set_config(&self.config);
161 self.cs.set_low().map_err(SpiDeviceError::Cs)?; 265 self.cs.set_low().map_err(SpiDeviceError::Cs)?;
162 266
163 let f_res = f(&mut bus); 267 let op_res = operations.iter_mut().try_for_each(|op| match op {
268 Operation::Read(buf) => bus.read(buf),
269 Operation::Write(buf) => bus.write(buf),
270 Operation::Transfer(read, write) => bus.transfer(read, write),
271 Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
272 });
164 273
165 // On failure, it's important to still flush and deassert CS. 274 // On failure, it's important to still flush and deassert CS.
166 let flush_res = bus.flush(); 275 let flush_res = bus.flush();
167 let cs_res = self.cs.set_high(); 276 let cs_res = self.cs.set_high();
168 277
169 let f_res = f_res.map_err(SpiDeviceError::Spi)?; 278 let op_res = op_res.map_err(SpiDeviceError::Spi)?;
170 flush_res.map_err(SpiDeviceError::Spi)?; 279 flush_res.map_err(SpiDeviceError::Spi)?;
171 cs_res.map_err(SpiDeviceError::Cs)?; 280 cs_res.map_err(SpiDeviceError::Cs)?;
172 Ok(f_res) 281 Ok(op_res)
173 }) 282 })
174 } 283 }
175} 284}
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml
index c2868eb98..29e1bd478 100644
--- a/embassy-executor/Cargo.toml
+++ b/embassy-executor/Cargo.toml
@@ -14,30 +14,42 @@ categories = [
14[package.metadata.embassy_docs] 14[package.metadata.embassy_docs]
15src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" 15src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
16src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" 16src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
17features = ["nightly", "defmt"] 17features = ["nightly", "defmt", "pender-callback"]
18flavors = [ 18flavors = [
19 { name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] }, 19 { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] },
20 { name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] }, 20 { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] },
21 { name = "thumbv6m-none-eabi", target = "thumbv6m-none-eabi", features = [] }, 21 { name = "cortex-m", target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] },
22 { name = "thumbv7m-none-eabi", target = "thumbv7m-none-eabi", features = [] }, 22 { name = "riscv32", target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32", "executor-thread"] },
23 { name = "thumbv7em-none-eabi", target = "thumbv7em-none-eabi", features = [] },
24 { name = "thumbv7em-none-eabihf", target = "thumbv7em-none-eabihf", features = [] },
25 { name = "thumbv8m.base-none-eabi", target = "thumbv8m.base-none-eabi", features = [] },
26 { name = "thumbv8m.main-none-eabi", target = "thumbv8m.main-none-eabi", features = [] },
27 { name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] },
28] 23]
29 24
30[package.metadata.docs.rs] 25[package.metadata.docs.rs]
31features = ["std", "nightly", "defmt"] 26default-target = "thumbv7em-none-eabi"
27targets = ["thumbv7em-none-eabi"]
28features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-thread", "executor-interrupt"]
32 29
33[features] 30[features]
34default = [] 31
35std = ["critical-section/std"] 32# Architecture
36wasm = ["dep:wasm-bindgen", "dep:js-sys"] 33_arch = [] # some arch was picked
34arch-std = ["_arch", "critical-section/std"]
35arch-cortex-m = ["_arch", "dep:cortex-m"]
36arch-xtensa = ["_arch"]
37arch-riscv32 = ["_arch"]
38arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
39
40# Enable creating a `Pender` from an arbitrary function pointer callback.
41pender-callback = []
42
43# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
44executor-thread = []
45# Enable the interrupt-mode executor (available in Cortex-M only)
46executor-interrupt = []
37 47
38# Enable nightly-only features 48# Enable nightly-only features
39nightly = [] 49nightly = []
40 50
51turbowakers = []
52
41integrated-timers = ["dep:embassy-time"] 53integrated-timers = ["dep:embassy-time"]
42 54
43# Trace interrupt invocations with rtos-trace. 55# Trace interrupt invocations with rtos-trace.
@@ -53,9 +65,11 @@ embassy-macros = { version = "0.1.0", path = "../embassy-macros" }
53embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true} 65embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true}
54atomic-polyfill = "1.0.1" 66atomic-polyfill = "1.0.1"
55critical-section = "1.1" 67critical-section = "1.1"
56cfg-if = "1.0.0"
57static_cell = "1.0" 68static_cell = "1.0"
58 69
59# WASM dependencies 70# arch-cortex-m dependencies
71cortex-m = { version = "0.7.6", optional = true }
72
73# arch-wasm dependencies
60wasm-bindgen = { version = "0.2.82", optional = true } 74wasm-bindgen = { version = "0.2.82", optional = true }
61js-sys = { version = "0.3", optional = true } 75js-sys = { version = "0.3", optional = true }
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs
index 4b27a264e..d6a55c4c7 100644
--- a/embassy-executor/src/arch/cortex_m.rs
+++ b/embassy-executor/src/arch/cortex_m.rs
@@ -1,59 +1,209 @@
1use core::arch::asm; 1#[cfg(feature = "executor-thread")]
2use core::marker::PhantomData; 2pub use thread::*;
3use core::ptr; 3#[cfg(feature = "executor-thread")]
4 4mod thread {
5use super::{raw, Spawner}; 5 use core::arch::asm;
6 6 use core::marker::PhantomData;
7/// Thread mode executor, using WFE/SEV. 7
8/// 8 #[cfg(feature = "nightly")]
9/// This is the simplest and most common kind of executor. It runs on 9 pub use embassy_macros::main_cortex_m as main;
10/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction 10
11/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction 11 use crate::raw::{Pender, PenderInner};
12/// is executed, to make the `WFE` exit from sleep and poll the task. 12 use crate::{raw, Spawner};
13/// 13
14/// This executor allows for ultra low power consumption for chips where `WFE` 14 #[derive(Copy, Clone)]
15/// triggers low-power sleep without extra steps. If your chip requires extra steps, 15 pub(crate) struct ThreadPender;
16/// you may use [`raw::Executor`] directly to program custom behavior. 16
17pub struct Executor { 17 impl ThreadPender {
18 inner: raw::Executor, 18 pub(crate) fn pend(self) {
19 not_send: PhantomData<*mut ()>, 19 unsafe { core::arch::asm!("sev") }
20 }
21 }
22
23 /// Thread mode executor, using WFE/SEV.
24 ///
25 /// This is the simplest and most common kind of executor. It runs on
26 /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction
27 /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction
28 /// is executed, to make the `WFE` exit from sleep and poll the task.
29 ///
30 /// This executor allows for ultra low power consumption for chips where `WFE`
31 /// triggers low-power sleep without extra steps. If your chip requires extra steps,
32 /// you may use [`raw::Executor`] directly to program custom behavior.
33 pub struct Executor {
34 inner: raw::Executor,
35 not_send: PhantomData<*mut ()>,
36 }
37
38 impl Executor {
39 /// Create a new Executor.
40 pub fn new() -> Self {
41 Self {
42 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
43 not_send: PhantomData,
44 }
45 }
46
47 /// Run the executor.
48 ///
49 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
50 /// this executor. Use it to spawn the initial task(s). After `init` returns,
51 /// the executor starts running the tasks.
52 ///
53 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
54 /// for example by passing it as an argument to the initial tasks.
55 ///
56 /// This function requires `&'static mut self`. This means you have to store the
57 /// Executor instance in a place where it'll live forever and grants you mutable
58 /// access. There's a few ways to do this:
59 ///
60 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
61 /// - a `static mut` (unsafe)
62 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
63 ///
64 /// This function never returns.
65 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
66 init(self.inner.spawner());
67
68 loop {
69 unsafe {
70 self.inner.poll();
71 asm!("wfe");
72 };
73 }
74 }
75 }
20} 76}
21 77
22impl Executor { 78#[cfg(feature = "executor-interrupt")]
23 /// Create a new Executor. 79pub use interrupt::*;
24 pub fn new() -> Self { 80#[cfg(feature = "executor-interrupt")]
25 Self { 81mod interrupt {
26 inner: raw::Executor::new(|_| unsafe { asm!("sev") }, ptr::null_mut()), 82 use core::cell::UnsafeCell;
27 not_send: PhantomData, 83 use core::mem::MaybeUninit;
84
85 use atomic_polyfill::{AtomicBool, Ordering};
86 use cortex_m::interrupt::InterruptNumber;
87 use cortex_m::peripheral::NVIC;
88
89 use crate::raw::{self, Pender, PenderInner};
90
91 #[derive(Clone, Copy)]
92 pub(crate) struct InterruptPender(u16);
93
94 impl InterruptPender {
95 pub(crate) fn pend(self) {
96 // STIR is faster, but is only available in v7 and higher.
97 #[cfg(not(armv6m))]
98 {
99 let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) };
100 nvic.request(self);
101 }
102
103 #[cfg(armv6m)]
104 cortex_m::peripheral::NVIC::pend(self);
28 } 105 }
29 } 106 }
30 107
31 /// Run the executor. 108 unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender {
109 fn number(self) -> u16 {
110 self.0
111 }
112 }
113
114 /// Interrupt mode executor.
32 /// 115 ///
33 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 116 /// This executor runs tasks in interrupt mode. The interrupt handler is set up
34 /// this executor. Use it to spawn the initial task(s). After `init` returns, 117 /// to poll tasks, and when a task is woken the interrupt is pended from software.
35 /// the executor starts running the tasks.
36 /// 118 ///
37 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 119 /// This allows running async tasks at a priority higher than thread mode. One
38 /// for example by passing it as an argument to the initial tasks. 120 /// use case is to leave thread mode free for non-async tasks. Another use case is
121 /// to run multiple executors: one in thread mode for low priority tasks and another in
122 /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
123 /// priority ones.
39 /// 124 ///
40 /// This function requires `&'static mut self`. This means you have to store the 125 /// It is even possible to run multiple interrupt mode executors at different priorities,
41 /// Executor instance in a place where it'll live forever and grants you mutable 126 /// by assigning different priorities to the interrupts. For an example on how to do this,
42 /// access. There's a few ways to do this: 127 /// See the 'multiprio' example for 'embassy-nrf'.
43 /// 128 ///
44 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 129 /// To use it, you have to pick an interrupt that won't be used by the hardware.
45 /// - a `static mut` (unsafe) 130 /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
46 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 131 /// If this is not the case, you may use an interrupt from any unused peripheral.
47 /// 132 ///
48 /// This function never returns. 133 /// It is somewhat more complex to use, it's recommended to use the thread-mode
49 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 134 /// [`Executor`] instead, if it works for your use case.
50 init(self.inner.spawner()); 135 pub struct InterruptExecutor {
136 started: AtomicBool,
137 executor: UnsafeCell<MaybeUninit<raw::Executor>>,
138 }
139
140 unsafe impl Send for InterruptExecutor {}
141 unsafe impl Sync for InterruptExecutor {}
142
143 impl InterruptExecutor {
144 /// Create a new, not started `InterruptExecutor`.
145 #[inline]
146 pub const fn new() -> Self {
147 Self {
148 started: AtomicBool::new(false),
149 executor: UnsafeCell::new(MaybeUninit::uninit()),
150 }
151 }
152
153 /// Executor interrupt callback.
154 ///
155 /// # Safety
156 ///
157 /// You MUST call this from the interrupt handler, and from nowhere else.
158 pub unsafe fn on_interrupt(&'static self) {
159 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
160 executor.poll();
161 }
162
163 /// Start the executor.
164 ///
165 /// This initializes the executor, enables the interrupt, and returns.
166 /// The executor keeps running in the background through the interrupt.
167 ///
168 /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]
169 /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a
170 /// different "thread" (the interrupt), so spawning tasks on it is effectively
171 /// sending them.
172 ///
173 /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from
174 /// a task running in it.
175 ///
176 /// # Interrupt requirements
177 ///
178 /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).
179 ///
180 /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.
181 ///
182 /// You must set the interrupt priority before calling this method. You MUST NOT
183 /// do it after.
184 ///
185 pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner {
186 if self
187 .started
188 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
189 .is_err()
190 {
191 panic!("InterruptExecutor::start() called multiple times on the same executor.");
192 }
51 193
52 loop {
53 unsafe { 194 unsafe {
54 self.inner.poll(); 195 (&mut *self.executor.get())
55 asm!("wfe"); 196 .as_mut_ptr()
56 }; 197 .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender(
198 irq.number(),
199 )))))
200 }
201
202 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
203
204 unsafe { NVIC::unmask(irq) }
205
206 executor.spawner().make_send()
57 } 207 }
58 } 208 }
59} 209}
diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs
index e97a56cda..f66daeae4 100644
--- a/embassy-executor/src/arch/riscv32.rs
+++ b/embassy-executor/src/arch/riscv32.rs
@@ -1,72 +1,83 @@
1use core::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2use core::ptr; 2compile_error!("`executor-interrupt` is not supported with `arch-riscv32`.");
3use core::sync::atomic::{AtomicBool, Ordering};
4 3
5use super::{raw, Spawner}; 4#[cfg(feature = "executor-thread")]
5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
8 use core::marker::PhantomData;
9 use core::sync::atomic::{AtomicBool, Ordering};
6 10
7/// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV 11 use crate::raw::{Pender, PenderInner};
8/// 12 use crate::{raw, Spawner};
9static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
10 13
11/// RISCV32 Executor 14 #[derive(Copy, Clone)]
12pub struct Executor { 15 pub(crate) struct ThreadPender;
13 inner: raw::Executor,
14 not_send: PhantomData<*mut ()>,
15}
16 16
17impl Executor { 17 impl ThreadPender {
18 /// Create a new Executor. 18 #[allow(unused)]
19 pub fn new() -> Self { 19 pub(crate) fn pend(self) {
20 Self { 20 SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
21 // use Signal_Work_Thread_Mode as substitute for local interrupt register
22 inner: raw::Executor::new(
23 |_| {
24 SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
25 },
26 ptr::null_mut(),
27 ),
28 not_send: PhantomData,
29 } 21 }
30 } 22 }
31 23
32 /// Run the executor. 24 /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
33 /// 25 static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
34 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 26
35 /// this executor. Use it to spawn the initial task(s). After `init` returns, 27 /// RISCV32 Executor
36 /// the executor starts running the tasks. 28 pub struct Executor {
37 /// 29 inner: raw::Executor,
38 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 30 not_send: PhantomData<*mut ()>,
39 /// for example by passing it as an argument to the initial tasks. 31 }
40 /// 32
41 /// This function requires `&'static mut self`. This means you have to store the 33 impl Executor {
42 /// Executor instance in a place where it'll live forever and grants you mutable 34 /// Create a new Executor.
43 /// access. There's a few ways to do this: 35 pub fn new() -> Self {
44 /// 36 Self {
45 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 37 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
46 /// - a `static mut` (unsafe) 38 not_send: PhantomData,
47 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 39 }
48 /// 40 }
49 /// This function never returns. 41
50 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 42 /// Run the executor.
51 init(self.inner.spawner()); 43 ///
44 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
45 /// this executor. Use it to spawn the initial task(s). After `init` returns,
46 /// the executor starts running the tasks.
47 ///
48 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
49 /// for example by passing it as an argument to the initial tasks.
50 ///
51 /// This function requires `&'static mut self`. This means you have to store the
52 /// Executor instance in a place where it'll live forever and grants you mutable
53 /// access. There's a few ways to do this:
54 ///
55 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
56 /// - a `static mut` (unsafe)
57 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
58 ///
59 /// This function never returns.
60 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
61 init(self.inner.spawner());
52 62
53 loop { 63 loop {
54 unsafe { 64 unsafe {
55 self.inner.poll(); 65 self.inner.poll();
56 // we do not care about race conditions between the load and store operations, interrupts 66 // we do not care about race conditions between the load and store operations, interrupts
57 //will only set this value to true. 67 //will only set this value to true.
58 critical_section::with(|_| { 68 critical_section::with(|_| {
59 // if there is work to do, loop back to polling 69 // if there is work to do, loop back to polling
60 // TODO can we relax this? 70 // TODO can we relax this?
61 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { 71 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
62 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); 72 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
63 } 73 }
64 // if not, wait for interrupt 74 // if not, wait for interrupt
65 else { 75 else {
66 core::arch::asm!("wfi"); 76 core::arch::asm!("wfi");
67 } 77 }
68 }); 78 });
69 // if an interrupt occurred while waiting, it will be serviced here 79 // if an interrupt occurred while waiting, it will be serviced here
80 }
70 } 81 }
71 } 82 }
72 } 83 }
diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs
index 701f0eb18..4e4a178f0 100644
--- a/embassy-executor/src/arch/std.rs
+++ b/embassy-executor/src/arch/std.rs
@@ -1,84 +1,100 @@
1use std::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2use std::sync::{Condvar, Mutex}; 2compile_error!("`executor-interrupt` is not supported with `arch-std`.");
3 3
4use super::{raw, Spawner}; 4#[cfg(feature = "executor-thread")]
5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
8 use std::marker::PhantomData;
9 use std::sync::{Condvar, Mutex};
5 10
6/// Single-threaded std-based executor. 11 #[cfg(feature = "nightly")]
7pub struct Executor { 12 pub use embassy_macros::main_std as main;
8 inner: raw::Executor, 13
9 not_send: PhantomData<*mut ()>, 14 use crate::raw::{Pender, PenderInner};
10 signaler: &'static Signaler, 15 use crate::{raw, Spawner};
11}
12 16
13impl Executor { 17 #[derive(Copy, Clone)]
14 /// Create a new Executor. 18 pub(crate) struct ThreadPender(&'static Signaler);
15 pub fn new() -> Self { 19
16 let signaler = &*Box::leak(Box::new(Signaler::new())); 20 impl ThreadPender {
17 Self { 21 #[allow(unused)]
18 inner: raw::Executor::new( 22 pub(crate) fn pend(self) {
19 |p| unsafe { 23 self.0.signal()
20 let s = &*(p as *const () as *const Signaler);
21 s.signal()
22 },
23 signaler as *const _ as _,
24 ),
25 not_send: PhantomData,
26 signaler,
27 } 24 }
28 } 25 }
29 26
30 /// Run the executor. 27 /// Single-threaded std-based executor.
31 /// 28 pub struct Executor {
32 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 29 inner: raw::Executor,
33 /// this executor. Use it to spawn the initial task(s). After `init` returns, 30 not_send: PhantomData<*mut ()>,
34 /// the executor starts running the tasks. 31 signaler: &'static Signaler,
35 /// 32 }
36 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
37 /// for example by passing it as an argument to the initial tasks.
38 ///
39 /// This function requires `&'static mut self`. This means you have to store the
40 /// Executor instance in a place where it'll live forever and grants you mutable
41 /// access. There's a few ways to do this:
42 ///
43 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
44 /// - a `static mut` (unsafe)
45 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
46 ///
47 /// This function never returns.
48 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
49 init(self.inner.spawner());
50 33
51 loop { 34 impl Executor {
52 unsafe { self.inner.poll() }; 35 /// Create a new Executor.
53 self.signaler.wait() 36 pub fn new() -> Self {
37 let signaler = &*Box::leak(Box::new(Signaler::new()));
38 Self {
39 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))),
40 not_send: PhantomData,
41 signaler,
42 }
54 } 43 }
55 }
56}
57 44
58struct Signaler { 45 /// Run the executor.
59 mutex: Mutex<bool>, 46 ///
60 condvar: Condvar, 47 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
61} 48 /// this executor. Use it to spawn the initial task(s). After `init` returns,
49 /// the executor starts running the tasks.
50 ///
51 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
52 /// for example by passing it as an argument to the initial tasks.
53 ///
54 /// This function requires `&'static mut self`. This means you have to store the
55 /// Executor instance in a place where it'll live forever and grants you mutable
56 /// access. There's a few ways to do this:
57 ///
58 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
59 /// - a `static mut` (unsafe)
60 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
61 ///
62 /// This function never returns.
63 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
64 init(self.inner.spawner());
62 65
63impl Signaler { 66 loop {
64 fn new() -> Self { 67 unsafe { self.inner.poll() };
65 Self { 68 self.signaler.wait()
66 mutex: Mutex::new(false), 69 }
67 condvar: Condvar::new(),
68 } 70 }
69 } 71 }
70 72
71 fn wait(&self) { 73 struct Signaler {
72 let mut signaled = self.mutex.lock().unwrap(); 74 mutex: Mutex<bool>,
73 while !*signaled { 75 condvar: Condvar,
74 signaled = self.condvar.wait(signaled).unwrap();
75 }
76 *signaled = false;
77 } 76 }
78 77
79 fn signal(&self) { 78 impl Signaler {
80 let mut signaled = self.mutex.lock().unwrap(); 79 fn new() -> Self {
81 *signaled = true; 80 Self {
82 self.condvar.notify_one(); 81 mutex: Mutex::new(false),
82 condvar: Condvar::new(),
83 }
84 }
85
86 fn wait(&self) {
87 let mut signaled = self.mutex.lock().unwrap();
88 while !*signaled {
89 signaled = self.condvar.wait(signaled).unwrap();
90 }
91 *signaled = false;
92 }
93
94 fn signal(&self) {
95 let mut signaled = self.mutex.lock().unwrap();
96 *signaled = true;
97 self.condvar.notify_one();
98 }
83 } 99 }
84} 100}
diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs
index 98091cfbb..08ab16b99 100644
--- a/embassy-executor/src/arch/wasm.rs
+++ b/embassy-executor/src/arch/wasm.rs
@@ -1,74 +1,88 @@
1use core::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2compile_error!("`executor-interrupt` is not supported with `arch-wasm`.");
2 3
3use js_sys::Promise; 4#[cfg(feature = "executor-thread")]
4use wasm_bindgen::prelude::*; 5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
5 8
6use super::raw::util::UninitCell; 9 use core::marker::PhantomData;
7use super::raw::{self};
8use super::Spawner;
9 10
10/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. 11 #[cfg(feature = "nightly")]
11pub struct Executor { 12 pub use embassy_macros::main_wasm as main;
12 inner: raw::Executor, 13 use js_sys::Promise;
13 ctx: &'static WasmContext, 14 use wasm_bindgen::prelude::*;
14 not_send: PhantomData<*mut ()>,
15}
16 15
17pub(crate) struct WasmContext { 16 use crate::raw::util::UninitCell;
18 promise: Promise, 17 use crate::raw::{Pender, PenderInner};
19 closure: UninitCell<Closure<dyn FnMut(JsValue)>>, 18 use crate::{raw, Spawner};
20} 19
20 /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
21 pub struct Executor {
22 inner: raw::Executor,
23 ctx: &'static WasmContext,
24 not_send: PhantomData<*mut ()>,
25 }
26
27 pub(crate) struct WasmContext {
28 promise: Promise,
29 closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
30 }
31
32 #[derive(Copy, Clone)]
33 pub(crate) struct ThreadPender(&'static WasmContext);
21 34
22impl WasmContext { 35 impl ThreadPender {
23 pub fn new() -> Self { 36 #[allow(unused)]
24 Self { 37 pub(crate) fn pend(self) {
25 promise: Promise::resolve(&JsValue::undefined()), 38 let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() });
26 closure: UninitCell::uninit(),
27 } 39 }
28 } 40 }
29}
30 41
31impl Executor { 42 impl WasmContext {
32 /// Create a new Executor. 43 pub fn new() -> Self {
33 pub fn new() -> Self { 44 Self {
34 let ctx = &*Box::leak(Box::new(WasmContext::new())); 45 promise: Promise::resolve(&JsValue::undefined()),
35 let inner = raw::Executor::new( 46 closure: UninitCell::uninit(),
36 |p| unsafe { 47 }
37 let ctx = &*(p as *const () as *const WasmContext);
38 let _ = ctx.promise.then(ctx.closure.as_mut());
39 },
40 ctx as *const _ as _,
41 );
42 Self {
43 inner,
44 not_send: PhantomData,
45 ctx,
46 } 48 }
47 } 49 }
48 50
49 /// Run the executor. 51 impl Executor {
50 /// 52 /// Create a new Executor.
51 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 53 pub fn new() -> Self {
52 /// this executor. Use it to spawn the initial task(s). After `init` returns, 54 let ctx = &*Box::leak(Box::new(WasmContext::new()));
53 /// the executor starts running the tasks. 55 Self {
54 /// 56 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))),
55 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 57 not_send: PhantomData,
56 /// for example by passing it as an argument to the initial tasks. 58 ctx,
57 /// 59 }
58 /// This function requires `&'static mut self`. This means you have to store the 60 }
59 /// Executor instance in a place where it'll live forever and grants you mutable 61
60 /// access. There's a few ways to do this: 62 /// Run the executor.
61 /// 63 ///
62 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 64 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
63 /// - a `static mut` (unsafe) 65 /// this executor. Use it to spawn the initial task(s). After `init` returns,
64 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 66 /// the executor starts running the tasks.
65 pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { 67 ///
66 unsafe { 68 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
67 let executor = &self.inner; 69 /// for example by passing it as an argument to the initial tasks.
68 self.ctx.closure.write(Closure::new(move |_| { 70 ///
69 executor.poll(); 71 /// This function requires `&'static mut self`. This means you have to store the
70 })); 72 /// Executor instance in a place where it'll live forever and grants you mutable
71 init(self.inner.spawner()); 73 /// access. There's a few ways to do this:
74 ///
75 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
76 /// - a `static mut` (unsafe)
77 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
78 pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
79 unsafe {
80 let executor = &self.inner;
81 self.ctx.closure.write(Closure::new(move |_| {
82 executor.poll();
83 }));
84 init(self.inner.spawner());
85 }
72 } 86 }
73 } 87 }
74} 88}
diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs
index 4ee0d9f78..61ea92c16 100644
--- a/embassy-executor/src/arch/xtensa.rs
+++ b/embassy-executor/src/arch/xtensa.rs
@@ -1,73 +1,84 @@
1use core::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2use core::ptr; 2compile_error!("`executor-interrupt` is not supported with `arch-xtensa`.");
3use core::sync::atomic::{AtomicBool, Ordering};
4 3
5use super::{raw, Spawner}; 4#[cfg(feature = "executor-thread")]
5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
8 use core::marker::PhantomData;
9 use core::sync::atomic::{AtomicBool, Ordering};
6 10
7/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa 11 use crate::raw::{Pender, PenderInner};
8/// 12 use crate::{raw, Spawner};
9static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
10 13
11/// Xtensa Executor 14 #[derive(Copy, Clone)]
12pub struct Executor { 15 pub(crate) struct ThreadPender;
13 inner: raw::Executor,
14 not_send: PhantomData<*mut ()>,
15}
16 16
17impl Executor { 17 impl ThreadPender {
18 /// Create a new Executor. 18 #[allow(unused)]
19 pub fn new() -> Self { 19 pub(crate) fn pend(self) {
20 Self { 20 SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
21 // use Signal_Work_Thread_Mode as substitute for local interrupt register
22 inner: raw::Executor::new(
23 |_| {
24 SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
25 },
26 ptr::null_mut(),
27 ),
28 not_send: PhantomData,
29 } 21 }
30 } 22 }
31 23
32 /// Run the executor. 24 /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
33 /// 25 static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
34 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 26
35 /// this executor. Use it to spawn the initial task(s). After `init` returns, 27 /// Xtensa Executor
36 /// the executor starts running the tasks. 28 pub struct Executor {
37 /// 29 inner: raw::Executor,
38 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 30 not_send: PhantomData<*mut ()>,
39 /// for example by passing it as an argument to the initial tasks. 31 }
40 /// 32
41 /// This function requires `&'static mut self`. This means you have to store the 33 impl Executor {
42 /// Executor instance in a place where it'll live forever and grants you mutable 34 /// Create a new Executor.
43 /// access. There's a few ways to do this: 35 pub fn new() -> Self {
44 /// 36 Self {
45 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 37 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
46 /// - a `static mut` (unsafe) 38 not_send: PhantomData,
47 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 39 }
48 /// 40 }
49 /// This function never returns. 41
50 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 42 /// Run the executor.
51 init(self.inner.spawner()); 43 ///
44 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
45 /// this executor. Use it to spawn the initial task(s). After `init` returns,
46 /// the executor starts running the tasks.
47 ///
48 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
49 /// for example by passing it as an argument to the initial tasks.
50 ///
51 /// This function requires `&'static mut self`. This means you have to store the
52 /// Executor instance in a place where it'll live forever and grants you mutable
53 /// access. There's a few ways to do this:
54 ///
55 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
56 /// - a `static mut` (unsafe)
57 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
58 ///
59 /// This function never returns.
60 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
61 init(self.inner.spawner());
52 62
53 loop { 63 loop {
54 unsafe { 64 unsafe {
55 self.inner.poll(); 65 self.inner.poll();
56 // we do not care about race conditions between the load and store operations, interrupts 66 // we do not care about race conditions between the load and store operations, interrupts
57 // will only set this value to true. 67 // will only set this value to true.
58 // if there is work to do, loop back to polling 68 // if there is work to do, loop back to polling
59 // TODO can we relax this? 69 // TODO can we relax this?
60 critical_section::with(|_| { 70 critical_section::with(|_| {
61 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { 71 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
62 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); 72 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
63 } else { 73 } else {
64 // waiti sets the PS.INTLEVEL when slipping into sleep 74 // waiti sets the PS.INTLEVEL when slipping into sleep
65 // because critical sections in Xtensa are implemented via increasing 75 // because critical sections in Xtensa are implemented via increasing
66 // PS.INTLEVEL the critical section ends here 76 // PS.INTLEVEL the critical section ends here
67 // take care not add code after `waiti` if it needs to be inside the CS 77 // take care not add code after `waiti` if it needs to be inside the CS
68 core::arch::asm!("waiti 0"); // critical section ends here 78 core::arch::asm!("waiti 0"); // critical section ends here
69 } 79 }
70 }); 80 });
81 }
71 } 82 }
72 } 83 }
73 } 84 }
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs
index 4c7e2f4cd..3ce687eb6 100644
--- a/embassy-executor/src/lib.rs
+++ b/embassy-executor/src/lib.rs
@@ -1,5 +1,5 @@
1#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] 1#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)]
2#![cfg_attr(all(feature = "nightly", target_arch = "xtensa"), feature(asm_experimental_arch))] 2#![cfg_attr(all(feature = "nightly", feature = "arch-xtensa"), feature(asm_experimental_arch))]
3#![allow(clippy::new_without_default)] 3#![allow(clippy::new_without_default)]
4#![doc = include_str!("../README.md")] 4#![doc = include_str!("../README.md")]
5#![warn(missing_docs)] 5#![warn(missing_docs)]
@@ -10,47 +10,43 @@ pub(crate) mod fmt;
10#[cfg(feature = "nightly")] 10#[cfg(feature = "nightly")]
11pub use embassy_macros::task; 11pub use embassy_macros::task;
12 12
13cfg_if::cfg_if! { 13macro_rules! check_at_most_one {
14 if #[cfg(cortex_m)] { 14 (@amo [$($feats:literal)*] [] [$($res:tt)*]) => {
15 #[path="arch/cortex_m.rs"] 15 #[cfg(any($($res)*))]
16 mod arch; 16 compile_error!(concat!("At most one of these features can be enabled at the same time:", $(" `", $feats, "`",)*));
17 pub use arch::*; 17 };
18 #[cfg(feature = "nightly")] 18 (@amo $feats:tt [$curr:literal $($rest:literal)*] [$($res:tt)*]) => {
19 pub use embassy_macros::main_cortex_m as main; 19 check_at_most_one!(@amo $feats [$($rest)*] [$($res)* $(all(feature=$curr, feature=$rest),)*]);
20 } 20 };
21 else if #[cfg(target_arch="riscv32")] { 21 ($($f:literal),*$(,)?) => {
22 #[path="arch/riscv32.rs"] 22 check_at_most_one!(@amo [$($f)*] [$($f)*] []);
23 mod arch; 23 };
24 pub use arch::*;
25 #[cfg(feature = "nightly")]
26 pub use embassy_macros::main_riscv as main;
27 }
28 else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] {
29 #[path="arch/xtensa.rs"]
30 mod arch;
31 pub use arch::*;
32 }
33 else if #[cfg(feature="wasm")] {
34 #[path="arch/wasm.rs"]
35 mod arch;
36 pub use arch::*;
37 #[cfg(feature = "nightly")]
38 pub use embassy_macros::main_wasm as main;
39 }
40 else if #[cfg(feature="std")] {
41 #[path="arch/std.rs"]
42 mod arch;
43 pub use arch::*;
44 #[cfg(feature = "nightly")]
45 pub use embassy_macros::main_std as main;
46 }
47} 24}
25check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-xtensa", "arch-std", "arch-wasm",);
26
27#[cfg(feature = "_arch")]
28#[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.rs")]
29#[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")]
30#[cfg_attr(feature = "arch-xtensa", path = "arch/xtensa.rs")]
31#[cfg_attr(feature = "arch-std", path = "arch/std.rs")]
32#[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")]
33mod arch;
34
35#[cfg(feature = "_arch")]
36pub use arch::*;
48 37
38pub mod raw;
39
40mod spawner;
41pub use spawner::*;
42
43/// Implementation details for embassy macros.
44/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
49#[doc(hidden)] 45#[doc(hidden)]
50/// Implementation details for embassy macros. DO NOT USE. 46pub mod _export {
51pub mod export {
52 #[cfg(feature = "rtos-trace")] 47 #[cfg(feature = "rtos-trace")]
53 pub use rtos_trace::trace; 48 pub use rtos_trace::trace;
49 pub use static_cell::StaticCell;
54 50
55 /// Expands the given block of code when `embassy-executor` is compiled with 51 /// Expands the given block of code when `embassy-executor` is compiled with
56 /// the `rtos-trace-interrupt` feature. 52 /// the `rtos-trace-interrupt` feature.
@@ -70,14 +66,3 @@ pub mod export {
70 ($($tt:tt)*) => {}; 66 ($($tt:tt)*) => {};
71 } 67 }
72} 68}
73
74pub mod raw;
75
76mod spawner;
77pub use spawner::*;
78
79/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
80#[doc(hidden)]
81pub mod _export {
82 pub use static_cell::StaticCell;
83}
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index 15ff18fc8..bd0cff26b 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -11,6 +11,7 @@ mod run_queue;
11#[cfg(feature = "integrated-timers")] 11#[cfg(feature = "integrated-timers")]
12mod timer_queue; 12mod timer_queue;
13pub(crate) mod util; 13pub(crate) mod util;
14#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")]
14mod waker; 15mod waker;
15 16
16use core::future::Future; 17use core::future::Future;
@@ -18,11 +19,9 @@ use core::marker::PhantomData;
18use core::mem; 19use core::mem;
19use core::pin::Pin; 20use core::pin::Pin;
20use core::ptr::NonNull; 21use core::ptr::NonNull;
21use core::sync::atomic::AtomicPtr;
22use core::task::{Context, Poll}; 22use core::task::{Context, Poll};
23 23
24use atomic_polyfill::{AtomicU32, Ordering}; 24use atomic_polyfill::{AtomicU32, Ordering};
25use critical_section::CriticalSection;
26#[cfg(feature = "integrated-timers")] 25#[cfg(feature = "integrated-timers")]
27use embassy_time::driver::{self, AlarmHandle}; 26use embassy_time::driver::{self, AlarmHandle};
28#[cfg(feature = "integrated-timers")] 27#[cfg(feature = "integrated-timers")]
@@ -289,10 +288,60 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
289 } 288 }
290} 289}
291 290
291#[derive(Clone, Copy)]
292pub(crate) enum PenderInner {
293 #[cfg(feature = "executor-thread")]
294 Thread(crate::arch::ThreadPender),
295 #[cfg(feature = "executor-interrupt")]
296 Interrupt(crate::arch::InterruptPender),
297 #[cfg(feature = "pender-callback")]
298 Callback { func: fn(*mut ()), context: *mut () },
299}
300
301unsafe impl Send for PenderInner {}
302unsafe impl Sync for PenderInner {}
303
304/// Platform/architecture-specific action executed when an executor has pending work.
305///
306/// When a task within an executor is woken, the `Pender` is called. This does a
307/// platform/architecture-specific action to signal there is pending work in the executor.
308/// When this happens, you must arrange for [`Executor::poll`] to be called.
309///
310/// You can think of it as a waker, but for the whole executor.
311pub struct Pender(pub(crate) PenderInner);
312
313impl Pender {
314 /// Create a `Pender` that will call an arbitrary function pointer.
315 ///
316 /// # Arguments
317 ///
318 /// - `func`: The function pointer to call.
319 /// - `context`: Opaque context pointer, that will be passed to the function pointer.
320 #[cfg(feature = "pender-callback")]
321 pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self {
322 Self(PenderInner::Callback {
323 func,
324 context: context.into(),
325 })
326 }
327}
328
329impl Pender {
330 pub(crate) fn pend(&self) {
331 match self.0 {
332 #[cfg(feature = "executor-thread")]
333 PenderInner::Thread(x) => x.pend(),
334 #[cfg(feature = "executor-interrupt")]
335 PenderInner::Interrupt(x) => x.pend(),
336 #[cfg(feature = "pender-callback")]
337 PenderInner::Callback { func, context } => func(context),
338 }
339 }
340}
341
292pub(crate) struct SyncExecutor { 342pub(crate) struct SyncExecutor {
293 run_queue: RunQueue, 343 run_queue: RunQueue,
294 signal_fn: fn(*mut ()), 344 pender: Pender,
295 signal_ctx: AtomicPtr<()>,
296 345
297 #[cfg(feature = "integrated-timers")] 346 #[cfg(feature = "integrated-timers")]
298 pub(crate) timer_queue: timer_queue::TimerQueue, 347 pub(crate) timer_queue: timer_queue::TimerQueue,
@@ -301,16 +350,13 @@ pub(crate) struct SyncExecutor {
301} 350}
302 351
303impl SyncExecutor { 352impl SyncExecutor {
304 pub(crate) fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { 353 pub(crate) fn new(pender: Pender) -> Self {
305 #[cfg(feature = "integrated-timers")] 354 #[cfg(feature = "integrated-timers")]
306 let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; 355 let alarm = unsafe { unwrap!(driver::allocate_alarm()) };
307 #[cfg(feature = "integrated-timers")]
308 driver::set_alarm_callback(alarm, signal_fn, signal_ctx);
309 356
310 Self { 357 Self {
311 run_queue: RunQueue::new(), 358 run_queue: RunQueue::new(),
312 signal_fn, 359 pender,
313 signal_ctx: AtomicPtr::new(signal_ctx),
314 360
315 #[cfg(feature = "integrated-timers")] 361 #[cfg(feature = "integrated-timers")]
316 timer_queue: timer_queue::TimerQueue::new(), 362 timer_queue: timer_queue::TimerQueue::new(),
@@ -326,30 +372,37 @@ impl SyncExecutor {
326 /// - `task` must be set up to run in this executor. 372 /// - `task` must be set up to run in this executor.
327 /// - `task` must NOT be already enqueued (in this executor or another one). 373 /// - `task` must NOT be already enqueued (in this executor or another one).
328 #[inline(always)] 374 #[inline(always)]
329 unsafe fn enqueue(&self, cs: CriticalSection, task: TaskRef) { 375 unsafe fn enqueue(&self, task: TaskRef) {
330 #[cfg(feature = "rtos-trace")] 376 #[cfg(feature = "rtos-trace")]
331 trace::task_ready_begin(task.as_ptr() as u32); 377 trace::task_ready_begin(task.as_ptr() as u32);
332 378
333 if self.run_queue.enqueue(cs, task) { 379 if self.run_queue.enqueue(task) {
334 (self.signal_fn)(self.signal_ctx.load(Ordering::Relaxed)) 380 self.pender.pend();
335 } 381 }
336 } 382 }
337 383
384 #[cfg(feature = "integrated-timers")]
385 fn alarm_callback(ctx: *mut ()) {
386 let this: &Self = unsafe { &*(ctx as *const Self) };
387 this.pender.pend();
388 }
389
338 pub(super) unsafe fn spawn(&'static self, task: TaskRef) { 390 pub(super) unsafe fn spawn(&'static self, task: TaskRef) {
339 task.header().executor.set(Some(self)); 391 task.header().executor.set(Some(self));
340 392
341 #[cfg(feature = "rtos-trace")] 393 #[cfg(feature = "rtos-trace")]
342 trace::task_new(task.as_ptr() as u32); 394 trace::task_new(task.as_ptr() as u32);
343 395
344 critical_section::with(|cs| { 396 self.enqueue(task);
345 self.enqueue(cs, task);
346 })
347 } 397 }
348 398
349 /// # Safety 399 /// # Safety
350 /// 400 ///
351 /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. 401 /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.
352 pub(crate) unsafe fn poll(&'static self) { 402 pub(crate) unsafe fn poll(&'static self) {
403 #[cfg(feature = "integrated-timers")]
404 driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ());
405
353 #[allow(clippy::never_loop)] 406 #[allow(clippy::never_loop)]
354 loop { 407 loop {
355 #[cfg(feature = "integrated-timers")] 408 #[cfg(feature = "integrated-timers")]
@@ -416,14 +469,14 @@ impl SyncExecutor {
416/// 469///
417/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks 470/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks
418/// that "want to run"). 471/// that "want to run").
419/// - You must supply a `signal_fn`. The executor will call it to notify you it has work 472/// - You must supply a [`Pender`]. The executor will call it to notify you it has work
420/// to do. You must arrange for `poll()` to be called as soon as possible. 473/// to do. You must arrange for `poll()` to be called as soon as possible.
421/// 474///
422/// `signal_fn` can be called from *any* context: any thread, any interrupt priority 475/// The [`Pender`] can be called from *any* context: any thread, any interrupt priority
423/// level, etc. It may be called synchronously from any `Executor` method call as well. 476/// level, etc. It may be called synchronously from any `Executor` method call as well.
424/// You must deal with this correctly. 477/// You must deal with this correctly.
425/// 478///
426/// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates 479/// In particular, you must NOT call `poll` directly from the pender callback, as this violates
427/// the requirement for `poll` to not be called reentrantly. 480/// the requirement for `poll` to not be called reentrantly.
428#[repr(transparent)] 481#[repr(transparent)]
429pub struct Executor { 482pub struct Executor {
@@ -436,15 +489,15 @@ impl Executor {
436 pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self { 489 pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self {
437 mem::transmute(inner) 490 mem::transmute(inner)
438 } 491 }
492
439 /// Create a new executor. 493 /// Create a new executor.
440 /// 494 ///
441 /// When the executor has work to do, it will call `signal_fn` with 495 /// When the executor has work to do, it will call the [`Pender`].
442 /// `signal_ctx` as argument.
443 /// 496 ///
444 /// See [`Executor`] docs for details on `signal_fn`. 497 /// See [`Executor`] docs for details on `Pender`.
445 pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { 498 pub fn new(pender: Pender) -> Self {
446 Self { 499 Self {
447 inner: SyncExecutor::new(signal_fn, signal_ctx), 500 inner: SyncExecutor::new(pender),
448 _not_sync: PhantomData, 501 _not_sync: PhantomData,
449 } 502 }
450 } 503 }
@@ -467,16 +520,16 @@ impl Executor {
467 /// This loops over all tasks that are queued to be polled (i.e. they're 520 /// This loops over all tasks that are queued to be polled (i.e. they're
468 /// freshly spawned or they've been woken). Other tasks are not polled. 521 /// freshly spawned or they've been woken). Other tasks are not polled.
469 /// 522 ///
470 /// You must call `poll` after receiving a call to `signal_fn`. It is OK 523 /// You must call `poll` after receiving a call to the [`Pender`]. It is OK
471 /// to call `poll` even when not requested by `signal_fn`, but it wastes 524 /// to call `poll` even when not requested by the `Pender`, but it wastes
472 /// energy. 525 /// energy.
473 /// 526 ///
474 /// # Safety 527 /// # Safety
475 /// 528 ///
476 /// You must NOT call `poll` reentrantly on the same executor. 529 /// You must NOT call `poll` reentrantly on the same executor.
477 /// 530 ///
478 /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you 531 /// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you
479 /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to 532 /// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to
480 /// somehow schedule for `poll()` to be called later, at a time you know for sure there's 533 /// somehow schedule for `poll()` to be called later, at a time you know for sure there's
481 /// no `poll()` already running. 534 /// no `poll()` already running.
482 pub unsafe fn poll(&'static self) { 535 pub unsafe fn poll(&'static self) {
@@ -496,24 +549,25 @@ impl Executor {
496/// 549///
497/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`]. 550/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
498pub fn wake_task(task: TaskRef) { 551pub fn wake_task(task: TaskRef) {
499 critical_section::with(|cs| { 552 let header = task.header();
500 let header = task.header();
501 let state = header.state.load(Ordering::Relaxed);
502 553
554 let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
503 // If already scheduled, or if not started, 555 // If already scheduled, or if not started,
504 if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { 556 if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
505 return; 557 None
558 } else {
559 // Mark it as scheduled
560 Some(state | STATE_RUN_QUEUED)
506 } 561 }
562 });
507 563
508 // Mark it as scheduled 564 if res.is_ok() {
509 header.state.store(state | STATE_RUN_QUEUED, Ordering::Relaxed);
510
511 // We have just marked the task as scheduled, so enqueue it. 565 // We have just marked the task as scheduled, so enqueue it.
512 unsafe { 566 unsafe {
513 let executor = header.executor.get().unwrap_unchecked(); 567 let executor = header.executor.get().unwrap_unchecked();
514 executor.enqueue(cs, task); 568 executor.enqueue(task);
515 } 569 }
516 }) 570 }
517} 571}
518 572
519#[cfg(feature = "integrated-timers")] 573#[cfg(feature = "integrated-timers")]
diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs
index 362157535..a88174a0c 100644
--- a/embassy-executor/src/raw/run_queue.rs
+++ b/embassy-executor/src/raw/run_queue.rs
@@ -2,7 +2,6 @@ use core::ptr;
2use core::ptr::NonNull; 2use core::ptr::NonNull;
3 3
4use atomic_polyfill::{AtomicPtr, Ordering}; 4use atomic_polyfill::{AtomicPtr, Ordering};
5use critical_section::CriticalSection;
6 5
7use super::{TaskHeader, TaskRef}; 6use super::{TaskHeader, TaskRef};
8 7
@@ -46,11 +45,18 @@ impl RunQueue {
46 /// 45 ///
47 /// `item` must NOT be already enqueued in any queue. 46 /// `item` must NOT be already enqueued in any queue.
48 #[inline(always)] 47 #[inline(always)]
49 pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: TaskRef) -> bool { 48 pub(crate) unsafe fn enqueue(&self, task: TaskRef) -> bool {
50 let prev = self.head.load(Ordering::Relaxed); 49 let mut was_empty = false;
51 task.header().run_queue_item.next.store(prev, Ordering::Relaxed); 50
52 self.head.store(task.as_ptr() as _, Ordering::Relaxed); 51 self.head
53 prev.is_null() 52 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
53 was_empty = prev.is_null();
54 task.header().run_queue_item.next.store(prev, Ordering::Relaxed);
55 Some(task.as_ptr() as *mut _)
56 })
57 .ok();
58
59 was_empty
54 } 60 }
55 61
56 /// Empty the queue, then call `on_task` for each task that was in the queue. 62 /// Empty the queue, then call `on_task` for each task that was in the queue.
diff --git a/embassy-executor/src/raw/waker_turbo.rs b/embassy-executor/src/raw/waker_turbo.rs
new file mode 100644
index 000000000..435a0ff7e
--- /dev/null
+++ b/embassy-executor/src/raw/waker_turbo.rs
@@ -0,0 +1,34 @@
1use core::ptr::NonNull;
2use core::task::Waker;
3
4use super::{wake_task, TaskHeader, TaskRef};
5
6pub(crate) unsafe fn from_task(p: TaskRef) -> Waker {
7 Waker::from_turbo_ptr(NonNull::new_unchecked(p.as_ptr() as _))
8}
9
10/// Get a task pointer from a waker.
11///
12/// This can be used as an optimization in wait queues to store task pointers
13/// (1 word) instead of full Wakers (2 words). This saves a bit of RAM and helps
14/// avoid dynamic dispatch.
15///
16/// You can use the returned task pointer to wake the task with [`wake_task`](super::wake_task).
17///
18/// # Panics
19///
20/// Panics if the waker is not created by the Embassy executor.
21pub fn task_from_waker(waker: &Waker) -> TaskRef {
22 let ptr = waker.as_turbo_ptr().as_ptr();
23
24 // safety: our wakers are always created with `TaskRef::as_ptr`
25 unsafe { TaskRef::from_ptr(ptr as *const TaskHeader) }
26}
27
28#[inline(never)]
29#[no_mangle]
30fn _turbo_wake(ptr: NonNull<()>) {
31 // safety: our wakers are always created with `TaskRef::as_ptr`
32 let task = unsafe { TaskRef::from_ptr(ptr.as_ptr() as *const TaskHeader) };
33 wake_task(task)
34}
diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml
index cbe78e592..604358c5b 100644
--- a/embassy-lora/Cargo.toml
+++ b/embassy-lora/Cargo.toml
@@ -9,9 +9,9 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em
9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" 9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/"
10features = ["time", "defmt"] 10features = ["time", "defmt"]
11flavors = [ 11flavors = [
12 { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] }, 12 { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] },
13 { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, 13 { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x"] },
14 { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, 14 { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/time-driver-any"] },
15] 15]
16 16
17[lib] 17[lib]
@@ -19,7 +19,7 @@ flavors = [
19[features] 19[features]
20sx126x = [] 20sx126x = []
21sx127x = [] 21sx127x = []
22stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] 22stm32wl = ["dep:embassy-stm32"]
23time = [] 23time = []
24defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] 24defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"]
25 25
@@ -31,8 +31,8 @@ log = { version = "0.4.14", optional = true }
31embassy-time = { version = "0.1.0", path = "../embassy-time" } 31embassy-time = { version = "0.1.0", path = "../embassy-time" }
32embassy-sync = { version = "0.1.0", path = "../embassy-sync" } 32embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
33embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } 33embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
34embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } 34embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
35embedded-hal-async = { version = "=0.2.0-alpha.0" } 35embedded-hal-async = { version = "=0.2.0-alpha.1" }
36embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } 36embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false }
37futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } 37futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
38embedded-hal = { version = "0.2", features = ["unproven"] } 38embedded-hal = { version = "0.2", features = ["unproven"] }
diff --git a/embassy-macros/src/macros/cortex_m_interrupt_take.rs b/embassy-macros/src/macros/cortex_m_interrupt_take.rs
index e2ebf98c7..4806d1c12 100644
--- a/embassy-macros/src/macros/cortex_m_interrupt_take.rs
+++ b/embassy-macros/src/macros/cortex_m_interrupt_take.rs
@@ -10,12 +10,12 @@ pub fn run(name: syn::Ident) -> Result<TokenStream, TokenStream> {
10 let (isr_enter, isr_exit) = ( 10 let (isr_enter, isr_exit) = (
11 quote! { 11 quote! {
12 ::embassy_executor::rtos_trace_interrupt! { 12 ::embassy_executor::rtos_trace_interrupt! {
13 ::embassy_executor::export::trace::isr_enter(); 13 ::embassy_executor::_export::trace::isr_enter();
14 } 14 }
15 }, 15 },
16 quote! { 16 quote! {
17 ::embassy_executor::rtos_trace_interrupt! { 17 ::embassy_executor::rtos_trace_interrupt! {
18 ::embassy_executor::export::trace::isr_exit(); 18 ::embassy_executor::_export::trace::isr_exit();
19 } 19 }
20 }, 20 },
21 ); 21 );
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index 4e62ca89e..4a4e7c9f9 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -87,8 +87,8 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
87embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } 87embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
88 88
89embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 89embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
90embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} 90embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
91embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} 91embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
92embedded-io = { version = "0.4.0", features = ["async"], optional = true } 92embedded-io = { version = "0.4.0", features = ["async"], optional = true }
93 93
94defmt = { version = "0.3", optional = true } 94defmt = { version = "0.3", optional = true }
diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs
index ef4c929a3..9ae569609 100644
--- a/embassy-nrf/src/twim.rs
+++ b/embassy-nrf/src/twim.rs
@@ -846,20 +846,6 @@ mod eh1 {
846 self.blocking_write(address, buffer) 846 self.blocking_write(address, buffer)
847 } 847 }
848 848
849 fn write_iter<B>(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error>
850 where
851 B: IntoIterator<Item = u8>,
852 {
853 todo!();
854 }
855
856 fn write_iter_read<B>(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error>
857 where
858 B: IntoIterator<Item = u8>,
859 {
860 todo!();
861 }
862
863 fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { 849 fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
864 self.blocking_write_read(address, wr_buffer, rd_buffer) 850 self.blocking_write_read(address, wr_buffer, rd_buffer)
865 } 851 }
@@ -871,13 +857,6 @@ mod eh1 {
871 ) -> Result<(), Self::Error> { 857 ) -> Result<(), Self::Error> {
872 todo!(); 858 todo!();
873 } 859 }
874
875 fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
876 where
877 O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
878 {
879 todo!();
880 }
881 } 860 }
882} 861}
883 862
@@ -885,28 +864,22 @@ mod eh1 {
885mod eha { 864mod eha {
886 use super::*; 865 use super::*;
887 impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { 866 impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> {
888 async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), Error> { 867 async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
889 self.read(address, buffer).await 868 self.read(address, read).await
890 } 869 }
891 870
892 async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), Error> { 871 async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
893 self.write(address, bytes).await 872 self.write(address, write).await
894 } 873 }
895 874 async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
896 async fn write_read<'a>( 875 self.write_read(address, write, read).await
897 &'a mut self,
898 address: u8,
899 wr_buffer: &'a [u8],
900 rd_buffer: &'a mut [u8],
901 ) -> Result<(), Error> {
902 self.write_read(address, wr_buffer, rd_buffer).await
903 } 876 }
904 877
905 async fn transaction<'a, 'b>( 878 async fn transaction(
906 &'a mut self, 879 &mut self,
907 address: u8, 880 address: u8,
908 operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], 881 operations: &mut [embedded_hal_1::i2c::Operation<'_>],
909 ) -> Result<(), Error> { 882 ) -> Result<(), Self::Error> {
910 let _ = address; 883 let _ = address;
911 let _ = operations; 884 let _ = operations;
912 todo!() 885 todo!()
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index 209c665b0..cb9c7be77 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -65,9 +65,9 @@ rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c90
65#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } 65#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
66 66
67embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 67embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
68embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} 68embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
69embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} 69embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
70embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} 70embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true}
71 71
72paste = "1.0" 72paste = "1.0"
73pio-proc = {version= "0.2", optional = true} 73pio-proc = {version= "0.2", optional = true}
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs
index fb45ef7cf..98e182868 100644
--- a/embassy-rp/src/gpio.rs
+++ b/embassy-rp/src/gpio.rs
@@ -437,6 +437,37 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
437 pub fn is_low(&self) -> bool { 437 pub fn is_low(&self) -> bool {
438 self.pin.is_low() 438 self.pin.is_low()
439 } 439 }
440
441 /// Returns current pin level
442 #[inline]
443 pub fn get_level(&self) -> Level {
444 self.is_high().into()
445 }
446
447 #[inline]
448 pub async fn wait_for_high(&mut self) {
449 self.pin.wait_for_high().await;
450 }
451
452 #[inline]
453 pub async fn wait_for_low(&mut self) {
454 self.pin.wait_for_low().await;
455 }
456
457 #[inline]
458 pub async fn wait_for_rising_edge(&mut self) {
459 self.pin.wait_for_rising_edge().await;
460 }
461
462 #[inline]
463 pub async fn wait_for_falling_edge(&mut self) {
464 self.pin.wait_for_falling_edge().await;
465 }
466
467 #[inline]
468 pub async fn wait_for_any_edge(&mut self) {
469 self.pin.wait_for_any_edge().await;
470 }
440} 471}
441 472
442/// GPIO flexible pin. 473/// GPIO flexible pin.
@@ -1117,4 +1148,32 @@ mod eh1 {
1117 Ok(()) 1148 Ok(())
1118 } 1149 }
1119 } 1150 }
1151
1152 #[cfg(feature = "nightly")]
1153 impl<'d, T: Pin> embedded_hal_async::digital::Wait for OutputOpenDrain<'d, T> {
1154 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
1155 self.wait_for_high().await;
1156 Ok(())
1157 }
1158
1159 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
1160 self.wait_for_low().await;
1161 Ok(())
1162 }
1163
1164 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
1165 self.wait_for_rising_edge().await;
1166 Ok(())
1167 }
1168
1169 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
1170 self.wait_for_falling_edge().await;
1171 Ok(())
1172 }
1173
1174 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
1175 self.wait_for_any_edge().await;
1176 Ok(())
1177 }
1178 }
1120} 1179}
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs
index e48e16d81..40e85c66f 100644
--- a/embassy-rp/src/i2c.rs
+++ b/embassy-rp/src/i2c.rs
@@ -490,14 +490,14 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
490 } 490 }
491 } 491 }
492 492
493 fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { 493 fn read_blocking_internal(&mut self, read: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {
494 if buffer.is_empty() { 494 if read.is_empty() {
495 return Err(Error::InvalidReadBufferLength); 495 return Err(Error::InvalidReadBufferLength);
496 } 496 }
497 497
498 let p = T::regs(); 498 let p = T::regs();
499 let lastindex = buffer.len() - 1; 499 let lastindex = read.len() - 1;
500 for (i, byte) in buffer.iter_mut().enumerate() { 500 for (i, byte) in read.iter_mut().enumerate() {
501 let first = i == 0; 501 let first = i == 0;
502 let last = i == lastindex; 502 let last = i == lastindex;
503 503
@@ -524,15 +524,15 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
524 Ok(()) 524 Ok(())
525 } 525 }
526 526
527 fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { 527 fn write_blocking_internal(&mut self, write: &[u8], send_stop: bool) -> Result<(), Error> {
528 if bytes.is_empty() { 528 if write.is_empty() {
529 return Err(Error::InvalidWriteBufferLength); 529 return Err(Error::InvalidWriteBufferLength);
530 } 530 }
531 531
532 let p = T::regs(); 532 let p = T::regs();
533 533
534 for (i, byte) in bytes.iter().enumerate() { 534 for (i, byte) in write.iter().enumerate() {
535 let last = i == bytes.len() - 1; 535 let last = i == write.len() - 1;
536 536
537 // NOTE(unsafe) We have &mut self 537 // NOTE(unsafe) We have &mut self
538 unsafe { 538 unsafe {
@@ -572,21 +572,21 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
572 // Blocking public API 572 // Blocking public API
573 // ========================= 573 // =========================
574 574
575 pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { 575 pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {
576 Self::setup(address.into())?; 576 Self::setup(address.into())?;
577 self.read_blocking_internal(buffer, true, true) 577 self.read_blocking_internal(read, true, true)
578 // Automatic Stop 578 // Automatic Stop
579 } 579 }
580 580
581 pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { 581 pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
582 Self::setup(address.into())?; 582 Self::setup(address.into())?;
583 self.write_blocking_internal(bytes, true) 583 self.write_blocking_internal(write, true)
584 } 584 }
585 585
586 pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { 586 pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
587 Self::setup(address.into())?; 587 Self::setup(address.into())?;
588 self.write_blocking_internal(bytes, false)?; 588 self.write_blocking_internal(write, false)?;
589 self.read_blocking_internal(buffer, true, true) 589 self.read_blocking_internal(read, true, true)
590 // Automatic Stop 590 // Automatic Stop
591 } 591 }
592} 592}
@@ -644,48 +644,22 @@ mod eh1 {
644 } 644 }
645 645
646 impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { 646 impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {
647 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 647 fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
648 self.blocking_read(address, buffer) 648 self.blocking_read(address, read)
649 } 649 }
650 650
651 fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { 651 fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
652 self.blocking_write(address, buffer) 652 self.blocking_write(address, write)
653 }
654
655 fn write_iter<B>(&mut self, address: u8, bytes: B) -> Result<(), Self::Error>
656 where
657 B: IntoIterator<Item = u8>,
658 {
659 let mut peekable = bytes.into_iter().peekable();
660 Self::setup(address.into())?;
661
662 while let Some(tx) = peekable.next() {
663 self.write_blocking_internal(&[tx], peekable.peek().is_none())?;
664 }
665 Ok(())
666 }
667
668 fn write_iter_read<B>(&mut self, address: u8, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error>
669 where
670 B: IntoIterator<Item = u8>,
671 {
672 let peekable = bytes.into_iter().peekable();
673 Self::setup(address.into())?;
674
675 for tx in peekable {
676 self.write_blocking_internal(&[tx], false)?
677 }
678 self.read_blocking_internal(buffer, true, true)
679 } 653 }
680 654
681 fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { 655 fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
682 self.blocking_write_read(address, wr_buffer, rd_buffer) 656 self.blocking_write_read(address, write, read)
683 } 657 }
684 658
685 fn transaction<'a>( 659 fn transaction(
686 &mut self, 660 &mut self,
687 address: u8, 661 address: u8,
688 operations: &mut [embedded_hal_1::i2c::Operation<'a>], 662 operations: &mut [embedded_hal_1::i2c::Operation<'_>],
689 ) -> Result<(), Self::Error> { 663 ) -> Result<(), Self::Error> {
690 Self::setup(address.into())?; 664 Self::setup(address.into())?;
691 for i in 0..operations.len() { 665 for i in 0..operations.len() {
@@ -697,22 +671,6 @@ mod eh1 {
697 } 671 }
698 Ok(()) 672 Ok(())
699 } 673 }
700
701 fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error>
702 where
703 O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
704 {
705 Self::setup(address.into())?;
706 let mut peekable = operations.into_iter().peekable();
707 while let Some(operation) = peekable.next() {
708 let last = peekable.peek().is_none();
709 match operation {
710 embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?,
711 embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
712 }
713 }
714 Ok(())
715 }
716 } 674 }
717} 675}
718#[cfg(all(feature = "unstable-traits", feature = "nightly"))] 676#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
@@ -727,36 +685,29 @@ mod nightly {
727 A: AddressMode + Into<u16> + 'static, 685 A: AddressMode + Into<u16> + 'static,
728 T: Instance + 'd, 686 T: Instance + 'd,
729 { 687 {
730 async fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Result<(), Self::Error> { 688 async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> {
731 let addr: u16 = address.into(); 689 let addr: u16 = address.into();
732 690
733 Self::setup(addr)?; 691 Self::setup(addr)?;
734 self.read_async_internal(read, false, true).await 692 self.read_async_internal(read, false, true).await
735 } 693 }
736 694
737 async fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Result<(), Self::Error> { 695 async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> {
738 let addr: u16 = address.into(); 696 let addr: u16 = address.into();
739 697
740 Self::setup(addr)?; 698 Self::setup(addr)?;
741 self.write_async_internal(write.iter().copied(), true).await 699 self.write_async_internal(write.iter().copied(), true).await
742 } 700 }
743 async fn write_read<'a>( 701
744 &'a mut self, 702 async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
745 address: A,
746 write: &'a [u8],
747 read: &'a mut [u8],
748 ) -> Result<(), Self::Error> {
749 let addr: u16 = address.into(); 703 let addr: u16 = address.into();
750 704
751 Self::setup(addr)?; 705 Self::setup(addr)?;
752 self.write_async_internal(write.iter().cloned(), false).await?; 706 self.write_async_internal(write.iter().cloned(), false).await?;
753 self.read_async_internal(read, false, true).await 707 self.read_async_internal(read, false, true).await
754 } 708 }
755 async fn transaction<'a, 'b>( 709
756 &'a mut self, 710 async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
757 address: A,
758 operations: &'a mut [Operation<'b>],
759 ) -> Result<(), Self::Error> {
760 let addr: u16 = address.into(); 711 let addr: u16 = address.into();
761 712
762 let mut iterator = operations.iter_mut(); 713 let mut iterator = operations.iter_mut();
diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs
index ebd621ecf..742a35d49 100644
--- a/embassy-rp/src/spi.rs
+++ b/embassy-rp/src/spi.rs
@@ -19,6 +19,7 @@ pub enum Error {
19} 19}
20 20
21#[non_exhaustive] 21#[non_exhaustive]
22#[derive(Clone)]
22pub struct Config { 23pub struct Config {
23 pub frequency: u32, 24 pub frequency: u32,
24 pub phase: Phase, 25 pub phase: Phase,
diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs
index 1a573b311..c620ed08c 100644
--- a/embassy-rp/src/uart/buffered.rs
+++ b/embassy-rp/src/uart/buffered.rs
@@ -175,6 +175,10 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
175 175
176 fn read<'a>(buf: &'a mut [u8]) -> impl Future<Output = Result<usize, Error>> + 'a { 176 fn read<'a>(buf: &'a mut [u8]) -> impl Future<Output = Result<usize, Error>> + 'a {
177 poll_fn(move |cx| { 177 poll_fn(move |cx| {
178 if buf.is_empty() {
179 return Poll::Ready(Ok(0));
180 }
181
178 let state = T::state(); 182 let state = T::state();
179 let mut rx_reader = unsafe { state.rx_buf.reader() }; 183 let mut rx_reader = unsafe { state.rx_buf.reader() };
180 let n = rx_reader.pop(|data| { 184 let n = rx_reader.pop(|data| {
@@ -202,6 +206,10 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> {
202 } 206 }
203 207
204 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { 208 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
209 if buf.is_empty() {
210 return Ok(0);
211 }
212
205 loop { 213 loop {
206 let state = T::state(); 214 let state = T::state();
207 let mut rx_reader = unsafe { state.rx_buf.reader() }; 215 let mut rx_reader = unsafe { state.rx_buf.reader() };
@@ -293,6 +301,10 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> {
293 301
294 fn write<'a>(buf: &'a [u8]) -> impl Future<Output = Result<usize, Error>> + 'a { 302 fn write<'a>(buf: &'a [u8]) -> impl Future<Output = Result<usize, Error>> + 'a {
295 poll_fn(move |cx| { 303 poll_fn(move |cx| {
304 if buf.is_empty() {
305 return Poll::Ready(Ok(0));
306 }
307
296 let state = T::state(); 308 let state = T::state();
297 let mut tx_writer = unsafe { state.tx_buf.writer() }; 309 let mut tx_writer = unsafe { state.tx_buf.writer() };
298 let n = tx_writer.push(|data| { 310 let n = tx_writer.push(|data| {
@@ -327,6 +339,10 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> {
327 } 339 }
328 340
329 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<usize, Error> { 341 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<usize, Error> {
342 if buf.is_empty() {
343 return Ok(0);
344 }
345
330 loop { 346 loop {
331 let state = T::state(); 347 let state = T::state();
332 let mut tx_writer = unsafe { state.tx_buf.writer() }; 348 let mut tx_writer = unsafe { state.tx_buf.writer() };
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs
index 682243a27..a945f2295 100644
--- a/embassy-rp/src/uart/mod.rs
+++ b/embassy-rp/src/uart/mod.rs
@@ -405,10 +405,6 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
405 Parity::ParityEven => (true, true), 405 Parity::ParityEven => (true, true),
406 }; 406 };
407 407
408 // PL011 needs a (dummy) line control register write to latch in the
409 // divisors. We don't want to actually change LCR contents here.
410 r.uartlcr_h().modify(|_| {});
411
412 r.uartlcr_h().write(|w| { 408 r.uartlcr_h().write(|w| {
413 w.set_wlen(config.data_bits.bits()); 409 w.set_wlen(config.data_bits.bits());
414 w.set_stp2(config.stop_bits == StopBits::STOP2); 410 w.set_stp2(config.stop_bits == StopBits::STOP2);
@@ -458,6 +454,10 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
458 // Load PL011's baud divisor registers 454 // Load PL011's baud divisor registers
459 r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); 455 r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd));
460 r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); 456 r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd));
457
458 // PL011 needs a (dummy) line control register write to latch in the
459 // divisors. We don't want to actually change LCR contents here.
460 r.uartlcr_h().modify(|_| {});
461 } 461 }
462 } 462 }
463} 463}
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 14ec3d70a..a8ebacd25 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -8,10 +8,7 @@ license = "MIT OR Apache-2.0"
8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" 8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/"
9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" 9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/"
10 10
11# TODO: sdmmc 11features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"]
12# TODO: net
13# TODO: subghz
14features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any"]
15flavors = [ 12flavors = [
16 { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, 13 { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" },
17 { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, 14 { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" },
@@ -22,6 +19,7 @@ flavors = [
22 { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, 19 { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" },
23 { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, 20 { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" },
24 { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" }, 21 { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" },
22 { regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" },
25 { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, 23 { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" },
26 { regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi" }, 24 { regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi" },
27 { regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" }, 25 { regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" },
@@ -44,9 +42,9 @@ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
44embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } 42embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
45 43
46embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 44embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
47embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} 45embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
48embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} 46embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
49embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} 47embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true}
50 48
51embedded-storage = "0.3.0" 49embedded-storage = "0.3.0"
52 50
@@ -60,7 +58,7 @@ sdio-host = "0.5.0"
60embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } 58embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true }
61critical-section = "1.1" 59critical-section = "1.1"
62atomic-polyfill = "1.0.1" 60atomic-polyfill = "1.0.1"
63stm32-metapac = { version = "2", features = ["rt"] } 61stm32-metapac = "5"
64vcell = "0.1.3" 62vcell = "0.1.3"
65bxcan = "0.7.0" 63bxcan = "0.7.0"
66nb = "1.0.0" 64nb = "1.0.0"
@@ -69,15 +67,18 @@ seq-macro = "0.3.0"
69cfg-if = "1.0.0" 67cfg-if = "1.0.0"
70embedded-io = { version = "0.4.0", features = ["async"], optional = true } 68embedded-io = { version = "0.4.0", features = ["async"], optional = true }
71 69
70[dev-dependencies]
71critical-section = { version = "1.1", features = ["std"] }
72
72[build-dependencies] 73[build-dependencies]
73proc-macro2 = "1.0.36" 74proc-macro2 = "1.0.36"
74quote = "1.0.15" 75quote = "1.0.15"
75stm32-metapac = { version = "2", default-features = false, features = ["metadata"]} 76stm32-metapac = { version = "5", default-features = false, features = ["metadata"]}
76 77
77[features] 78[features]
79default = ["stm32-metapac/rt"]
78defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] 80defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"]
79memory-x = ["stm32-metapac/memory-x"] 81memory-x = ["stm32-metapac/memory-x"]
80subghz = []
81exti = [] 82exti = []
82 83
83# Enables additional driver features that depend on embassy-time 84# Enables additional driver features that depend on embassy-time
@@ -830,6 +831,37 @@ stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke" ]
830stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] 831stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ]
831stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] 832stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ]
832stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] 833stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ]
834stm32h503cb = [ "stm32-metapac/stm32h503cb" ]
835stm32h503eb = [ "stm32-metapac/stm32h503eb" ]
836stm32h503kb = [ "stm32-metapac/stm32h503kb" ]
837stm32h503rb = [ "stm32-metapac/stm32h503rb" ]
838stm32h562ag = [ "stm32-metapac/stm32h562ag" ]
839stm32h562ai = [ "stm32-metapac/stm32h562ai" ]
840stm32h562ig = [ "stm32-metapac/stm32h562ig" ]
841stm32h562ii = [ "stm32-metapac/stm32h562ii" ]
842stm32h562rg = [ "stm32-metapac/stm32h562rg" ]
843stm32h562ri = [ "stm32-metapac/stm32h562ri" ]
844stm32h562vg = [ "stm32-metapac/stm32h562vg" ]
845stm32h562vi = [ "stm32-metapac/stm32h562vi" ]
846stm32h562zg = [ "stm32-metapac/stm32h562zg" ]
847stm32h562zi = [ "stm32-metapac/stm32h562zi" ]
848stm32h563ag = [ "stm32-metapac/stm32h563ag" ]
849stm32h563ai = [ "stm32-metapac/stm32h563ai" ]
850stm32h563ig = [ "stm32-metapac/stm32h563ig" ]
851stm32h563ii = [ "stm32-metapac/stm32h563ii" ]
852stm32h563mi = [ "stm32-metapac/stm32h563mi" ]
853stm32h563rg = [ "stm32-metapac/stm32h563rg" ]
854stm32h563ri = [ "stm32-metapac/stm32h563ri" ]
855stm32h563vg = [ "stm32-metapac/stm32h563vg" ]
856stm32h563vi = [ "stm32-metapac/stm32h563vi" ]
857stm32h563zg = [ "stm32-metapac/stm32h563zg" ]
858stm32h563zi = [ "stm32-metapac/stm32h563zi" ]
859stm32h573ai = [ "stm32-metapac/stm32h573ai" ]
860stm32h573ii = [ "stm32-metapac/stm32h573ii" ]
861stm32h573mi = [ "stm32-metapac/stm32h573mi" ]
862stm32h573ri = [ "stm32-metapac/stm32h573ri" ]
863stm32h573vi = [ "stm32-metapac/stm32h573vi" ]
864stm32h573zi = [ "stm32-metapac/stm32h573zi" ]
833stm32h723ve = [ "stm32-metapac/stm32h723ve" ] 865stm32h723ve = [ "stm32-metapac/stm32h723ve" ]
834stm32h723vg = [ "stm32-metapac/stm32h723vg" ] 866stm32h723vg = [ "stm32-metapac/stm32h723vg" ]
835stm32h723ze = [ "stm32-metapac/stm32h723ze" ] 867stm32h723ze = [ "stm32-metapac/stm32h723ze" ]
@@ -1312,6 +1344,22 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ]
1312stm32l562re = [ "stm32-metapac/stm32l562re" ] 1344stm32l562re = [ "stm32-metapac/stm32l562re" ]
1313stm32l562ve = [ "stm32-metapac/stm32l562ve" ] 1345stm32l562ve = [ "stm32-metapac/stm32l562ve" ]
1314stm32l562ze = [ "stm32-metapac/stm32l562ze" ] 1346stm32l562ze = [ "stm32-metapac/stm32l562ze" ]
1347stm32u535cb = [ "stm32-metapac/stm32u535cb" ]
1348stm32u535cc = [ "stm32-metapac/stm32u535cc" ]
1349stm32u535ce = [ "stm32-metapac/stm32u535ce" ]
1350stm32u535je = [ "stm32-metapac/stm32u535je" ]
1351stm32u535nc = [ "stm32-metapac/stm32u535nc" ]
1352stm32u535ne = [ "stm32-metapac/stm32u535ne" ]
1353stm32u535rb = [ "stm32-metapac/stm32u535rb" ]
1354stm32u535rc = [ "stm32-metapac/stm32u535rc" ]
1355stm32u535re = [ "stm32-metapac/stm32u535re" ]
1356stm32u535vc = [ "stm32-metapac/stm32u535vc" ]
1357stm32u535ve = [ "stm32-metapac/stm32u535ve" ]
1358stm32u545ce = [ "stm32-metapac/stm32u545ce" ]
1359stm32u545je = [ "stm32-metapac/stm32u545je" ]
1360stm32u545ne = [ "stm32-metapac/stm32u545ne" ]
1361stm32u545re = [ "stm32-metapac/stm32u545re" ]
1362stm32u545ve = [ "stm32-metapac/stm32u545ve" ]
1315stm32u575ag = [ "stm32-metapac/stm32u575ag" ] 1363stm32u575ag = [ "stm32-metapac/stm32u575ag" ]
1316stm32u575ai = [ "stm32-metapac/stm32u575ai" ] 1364stm32u575ai = [ "stm32-metapac/stm32u575ai" ]
1317stm32u575cg = [ "stm32-metapac/stm32u575cg" ] 1365stm32u575cg = [ "stm32-metapac/stm32u575cg" ]
@@ -1333,6 +1381,32 @@ stm32u585qi = [ "stm32-metapac/stm32u585qi" ]
1333stm32u585ri = [ "stm32-metapac/stm32u585ri" ] 1381stm32u585ri = [ "stm32-metapac/stm32u585ri" ]
1334stm32u585vi = [ "stm32-metapac/stm32u585vi" ] 1382stm32u585vi = [ "stm32-metapac/stm32u585vi" ]
1335stm32u585zi = [ "stm32-metapac/stm32u585zi" ] 1383stm32u585zi = [ "stm32-metapac/stm32u585zi" ]
1384stm32u595ai = [ "stm32-metapac/stm32u595ai" ]
1385stm32u595aj = [ "stm32-metapac/stm32u595aj" ]
1386stm32u595qi = [ "stm32-metapac/stm32u595qi" ]
1387stm32u595qj = [ "stm32-metapac/stm32u595qj" ]
1388stm32u595ri = [ "stm32-metapac/stm32u595ri" ]
1389stm32u595rj = [ "stm32-metapac/stm32u595rj" ]
1390stm32u595vi = [ "stm32-metapac/stm32u595vi" ]
1391stm32u595vj = [ "stm32-metapac/stm32u595vj" ]
1392stm32u595zi = [ "stm32-metapac/stm32u595zi" ]
1393stm32u595zj = [ "stm32-metapac/stm32u595zj" ]
1394stm32u599bj = [ "stm32-metapac/stm32u599bj" ]
1395stm32u599ni = [ "stm32-metapac/stm32u599ni" ]
1396stm32u599nj = [ "stm32-metapac/stm32u599nj" ]
1397stm32u599vi = [ "stm32-metapac/stm32u599vi" ]
1398stm32u599vj = [ "stm32-metapac/stm32u599vj" ]
1399stm32u599zi = [ "stm32-metapac/stm32u599zi" ]
1400stm32u599zj = [ "stm32-metapac/stm32u599zj" ]
1401stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ]
1402stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ]
1403stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ]
1404stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ]
1405stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ]
1406stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ]
1407stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ]
1408stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ]
1409stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ]
1336stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] 1410stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ]
1337stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] 1411stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ]
1338stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] 1412stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 3780c5a40..b01e8ba45 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -3,9 +3,9 @@ use std::fmt::Write as _;
3use std::path::PathBuf; 3use std::path::PathBuf;
4use std::{env, fs}; 4use std::{env, fs};
5 5
6use proc_macro2::TokenStream; 6use proc_macro2::{Ident, TokenStream};
7use quote::{format_ident, quote}; 7use quote::{format_ident, quote};
8use stm32_metapac::metadata::METADATA; 8use stm32_metapac::metadata::{MemoryRegionKind, METADATA};
9 9
10fn main() { 10fn main() {
11 let chip_name = match env::vars() 11 let chip_name = match env::vars()
@@ -50,10 +50,13 @@ fn main() {
50 // We *shouldn't* have singletons for these, but the HAL currently requires 50 // We *shouldn't* have singletons for these, but the HAL currently requires
51 // singletons, for using with RccPeripheral to enable/disable clocks to them. 51 // singletons, for using with RccPeripheral to enable/disable clocks to them.
52 "rcc" => { 52 "rcc" => {
53 if r.version.starts_with("h7") { 53 if r.version.starts_with("h5") || r.version.starts_with("h7") || r.version.starts_with("f4") {
54 singletons.push("MCO1".to_string()); 54 singletons.push("MCO1".to_string());
55 singletons.push("MCO2".to_string()); 55 singletons.push("MCO2".to_string());
56 } 56 }
57 if r.version.starts_with("l4") {
58 singletons.push("MCO".to_string());
59 }
57 singletons.push(p.name.to_string()); 60 singletons.push(p.name.to_string());
58 } 61 }
59 //"dbgmcu" => {} 62 //"dbgmcu" => {}
@@ -104,6 +107,94 @@ fn main() {
104 }); 107 });
105 108
106 // ======== 109 // ========
110 // Generate FLASH regions
111 let mut flash_regions = TokenStream::new();
112 let flash_memory_regions: Vec<_> = METADATA
113 .memory
114 .iter()
115 .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some())
116 .collect();
117 for region in flash_memory_regions.iter() {
118 let region_name = format_ident!("{}", get_flash_region_name(region.name));
119 let bank_variant = format_ident!(
120 "{}",
121 if region.name.starts_with("BANK_1") {
122 "Bank1"
123 } else if region.name.starts_with("BANK_2") {
124 "Bank2"
125 } else if region.name == "OTP" {
126 "Otp"
127 } else {
128 continue;
129 }
130 );
131 let base = region.address;
132 let size = region.size;
133 let settings = region.settings.as_ref().unwrap();
134 let erase_size = settings.erase_size;
135 let write_size = settings.write_size;
136 let erase_value = settings.erase_value;
137
138 flash_regions.extend(quote! {
139 pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion {
140 bank: crate::flash::FlashBank::#bank_variant,
141 base: #base,
142 size: #size,
143 erase_size: #erase_size,
144 write_size: #write_size,
145 erase_value: #erase_value,
146 };
147 });
148
149 let region_type = format_ident!("{}", get_flash_region_type_name(region.name));
150 flash_regions.extend(quote! {
151 #[cfg(flash)]
152 pub struct #region_type<'d>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>,);
153 });
154 }
155
156 let (fields, (inits, region_names)): (Vec<TokenStream>, (Vec<TokenStream>, Vec<Ident>)) = flash_memory_regions
157 .iter()
158 .map(|f| {
159 let region_name = get_flash_region_name(f.name);
160 let field_name = format_ident!("{}", region_name.to_lowercase());
161 let field_type = format_ident!("{}", get_flash_region_type_name(f.name));
162 let field = quote! {
163 pub #field_name: #field_type<'d>
164 };
165 let region_name = format_ident!("{}", region_name);
166 let init = quote! {
167 #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()})
168 };
169
170 (field, (init, region_name))
171 })
172 .unzip();
173
174 let regions_len = flash_memory_regions.len();
175 flash_regions.extend(quote! {
176 #[cfg(flash)]
177 pub struct FlashLayout<'d> {
178 #(#fields),*
179 }
180
181 #[cfg(flash)]
182 impl<'d> FlashLayout<'d> {
183 pub(crate) fn new(mut p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self {
184 Self {
185 #(#inits),*
186 }
187 }
188 }
189
190 pub const FLASH_REGIONS: [&crate::flash::FlashRegion; #regions_len] = [
191 #(&#region_names),*
192 ];
193 });
194
195 g.extend(quote! { pub mod flash_regions { #flash_regions } });
196
197 // ========
107 // Generate DMA IRQs. 198 // Generate DMA IRQs.
108 199
109 let mut dma_irqs: HashMap<&str, Vec<(&str, &str)>> = HashMap::new(); 200 let mut dma_irqs: HashMap<&str, Vec<(&str, &str)>> = HashMap::new();
@@ -258,6 +349,7 @@ fn main() {
258 (("i2c", "SCL"), quote!(crate::i2c::SclPin)), 349 (("i2c", "SCL"), quote!(crate::i2c::SclPin)),
259 (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)), 350 (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)),
260 (("rcc", "MCO_2"), quote!(crate::rcc::McoPin)), 351 (("rcc", "MCO_2"), quote!(crate::rcc::McoPin)),
352 (("rcc", "MCO"), quote!(crate::rcc::McoPin)),
261 (("dcmi", "D0"), quote!(crate::dcmi::D0Pin)), 353 (("dcmi", "D0"), quote!(crate::dcmi::D0Pin)),
262 (("dcmi", "D1"), quote!(crate::dcmi::D1Pin)), 354 (("dcmi", "D1"), quote!(crate::dcmi::D1Pin)),
263 (("dcmi", "D2"), quote!(crate::dcmi::D2Pin)), 355 (("dcmi", "D2"), quote!(crate::dcmi::D2Pin)),
@@ -447,13 +539,25 @@ fn main() {
447 // MCO is special 539 // MCO is special
448 if pin.signal.starts_with("MCO_") { 540 if pin.signal.starts_with("MCO_") {
449 // Supported in H7 only for now 541 // Supported in H7 only for now
450 if regs.version.starts_with("h7") { 542 if regs.version.starts_with("h5")
543 || regs.version.starts_with("h7")
544 || regs.version.starts_with("f4")
545 {
451 peri = format_ident!("{}", pin.signal.replace("_", "")); 546 peri = format_ident!("{}", pin.signal.replace("_", ""));
452 } else { 547 } else {
453 continue; 548 continue;
454 } 549 }
455 } 550 }
456 551
552 if pin.signal == "MCO" {
553 // Supported in H7 only for now
554 if regs.version.starts_with("l4") {
555 peri = format_ident!("MCO");
556 } else {
557 continue;
558 }
559 }
560
457 g.extend(quote! { 561 g.extend(quote! {
458 pin_trait_impl!(#tr, #peri, #pin_name, #af); 562 pin_trait_impl!(#tr, #peri, #pin_name, #af);
459 }) 563 })
@@ -565,11 +669,25 @@ fn main() {
565 // ======== 669 // ========
566 // Write foreach_foo! macrotables 670 // Write foreach_foo! macrotables
567 671
672 let mut flash_regions_table: Vec<Vec<String>> = Vec::new();
568 let mut interrupts_table: Vec<Vec<String>> = Vec::new(); 673 let mut interrupts_table: Vec<Vec<String>> = Vec::new();
569 let mut peripherals_table: Vec<Vec<String>> = Vec::new(); 674 let mut peripherals_table: Vec<Vec<String>> = Vec::new();
570 let mut pins_table: Vec<Vec<String>> = Vec::new(); 675 let mut pins_table: Vec<Vec<String>> = Vec::new();
571 let mut dma_channels_table: Vec<Vec<String>> = Vec::new(); 676 let mut dma_channels_table: Vec<Vec<String>> = Vec::new();
572 677
678 for m in METADATA
679 .memory
680 .iter()
681 .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some())
682 {
683 let settings = m.settings.as_ref().unwrap();
684 let mut row = Vec::new();
685 row.push(get_flash_region_type_name(m.name));
686 row.push(settings.write_size.to_string());
687 row.push(settings.erase_size.to_string());
688 flash_regions_table.push(row);
689 }
690
573 let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32; 691 let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32;
574 let gpio_stride = 0x400; 692 let gpio_stride = 0x400;
575 693
@@ -666,6 +784,7 @@ fn main() {
666 784
667 let mut m = String::new(); 785 let mut m = String::new();
668 786
787 make_table(&mut m, "foreach_flash_region", &flash_regions_table);
669 make_table(&mut m, "foreach_interrupt", &interrupts_table); 788 make_table(&mut m, "foreach_interrupt", &interrupts_table);
670 make_table(&mut m, "foreach_peripheral", &peripherals_table); 789 make_table(&mut m, "foreach_peripheral", &peripherals_table);
671 make_table(&mut m, "foreach_pin", &pins_table); 790 make_table(&mut m, "foreach_pin", &pins_table);
@@ -818,3 +937,19 @@ macro_rules! {} {{
818 ) 937 )
819 .unwrap(); 938 .unwrap();
820} 939}
940
941fn get_flash_region_name(name: &str) -> String {
942 let name = name.replace("BANK_", "BANK").replace("REGION_", "REGION");
943 if name.contains("REGION") {
944 name
945 } else {
946 name + "_REGION"
947 }
948}
949
950fn get_flash_region_type_name(name: &str) -> String {
951 get_flash_region_name(name)
952 .replace("BANK", "Bank")
953 .replace("REGION", "Region")
954 .replace("_", "")
955}
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index ec49dace7..56ecd63ca 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -7,21 +7,18 @@
7#[cfg_attr(adc_v4, path = "v4.rs")] 7#[cfg_attr(adc_v4, path = "v4.rs")]
8mod _version; 8mod _version;
9 9
10#[cfg(not(any(adc_f1, adc_v1)))] 10#[cfg(not(adc_f1))]
11mod resolution; 11mod resolution;
12#[cfg(not(adc_v1))]
13mod sample_time; 12mod sample_time;
14 13
15#[allow(unused)] 14#[allow(unused)]
16pub use _version::*; 15pub use _version::*;
17#[cfg(not(any(adc_f1, adc_v1)))] 16#[cfg(not(adc_f1))]
18pub use resolution::Resolution; 17pub use resolution::Resolution;
19#[cfg(not(adc_v1))]
20pub use sample_time::SampleTime; 18pub use sample_time::SampleTime;
21 19
22use crate::peripherals; 20use crate::peripherals;
23 21
24#[cfg(not(adc_v1))]
25pub struct Adc<'d, T: Instance> { 22pub struct Adc<'d, T: Instance> {
26 #[allow(unused)] 23 #[allow(unused)]
27 adc: crate::PeripheralRef<'d, T>, 24 adc: crate::PeripheralRef<'d, T>,
@@ -44,9 +41,9 @@ pub(crate) mod sealed {
44 } 41 }
45} 42}
46 43
47#[cfg(not(any(adc_f1, adc_v2, adc_v4)))] 44#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4)))]
48pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {} 45pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
49#[cfg(any(adc_f1, adc_v2, adc_v4))] 46#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4))]
50pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {} 47pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
51 48
52pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} 49pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs
index 62b52a46c..67fb9b8c0 100644
--- a/embassy-stm32/src/adc/resolution.rs
+++ b/embassy-stm32/src/adc/resolution.rs
@@ -1,4 +1,4 @@
1#[cfg(any(adc_v2, adc_v3, adc_g0))] 1#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))]
2#[derive(Clone, Copy, Debug, Eq, PartialEq)] 2#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3pub enum Resolution { 3pub enum Resolution {
4 TwelveBit, 4 TwelveBit,
@@ -19,7 +19,7 @@ pub enum Resolution {
19 19
20impl Default for Resolution { 20impl Default for Resolution {
21 fn default() -> Self { 21 fn default() -> Self {
22 #[cfg(any(adc_v2, adc_v3, adc_g0))] 22 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))]
23 { 23 {
24 Self::TwelveBit 24 Self::TwelveBit
25 } 25 }
@@ -40,7 +40,7 @@ impl From<Resolution> for crate::pac::adc::vals::Res {
40 Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, 40 Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
41 Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, 41 Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
42 Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, 42 Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
43 #[cfg(any(adc_v2, adc_v3, adc_g0))] 43 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))]
44 Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, 44 Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
45 } 45 }
46 } 46 }
@@ -56,7 +56,7 @@ impl Resolution {
56 Resolution::TwelveBit => (1 << 12) - 1, 56 Resolution::TwelveBit => (1 << 12) - 1,
57 Resolution::TenBit => (1 << 10) - 1, 57 Resolution::TenBit => (1 << 10) - 1,
58 Resolution::EightBit => (1 << 8) - 1, 58 Resolution::EightBit => (1 << 8) - 1,
59 #[cfg(any(adc_v2, adc_v3, adc_g0))] 59 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))]
60 Resolution::SixBit => (1 << 6) - 1, 60 Resolution::SixBit => (1 << 6) - 1,
61 } 61 }
62 } 62 }
diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs
index bc5fb1d6f..0faa1e3c0 100644
--- a/embassy-stm32/src/adc/sample_time.rs
+++ b/embassy-stm32/src/adc/sample_time.rs
@@ -25,7 +25,7 @@ macro_rules! impl_sample_time {
25 }; 25 };
26} 26}
27 27
28#[cfg(adc_f1)] 28#[cfg(any(adc_f1, adc_v1))]
29impl_sample_time!( 29impl_sample_time!(
30 "1.5", 30 "1.5",
31 Cycles1_5, 31 Cycles1_5,
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs
index 8b1378917..82a8c3efb 100644
--- a/embassy-stm32/src/adc/v1.rs
+++ b/embassy-stm32/src/adc/v1.rs
@@ -1 +1,171 @@
1use embassy_hal_common::into_ref;
2use embedded_hal_02::blocking::delay::DelayUs;
1 3
4use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime};
5use crate::peripherals::ADC;
6use crate::Peripheral;
7
8pub const VDDA_CALIB_MV: u32 = 3300;
9pub const VREF_INT: u32 = 1230;
10
11pub struct Vbat;
12impl InternalChannel<ADC> for Vbat {}
13impl super::sealed::InternalChannel<ADC> for Vbat {
14 fn channel(&self) -> u8 {
15 18
16 }
17}
18
19pub struct Vref;
20impl InternalChannel<ADC> for Vref {}
21impl super::sealed::InternalChannel<ADC> for Vref {
22 fn channel(&self) -> u8 {
23 17
24 }
25}
26
27pub struct Temperature;
28impl InternalChannel<ADC> for Temperature {}
29impl super::sealed::InternalChannel<ADC> for Temperature {
30 fn channel(&self) -> u8 {
31 16
32 }
33}
34
35impl<'d, T: Instance> Adc<'d, T> {
36 pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
37 into_ref!(adc);
38 T::enable();
39 T::reset();
40
41 // Delay 1μs when using HSI14 as the ADC clock.
42 //
43 // Table 57. ADC characteristics
44 // tstab = 14 * 1/fadc
45 delay.delay_us(1);
46
47 let s = Self {
48 adc,
49 sample_time: Default::default(),
50 };
51 s.calibrate();
52 s
53 }
54
55 pub fn enable_vbat(&self, _delay: &mut impl DelayUs<u32>) -> Vbat {
56 // SMP must be ≥ 56 ADC clock cycles when using HSI14.
57 //
58 // 6.3.20 Vbat monitoring characteristics
59 // ts_vbat ≥ 4μs
60 unsafe {
61 T::regs().ccr().modify(|reg| reg.set_vbaten(true));
62 }
63 Vbat
64 }
65
66 pub fn enable_vref(&self, delay: &mut impl DelayUs<u32>) -> Vref {
67 // Table 28. Embedded internal reference voltage
68 // tstart = 10μs
69 unsafe {
70 T::regs().ccr().modify(|reg| reg.set_vrefen(true));
71 }
72 delay.delay_us(10);
73 Vref
74 }
75
76 pub fn enable_temperature(&self, delay: &mut impl DelayUs<u32>) -> Temperature {
77 // SMP must be ≥ 56 ADC clock cycles when using HSI14.
78 //
79 // 6.3.19 Temperature sensor characteristics
80 // tstart ≤ 10μs
81 // ts_temp ≥ 4μs
82 unsafe {
83 T::regs().ccr().modify(|reg| reg.set_tsen(true));
84 }
85 delay.delay_us(10);
86 Temperature
87 }
88
89 fn calibrate(&self) {
90 unsafe {
91 // A.7.1 ADC calibration code example
92 if T::regs().cr().read().aden() {
93 T::regs().cr().modify(|reg| reg.set_addis(true));
94 }
95 while T::regs().cr().read().aden() {
96 // spin
97 }
98 T::regs().cfgr1().modify(|reg| reg.set_dmaen(false));
99 T::regs().cr().modify(|reg| reg.set_adcal(true));
100 while T::regs().cr().read().adcal() {
101 // spin
102 }
103 }
104 }
105
106 pub fn set_sample_time(&mut self, sample_time: SampleTime) {
107 self.sample_time = sample_time;
108 }
109
110 pub fn set_resolution(&mut self, resolution: Resolution) {
111 unsafe {
112 T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
113 }
114 }
115
116 pub fn read<P>(&mut self, pin: &mut P) -> u16
117 where
118 P: AdcPin<T> + crate::gpio::sealed::Pin,
119 {
120 let channel = pin.channel();
121 unsafe {
122 pin.set_as_analog();
123 self.read_channel(channel)
124 }
125 }
126
127 pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
128 let channel = channel.channel();
129 unsafe { self.read_channel(channel) }
130 }
131
132 unsafe fn read_channel(&mut self, channel: u8) -> u16 {
133 // A.7.2 ADC enable sequence code example
134 if T::regs().isr().read().adrdy() {
135 T::regs().isr().modify(|reg| reg.set_adrdy(true));
136 }
137 T::regs().cr().modify(|reg| reg.set_aden(true));
138 while !T::regs().isr().read().adrdy() {
139 // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration
140 // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the
141 // ADEN bit until the ADRDY flag goes high.
142 T::regs().cr().modify(|reg| reg.set_aden(true));
143 }
144
145 T::regs().isr().modify(|reg| {
146 reg.set_eoc(true);
147 reg.set_eosmp(true);
148 });
149
150 // A.7.5 Single conversion sequence code example - Software trigger
151 T::regs().chselr().write(|reg| reg.set_chselx(channel as usize, true));
152 T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into()));
153 T::regs().cr().modify(|reg| reg.set_adstart(true));
154 while !T::regs().isr().read().eoc() {
155 // spin
156 }
157 let value = T::regs().dr().read().0 as u16;
158
159 // A.7.3 ADC disable code example
160 T::regs().cr().modify(|reg| reg.set_adstp(true));
161 while T::regs().cr().read().adstp() {
162 // spin
163 }
164 T::regs().cr().modify(|reg| reg.set_addis(true));
165 while T::regs().cr().read().aden() {
166 // spin
167 }
168
169 value
170 }
171}
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs
index 442fee48e..6f26fd194 100644
--- a/embassy-stm32/src/dma/gpdma.rs
+++ b/embassy-stm32/src/dma/gpdma.rs
@@ -190,6 +190,10 @@ mod low_level_api {
190 fence(Ordering::SeqCst); 190 fence(Ordering::SeqCst);
191 191
192 let ch = dma.ch(channel_number as _); 192 let ch = dma.ch(channel_number as _);
193
194 // Reset ch
195 ch.cr().write(|w| w.set_reset(true));
196
193 ch.llr().write(|_| {}); // no linked list 197 ch.llr().write(|_| {}); // no linked list
194 ch.tr1().write(|w| { 198 ch.tr1().write(|w| {
195 w.set_sdw(data_size.into()); 199 w.set_sdw(data_size.into());
@@ -252,7 +256,7 @@ mod low_level_api {
252 /// Gets the running status of the channel 256 /// Gets the running status of the channel
253 pub unsafe fn is_running(dma: Gpdma, ch: u8) -> bool { 257 pub unsafe fn is_running(dma: Gpdma, ch: u8) -> bool {
254 let ch = dma.ch(ch as _); 258 let ch = dma.ch(ch as _);
255 !ch.sr().read().idlef() 259 !ch.sr().read().tcf()
256 } 260 }
257 261
258 /// Gets the total remaining transfers for the channel 262 /// Gets the total remaining transfers for the channel
@@ -291,7 +295,10 @@ mod low_level_api {
291 } 295 }
292 296
293 if sr.suspf() || sr.tcf() { 297 if sr.suspf() || sr.tcf() {
294 ch.cr().write(|w| w.set_reset(true)); 298 // disable all xxIEs to prevent the irq from firing again.
299 ch.cr().write(|_| {});
300
301 // Wake the future. It'll look at tcf and see it's set.
295 STATE.channels[state_index].waker.wake(); 302 STATE.channels[state_index].waker.wake();
296 } 303 }
297 } 304 }
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs
index fcb4a296c..d49b1f767 100644
--- a/embassy-stm32/src/eth/v2/mod.rs
+++ b/embassy-stm32/src/eth/v2/mod.rs
@@ -9,7 +9,7 @@ pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing};
9use super::*; 9use super::*;
10use crate::gpio::sealed::{AFType, Pin as _}; 10use crate::gpio::sealed::{AFType, Pin as _};
11use crate::gpio::{AnyPin, Speed}; 11use crate::gpio::{AnyPin, Speed};
12use crate::pac::{ETH, RCC, SYSCFG}; 12use crate::pac::ETH;
13use crate::Peripheral; 13use crate::Peripheral;
14 14
15const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet 15const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet
@@ -60,16 +60,33 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
60 unsafe { 60 unsafe {
61 // Enable the necessary Clocks 61 // Enable the necessary Clocks
62 // NOTE(unsafe) We have exclusive access to the registers 62 // NOTE(unsafe) We have exclusive access to the registers
63 #[cfg(not(rcc_h5))]
63 critical_section::with(|_| { 64 critical_section::with(|_| {
64 RCC.apb4enr().modify(|w| w.set_syscfgen(true)); 65 crate::pac::RCC.apb4enr().modify(|w| w.set_syscfgen(true));
65 RCC.ahb1enr().modify(|w| { 66 crate::pac::RCC.ahb1enr().modify(|w| {
66 w.set_eth1macen(true); 67 w.set_eth1macen(true);
67 w.set_eth1txen(true); 68 w.set_eth1txen(true);
68 w.set_eth1rxen(true); 69 w.set_eth1rxen(true);
69 }); 70 });
70 71
71 // RMII 72 // RMII
72 SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); 73 crate::pac::SYSCFG.pmcr().modify(|w| w.set_epis(0b100));
74 });
75
76 #[cfg(rcc_h5)]
77 critical_section::with(|_| {
78 crate::pac::RCC.apb3enr().modify(|w| w.set_sbsen(true));
79
80 crate::pac::RCC.ahb1enr().modify(|w| {
81 w.set_ethen(true);
82 w.set_ethtxen(true);
83 w.set_ethrxen(true);
84 });
85
86 // RMII
87 crate::pac::SBS
88 .pmcr()
89 .modify(|w| w.set_eth_sel_phy(crate::pac::sbs::vals::EthSelPhy::B_0X4));
73 }); 90 });
74 91
75 config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); 92 config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs
index e1ce09a49..10109e56a 100644
--- a/embassy-stm32/src/exti.rs
+++ b/embassy-stm32/src/exti.rs
@@ -25,11 +25,11 @@ fn cpu_regs() -> pac::exti::Exti {
25 EXTI 25 EXTI
26} 26}
27 27
28#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5)))] 28#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))]
29fn exticr_regs() -> pac::syscfg::Syscfg { 29fn exticr_regs() -> pac::syscfg::Syscfg {
30 pac::SYSCFG 30 pac::SYSCFG
31} 31}
32#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] 32#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
33fn exticr_regs() -> pac::exti::Exti { 33fn exticr_regs() -> pac::exti::Exti {
34 EXTI 34 EXTI
35} 35}
@@ -39,9 +39,9 @@ fn exticr_regs() -> pac::afio::Afio {
39} 39}
40 40
41pub unsafe fn on_irq() { 41pub unsafe fn on_irq() {
42 #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] 42 #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))]
43 let bits = EXTI.pr(0).read().0; 43 let bits = EXTI.pr(0).read().0;
44 #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] 44 #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
45 let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; 45 let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0;
46 46
47 // Mask all the channels that fired. 47 // Mask all the channels that fired.
@@ -53,9 +53,9 @@ pub unsafe fn on_irq() {
53 } 53 }
54 54
55 // Clear pending 55 // Clear pending
56 #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] 56 #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))]
57 EXTI.pr(0).write_value(Lines(bits)); 57 EXTI.pr(0).write_value(Lines(bits));
58 #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] 58 #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
59 { 59 {
60 EXTI.rpr(0).write_value(Lines(bits)); 60 EXTI.rpr(0).write_value(Lines(bits));
61 EXTI.fpr(0).write_value(Lines(bits)); 61 EXTI.fpr(0).write_value(Lines(bits));
@@ -213,9 +213,9 @@ impl<'a> ExtiInputFuture<'a> {
213 EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); 213 EXTI.ftsr(0).modify(|w| w.set_line(pin, falling));
214 214
215 // clear pending bit 215 // clear pending bit
216 #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] 216 #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))]
217 EXTI.pr(0).write(|w| w.set_line(pin, true)); 217 EXTI.pr(0).write(|w| w.set_line(pin, true));
218 #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] 218 #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
219 { 219 {
220 EXTI.rpr(0).write(|w| w.set_line(pin, true)); 220 EXTI.rpr(0).write(|w| w.set_line(pin, true));
221 EXTI.fpr(0).write(|w| w.set_line(pin, true)); 221 EXTI.fpr(0).write(|w| w.set_line(pin, true));
@@ -364,7 +364,7 @@ pub(crate) unsafe fn init() {
364 364
365 foreach_exti_irq!(enable_irq); 365 foreach_exti_irq!(enable_irq);
366 366
367 #[cfg(not(any(rcc_wb, rcc_wl5, rcc_wle, stm32f1)))] 367 #[cfg(not(any(rcc_wb, rcc_wl5, rcc_wle, stm32f1, exti_h5, exti_h50)))]
368 <crate::peripherals::SYSCFG as crate::rcc::sealed::RccPeripheral>::enable(); 368 <crate::peripherals::SYSCFG as crate::rcc::sealed::RccPeripheral>::enable();
369 #[cfg(stm32f1)] 369 #[cfg(stm32f1)]
370 <crate::peripherals::AFIO as crate::rcc::sealed::RccPeripheral>::enable(); 370 <crate::peripherals::AFIO as crate::rcc::sealed::RccPeripheral>::enable();
diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs
new file mode 100644
index 000000000..8235d6f08
--- /dev/null
+++ b/embassy-stm32/src/flash/common.rs
@@ -0,0 +1,211 @@
1use atomic_polyfill::{fence, Ordering};
2use embassy_hal_common::drop::OnDrop;
3use embassy_hal_common::{into_ref, PeripheralRef};
4
5use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE};
6use crate::flash::FlashBank;
7use crate::Peripheral;
8
9pub struct Flash<'d> {
10 inner: PeripheralRef<'d, crate::peripherals::FLASH>,
11}
12
13impl<'d> Flash<'d> {
14 pub fn new(p: impl Peripheral<P = crate::peripherals::FLASH> + 'd) -> Self {
15 into_ref!(p);
16 Self { inner: p }
17 }
18
19 pub fn into_regions(self) -> FlashLayout<'d> {
20 FlashLayout::new(self.release())
21 }
22
23 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
24 blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes)
25 }
26
27 pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
28 unsafe { blocking_write(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) }
29 }
30
31 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
32 unsafe { blocking_erase(FLASH_BASE as u32, from, to) }
33 }
34
35 pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> {
36 let mut flash = self;
37 unsafe { flash.inner.clone_unchecked() }
38 }
39}
40
41fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
42 if offset + bytes.len() as u32 > size {
43 return Err(Error::Size);
44 }
45
46 let start_address = base + offset;
47 let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) };
48 bytes.copy_from_slice(flash_data);
49 Ok(())
50}
51
52unsafe fn blocking_write(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> {
53 if offset + bytes.len() as u32 > size {
54 return Err(Error::Size);
55 }
56 if offset % WRITE_SIZE as u32 != 0 || bytes.len() % WRITE_SIZE != 0 {
57 return Err(Error::Unaligned);
58 }
59
60 let mut address = base + offset;
61 trace!("Writing {} bytes at 0x{:x}", bytes.len(), address);
62
63 for chunk in bytes.chunks(WRITE_SIZE) {
64 critical_section::with(|_| {
65 family::clear_all_err();
66 fence(Ordering::SeqCst);
67 family::unlock();
68 fence(Ordering::SeqCst);
69 family::begin_write();
70 fence(Ordering::SeqCst);
71
72 let _on_drop = OnDrop::new(|| {
73 family::end_write();
74 fence(Ordering::SeqCst);
75 family::lock();
76 });
77
78 family::blocking_write(address, chunk.try_into().unwrap())
79 })?;
80 address += WRITE_SIZE as u32;
81 }
82 Ok(())
83}
84
85unsafe fn blocking_erase(base: u32, from: u32, to: u32) -> Result<(), Error> {
86 let start_address = base + from;
87 let end_address = base + to;
88 let regions = family::get_flash_regions();
89
90 // Test if the address range is aligned at sector base addresses
91 let mut address = start_address;
92 while address < end_address {
93 let sector = get_sector(address, regions);
94 if sector.start != address {
95 return Err(Error::Unaligned);
96 }
97 address += sector.size;
98 }
99 if address != end_address {
100 return Err(Error::Unaligned);
101 }
102
103 trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address);
104
105 let mut address = start_address;
106 while address < end_address {
107 let sector = get_sector(address, regions);
108 trace!("Erasing sector: {:?}", sector);
109
110 critical_section::with(|_| {
111 family::clear_all_err();
112 fence(Ordering::SeqCst);
113 family::unlock();
114 fence(Ordering::SeqCst);
115
116 let _on_drop = OnDrop::new(|| {
117 family::lock();
118 });
119
120 family::blocking_erase_sector(&sector)
121 })?;
122 address += sector.size;
123 }
124 Ok(())
125}
126
127pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector {
128 let mut current_bank = FlashBank::Bank1;
129 let mut bank_offset = 0;
130 for region in regions {
131 if region.bank != current_bank {
132 current_bank = region.bank;
133 bank_offset = 0;
134 }
135
136 if address < region.end() {
137 let index_in_region = (address - region.base) / region.erase_size;
138 return FlashSector {
139 bank: region.bank,
140 index_in_bank: bank_offset + index_in_region as u8,
141 start: region.base + index_in_region * region.erase_size,
142 size: region.erase_size,
143 };
144 }
145
146 bank_offset += region.sectors();
147 }
148
149 panic!("Flash sector not found");
150}
151
152impl FlashRegion {
153 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
154 blocking_read(self.base, self.size, offset, bytes)
155 }
156
157 pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
158 unsafe { blocking_write(self.base, self.size, offset, bytes) }
159 }
160
161 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
162 unsafe { blocking_erase(self.base, from, to) }
163 }
164}
165
166foreach_flash_region! {
167 ($type_name:ident, $write_size:literal, $erase_size:literal) => {
168 impl crate::_generated::flash_regions::$type_name<'_> {
169 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
170 blocking_read(self.0.base, self.0.size, offset, bytes)
171 }
172
173 pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
174 unsafe { blocking_write(self.0.base, self.0.size, offset, bytes) }
175 }
176
177 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
178 unsafe { blocking_erase(self.0.base, from, to) }
179 }
180 }
181
182 impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name<'_> {
183 type Error = Error;
184 }
185
186 impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> {
187 const READ_SIZE: usize = 1;
188
189 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
190 self.blocking_read(offset, bytes)
191 }
192
193 fn capacity(&self) -> usize {
194 self.0.size as usize
195 }
196 }
197
198 impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_> {
199 const WRITE_SIZE: usize = $write_size;
200 const ERASE_SIZE: usize = $erase_size;
201
202 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
203 self.blocking_write(offset, bytes)
204 }
205
206 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
207 self.blocking_erase(from, to)
208 }
209 }
210 };
211}
diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs
index 1cb08ee1a..10a09c42c 100644
--- a/embassy-stm32/src/flash/f3.rs
+++ b/embassy-stm32/src/flash/f3.rs
@@ -1,9 +1,16 @@
1use core::convert::TryInto; 1use core::convert::TryInto;
2use core::ptr::write_volatile; 2use core::ptr::write_volatile;
3 3
4use atomic_polyfill::{fence, Ordering};
5
6use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
4use crate::flash::Error; 7use crate::flash::Error;
5use crate::pac; 8use crate::pac;
6 9
10pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
11 &FLASH_REGIONS
12}
13
7pub(crate) unsafe fn lock() { 14pub(crate) unsafe fn lock() {
8 pac::FLASH.cr().modify(|w| w.set_lock(true)); 15 pac::FLASH.cr().modify(|w| w.set_lock(true));
9} 16}
@@ -13,58 +20,55 @@ pub(crate) unsafe fn unlock() {
13 pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); 20 pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB));
14} 21}
15 22
16pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { 23pub(crate) unsafe fn begin_write() {
24 assert_eq!(0, WRITE_SIZE % 2);
25
17 pac::FLASH.cr().write(|w| w.set_pg(true)); 26 pac::FLASH.cr().write(|w| w.set_pg(true));
27}
18 28
19 let ret = { 29pub(crate) unsafe fn end_write() {
20 let mut ret: Result<(), Error> = Ok(()); 30 pac::FLASH.cr().write(|w| w.set_pg(false));
21 let mut offset = offset; 31}
22 for chunk in buf.chunks(2) {
23 write_volatile(offset as *mut u16, u16::from_le_bytes(chunk[0..2].try_into().unwrap()));
24 offset += chunk.len() as u32;
25 32
26 ret = blocking_wait_ready(); 33pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
27 if ret.is_err() { 34 let mut address = start_address;
28 break; 35 for chunk in buf.chunks(2) {
29 } 36 write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap()));
30 } 37 address += chunk.len() as u32;
31 ret
32 };
33 38
34 pac::FLASH.cr().write(|w| w.set_pg(false)); 39 // prevents parallelism errors
40 fence(Ordering::SeqCst);
41 }
35 42
36 ret 43 blocking_wait_ready()
37} 44}
38 45
39pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { 46pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
40 for page in (from..to).step_by(super::ERASE_SIZE) { 47 pac::FLASH.cr().modify(|w| {
41 pac::FLASH.cr().modify(|w| { 48 w.set_per(true);
42 w.set_per(true); 49 });
43 });
44 50
45 pac::FLASH.ar().write(|w| w.set_far(page)); 51 pac::FLASH.ar().write(|w| w.set_far(sector.start));
46 52
47 pac::FLASH.cr().modify(|w| { 53 pac::FLASH.cr().modify(|w| {
48 w.set_strt(true); 54 w.set_strt(true);
49 }); 55 });
50 56
51 let mut ret: Result<(), Error> = blocking_wait_ready(); 57 let mut ret: Result<(), Error> = blocking_wait_ready();
52 58
53 if !pac::FLASH.sr().read().eop() { 59 if !pac::FLASH.sr().read().eop() {
54 trace!("FLASH: EOP not set"); 60 trace!("FLASH: EOP not set");
55 ret = Err(Error::Prog); 61 ret = Err(Error::Prog);
56 } else { 62 } else {
57 pac::FLASH.sr().write(|w| w.set_eop(true)); 63 pac::FLASH.sr().write(|w| w.set_eop(true));
58 } 64 }
59 65
60 pac::FLASH.cr().modify(|w| w.set_per(false)); 66 pac::FLASH.cr().modify(|w| w.set_per(false));
61 67
62 clear_all_err(); 68 clear_all_err();
63 if ret.is_err() { 69 if ret.is_err() {
64 return ret; 70 return ret;
65 }
66 } 71 }
67
68 Ok(()) 72 Ok(())
69} 73}
70 74
@@ -82,7 +86,7 @@ pub(crate) unsafe fn clear_all_err() {
82 }); 86 });
83} 87}
84 88
85pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { 89unsafe fn blocking_wait_ready() -> Result<(), Error> {
86 loop { 90 loop {
87 let sr = pac::FLASH.sr().read(); 91 let sr = pac::FLASH.sr().read();
88 92
diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs
index 9e23a8adf..2ce9df69f 100644
--- a/embassy-stm32/src/flash/f4.rs
+++ b/embassy-stm32/src/flash/f4.rs
@@ -2,27 +2,108 @@ use core::convert::TryInto;
2use core::ptr::write_volatile; 2use core::ptr::write_volatile;
3use core::sync::atomic::{fence, Ordering}; 3use core::sync::atomic::{fence, Ordering};
4 4
5use super::{ERASE_SIZE, FLASH_BASE, FLASH_SIZE}; 5use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
6use crate::flash::Error; 6use crate::flash::Error;
7use crate::pac; 7use crate::pac;
8 8
9const SECOND_BANK_SECTOR_START: u32 = 12; 9#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
10mod alt_regions {
11 use embassy_hal_common::PeripheralRef;
12 use stm32_metapac::FLASH_SIZE;
10 13
11unsafe fn is_dual_bank() -> bool { 14 use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3};
12 match FLASH_SIZE / 1024 { 15 use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashBank, FlashRegion};
13 // 1 MB devices depend on configuration 16 use crate::peripherals::FLASH;
14 1024 => { 17
15 if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) { 18 pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion {
16 pac::FLASH.optcr().read().db1m() 19 size: 3 * BANK1_REGION3.erase_size,
17 } else { 20 ..BANK1_REGION3
18 false 21 };
22 pub const ALT_BANK2_REGION1: FlashRegion = FlashRegion {
23 bank: FlashBank::Bank2,
24 base: BANK1_REGION1.base + FLASH_SIZE as u32 / 2,
25 ..BANK1_REGION1
26 };
27 pub const ALT_BANK2_REGION2: FlashRegion = FlashRegion {
28 bank: FlashBank::Bank2,
29 base: BANK1_REGION2.base + FLASH_SIZE as u32 / 2,
30 ..BANK1_REGION2
31 };
32 pub const ALT_BANK2_REGION3: FlashRegion = FlashRegion {
33 bank: FlashBank::Bank2,
34 base: BANK1_REGION3.base + FLASH_SIZE as u32 / 2,
35 size: 3 * BANK1_REGION3.erase_size,
36 ..BANK1_REGION3
37 };
38
39 pub const ALT_FLASH_REGIONS: [&FlashRegion; 6] = [
40 &BANK1_REGION1,
41 &BANK1_REGION2,
42 &ALT_BANK1_REGION3,
43 &ALT_BANK2_REGION1,
44 &ALT_BANK2_REGION2,
45 &ALT_BANK2_REGION3,
46 ];
47
48 pub type AltBank1Region1<'d> = Bank1Region1<'d>;
49 pub type AltBank1Region2<'d> = Bank1Region2<'d>;
50 pub struct AltBank1Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
51 pub struct AltBank2Region1<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
52 pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
53 pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
54
55 pub struct AltFlashLayout<'d> {
56 pub bank1_region1: AltBank1Region1<'d>,
57 pub bank1_region2: AltBank1Region2<'d>,
58 pub bank1_region3: AltBank1Region3<'d>,
59 pub bank2_region1: AltBank2Region1<'d>,
60 pub bank2_region2: AltBank2Region2<'d>,
61 pub bank2_region3: AltBank2Region3<'d>,
62 }
63
64 impl<'d> Flash<'d> {
65 pub fn into_alt_regions(self) -> AltFlashLayout<'d> {
66 unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) };
67
68 // SAFETY: We never expose the cloned peripheral references, and their instance is not public.
69 // Also, all flash region operations are protected with a cs.
70 let mut p = self.release();
71 AltFlashLayout {
72 bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }),
73 bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }),
74 bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }),
75 bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }),
76 bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }),
77 bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }),
19 } 78 }
20 } 79 }
21 // 2 MB devices are always dual bank
22 2048 => true,
23 // All other devices are single bank
24 _ => false,
25 } 80 }
81
82 impl Drop for AltFlashLayout<'_> {
83 fn drop(&mut self) {
84 unsafe {
85 super::lock();
86 crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false))
87 };
88 }
89 }
90}
91
92#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
93pub use alt_regions::{AltFlashLayout, ALT_FLASH_REGIONS};
94
95#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
96pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
97 if unsafe { pac::FLASH.optcr().read().db1m() } {
98 &ALT_FLASH_REGIONS
99 } else {
100 &FLASH_REGIONS
101 }
102}
103
104#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))]
105pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
106 &FLASH_REGIONS
26} 107}
27 108
28pub(crate) unsafe fn lock() { 109pub(crate) unsafe fn lock() {
@@ -34,93 +115,34 @@ pub(crate) unsafe fn unlock() {
34 pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); 115 pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
35} 116}
36 117
37pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { 118pub(crate) unsafe fn begin_write() {
119 assert_eq!(0, WRITE_SIZE % 4);
120
38 pac::FLASH.cr().write(|w| { 121 pac::FLASH.cr().write(|w| {
39 w.set_pg(true); 122 w.set_pg(true);
40 w.set_psize(pac::flash::vals::Psize::PSIZE32); 123 w.set_psize(pac::flash::vals::Psize::PSIZE32);
41 }); 124 });
42
43 let ret = {
44 let mut ret: Result<(), Error> = Ok(());
45 let mut offset = offset;
46 for chunk in buf.chunks(super::WRITE_SIZE) {
47 for val in chunk.chunks(4) {
48 write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap()));
49 offset += val.len() as u32;
50
51 // prevents parallelism errors
52 fence(Ordering::SeqCst);
53 }
54
55 ret = blocking_wait_ready();
56 if ret.is_err() {
57 break;
58 }
59 }
60 ret
61 };
62
63 pac::FLASH.cr().write(|w| w.set_pg(false));
64
65 ret
66} 125}
67 126
68struct FlashSector { 127pub(crate) unsafe fn end_write() {
69 index: u8, 128 pac::FLASH.cr().write(|w| w.set_pg(false));
70 size: u32,
71}
72
73fn get_sector(addr: u32, dual_bank: bool) -> FlashSector {
74 let offset = addr - FLASH_BASE as u32;
75
76 let bank_size = match dual_bank {
77 true => FLASH_SIZE / 2,
78 false => FLASH_SIZE,
79 } as u32;
80
81 let bank = offset / bank_size;
82 let offset_in_bank = offset % bank_size;
83
84 let index_in_bank = if offset_in_bank >= ERASE_SIZE as u32 / 2 {
85 4 + offset_in_bank / ERASE_SIZE as u32
86 } else {
87 offset_in_bank / (ERASE_SIZE as u32 / 8)
88 };
89
90 // First 4 sectors are 16KB, then one 64KB, and rest are 128KB
91 let size = match index_in_bank {
92 0..=3 => 16 * 1024,
93 4 => 64 * 1024,
94 _ => 128 * 1024,
95 };
96
97 let index = if bank == 1 {
98 SECOND_BANK_SECTOR_START + index_in_bank
99 } else {
100 index_in_bank
101 } as u8;
102
103 FlashSector { index, size }
104} 129}
105 130
106pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { 131pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
107 let mut addr = from; 132 let mut address = start_address;
108 let dual_bank = is_dual_bank(); 133 for val in buf.chunks(4) {
134 write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
135 address += val.len() as u32;
109 136
110 while addr < to { 137 // prevents parallelism errors
111 let sector = get_sector(addr, dual_bank); 138 fence(Ordering::SeqCst);
112 erase_sector(sector.index)?;
113 addr += sector.size;
114 } 139 }
115 140
116 Ok(()) 141 blocking_wait_ready()
117} 142}
118 143
119unsafe fn erase_sector(sector: u8) -> Result<(), Error> { 144pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
120 let bank = sector / SECOND_BANK_SECTOR_START as u8; 145 let snb = ((sector.bank as u8) << 4) + sector.index_in_bank;
121 let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_START as u8);
122
123 trace!("Erasing sector: {}", sector);
124 146
125 pac::FLASH.cr().modify(|w| { 147 pac::FLASH.cr().modify(|w| {
126 w.set_ser(true); 148 w.set_ser(true);
@@ -148,7 +170,7 @@ pub(crate) unsafe fn clear_all_err() {
148 }); 170 });
149} 171}
150 172
151pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { 173unsafe fn blocking_wait_ready() -> Result<(), Error> {
152 loop { 174 loop {
153 let sr = pac::FLASH.sr().read(); 175 let sr = pac::FLASH.sr().read();
154 176
@@ -173,3 +195,80 @@ pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
173 } 195 }
174 } 196 }
175} 197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use crate::flash::{get_sector, FlashBank};
203
204 #[test]
205 #[cfg(stm32f429)]
206 fn can_get_sector_single_bank() {
207 const SMALL_SECTOR_SIZE: u32 = 16 * 1024;
208 const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024;
209 const LARGE_SECTOR_SIZE: u32 = 128 * 1024;
210
211 let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| {
212 assert_eq!(
213 FlashSector {
214 bank: FlashBank::Bank1,
215 index_in_bank,
216 start,
217 size
218 },
219 get_sector(address, &FLASH_REGIONS)
220 )
221 };
222
223 assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
224 assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);
225 assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);
226 assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);
227
228 assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);
229 assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);
230
231 assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);
232 assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);
233 assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000);
234 assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);
235
236 let assert_sector = |bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| {
237 assert_eq!(
238 FlashSector {
239 bank,
240 index_in_bank,
241 start,
242 size
243 },
244 get_sector(address, &ALT_FLASH_REGIONS)
245 )
246 };
247
248 assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
249 assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);
250 assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);
251 assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);
252
253 assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);
254 assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);
255
256 assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);
257 assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);
258 assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000);
259 assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF);
260
261 assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000);
262 assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF);
263 assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000);
264 assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF);
265
266 assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000);
267 assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF);
268
269 assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000);
270 assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF);
271 assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000);
272 assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);
273 }
274}
diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs
index dd0d8439d..6427d5a09 100644
--- a/embassy-stm32/src/flash/f7.rs
+++ b/embassy-stm32/src/flash/f7.rs
@@ -2,9 +2,14 @@ use core::convert::TryInto;
2use core::ptr::write_volatile; 2use core::ptr::write_volatile;
3use core::sync::atomic::{fence, Ordering}; 3use core::sync::atomic::{fence, Ordering};
4 4
5use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
5use crate::flash::Error; 6use crate::flash::Error;
6use crate::pac; 7use crate::pac;
7 8
9pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
10 &FLASH_REGIONS
11}
12
8pub(crate) unsafe fn lock() { 13pub(crate) unsafe fn lock() {
9 pac::FLASH.cr().modify(|w| w.set_lock(true)); 14 pac::FLASH.cr().modify(|w| w.set_lock(true));
10} 15}
@@ -14,64 +19,36 @@ pub(crate) unsafe fn unlock() {
14 pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); 19 pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
15} 20}
16 21
17pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { 22pub(crate) unsafe fn begin_write() {
23 assert_eq!(0, WRITE_SIZE % 4);
24
18 pac::FLASH.cr().write(|w| { 25 pac::FLASH.cr().write(|w| {
19 w.set_pg(true); 26 w.set_pg(true);
20 w.set_psize(pac::flash::vals::Psize::PSIZE32); 27 w.set_psize(pac::flash::vals::Psize::PSIZE32);
21 }); 28 });
29}
22 30
23 let ret = { 31pub(crate) unsafe fn end_write() {
24 let mut ret: Result<(), Error> = Ok(());
25 let mut offset = offset;
26 for chunk in buf.chunks(super::WRITE_SIZE) {
27 for val in chunk.chunks(4) {
28 write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap()));
29 offset += val.len() as u32;
30
31 // prevents parallelism errors
32 fence(Ordering::SeqCst);
33 }
34
35 ret = blocking_wait_ready();
36 if ret.is_err() {
37 break;
38 }
39 }
40 ret
41 };
42
43 pac::FLASH.cr().write(|w| w.set_pg(false)); 32 pac::FLASH.cr().write(|w| w.set_pg(false));
44
45 ret
46} 33}
47 34
48pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { 35pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
49 let start_sector = if from >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 { 36 let mut address = start_address;
50 4 + (from - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32 37 for val in buf.chunks(4) {
51 } else { 38 write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
52 (from - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8) 39 address += val.len() as u32;
53 }; 40
54 41 // prevents parallelism errors
55 let end_sector = if to >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 { 42 fence(Ordering::SeqCst);
56 4 + (to - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32
57 } else {
58 (to - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8)
59 };
60
61 for sector in start_sector..end_sector {
62 let ret = erase_sector(sector as u8);
63 if ret.is_err() {
64 return ret;
65 }
66 } 43 }
67 44
68 Ok(()) 45 blocking_wait_ready()
69} 46}
70 47
71unsafe fn erase_sector(sector: u8) -> Result<(), Error> { 48pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
72 pac::FLASH.cr().modify(|w| { 49 pac::FLASH.cr().modify(|w| {
73 w.set_ser(true); 50 w.set_ser(true);
74 w.set_snb(sector) 51 w.set_snb(sector.index_in_bank)
75 }); 52 });
76 53
77 pac::FLASH.cr().modify(|w| { 54 pac::FLASH.cr().modify(|w| {
@@ -107,7 +84,7 @@ pub(crate) unsafe fn clear_all_err() {
107 }); 84 });
108} 85}
109 86
110pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { 87unsafe fn blocking_wait_ready() -> Result<(), Error> {
111 loop { 88 loop {
112 let sr = pac::FLASH.sr().read(); 89 let sr = pac::FLASH.sr().read();
113 90
@@ -132,3 +109,75 @@ pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
132 } 109 }
133 } 110 }
134} 111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use crate::flash::{get_sector, FlashBank};
117
118 #[test]
119 #[cfg(stm32f732)]
120 fn can_get_sector() {
121 const SMALL_SECTOR_SIZE: u32 = 16 * 1024;
122 const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024;
123 const LARGE_SECTOR_SIZE: u32 = 128 * 1024;
124
125 let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| {
126 assert_eq!(
127 FlashSector {
128 bank: FlashBank::Bank1,
129 index_in_bank,
130 start,
131 size
132 },
133 get_sector(address, &FLASH_REGIONS)
134 )
135 };
136
137 assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
138 assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);
139 assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);
140 assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);
141
142 assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);
143 assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);
144
145 assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);
146 assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);
147 assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000);
148 assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF);
149 }
150
151 #[test]
152 #[cfg(stm32f769)]
153 fn can_get_sector() {
154 const SMALL_SECTOR_SIZE: u32 = 32 * 1024;
155 const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024;
156 const LARGE_SECTOR_SIZE: u32 = 256 * 1024;
157
158 let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| {
159 assert_eq!(
160 FlashSector {
161 bank: FlashBank::Bank1,
162 index_in_bank,
163 start,
164 size
165 },
166 get_sector(address, &FLASH_REGIONS)
167 )
168 };
169
170 assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);
171 assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_7FFF);
172 assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_8000);
173 assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_FFFF);
174
175 assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0802_0000);
176 assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0803_FFFF);
177
178 assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0804_0000);
179 assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF);
180 assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000);
181 assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);
182 }
183}
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs
index 7de95ac11..4f38d50c0 100644
--- a/embassy-stm32/src/flash/h7.rs
+++ b/embassy-stm32/src/flash/h7.rs
@@ -1,13 +1,18 @@
1use core::convert::TryInto; 1use core::convert::TryInto;
2use core::ptr::write_volatile; 2use core::ptr::write_volatile;
3 3
4use atomic_polyfill::{fence, Ordering};
5
6use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE};
4use crate::flash::Error; 7use crate::flash::Error;
5use crate::pac; 8use crate::pac;
6 9
7const SECOND_BANK_OFFSET: usize = 0x0010_0000;
8
9const fn is_dual_bank() -> bool { 10const fn is_dual_bank() -> bool {
10 super::FLASH_SIZE / 2 > super::ERASE_SIZE 11 FLASH_REGIONS.len() == 2
12}
13
14pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
15 &FLASH_REGIONS
11} 16}
12 17
13pub(crate) unsafe fn lock() { 18pub(crate) unsafe fn lock() {
@@ -20,90 +25,64 @@ pub(crate) unsafe fn lock() {
20pub(crate) unsafe fn unlock() { 25pub(crate) unsafe fn unlock() {
21 pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0x4567_0123)); 26 pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0x4567_0123));
22 pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB)); 27 pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
23
24 if is_dual_bank() { 28 if is_dual_bank() {
25 pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0x4567_0123)); 29 pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0x4567_0123));
26 pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB)); 30 pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
27 } 31 }
28} 32}
29 33
30pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { 34pub(crate) unsafe fn begin_write() {
31 let bank = if !is_dual_bank() || (offset - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { 35 assert_eq!(0, WRITE_SIZE % 4);
36}
37
38pub(crate) unsafe fn end_write() {}
39
40pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
41 // We cannot have the write setup sequence in begin_write as it depends on the address
42 let bank = if start_address < BANK1_REGION.end() {
32 pac::FLASH.bank(0) 43 pac::FLASH.bank(0)
33 } else { 44 } else {
34 pac::FLASH.bank(1) 45 pac::FLASH.bank(1)
35 }; 46 };
36
37 bank.cr().write(|w| { 47 bank.cr().write(|w| {
38 w.set_pg(true); 48 w.set_pg(true);
39 w.set_psize(2); // 32 bits at once 49 w.set_psize(2); // 32 bits at once
40 }); 50 });
41
42 cortex_m::asm::isb(); 51 cortex_m::asm::isb();
43 cortex_m::asm::dsb(); 52 cortex_m::asm::dsb();
44 core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); 53 fence(Ordering::SeqCst);
45 54
46 let ret = { 55 let mut res = None;
47 let mut ret: Result<(), Error> = Ok(()); 56 let mut address = start_address;
48 let mut offset = offset; 57 for val in buf.chunks(4) {
49 'outer: for chunk in buf.chunks(super::WRITE_SIZE) { 58 write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
50 for val in chunk.chunks(4) { 59 address += val.len() as u32;
51 trace!("Writing at {:x}", offset); 60
52 write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); 61 res = Some(blocking_wait_ready(bank));
53 offset += val.len() as u32; 62 bank.sr().modify(|w| {
54 63 if w.eop() {
55 ret = blocking_wait_ready(bank); 64 w.set_eop(true);
56 bank.sr().modify(|w| {
57 if w.eop() {
58 w.set_eop(true);
59 }
60 });
61 if ret.is_err() {
62 break 'outer;
63 }
64 } 65 }
66 });
67 if res.unwrap().is_err() {
68 break;
65 } 69 }
66 ret 70 }
67 };
68 71
69 bank.cr().write(|w| w.set_pg(false)); 72 bank.cr().write(|w| w.set_pg(false));
70 73
71 cortex_m::asm::isb(); 74 cortex_m::asm::isb();
72 cortex_m::asm::dsb(); 75 cortex_m::asm::dsb();
73 core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); 76 fence(Ordering::SeqCst);
74
75 ret
76}
77
78pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
79 let from = from - super::FLASH_BASE as u32;
80 let to = to - super::FLASH_BASE as u32;
81
82 let (start, end) = if to <= super::FLASH_SIZE as u32 {
83 let start_sector = from / super::ERASE_SIZE as u32;
84 let end_sector = to / super::ERASE_SIZE as u32;
85 (start_sector, end_sector)
86 } else {
87 error!("Attempting to write outside of defined sectors {:x} {:x}", from, to);
88 return Err(Error::Unaligned);
89 };
90
91 trace!("Erasing sectors from {} to {}", start, end);
92 for sector in start..end {
93 let bank = if sector >= 8 { 1 } else { 0 };
94 let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8);
95 if ret.is_err() {
96 return ret;
97 }
98 }
99 77
100 Ok(()) 78 res.unwrap()
101} 79}
102 80
103unsafe fn erase_sector(bank: pac::flash::Bank, sector: u8) -> Result<(), Error> { 81pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
82 let bank = pac::FLASH.bank(sector.bank as usize);
104 bank.cr().modify(|w| { 83 bank.cr().modify(|w| {
105 w.set_ser(true); 84 w.set_ser(true);
106 w.set_snb(sector) 85 w.set_snb(sector.index_in_bank)
107 }); 86 });
108 87
109 bank.cr().modify(|w| { 88 bank.cr().modify(|w| {
@@ -160,7 +139,7 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) {
160 }); 139 });
161} 140}
162 141
163pub(crate) unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { 142unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {
164 loop { 143 loop {
165 let sr = bank.sr().read(); 144 let sr = bank.sr().read();
166 145
diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs
index 5048a3314..7d9cc6ea3 100644
--- a/embassy-stm32/src/flash/l.rs
+++ b/embassy-stm32/src/flash/l.rs
@@ -1,9 +1,15 @@
1use core::convert::TryInto;
2use core::ptr::write_volatile; 1use core::ptr::write_volatile;
3 2
3use atomic_polyfill::{fence, Ordering};
4
5use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
4use crate::flash::Error; 6use crate::flash::Error;
5use crate::pac; 7use crate::pac;
6 8
9pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
10 &FLASH_REGIONS
11}
12
7pub(crate) unsafe fn lock() { 13pub(crate) unsafe fn lock() {
8 #[cfg(any(flash_wl, flash_wb, flash_l4))] 14 #[cfg(any(flash_wl, flash_wb, flash_l4))]
9 pac::FLASH.cr().modify(|w| w.set_lock(true)); 15 pac::FLASH.cr().modify(|w| w.set_lock(true));
@@ -33,82 +39,75 @@ pub(crate) unsafe fn unlock() {
33 } 39 }
34} 40}
35 41
36pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { 42pub(crate) unsafe fn begin_write() {
43 assert_eq!(0, WRITE_SIZE % 4);
44
37 #[cfg(any(flash_wl, flash_wb, flash_l4))] 45 #[cfg(any(flash_wl, flash_wb, flash_l4))]
38 pac::FLASH.cr().write(|w| w.set_pg(true)); 46 pac::FLASH.cr().write(|w| w.set_pg(true));
47}
39 48
40 let ret = { 49pub(crate) unsafe fn end_write() {
41 let mut ret: Result<(), Error> = Ok(());
42 let mut offset = offset;
43 for chunk in buf.chunks(super::WRITE_SIZE) {
44 for val in chunk.chunks(4) {
45 write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap()));
46 offset += val.len() as u32;
47 }
48
49 ret = blocking_wait_ready();
50 if ret.is_err() {
51 break;
52 }
53 }
54 ret
55 };
56
57 #[cfg(any(flash_wl, flash_wb, flash_l4))] 50 #[cfg(any(flash_wl, flash_wb, flash_l4))]
58 pac::FLASH.cr().write(|w| w.set_pg(false)); 51 pac::FLASH.cr().write(|w| w.set_pg(false));
59
60 ret
61} 52}
62 53
63pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { 54pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
64 for page in (from..to).step_by(super::ERASE_SIZE) { 55 let mut address = start_address;
65 #[cfg(any(flash_l0, flash_l1))] 56 for val in buf.chunks(4) {
66 { 57 write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
67 pac::FLASH.pecr().modify(|w| { 58 address += val.len() as u32;
68 w.set_erase(true);
69 w.set_prog(true);
70 });
71 59
72 write_volatile(page as *mut u32, 0xFFFFFFFF); 60 // prevents parallelism errors
73 } 61 fence(Ordering::SeqCst);
74 62 }
75 #[cfg(any(flash_wl, flash_wb, flash_l4))]
76 {
77 let idx = (page - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32;
78
79 #[cfg(flash_l4)]
80 let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) };
81
82 pac::FLASH.cr().modify(|w| {
83 w.set_per(true);
84 w.set_pnb(idx as u8);
85 #[cfg(any(flash_wl, flash_wb))]
86 w.set_strt(true);
87 #[cfg(any(flash_l4))]
88 w.set_start(true);
89 #[cfg(any(flash_l4))]
90 w.set_bker(bank);
91 });
92 }
93
94 let ret: Result<(), Error> = blocking_wait_ready();
95 63
96 #[cfg(any(flash_wl, flash_wb, flash_l4))] 64 blocking_wait_ready()
97 pac::FLASH.cr().modify(|w| w.set_per(false)); 65}
98 66
99 #[cfg(any(flash_l0, flash_l1))] 67pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
68 #[cfg(any(flash_l0, flash_l1))]
69 {
100 pac::FLASH.pecr().modify(|w| { 70 pac::FLASH.pecr().modify(|w| {
101 w.set_erase(false); 71 w.set_erase(true);
102 w.set_prog(false); 72 w.set_prog(true);
103 }); 73 });
104 74
105 clear_all_err(); 75 write_volatile(sector.start as *mut u32, 0xFFFFFFFF);
106 if ret.is_err() { 76 }
107 return ret; 77
108 } 78 #[cfg(any(flash_wl, flash_wb, flash_l4))]
79 {
80 let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32;
81
82 #[cfg(flash_l4)]
83 let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) };
84
85 pac::FLASH.cr().modify(|w| {
86 w.set_per(true);
87 w.set_pnb(idx as u8);
88 #[cfg(any(flash_wl, flash_wb))]
89 w.set_strt(true);
90 #[cfg(any(flash_l4))]
91 w.set_start(true);
92 #[cfg(any(flash_l4))]
93 w.set_bker(bank);
94 });
109 } 95 }
110 96
111 Ok(()) 97 let ret: Result<(), Error> = blocking_wait_ready();
98
99 #[cfg(any(flash_wl, flash_wb, flash_l4))]
100 pac::FLASH.cr().modify(|w| w.set_per(false));
101
102 #[cfg(any(flash_l0, flash_l1))]
103 pac::FLASH.pecr().modify(|w| {
104 w.set_erase(false);
105 w.set_prog(false);
106 });
107
108 clear_all_err();
109
110 ret
112} 111}
113 112
114pub(crate) unsafe fn clear_all_err() { 113pub(crate) unsafe fn clear_all_err() {
@@ -149,7 +148,7 @@ pub(crate) unsafe fn clear_all_err() {
149 }); 148 });
150} 149}
151 150
152pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { 151unsafe fn blocking_wait_ready() -> Result<(), Error> {
153 loop { 152 loop {
154 let sr = pac::FLASH.sr().read(); 153 let sr = pac::FLASH.sr().read();
155 154
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
index b7166a437..231ff1f9e 100644
--- a/embassy-stm32/src/flash/mod.rs
+++ b/embassy-stm32/src/flash/mod.rs
@@ -1,89 +1,67 @@
1use embassy_hal_common::{into_ref, PeripheralRef}; 1use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
2use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
3 2
4pub use crate::pac::{ERASE_SIZE, ERASE_VALUE, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; 3#[cfg(flash)]
5use crate::peripherals::FLASH; 4mod common;
6use crate::Peripheral;
7const FLASH_END: usize = FLASH_BASE + FLASH_SIZE;
8 5
9#[cfg_attr(any(flash_wl, flash_wb, flash_l0, flash_l1, flash_l4), path = "l.rs")] 6#[cfg(flash)]
10#[cfg_attr(flash_f3, path = "f3.rs")] 7pub use common::*;
11#[cfg_attr(flash_f4, path = "f4.rs")]
12#[cfg_attr(flash_f7, path = "f7.rs")]
13#[cfg_attr(flash_h7, path = "h7.rs")]
14mod family;
15 8
16pub struct Flash<'d> { 9pub use crate::_generated::flash_regions::*;
17 _inner: PeripheralRef<'d, FLASH>, 10pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE};
18}
19 11
20impl<'d> Flash<'d> { 12#[derive(Debug)]
21 pub fn new(p: impl Peripheral<P = FLASH> + 'd) -> Self { 13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22 into_ref!(p); 14pub struct FlashRegion {
23 Self { _inner: p } 15 pub bank: FlashBank,
24 } 16 pub base: u32,
25 17 pub size: u32,
26 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { 18 pub erase_size: u32,
27 let offset = FLASH_BASE as u32 + offset; 19 pub write_size: u32,
28 if offset as usize >= FLASH_END || offset as usize + bytes.len() > FLASH_END { 20 pub erase_value: u8,
29 return Err(Error::Size); 21}
30 }
31
32 let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) };
33 bytes.copy_from_slice(flash_data);
34 Ok(())
35 }
36
37 pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> {
38 let offset = FLASH_BASE as u32 + offset;
39 if offset as usize + buf.len() > FLASH_END {
40 return Err(Error::Size);
41 }
42 if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 {
43 return Err(Error::Unaligned);
44 }
45 trace!("Writing {} bytes at 0x{:x}", buf.len(), offset);
46
47 self.clear_all_err();
48
49 unsafe {
50 family::unlock();
51 let res = family::blocking_write(offset, buf);
52 family::lock();
53 res
54 }
55 }
56 22
57 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { 23#[derive(Debug, PartialEq)]
58 let from = FLASH_BASE as u32 + from; 24#[cfg_attr(feature = "defmt", derive(defmt::Format))]
59 let to = FLASH_BASE as u32 + to; 25pub struct FlashSector {
60 if to < from || to as usize > FLASH_END { 26 pub bank: FlashBank,
61 return Err(Error::Size); 27 pub index_in_bank: u8,
62 } 28 pub start: u32,
63 if (from as usize % ERASE_SIZE) != 0 || (to as usize % ERASE_SIZE) != 0 { 29 pub size: u32,
64 return Err(Error::Unaligned); 30}
65 }
66 31
67 self.clear_all_err(); 32#[derive(Clone, Copy, Debug, PartialEq)]
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34pub enum FlashBank {
35 Bank1 = 0,
36 Bank2 = 1,
37 Otp,
38}
68 39
69 unsafe { 40impl FlashRegion {
70 family::unlock(); 41 pub const fn end(&self) -> u32 {
71 let res = family::blocking_erase(from, to); 42 self.base + self.size
72 family::lock();
73 res
74 }
75 } 43 }
76 44
77 fn clear_all_err(&mut self) { 45 pub const fn sectors(&self) -> u8 {
78 unsafe { family::clear_all_err() }; 46 (self.size / self.erase_size) as u8
79 } 47 }
80} 48}
81 49
82impl Drop for Flash<'_> { 50#[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")]
83 fn drop(&mut self) { 51#[cfg_attr(flash_f3, path = "f3.rs")]
84 unsafe { family::lock() }; 52#[cfg_attr(flash_f4, path = "f4.rs")]
85 } 53#[cfg_attr(flash_f7, path = "f7.rs")]
86} 54#[cfg_attr(flash_h7, path = "h7.rs")]
55#[cfg_attr(
56 not(any(
57 flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f3, flash_f4, flash_f7, flash_h7
58 )),
59 path = "other.rs"
60)]
61mod family;
62
63#[allow(unused_imports)]
64pub use family::*;
87 65
88#[derive(Debug, Copy, Clone, PartialEq, Eq)] 66#[derive(Debug, Copy, Clone, PartialEq, Eq)]
89#[cfg_attr(feature = "defmt", derive(defmt::Format))] 67#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -97,10 +75,6 @@ pub enum Error {
97 Parallelism, 75 Parallelism,
98} 76}
99 77
100impl<'d> ErrorType for Flash<'d> {
101 type Error = Error;
102}
103
104impl NorFlashError for Error { 78impl NorFlashError for Error {
105 fn kind(&self) -> NorFlashErrorKind { 79 fn kind(&self) -> NorFlashErrorKind {
106 match self { 80 match self {
@@ -110,28 +84,3 @@ impl NorFlashError for Error {
110 } 84 }
111 } 85 }
112} 86}
113
114impl<'d> ReadNorFlash for Flash<'d> {
115 const READ_SIZE: usize = WRITE_SIZE;
116
117 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
118 self.blocking_read(offset, bytes)
119 }
120
121 fn capacity(&self) -> usize {
122 FLASH_SIZE
123 }
124}
125
126impl<'d> NorFlash for Flash<'d> {
127 const WRITE_SIZE: usize = WRITE_SIZE;
128 const ERASE_SIZE: usize = ERASE_SIZE;
129
130 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
131 self.blocking_erase(from, to)
132 }
133
134 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
135 self.blocking_write(offset, bytes)
136 }
137}
diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs
new file mode 100644
index 000000000..c151cb828
--- /dev/null
+++ b/embassy-stm32/src/flash/other.rs
@@ -0,0 +1,29 @@
1#![allow(unused)]
2
3use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
4
5pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
6 &FLASH_REGIONS
7}
8
9pub(crate) unsafe fn lock() {
10 unimplemented!();
11}
12pub(crate) unsafe fn unlock() {
13 unimplemented!();
14}
15pub(crate) unsafe fn begin_write() {
16 unimplemented!();
17}
18pub(crate) unsafe fn end_write() {
19 unimplemented!();
20}
21pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
22 unimplemented!();
23}
24pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> {
25 unimplemented!();
26}
27pub(crate) unsafe fn clear_all_err() {
28 unimplemented!();
29}
diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs
index 4fca1ca2b..939e2750e 100644
--- a/embassy-stm32/src/i2c/timeout.rs
+++ b/embassy-stm32/src/i2c/timeout.rs
@@ -28,64 +28,64 @@ impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> {
28 } 28 }
29 29
30 /// Blocking read with a custom timeout 30 /// Blocking read with a custom timeout
31 pub fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> { 31 pub fn blocking_read_timeout(&mut self, addr: u8, read: &mut [u8], timeout: Duration) -> Result<(), Error> {
32 self.i2c.blocking_read_timeout(addr, buffer, timeout_fn(timeout)) 32 self.i2c.blocking_read_timeout(addr, read, timeout_fn(timeout))
33 } 33 }
34 34
35 /// Blocking read with default timeout, provided in [`TimeoutI2c::new()`] 35 /// Blocking read with default timeout, provided in [`TimeoutI2c::new()`]
36 pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { 36 pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> {
37 self.blocking_read_timeout(addr, buffer, self.timeout) 37 self.blocking_read_timeout(addr, read, self.timeout)
38 } 38 }
39 39
40 /// Blocking write with a custom timeout 40 /// Blocking write with a custom timeout
41 pub fn blocking_write_timeout(&mut self, addr: u8, bytes: &[u8], timeout: Duration) -> Result<(), Error> { 41 pub fn blocking_write_timeout(&mut self, addr: u8, write: &[u8], timeout: Duration) -> Result<(), Error> {
42 self.i2c.blocking_write_timeout(addr, bytes, timeout_fn(timeout)) 42 self.i2c.blocking_write_timeout(addr, write, timeout_fn(timeout))
43 } 43 }
44 44
45 /// Blocking write with default timeout, provided in [`TimeoutI2c::new()`] 45 /// Blocking write with default timeout, provided in [`TimeoutI2c::new()`]
46 pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { 46 pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> {
47 self.blocking_write_timeout(addr, bytes, self.timeout) 47 self.blocking_write_timeout(addr, write, self.timeout)
48 } 48 }
49 49
50 /// Blocking write-read with a custom timeout 50 /// Blocking write-read with a custom timeout
51 pub fn blocking_write_read_timeout( 51 pub fn blocking_write_read_timeout(
52 &mut self, 52 &mut self,
53 addr: u8, 53 addr: u8,
54 bytes: &[u8], 54 write: &[u8],
55 buffer: &mut [u8], 55 read: &mut [u8],
56 timeout: Duration, 56 timeout: Duration,
57 ) -> Result<(), Error> { 57 ) -> Result<(), Error> {
58 self.i2c 58 self.i2c
59 .blocking_write_read_timeout(addr, bytes, buffer, timeout_fn(timeout)) 59 .blocking_write_read_timeout(addr, write, read, timeout_fn(timeout))
60 } 60 }
61 61
62 /// Blocking write-read with default timeout, provided in [`TimeoutI2c::new()`] 62 /// Blocking write-read with default timeout, provided in [`TimeoutI2c::new()`]
63 pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { 63 pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
64 self.blocking_write_read_timeout(addr, bytes, buffer, self.timeout) 64 self.blocking_write_read_timeout(addr, write, read, self.timeout)
65 } 65 }
66} 66}
67 67
68impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> { 68impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> {
69 type Error = Error; 69 type Error = Error;
70 70
71 fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 71 fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> {
72 self.blocking_read(addr, buffer) 72 self.blocking_read(addr, read)
73 } 73 }
74} 74}
75 75
76impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> { 76impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> {
77 type Error = Error; 77 type Error = Error;
78 78
79 fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { 79 fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> {
80 self.blocking_write(addr, bytes) 80 self.blocking_write(addr, write)
81 } 81 }
82} 82}
83 83
84impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> { 84impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> {
85 type Error = Error; 85 type Error = Error;
86 86
87 fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { 87 fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
88 self.blocking_write_read(addr, bytes, buffer) 88 self.blocking_write_read(addr, write, read)
89 } 89 }
90} 90}
91 91
@@ -98,45 +98,24 @@ mod eh1 {
98 } 98 }
99 99
100 impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> { 100 impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> {
101 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 101 fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
102 self.blocking_read(address, buffer) 102 self.blocking_read(address, read)
103 } 103 }
104 104
105 fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { 105 fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
106 self.blocking_write(address, buffer) 106 self.blocking_write(address, write)
107 } 107 }
108 108
109 fn write_iter<B>(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error> 109 fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
110 where 110 self.blocking_write_read(address, write, read)
111 B: IntoIterator<Item = u8>,
112 {
113 todo!();
114 } 111 }
115 112
116 fn write_iter_read<B>(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> 113 fn transaction(
117 where
118 B: IntoIterator<Item = u8>,
119 {
120 todo!();
121 }
122
123 fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
124 self.blocking_write_read(address, wr_buffer, rd_buffer)
125 }
126
127 fn transaction<'a>(
128 &mut self, 114 &mut self,
129 _address: u8, 115 _address: u8,
130 _operations: &mut [embedded_hal_1::i2c::Operation<'a>], 116 _operations: &mut [embedded_hal_1::i2c::Operation<'_>],
131 ) -> Result<(), Self::Error> { 117 ) -> Result<(), Self::Error> {
132 todo!(); 118 todo!();
133 } 119 }
134
135 fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
136 where
137 O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
138 {
139 todo!();
140 }
141 } 120 }
142} 121}
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs
index f140e2b0d..4b47f0eb1 100644
--- a/embassy-stm32/src/i2c/v1.rs
+++ b/embassy-stm32/src/i2c/v1.rs
@@ -307,18 +307,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
307 } 307 }
308 } 308 }
309 309
310 pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { 310 pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> {
311 self.blocking_read_timeout(addr, buffer, || Ok(())) 311 self.blocking_read_timeout(addr, read, || Ok(()))
312 } 312 }
313 313
314 pub fn blocking_write_timeout( 314 pub fn blocking_write_timeout(
315 &mut self, 315 &mut self,
316 addr: u8, 316 addr: u8,
317 bytes: &[u8], 317 write: &[u8],
318 check_timeout: impl Fn() -> Result<(), Error>, 318 check_timeout: impl Fn() -> Result<(), Error>,
319 ) -> Result<(), Error> { 319 ) -> Result<(), Error> {
320 unsafe { 320 unsafe {
321 self.write_bytes(addr, bytes, &check_timeout)?; 321 self.write_bytes(addr, write, &check_timeout)?;
322 // Send a STOP condition 322 // Send a STOP condition
323 T::regs().cr1().modify(|reg| reg.set_stop(true)); 323 T::regs().cr1().modify(|reg| reg.set_stop(true));
324 // Wait for STOP condition to transmit. 324 // Wait for STOP condition to transmit.
@@ -331,49 +331,49 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
331 Ok(()) 331 Ok(())
332 } 332 }
333 333
334 pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { 334 pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> {
335 self.blocking_write_timeout(addr, bytes, || Ok(())) 335 self.blocking_write_timeout(addr, write, || Ok(()))
336 } 336 }
337 337
338 pub fn blocking_write_read_timeout( 338 pub fn blocking_write_read_timeout(
339 &mut self, 339 &mut self,
340 addr: u8, 340 addr: u8,
341 bytes: &[u8], 341 write: &[u8],
342 buffer: &mut [u8], 342 read: &mut [u8],
343 check_timeout: impl Fn() -> Result<(), Error>, 343 check_timeout: impl Fn() -> Result<(), Error>,
344 ) -> Result<(), Error> { 344 ) -> Result<(), Error> {
345 unsafe { self.write_bytes(addr, bytes, &check_timeout)? }; 345 unsafe { self.write_bytes(addr, write, &check_timeout)? };
346 self.blocking_read_timeout(addr, buffer, &check_timeout)?; 346 self.blocking_read_timeout(addr, read, &check_timeout)?;
347 347
348 Ok(()) 348 Ok(())
349 } 349 }
350 350
351 pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { 351 pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
352 self.blocking_write_read_timeout(addr, bytes, buffer, || Ok(())) 352 self.blocking_write_read_timeout(addr, write, read, || Ok(()))
353 } 353 }
354} 354}
355 355
356impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { 356impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
357 type Error = Error; 357 type Error = Error;
358 358
359 fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 359 fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> {
360 self.blocking_read(addr, buffer) 360 self.blocking_read(addr, read)
361 } 361 }
362} 362}
363 363
364impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { 364impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
365 type Error = Error; 365 type Error = Error;
366 366
367 fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { 367 fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> {
368 self.blocking_write(addr, bytes) 368 self.blocking_write(addr, write)
369 } 369 }
370} 370}
371 371
372impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { 372impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
373 type Error = Error; 373 type Error = Error;
374 374
375 fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { 375 fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
376 self.blocking_write_read(addr, bytes, buffer) 376 self.blocking_write_read(addr, write, read)
377 } 377 }
378} 378}
379 379
@@ -402,46 +402,25 @@ mod eh1 {
402 } 402 }
403 403
404 impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> { 404 impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> {
405 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 405 fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
406 self.blocking_read(address, buffer) 406 self.blocking_read(address, read)
407 }
408
409 fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> {
410 self.blocking_write(address, buffer)
411 }
412
413 fn write_iter<B>(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error>
414 where
415 B: IntoIterator<Item = u8>,
416 {
417 todo!();
418 } 407 }
419 408
420 fn write_iter_read<B>(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> 409 fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
421 where 410 self.blocking_write(address, write)
422 B: IntoIterator<Item = u8>,
423 {
424 todo!();
425 } 411 }
426 412
427 fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { 413 fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
428 self.blocking_write_read(address, wr_buffer, rd_buffer) 414 self.blocking_write_read(address, write, read)
429 } 415 }
430 416
431 fn transaction<'a>( 417 fn transaction(
432 &mut self, 418 &mut self,
433 _address: u8, 419 _address: u8,
434 _operations: &mut [embedded_hal_1::i2c::Operation<'a>], 420 _operations: &mut [embedded_hal_1::i2c::Operation<'_>],
435 ) -> Result<(), Self::Error> { 421 ) -> Result<(), Self::Error> {
436 todo!(); 422 todo!();
437 } 423 }
438
439 fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
440 where
441 O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
442 {
443 todo!();
444 }
445 } 424 }
446} 425}
447 426
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 06ff07b21..7218f7706 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -262,7 +262,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
262 if T::regs().isr().read().txis() { 262 if T::regs().isr().read().txis() {
263 T::regs().txdr().write(|w| w.set_txdata(0)); 263 T::regs().txdr().write(|w| w.set_txdata(0));
264 } 264 }
265 if T::regs().isr().read().txe() { 265 if !T::regs().isr().read().txe() {
266 T::regs().isr().modify(|w| w.set_txe(true)) 266 T::regs().isr().modify(|w| w.set_txe(true))
267 } 267 }
268 } 268 }
@@ -345,12 +345,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
345 fn read_internal( 345 fn read_internal(
346 &mut self, 346 &mut self,
347 address: u8, 347 address: u8,
348 buffer: &mut [u8], 348 read: &mut [u8],
349 restart: bool, 349 restart: bool,
350 check_timeout: impl Fn() -> Result<(), Error>, 350 check_timeout: impl Fn() -> Result<(), Error>,
351 ) -> Result<(), Error> { 351 ) -> Result<(), Error> {
352 let completed_chunks = buffer.len() / 255; 352 let completed_chunks = read.len() / 255;
353 let total_chunks = if completed_chunks * 255 == buffer.len() { 353 let total_chunks = if completed_chunks * 255 == read.len() {
354 completed_chunks 354 completed_chunks
355 } else { 355 } else {
356 completed_chunks + 1 356 completed_chunks + 1
@@ -360,7 +360,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
360 unsafe { 360 unsafe {
361 Self::master_read( 361 Self::master_read(
362 address, 362 address,
363 buffer.len().min(255), 363 read.len().min(255),
364 Stop::Automatic, 364 Stop::Automatic,
365 last_chunk_idx != 0, 365 last_chunk_idx != 0,
366 restart, 366 restart,
@@ -368,7 +368,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
368 )?; 368 )?;
369 } 369 }
370 370
371 for (number, chunk) in buffer.chunks_mut(255).enumerate() { 371 for (number, chunk) in read.chunks_mut(255).enumerate() {
372 if number != 0 { 372 if number != 0 {
373 // NOTE(unsafe) We have &mut self 373 // NOTE(unsafe) We have &mut self
374 unsafe { 374 unsafe {
@@ -391,12 +391,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
391 fn write_internal( 391 fn write_internal(
392 &mut self, 392 &mut self,
393 address: u8, 393 address: u8,
394 bytes: &[u8], 394 write: &[u8],
395 send_stop: bool, 395 send_stop: bool,
396 check_timeout: impl Fn() -> Result<(), Error>, 396 check_timeout: impl Fn() -> Result<(), Error>,
397 ) -> Result<(), Error> { 397 ) -> Result<(), Error> {
398 let completed_chunks = bytes.len() / 255; 398 let completed_chunks = write.len() / 255;
399 let total_chunks = if completed_chunks * 255 == bytes.len() { 399 let total_chunks = if completed_chunks * 255 == write.len() {
400 completed_chunks 400 completed_chunks
401 } else { 401 } else {
402 completed_chunks + 1 402 completed_chunks + 1
@@ -410,14 +410,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
410 unsafe { 410 unsafe {
411 Self::master_write( 411 Self::master_write(
412 address, 412 address,
413 bytes.len().min(255), 413 write.len().min(255),
414 Stop::Software, 414 Stop::Software,
415 last_chunk_idx != 0, 415 last_chunk_idx != 0,
416 &check_timeout, 416 &check_timeout,
417 )?; 417 )?;
418 } 418 }
419 419
420 for (number, chunk) in bytes.chunks(255).enumerate() { 420 for (number, chunk) in write.chunks(255).enumerate() {
421 if number != 0 { 421 if number != 0 {
422 // NOTE(unsafe) We have &mut self 422 // NOTE(unsafe) We have &mut self
423 unsafe { 423 unsafe {
@@ -448,7 +448,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
448 async fn write_dma_internal( 448 async fn write_dma_internal(
449 &mut self, 449 &mut self,
450 address: u8, 450 address: u8,
451 bytes: &[u8], 451 write: &[u8],
452 first_slice: bool, 452 first_slice: bool,
453 last_slice: bool, 453 last_slice: bool,
454 check_timeout: impl Fn() -> Result<(), Error>, 454 check_timeout: impl Fn() -> Result<(), Error>,
@@ -456,7 +456,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
456 where 456 where
457 TXDMA: crate::i2c::TxDma<T>, 457 TXDMA: crate::i2c::TxDma<T>,
458 { 458 {
459 let total_len = bytes.len(); 459 let total_len = write.len();
460 let completed_chunks = total_len / 255; 460 let completed_chunks = total_len / 255;
461 let total_chunks = if completed_chunks * 255 == total_len { 461 let total_chunks = if completed_chunks * 255 == total_len {
462 completed_chunks 462 completed_chunks
@@ -476,7 +476,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
476 476
477 let ch = &mut self.tx_dma; 477 let ch = &mut self.tx_dma;
478 let request = ch.request(); 478 let request = ch.request();
479 crate::dma::write(ch, request, bytes, dst) 479 crate::dma::write(ch, request, write, dst)
480 }; 480 };
481 481
482 let state = T::state(); 482 let state = T::state();
@@ -641,25 +641,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
641 // ========================= 641 // =========================
642 // Async public API 642 // Async public API
643 643
644 pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> 644 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error>
645 where 645 where
646 TXDMA: crate::i2c::TxDma<T>, 646 TXDMA: crate::i2c::TxDma<T>,
647 { 647 {
648 if bytes.is_empty() { 648 if write.is_empty() {
649 self.write_internal(address, bytes, true, || Ok(())) 649 self.write_internal(address, write, true, || Ok(()))
650 } else { 650 } else {
651 self.write_dma_internal(address, bytes, true, true, || Ok(())).await 651 self.write_dma_internal(address, write, true, true, || Ok(())).await
652 } 652 }
653 } 653 }
654 654
655 pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> 655 pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error>
656 where 656 where
657 TXDMA: crate::i2c::TxDma<T>, 657 TXDMA: crate::i2c::TxDma<T>,
658 { 658 {
659 if bytes.is_empty() { 659 if write.is_empty() {
660 return Err(Error::ZeroLengthTransfer); 660 return Err(Error::ZeroLengthTransfer);
661 } 661 }
662 let mut iter = bytes.iter(); 662 let mut iter = write.iter();
663 663
664 let mut first = true; 664 let mut first = true;
665 let mut current = iter.next(); 665 let mut current = iter.next();
@@ -685,21 +685,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
685 } 685 }
686 } 686 }
687 687
688 pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> 688 pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error>
689 where 689 where
690 TXDMA: super::TxDma<T>, 690 TXDMA: super::TxDma<T>,
691 RXDMA: super::RxDma<T>, 691 RXDMA: super::RxDma<T>,
692 { 692 {
693 if bytes.is_empty() { 693 if write.is_empty() {
694 self.write_internal(address, bytes, false, || Ok(()))?; 694 self.write_internal(address, write, false, || Ok(()))?;
695 } else { 695 } else {
696 self.write_dma_internal(address, bytes, true, true, || Ok(())).await?; 696 self.write_dma_internal(address, write, true, true, || Ok(())).await?;
697 } 697 }
698 698
699 if buffer.is_empty() { 699 if read.is_empty() {
700 self.read_internal(address, buffer, true, || Ok(()))?; 700 self.read_internal(address, read, true, || Ok(()))?;
701 } else { 701 } else {
702 self.read_dma_internal(address, buffer, true, || Ok(())).await?; 702 self.read_dma_internal(address, read, true, || Ok(())).await?;
703 } 703 }
704 704
705 Ok(()) 705 Ok(())
@@ -711,57 +711,57 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
711 pub fn blocking_read_timeout( 711 pub fn blocking_read_timeout(
712 &mut self, 712 &mut self,
713 address: u8, 713 address: u8,
714 buffer: &mut [u8], 714 read: &mut [u8],
715 check_timeout: impl Fn() -> Result<(), Error>, 715 check_timeout: impl Fn() -> Result<(), Error>,
716 ) -> Result<(), Error> { 716 ) -> Result<(), Error> {
717 self.read_internal(address, buffer, false, &check_timeout) 717 self.read_internal(address, read, false, &check_timeout)
718 // Automatic Stop 718 // Automatic Stop
719 } 719 }
720 720
721 pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { 721 pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {
722 self.blocking_read_timeout(address, buffer, || Ok(())) 722 self.blocking_read_timeout(address, read, || Ok(()))
723 } 723 }
724 724
725 pub fn blocking_write_timeout( 725 pub fn blocking_write_timeout(
726 &mut self, 726 &mut self,
727 address: u8, 727 address: u8,
728 bytes: &[u8], 728 write: &[u8],
729 check_timeout: impl Fn() -> Result<(), Error>, 729 check_timeout: impl Fn() -> Result<(), Error>,
730 ) -> Result<(), Error> { 730 ) -> Result<(), Error> {
731 self.write_internal(address, bytes, true, &check_timeout) 731 self.write_internal(address, write, true, &check_timeout)
732 } 732 }
733 733
734 pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { 734 pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
735 self.blocking_write_timeout(address, bytes, || Ok(())) 735 self.blocking_write_timeout(address, write, || Ok(()))
736 } 736 }
737 737
738 pub fn blocking_write_read_timeout( 738 pub fn blocking_write_read_timeout(
739 &mut self, 739 &mut self,
740 address: u8, 740 address: u8,
741 bytes: &[u8], 741 write: &[u8],
742 buffer: &mut [u8], 742 read: &mut [u8],
743 check_timeout: impl Fn() -> Result<(), Error>, 743 check_timeout: impl Fn() -> Result<(), Error>,
744 ) -> Result<(), Error> { 744 ) -> Result<(), Error> {
745 self.write_internal(address, bytes, false, &check_timeout)?; 745 self.write_internal(address, write, false, &check_timeout)?;
746 self.read_internal(address, buffer, true, &check_timeout) 746 self.read_internal(address, read, true, &check_timeout)
747 // Automatic Stop 747 // Automatic Stop
748 } 748 }
749 749
750 pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { 750 pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
751 self.blocking_write_read_timeout(address, bytes, buffer, || Ok(())) 751 self.blocking_write_read_timeout(address, write, read, || Ok(()))
752 } 752 }
753 753
754 pub fn blocking_write_vectored_timeout( 754 pub fn blocking_write_vectored_timeout(
755 &mut self, 755 &mut self,
756 address: u8, 756 address: u8,
757 bytes: &[&[u8]], 757 write: &[&[u8]],
758 check_timeout: impl Fn() -> Result<(), Error>, 758 check_timeout: impl Fn() -> Result<(), Error>,
759 ) -> Result<(), Error> { 759 ) -> Result<(), Error> {
760 if bytes.is_empty() { 760 if write.is_empty() {
761 return Err(Error::ZeroLengthTransfer); 761 return Err(Error::ZeroLengthTransfer);
762 } 762 }
763 let first_length = bytes[0].len(); 763 let first_length = write[0].len();
764 let last_slice_index = bytes.len() - 1; 764 let last_slice_index = write.len() - 1;
765 765
766 // NOTE(unsafe) We have &mut self 766 // NOTE(unsafe) We have &mut self
767 unsafe { 767 unsafe {
@@ -774,7 +774,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
774 )?; 774 )?;
775 } 775 }
776 776
777 for (idx, slice) in bytes.iter().enumerate() { 777 for (idx, slice) in write.iter().enumerate() {
778 let slice_len = slice.len(); 778 let slice_len = slice.len();
779 let completed_chunks = slice_len / 255; 779 let completed_chunks = slice_len / 255;
780 let total_chunks = if completed_chunks * 255 == slice_len { 780 let total_chunks = if completed_chunks * 255 == slice_len {
@@ -828,8 +828,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
828 Ok(()) 828 Ok(())
829 } 829 }
830 830
831 pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { 831 pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> {
832 self.blocking_write_vectored_timeout(address, bytes, || Ok(())) 832 self.blocking_write_vectored_timeout(address, write, || Ok(()))
833 } 833 }
834} 834}
835 835
@@ -847,16 +847,16 @@ mod eh02 {
847 impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { 847 impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> {
848 type Error = Error; 848 type Error = Error;
849 849
850 fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { 850 fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
851 self.blocking_write(address, bytes) 851 self.blocking_write(address, write)
852 } 852 }
853 } 853 }
854 854
855 impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { 855 impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> {
856 type Error = Error; 856 type Error = Error;
857 857
858 fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { 858 fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
859 self.blocking_write_read(address, bytes, buffer) 859 self.blocking_write_read(address, write, read)
860 } 860 }
861 } 861 }
862} 862}
@@ -1010,46 +1010,25 @@ mod eh1 {
1010 } 1010 }
1011 1011
1012 impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { 1012 impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> {
1013 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 1013 fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
1014 self.blocking_read(address, buffer) 1014 self.blocking_read(address, read)
1015 }
1016
1017 fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> {
1018 self.blocking_write(address, buffer)
1019 }
1020
1021 fn write_iter<B>(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error>
1022 where
1023 B: IntoIterator<Item = u8>,
1024 {
1025 todo!();
1026 } 1015 }
1027 1016
1028 fn write_iter_read<B>(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> 1017 fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
1029 where 1018 self.blocking_write(address, write)
1030 B: IntoIterator<Item = u8>,
1031 {
1032 todo!();
1033 } 1019 }
1034 1020
1035 fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { 1021 fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
1036 self.blocking_write_read(address, wr_buffer, rd_buffer) 1022 self.blocking_write_read(address, write, read)
1037 } 1023 }
1038 1024
1039 fn transaction<'a>( 1025 fn transaction(
1040 &mut self, 1026 &mut self,
1041 _address: u8, 1027 _address: u8,
1042 _operations: &mut [embedded_hal_1::i2c::Operation<'a>], 1028 _operations: &mut [embedded_hal_1::i2c::Operation<'_>],
1043 ) -> Result<(), Self::Error> { 1029 ) -> Result<(), Self::Error> {
1044 todo!(); 1030 todo!();
1045 } 1031 }
1046
1047 fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
1048 where
1049 O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
1050 {
1051 todo!();
1052 }
1053 } 1032 }
1054} 1033}
1055 1034
@@ -1059,27 +1038,22 @@ mod eha {
1059 use super::*; 1038 use super::*;
1060 1039
1061 impl<'d, T: Instance, TXDMA: TxDma<T>, RXDMA: RxDma<T>> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> { 1040 impl<'d, T: Instance, TXDMA: TxDma<T>, RXDMA: RxDma<T>> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> {
1062 async fn read<'a>(&'a mut self, address: u8, read: &'a mut [u8]) -> Result<(), Self::Error> { 1041 async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
1063 self.read(address, read).await 1042 self.read(address, read).await
1064 } 1043 }
1065 1044
1066 async fn write<'a>(&'a mut self, address: u8, write: &'a [u8]) -> Result<(), Self::Error> { 1045 async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
1067 self.write(address, write).await 1046 self.write(address, write).await
1068 } 1047 }
1069 1048
1070 async fn write_read<'a>( 1049 async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
1071 &'a mut self,
1072 address: u8,
1073 write: &'a [u8],
1074 read: &'a mut [u8],
1075 ) -> Result<(), Self::Error> {
1076 self.write_read(address, write, read).await 1050 self.write_read(address, write, read).await
1077 } 1051 }
1078 1052
1079 async fn transaction<'a, 'b>( 1053 async fn transaction(
1080 &'a mut self, 1054 &mut self,
1081 address: u8, 1055 address: u8,
1082 operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], 1056 operations: &mut [embedded_hal_1::i2c::Operation<'_>],
1083 ) -> Result<(), Self::Error> { 1057 ) -> Result<(), Self::Error> {
1084 let _ = address; 1058 let _ = address;
1085 let _ = operations; 1059 let _ = operations;
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 8dc4df2dc..d4d7155bd 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -43,9 +43,6 @@ pub mod i2c;
43 43
44#[cfg(crc)] 44#[cfg(crc)]
45pub mod crc; 45pub mod crc;
46#[cfg(any(
47 flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f4, flash_f7, flash_h7
48))]
49pub mod flash; 46pub mod flash;
50pub mod pwm; 47pub mod pwm;
51#[cfg(quadspi)] 48#[cfg(quadspi)]
@@ -56,6 +53,8 @@ pub mod rng;
56pub mod sdmmc; 53pub mod sdmmc;
57#[cfg(spi)] 54#[cfg(spi)]
58pub mod spi; 55pub mod spi;
56#[cfg(stm32wl)]
57pub mod subghz;
59#[cfg(usart)] 58#[cfg(usart)]
60pub mod usart; 59pub mod usart;
61#[cfg(all(usb, feature = "time"))] 60#[cfg(all(usb, feature = "time"))]
@@ -65,9 +64,6 @@ pub mod usb_otg;
65#[cfg(iwdg)] 64#[cfg(iwdg)]
66pub mod wdg; 65pub mod wdg;
67 66
68#[cfg(feature = "subghz")]
69pub mod subghz;
70
71// This must go last, so that it sees all the impl_foo! macros defined earlier. 67// This must go last, so that it sees all the impl_foo! macros defined earlier.
72pub(crate) mod _generated { 68pub(crate) mod _generated {
73 #![allow(dead_code)] 69 #![allow(dead_code)]
diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs
new file mode 100644
index 000000000..13edfbaa3
--- /dev/null
+++ b/embassy-stm32/src/pwm/complementary_pwm.rs
@@ -0,0 +1,124 @@
1use core::marker::PhantomData;
2
3use embassy_hal_common::{into_ref, PeripheralRef};
4pub use stm32_metapac::timer::vals::Ckd;
5
6use super::simple_pwm::*;
7use super::*;
8#[allow(unused_imports)]
9use crate::gpio::sealed::{AFType, Pin};
10use crate::gpio::AnyPin;
11use crate::time::Hertz;
12use crate::Peripheral;
13
14pub struct ComplementaryPwmPin<'d, Perip, Channel> {
15 _pin: PeripheralRef<'d, AnyPin>,
16 phantom: PhantomData<(Perip, Channel)>,
17}
18
19macro_rules! complementary_channel_impl {
20 ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => {
21 impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> {
22 pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<Perip>> + 'd) -> Self {
23 into_ref!(pin);
24 critical_section::with(|_| unsafe {
25 pin.set_low();
26 pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
27 #[cfg(gpio_v2)]
28 pin.set_speed(crate::gpio::Speed::VeryHigh);
29 });
30 ComplementaryPwmPin {
31 _pin: pin.map_into(),
32 phantom: PhantomData,
33 }
34 }
35 }
36 };
37}
38
39complementary_channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin);
40complementary_channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin);
41complementary_channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin);
42complementary_channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin);
43
44pub struct ComplementaryPwm<'d, T> {
45 inner: PeripheralRef<'d, T>,
46}
47
48impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
49 pub fn new(
50 tim: impl Peripheral<P = T> + 'd,
51 _ch1: Option<PwmPin<'d, T, Ch1>>,
52 _ch1n: Option<ComplementaryPwmPin<'d, T, Ch1>>,
53 _ch2: Option<PwmPin<'d, T, Ch2>>,
54 _ch2n: Option<ComplementaryPwmPin<'d, T, Ch2>>,
55 _ch3: Option<PwmPin<'d, T, Ch3>>,
56 _ch3n: Option<ComplementaryPwmPin<'d, T, Ch3>>,
57 _ch4: Option<PwmPin<'d, T, Ch4>>,
58 _ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>,
59 freq: Hertz,
60 ) -> Self {
61 Self::new_inner(tim, freq)
62 }
63
64 fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz) -> Self {
65 into_ref!(tim);
66
67 T::enable();
68 <T as crate::rcc::sealed::RccPeripheral>::reset();
69
70 let mut this = Self { inner: tim };
71
72 this.inner.set_frequency(freq);
73 this.inner.start();
74
75 unsafe {
76 this.inner.enable_outputs(true);
77
78 this.inner
79 .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1);
80 this.inner
81 .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1);
82 this.inner
83 .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1);
84 this.inner
85 .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1);
86 }
87 this
88 }
89
90 pub fn enable(&mut self, channel: Channel) {
91 unsafe {
92 self.inner.enable_channel(channel, true);
93 self.inner.enable_complementary_channel(channel, true);
94 }
95 }
96
97 pub fn disable(&mut self, channel: Channel) {
98 unsafe {
99 self.inner.enable_complementary_channel(channel, false);
100 self.inner.enable_channel(channel, false);
101 }
102 }
103
104 pub fn set_freq(&mut self, freq: Hertz) {
105 self.inner.set_frequency(freq);
106 }
107
108 pub fn get_max_duty(&self) -> u16 {
109 unsafe { self.inner.get_max_compare_value() }
110 }
111
112 pub fn set_duty(&mut self, channel: Channel, duty: u16) {
113 assert!(duty < self.get_max_duty());
114 unsafe { self.inner.set_compare_value(channel, duty) }
115 }
116
117 pub fn set_dead_time_clock_division(&mut self, value: Ckd) {
118 unsafe { self.inner.set_dead_time_clock_division(value) }
119 }
120
121 pub fn set_dead_time_value(&mut self, value: u8) {
122 unsafe { self.inner.set_dead_time_value(value) }
123 }
124}
diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs
index d3713391c..0bef07089 100644
--- a/embassy-stm32/src/pwm/mod.rs
+++ b/embassy-stm32/src/pwm/mod.rs
@@ -1,5 +1,8 @@
1pub mod complementary_pwm;
1pub mod simple_pwm; 2pub mod simple_pwm;
2 3
4use stm32_metapac::timer::vals::Ckd;
5
3#[cfg(feature = "unstable-pac")] 6#[cfg(feature = "unstable-pac")]
4pub mod low_level { 7pub mod low_level {
5 pub use super::sealed::*; 8 pub use super::sealed::*;
@@ -67,6 +70,14 @@ pub(crate) mod sealed {
67 unsafe fn get_max_compare_value(&self) -> u16; 70 unsafe fn get_max_compare_value(&self) -> u16;
68 } 71 }
69 72
73 pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance {
74 unsafe fn set_dead_time_clock_division(&mut self, value: Ckd);
75
76 unsafe fn set_dead_time_value(&mut self, value: u8);
77
78 unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool);
79 }
80
70 pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance { 81 pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance {
71 unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); 82 unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode);
72 83
@@ -82,6 +93,12 @@ pub trait CaptureCompare16bitInstance:
82 sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static 93 sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static
83{ 94{
84} 95}
96
97pub trait ComplementaryCaptureCompare16bitInstance:
98 sealed::ComplementaryCaptureCompare16bitInstance + crate::timer::AdvancedControlInstance + 'static
99{
100}
101
85pub trait CaptureCompare32bitInstance: 102pub trait CaptureCompare32bitInstance:
86 sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + crate::timer::GeneralPurpose32bitInstance + 'static 103 sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + crate::timer::GeneralPurpose32bitInstance + 'static
87{ 104{
@@ -209,6 +226,29 @@ foreach_interrupt! {
209 impl CaptureCompare16bitInstance for crate::peripherals::$inst { 226 impl CaptureCompare16bitInstance for crate::peripherals::$inst {
210 227
211 } 228 }
229
230 impl crate::pwm::sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {
231 unsafe fn set_dead_time_clock_division(&mut self, value: Ckd) {
232 use crate::timer::sealed::AdvancedControlInstance;
233 Self::regs_advanced().cr1().modify(|w| w.set_ckd(value));
234 }
235
236 unsafe fn set_dead_time_value(&mut self, value: u8) {
237 use crate::timer::sealed::AdvancedControlInstance;
238 Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value));
239 }
240
241 unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) {
242 use crate::timer::sealed::AdvancedControlInstance;
243 Self::regs_advanced()
244 .ccer()
245 .modify(|w| w.set_ccne(channel.raw(), enable));
246 }
247 }
248
249 impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {
250
251 }
212 }; 252 };
213} 253}
214 254
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs
index 200bcce9c..2a17eb9b0 100644
--- a/embassy-stm32/src/rcc/f4.rs
+++ b/embassy-stm32/src/rcc/f4.rs
@@ -1,8 +1,16 @@
1use core::marker::PhantomData;
2
3use embassy_hal_common::into_ref;
4use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre};
5
1use super::sealed::RccPeripheral; 6use super::sealed::RccPeripheral;
7use crate::gpio::sealed::AFType;
8use crate::gpio::Speed;
2use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; 9use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
3use crate::pac::{FLASH, PWR, RCC}; 10use crate::pac::{FLASH, PWR, RCC};
4use crate::rcc::{set_freqs, Clocks}; 11use crate::rcc::{set_freqs, Clocks};
5use crate::time::Hertz; 12use crate::time::Hertz;
13use crate::{peripherals, Peripheral};
6 14
7/// HSI speed 15/// HSI speed
8pub const HSI_FREQ: Hertz = Hertz(16_000_000); 16pub const HSI_FREQ: Hertz = Hertz(16_000_000);
@@ -96,6 +104,164 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
96 } 104 }
97} 105}
98 106
107pub enum McoClock {
108 DIV1,
109 DIV2,
110 DIV3,
111 DIV4,
112 DIV5,
113}
114
115impl McoClock {
116 fn into_raw(&self) -> Mcopre {
117 match self {
118 McoClock::DIV1 => Mcopre::DIV1,
119 McoClock::DIV2 => Mcopre::DIV2,
120 McoClock::DIV3 => Mcopre::DIV3,
121 McoClock::DIV4 => Mcopre::DIV4,
122 McoClock::DIV5 => Mcopre::DIV5,
123 }
124 }
125}
126
127#[derive(Copy, Clone)]
128pub enum Mco1Source {
129 Hsi,
130 Lse,
131 Hse,
132 Pll,
133}
134
135impl Default for Mco1Source {
136 fn default() -> Self {
137 Self::Hsi
138 }
139}
140
141pub trait McoSource {
142 type Raw;
143
144 fn into_raw(&self) -> Self::Raw;
145}
146
147impl McoSource for Mco1Source {
148 type Raw = Mco1;
149 fn into_raw(&self) -> Self::Raw {
150 match self {
151 Mco1Source::Hsi => Mco1::HSI,
152 Mco1Source::Lse => Mco1::LSE,
153 Mco1Source::Hse => Mco1::HSE,
154 Mco1Source::Pll => Mco1::PLL,
155 }
156 }
157}
158
159#[derive(Copy, Clone)]
160pub enum Mco2Source {
161 SysClk,
162 Plli2s,
163 Hse,
164 Pll,
165}
166
167impl Default for Mco2Source {
168 fn default() -> Self {
169 Self::SysClk
170 }
171}
172
173impl McoSource for Mco2Source {
174 type Raw = Mco2;
175 fn into_raw(&self) -> Self::Raw {
176 match self {
177 Mco2Source::SysClk => Mco2::SYSCLK,
178 Mco2Source::Plli2s => Mco2::PLLI2S,
179 Mco2Source::Hse => Mco2::HSE,
180 Mco2Source::Pll => Mco2::PLL,
181 }
182 }
183}
184
185pub(crate) mod sealed {
186 use stm32_metapac::rcc::vals::Mcopre;
187 pub trait McoInstance {
188 type Source;
189 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre);
190 }
191}
192
193pub trait McoInstance: sealed::McoInstance + 'static {}
194
195pin_trait!(McoPin, McoInstance);
196
197impl sealed::McoInstance for peripherals::MCO1 {
198 type Source = Mco1;
199 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
200 RCC.cfgr().modify(|w| {
201 w.set_mco1(source);
202 w.set_mco1pre(prescaler);
203 });
204 match source {
205 Mco1::PLL => {
206 RCC.cr().modify(|w| w.set_pllon(true));
207 while !RCC.cr().read().pllrdy() {}
208 }
209 Mco1::HSI => {
210 RCC.cr().modify(|w| w.set_hsion(true));
211 while !RCC.cr().read().hsirdy() {}
212 }
213 _ => {}
214 }
215 }
216}
217impl McoInstance for peripherals::MCO1 {}
218
219impl sealed::McoInstance for peripherals::MCO2 {
220 type Source = Mco2;
221 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
222 RCC.cfgr().modify(|w| {
223 w.set_mco2(source);
224 w.set_mco2pre(prescaler);
225 });
226 match source {
227 Mco2::PLL => {
228 RCC.cr().modify(|w| w.set_pllon(true));
229 while !RCC.cr().read().pllrdy() {}
230 }
231 #[cfg(not(stm32f410))]
232 Mco2::PLLI2S => {
233 RCC.cr().modify(|w| w.set_plli2son(true));
234 while !RCC.cr().read().plli2srdy() {}
235 }
236 _ => {}
237 }
238 }
239}
240impl McoInstance for peripherals::MCO2 {}
241
242pub struct Mco<'d, T: McoInstance> {
243 phantom: PhantomData<&'d mut T>,
244}
245
246impl<'d, T: McoInstance> Mco<'d, T> {
247 pub fn new(
248 _peri: impl Peripheral<P = T> + 'd,
249 pin: impl Peripheral<P = impl McoPin<T>> + 'd,
250 source: impl McoSource<Raw = T::Source>,
251 prescaler: McoClock,
252 ) -> Self {
253 into_ref!(pin);
254
255 critical_section::with(|_| unsafe {
256 T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
257 pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
258 pin.set_speed(Speed::VeryHigh);
259 });
260
261 Self { phantom: PhantomData }
262 }
263}
264
99unsafe fn flash_setup(sysclk: u32) { 265unsafe fn flash_setup(sysclk: u32) {
100 use crate::pac::flash::vals::Latency; 266 use crate::pac::flash::vals::Latency;
101 267
diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs
new file mode 100644
index 000000000..17fbc6056
--- /dev/null
+++ b/embassy-stm32/src/rcc/h5.rs
@@ -0,0 +1,606 @@
1use core::marker::PhantomData;
2
3use stm32_metapac::rcc::vals::{Hpre, Ppre, Timpre};
4
5use crate::pac::pwr::vals::Vos;
6use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw};
7use crate::pac::{FLASH, PWR, RCC};
8use crate::rcc::{set_freqs, Clocks};
9use crate::time::Hertz;
10use crate::{peripherals, Peripheral};
11
12/// HSI speed
13pub const HSI_FREQ: Hertz = Hertz(64_000_000);
14
15/// CSI speed
16pub const CSI_FREQ: Hertz = Hertz(4_000_000);
17
18/// HSI48 speed
19pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
20
21/// LSI speed
22pub const LSI_FREQ: Hertz = Hertz(32_000);
23
24const VCO_MIN: u32 = 150_000_000;
25const VCO_MAX: u32 = 420_000_000;
26const VCO_WIDE_MIN: u32 = 128_000_000;
27const VCO_WIDE_MAX: u32 = 560_000_000;
28
29/// Voltage Scale
30///
31/// Represents the voltage range feeding the CPU core. The maximum core
32/// clock frequency depends on this value.
33#[derive(Copy, Clone, PartialEq)]
34pub enum VoltageScale {
35 /// VOS 0 range VCORE 1.30V - 1.40V
36 Scale0,
37 /// VOS 1 range VCORE 1.15V - 1.26V
38 Scale1,
39 /// VOS 2 range VCORE 1.05V - 1.15V
40 Scale2,
41 /// VOS 3 range VCORE 0.95V - 1.05V
42 Scale3,
43}
44
45pub enum HseMode {
46 /// crystal/ceramic oscillator (HSEBYP=0)
47 Oscillator,
48 /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)
49 BypassAnalog,
50 /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)
51 BypassDigital,
52}
53
54pub struct Hse {
55 /// HSE frequency.
56 pub freq: Hertz,
57 /// HSE mode.
58 pub mode: HseMode,
59}
60
61pub enum Hsi {
62 /// 64Mhz
63 Mhz64,
64 /// 32Mhz (divided by 2)
65 Mhz32,
66 /// 16Mhz (divided by 4)
67 Mhz16,
68 /// 8Mhz (divided by 8)
69 Mhz8,
70}
71
72pub enum Sysclk {
73 /// HSI selected as sysclk
74 HSI,
75 /// HSE selected as sysclk
76 HSE,
77 /// CSI selected as sysclk
78 CSI,
79 /// PLL1_P selected as sysclk
80 Pll1P,
81}
82
83pub enum PllSource {
84 Hsi,
85 Csi,
86 Hse,
87}
88
89pub struct Pll {
90 /// Source clock selection.
91 pub source: PllSource,
92
93 /// PLL pre-divider (DIVM). Must be between 1 and 63.
94 pub prediv: u8,
95
96 /// PLL multiplication factor. Must be between 4 and 512.
97 pub mul: u16,
98
99 /// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128.
100 /// On PLL1, it must be even (in particular, it cannot be 1.)
101 pub divp: Option<u16>,
102 /// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128.
103 pub divq: Option<u16>,
104 /// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128.
105 pub divr: Option<u16>,
106}
107
108/// AHB prescaler
109#[derive(Clone, Copy, PartialEq)]
110pub enum AHBPrescaler {
111 NotDivided,
112 Div2,
113 Div4,
114 Div8,
115 Div16,
116 Div64,
117 Div128,
118 Div256,
119 Div512,
120}
121
122impl AHBPrescaler {
123 fn div(&self, clk: Hertz) -> Hertz {
124 match self {
125 Self::NotDivided => clk,
126 Self::Div2 => clk / 2u32,
127 Self::Div4 => clk / 4u32,
128 Self::Div8 => clk / 8u32,
129 Self::Div16 => clk / 16u32,
130 Self::Div64 => clk / 64u32,
131 Self::Div128 => clk / 128u32,
132 Self::Div256 => clk / 256u32,
133 Self::Div512 => clk / 512u32,
134 }
135 }
136}
137
138/// APB prescaler
139#[derive(Clone, Copy)]
140pub enum APBPrescaler {
141 NotDivided,
142 Div2,
143 Div4,
144 Div8,
145 Div16,
146}
147
148impl APBPrescaler {
149 fn div(&self, clk: Hertz) -> Hertz {
150 match self {
151 Self::NotDivided => clk,
152 Self::Div2 => clk / 2u32,
153 Self::Div4 => clk / 4u32,
154 Self::Div8 => clk / 8u32,
155 Self::Div16 => clk / 16u32,
156 }
157 }
158
159 fn div_tim(&self, clk: Hertz, tim: TimerPrescaler) -> Hertz {
160 match (tim, self) {
161 // The timers kernel clock is equal to rcc_hclk1 if PPRE1 or PPRE2 corresponds to a
162 // division by 1 or 2, else it is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2
163 (TimerPrescaler::DefaultX2, Self::NotDivided) => clk,
164 (TimerPrescaler::DefaultX2, Self::Div2) => clk,
165 (TimerPrescaler::DefaultX2, Self::Div4) => clk / 2u32,
166 (TimerPrescaler::DefaultX2, Self::Div8) => clk / 4u32,
167 (TimerPrescaler::DefaultX2, Self::Div16) => clk / 8u32,
168 // The timers kernel clock is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 if PPRE1 or PPRE2
169 // corresponds to a division by 1, 2 or 4, else it is equal to 4 x Frcc_pclk1 or 4 x Frcc_pclk2
170 // this makes NO SENSE and is different than in the H7. Mistake in the RM??
171 (TimerPrescaler::DefaultX4, Self::NotDivided) => clk * 2u32,
172 (TimerPrescaler::DefaultX4, Self::Div2) => clk,
173 (TimerPrescaler::DefaultX4, Self::Div4) => clk / 2u32,
174 (TimerPrescaler::DefaultX4, Self::Div8) => clk / 2u32,
175 (TimerPrescaler::DefaultX4, Self::Div16) => clk / 4u32,
176 }
177 }
178}
179
180/// APB prescaler
181#[derive(Clone, Copy)]
182pub enum TimerPrescaler {
183 DefaultX2,
184 DefaultX4,
185}
186
187impl From<TimerPrescaler> for Timpre {
188 fn from(value: TimerPrescaler) -> Self {
189 match value {
190 TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2,
191 TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4,
192 }
193 }
194}
195
196impl From<APBPrescaler> for Ppre {
197 fn from(val: APBPrescaler) -> Ppre {
198 match val {
199 APBPrescaler::NotDivided => Ppre::DIV1,
200 APBPrescaler::Div2 => Ppre::DIV2,
201 APBPrescaler::Div4 => Ppre::DIV4,
202 APBPrescaler::Div8 => Ppre::DIV8,
203 APBPrescaler::Div16 => Ppre::DIV16,
204 }
205 }
206}
207
208impl From<AHBPrescaler> for Hpre {
209 fn from(val: AHBPrescaler) -> Hpre {
210 match val {
211 AHBPrescaler::NotDivided => Hpre::DIV1,
212 AHBPrescaler::Div2 => Hpre::DIV2,
213 AHBPrescaler::Div4 => Hpre::DIV4,
214 AHBPrescaler::Div8 => Hpre::DIV8,
215 AHBPrescaler::Div16 => Hpre::DIV16,
216 AHBPrescaler::Div64 => Hpre::DIV64,
217 AHBPrescaler::Div128 => Hpre::DIV128,
218 AHBPrescaler::Div256 => Hpre::DIV256,
219 AHBPrescaler::Div512 => Hpre::DIV512,
220 }
221 }
222}
223
224/// Configuration of the core clocks
225#[non_exhaustive]
226pub struct Config {
227 pub hsi: Option<Hsi>,
228 pub hse: Option<Hse>,
229 pub csi: bool,
230 pub hsi48: bool,
231 pub sys: Sysclk,
232
233 pub pll1: Option<Pll>,
234 pub pll2: Option<Pll>,
235 #[cfg(rcc_h5)]
236 pub pll3: Option<Pll>,
237
238 pub ahb_pre: AHBPrescaler,
239 pub apb1_pre: APBPrescaler,
240 pub apb2_pre: APBPrescaler,
241 pub apb3_pre: APBPrescaler,
242 pub timer_prescaler: TimerPrescaler,
243
244 pub voltage_scale: VoltageScale,
245}
246
247impl Default for Config {
248 fn default() -> Self {
249 Self {
250 hsi: Some(Hsi::Mhz64),
251 hse: None,
252 csi: false,
253 hsi48: false,
254 sys: Sysclk::HSI,
255 pll1: None,
256 pll2: None,
257 #[cfg(rcc_h5)]
258 pll3: None,
259
260 ahb_pre: AHBPrescaler::NotDivided,
261 apb1_pre: APBPrescaler::NotDivided,
262 apb2_pre: APBPrescaler::NotDivided,
263 apb3_pre: APBPrescaler::NotDivided,
264 timer_prescaler: TimerPrescaler::DefaultX2,
265
266 voltage_scale: VoltageScale::Scale3,
267 }
268 }
269}
270
271pub(crate) mod sealed {
272 pub trait McoInstance {
273 type Source;
274 unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
275 }
276}
277
278pub trait McoInstance: sealed::McoInstance + 'static {}
279
280pin_trait!(McoPin, McoInstance);
281
282macro_rules! impl_peri {
283 ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
284 impl sealed::McoInstance for peripherals::$peri {
285 type Source = $source;
286
287 unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
288 RCC.cfgr().modify(|w| {
289 w.$set_source(source);
290 w.$set_prescaler(prescaler);
291 });
292 }
293 }
294
295 impl McoInstance for peripherals::$peri {}
296 };
297}
298
299impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre);
300impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre);
301
302pub struct Mco<'d, T: McoInstance> {
303 phantom: PhantomData<&'d mut T>,
304}
305
306impl<'d, T: McoInstance> Mco<'d, T> {
307 pub fn new(
308 _peri: impl Peripheral<P = T> + 'd,
309 _pin: impl Peripheral<P = impl McoPin<T>> + 'd,
310 _source: T::Source,
311 ) -> Self {
312 todo!();
313 }
314}
315
316pub(crate) unsafe fn init(config: Config) {
317 let (vos, max_clk) = match config.voltage_scale {
318 VoltageScale::Scale0 => (Vos::SCALE0, Hertz(250_000_000)),
319 VoltageScale::Scale1 => (Vos::SCALE1, Hertz(200_000_000)),
320 VoltageScale::Scale2 => (Vos::SCALE2, Hertz(150_000_000)),
321 VoltageScale::Scale3 => (Vos::SCALE3, Hertz(100_000_000)),
322 };
323
324 // Configure voltage scale.
325 PWR.voscr().modify(|w| w.set_vos(vos));
326 while !PWR.vossr().read().vosrdy() {}
327
328 // Configure HSI
329 let hsi = match config.hsi {
330 None => {
331 RCC.cr().modify(|w| w.set_hsion(false));
332 None
333 }
334 Some(hsi) => {
335 let (freq, hsidiv) = match hsi {
336 Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1),
337 Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2),
338 Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4),
339 Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8),
340 };
341 RCC.cr().modify(|w| {
342 w.set_hsidiv(hsidiv);
343 w.set_hsion(true);
344 });
345 while !RCC.cr().read().hsirdy() {}
346 Some(freq)
347 }
348 };
349
350 // Configure HSE
351 let hse = match config.hse {
352 None => {
353 RCC.cr().modify(|w| w.set_hseon(false));
354 None
355 }
356 Some(hse) => {
357 let (byp, ext) = match hse.mode {
358 HseMode::Oscillator => (false, Hseext::ANALOG),
359 HseMode::BypassAnalog => (true, Hseext::ANALOG),
360 HseMode::BypassDigital => (true, Hseext::DIGITAL),
361 };
362
363 RCC.cr().modify(|w| {
364 w.set_hsebyp(byp);
365 w.set_hseext(ext);
366 });
367 RCC.cr().modify(|w| w.set_hseon(true));
368 while !RCC.cr().read().hserdy() {}
369 Some(hse.freq)
370 }
371 };
372
373 // Configure HSI48.
374 RCC.cr().modify(|w| w.set_hsi48on(config.hsi48));
375 let _hsi48 = match config.hsi48 {
376 false => None,
377 true => {
378 while !RCC.cr().read().hsi48rdy() {}
379 Some(CSI_FREQ)
380 }
381 };
382
383 // Configure CSI.
384 RCC.cr().modify(|w| w.set_csion(config.csi));
385 let csi = match config.csi {
386 false => None,
387 true => {
388 while !RCC.cr().read().csirdy() {}
389 Some(CSI_FREQ)
390 }
391 };
392
393 // Configure PLLs.
394 let pll_input = PllInput { csi, hse, hsi };
395 let pll1 = init_pll(0, config.pll1, &pll_input);
396 let _pll2 = init_pll(1, config.pll2, &pll_input);
397 #[cfg(rcc_h5)]
398 let _pll3 = init_pll(2, config.pll3, &pll_input);
399
400 // Configure sysclk
401 let (sys, sw) = match config.sys {
402 Sysclk::HSI => (unwrap!(hsi), Sw::HSI),
403 Sysclk::HSE => (unwrap!(hse), Sw::HSE),
404 Sysclk::CSI => (unwrap!(csi), Sw::CSI),
405 Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1),
406 };
407 assert!(sys <= max_clk);
408
409 let hclk = config.ahb_pre.div(sys);
410
411 let apb1 = config.apb1_pre.div(hclk);
412 let apb1_tim = config.apb1_pre.div_tim(hclk, config.timer_prescaler);
413 let apb2 = config.apb2_pre.div(hclk);
414 let apb2_tim = config.apb2_pre.div_tim(hclk, config.timer_prescaler);
415 let apb3 = config.apb3_pre.div(hclk);
416
417 flash_setup(hclk, config.voltage_scale);
418
419 // Set hpre
420 let hpre = config.ahb_pre.into();
421 RCC.cfgr2().modify(|w| w.set_hpre(hpre));
422 while RCC.cfgr2().read().hpre() != hpre {}
423
424 // set ppre
425 RCC.cfgr2().modify(|w| {
426 w.set_ppre1(config.apb1_pre.into());
427 w.set_ppre2(config.apb2_pre.into());
428 w.set_ppre3(config.apb3_pre.into());
429 });
430
431 RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into()));
432
433 RCC.cfgr().modify(|w| w.set_sw(sw));
434 while RCC.cfgr().read().sws() != sw {}
435
436 set_freqs(Clocks {
437 sys,
438 ahb1: hclk,
439 ahb2: hclk,
440 ahb3: hclk,
441 ahb4: hclk,
442 apb1,
443 apb2,
444 apb3,
445 apb1_tim,
446 apb2_tim,
447 adc: None,
448 });
449}
450
451struct PllInput {
452 hsi: Option<Hertz>,
453 hse: Option<Hertz>,
454 csi: Option<Hertz>,
455}
456
457struct PllOutput {
458 p: Option<Hertz>,
459 #[allow(dead_code)]
460 q: Option<Hertz>,
461 #[allow(dead_code)]
462 r: Option<Hertz>,
463}
464
465unsafe fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
466 let Some(config) = config else {
467 // Stop PLL
468 RCC.cr().modify(|w| w.set_pllon(num, false));
469 while RCC.cr().read().pllrdy(num) {}
470
471 // "To save power when PLL1 is not used, the value of PLL1M must be set to 0.""
472 RCC.pllcfgr(num).write(|w| {
473 w.set_divm(0);
474 });
475
476 return PllOutput{
477 p: None,
478 q: None,
479 r: None,
480 }
481 };
482
483 assert!(1 <= config.prediv && config.prediv <= 63);
484 assert!(4 <= config.mul && config.mul <= 512);
485
486 let (in_clk, src) = match config.source {
487 PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI),
488 PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE),
489 PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI),
490 };
491
492 let ref_clk = in_clk / config.prediv as u32;
493
494 let ref_range = match ref_clk.0 {
495 ..=1_999_999 => Pllrge::RANGE1,
496 ..=3_999_999 => Pllrge::RANGE2,
497 ..=7_999_999 => Pllrge::RANGE4,
498 ..=16_000_000 => Pllrge::RANGE8,
499 x => panic!("pll ref_clk out of range: {} mhz", x),
500 };
501
502 // The smaller range (150 to 420 MHz) must
503 // be chosen when the reference clock frequency is lower than 2 MHz.
504 let wide_allowed = ref_range != Pllrge::RANGE1;
505
506 let vco_clk = ref_clk * config.mul;
507 let vco_range = match vco_clk.0 {
508 VCO_MIN..=VCO_MAX => Pllvcosel::MEDIUMVCO,
509 VCO_WIDE_MIN..=VCO_WIDE_MAX if wide_allowed => Pllvcosel::WIDEVCO,
510 x => panic!("pll vco_clk out of range: {} mhz", x),
511 };
512
513 let p = config.divp.map(|div| {
514 assert!(1 <= div && div <= 128);
515 if num == 0 {
516 // on PLL1, DIVP must be even.
517 assert!(div % 2 == 0);
518 }
519
520 vco_clk / div
521 });
522 let q = config.divq.map(|div| {
523 assert!(1 <= div && div <= 128);
524 vco_clk / div
525 });
526 let r = config.divr.map(|div| {
527 assert!(1 <= div && div <= 128);
528 vco_clk / div
529 });
530
531 RCC.pllcfgr(num).write(|w| {
532 w.set_pllsrc(src);
533 w.set_divm(config.prediv);
534 w.set_pllvcosel(vco_range);
535 w.set_pllrge(ref_range);
536 w.set_pllfracen(false);
537 w.set_pllpen(p.is_some());
538 w.set_pllqen(q.is_some());
539 w.set_pllren(r.is_some());
540 });
541 RCC.plldivr(num).write(|w| {
542 w.set_plln(config.mul - 1);
543 w.set_pllp((config.divp.unwrap_or(1) - 1) as u8);
544 w.set_pllq((config.divq.unwrap_or(1) - 1) as u8);
545 w.set_pllr((config.divr.unwrap_or(1) - 1) as u8);
546 });
547
548 RCC.cr().modify(|w| w.set_pllon(num, true));
549 while !RCC.cr().read().pllrdy(num) {}
550
551 PllOutput { p, q, r }
552}
553
554fn flash_setup(clk: Hertz, vos: VoltageScale) {
555 // RM0481 Rev 1, table 37
556 // LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0
557 // 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz
558 // 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz
559 // 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz
560 // 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz
561 // 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz
562 // 5 2 170 to 200 MHz 210 to 250 MHz
563
564 // See RM0433 Rev 7 Table 17. FLASH recommended number of wait
565 // states and programming delay
566 let (latency, wrhighfreq) = match (vos, clk.0) {
567 (VoltageScale::Scale0, ..=42_000_000) => (0, 0),
568 (VoltageScale::Scale0, ..=84_000_000) => (1, 0),
569 (VoltageScale::Scale0, ..=126_000_000) => (2, 1),
570 (VoltageScale::Scale0, ..=168_000_000) => (3, 1),
571 (VoltageScale::Scale0, ..=210_000_000) => (4, 2),
572 (VoltageScale::Scale0, ..=250_000_000) => (5, 2),
573
574 (VoltageScale::Scale1, ..=34_000_000) => (0, 0),
575 (VoltageScale::Scale1, ..=68_000_000) => (1, 0),
576 (VoltageScale::Scale1, ..=102_000_000) => (2, 1),
577 (VoltageScale::Scale1, ..=136_000_000) => (3, 1),
578 (VoltageScale::Scale1, ..=170_000_000) => (4, 2),
579 (VoltageScale::Scale1, ..=200_000_000) => (5, 2),
580
581 (VoltageScale::Scale2, ..=30_000_000) => (0, 0),
582 (VoltageScale::Scale2, ..=60_000_000) => (1, 0),
583 (VoltageScale::Scale2, ..=90_000_000) => (2, 1),
584 (VoltageScale::Scale2, ..=120_000_000) => (3, 1),
585 (VoltageScale::Scale2, ..=150_000_000) => (4, 2),
586
587 (VoltageScale::Scale3, ..=20_000_000) => (0, 0),
588 (VoltageScale::Scale3, ..=40_000_000) => (1, 0),
589 (VoltageScale::Scale3, ..=60_000_000) => (2, 1),
590 (VoltageScale::Scale3, ..=80_000_000) => (3, 1),
591 (VoltageScale::Scale3, ..=100_000_000) => (4, 2),
592
593 _ => unreachable!(),
594 };
595
596 defmt::debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq);
597
598 // NOTE(unsafe) Atomic write
599 unsafe {
600 FLASH.acr().write(|w| {
601 w.set_wrhighfreq(wrhighfreq);
602 w.set_latency(latency);
603 });
604 while FLASH.acr().read().latency() != latency {}
605 }
606}
diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs
index e650490fe..c1bf7d0cd 100644
--- a/embassy-stm32/src/rcc/l4.rs
+++ b/embassy-stm32/src/rcc/l4.rs
@@ -1,7 +1,15 @@
1use core::marker::PhantomData;
2
3use embassy_hal_common::into_ref;
4use stm32_metapac::rcc::vals::{Mcopre, Mcosel};
5
6use crate::gpio::sealed::AFType;
7use crate::gpio::Speed;
1use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; 8use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
2use crate::pac::{FLASH, RCC}; 9use crate::pac::{FLASH, RCC};
3use crate::rcc::{set_freqs, Clocks}; 10use crate::rcc::{set_freqs, Clocks};
4use crate::time::Hertz; 11use crate::time::Hertz;
12use crate::{peripherals, Peripheral};
5 13
6/// HSI speed 14/// HSI speed
7pub const HSI_FREQ: Hertz = Hertz(16_000_000); 15pub const HSI_FREQ: Hertz = Hertz(16_000_000);
@@ -298,6 +306,131 @@ impl Default for Config {
298 } 306 }
299} 307}
300 308
309pub enum McoClock {
310 DIV1,
311 DIV2,
312 DIV4,
313 DIV8,
314 DIV16,
315}
316
317impl McoClock {
318 fn into_raw(&self) -> Mcopre {
319 match self {
320 McoClock::DIV1 => Mcopre::DIV1,
321 McoClock::DIV2 => Mcopre::DIV2,
322 McoClock::DIV4 => Mcopre::DIV4,
323 McoClock::DIV8 => Mcopre::DIV8,
324 McoClock::DIV16 => Mcopre::DIV16,
325 }
326 }
327}
328
329#[derive(Copy, Clone)]
330pub enum Mco1Source {
331 Disabled,
332 Lse,
333 Lsi,
334 Hse,
335 Hsi16,
336 PllClk,
337 SysClk,
338 Msi,
339 #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
340 Hsi48,
341}
342
343impl Default for Mco1Source {
344 fn default() -> Self {
345 Self::Hsi16
346 }
347}
348
349pub trait McoSource {
350 type Raw;
351
352 fn into_raw(&self) -> Self::Raw;
353}
354
355impl McoSource for Mco1Source {
356 type Raw = Mcosel;
357 fn into_raw(&self) -> Self::Raw {
358 match self {
359 Mco1Source::Disabled => Mcosel::NOCLOCK,
360 Mco1Source::Lse => Mcosel::LSE,
361 Mco1Source::Lsi => Mcosel::LSI,
362 Mco1Source::Hse => Mcosel::HSE,
363 Mco1Source::Hsi16 => Mcosel::HSI16,
364 Mco1Source::PllClk => Mcosel::PLL,
365 Mco1Source::SysClk => Mcosel::SYSCLK,
366 Mco1Source::Msi => Mcosel::MSI,
367 #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
368 Mco1Source::Hsi48 => Mcosel::HSI48,
369 }
370 }
371}
372
373pub(crate) mod sealed {
374 use stm32_metapac::rcc::vals::Mcopre;
375 pub trait McoInstance {
376 type Source;
377 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre);
378 }
379}
380
381pub trait McoInstance: sealed::McoInstance + 'static {}
382
383pin_trait!(McoPin, McoInstance);
384
385impl sealed::McoInstance for peripherals::MCO {
386 type Source = Mcosel;
387
388 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
389 RCC.cfgr().modify(|w| {
390 w.set_mcosel(source);
391 w.set_mcopre(prescaler);
392 });
393
394 match source {
395 Mcosel::HSI16 => {
396 RCC.cr().modify(|w| w.set_hsion(true));
397 while !RCC.cr().read().hsirdy() {}
398 }
399 #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
400 Mcosel::HSI48 => {
401 RCC.crrcr().modify(|w| w.set_hsi48on(true));
402 while !RCC.crrcr().read().hsi48rdy() {}
403 }
404 _ => {}
405 }
406 }
407}
408
409impl McoInstance for peripherals::MCO {}
410
411pub struct Mco<'d, T: McoInstance> {
412 phantom: PhantomData<&'d mut T>,
413}
414
415impl<'d, T: McoInstance> Mco<'d, T> {
416 pub fn new(
417 _peri: impl Peripheral<P = T> + 'd,
418 pin: impl Peripheral<P = impl McoPin<T>> + 'd,
419 source: impl McoSource<Raw = T::Source>,
420 prescaler: McoClock,
421 ) -> Self {
422 into_ref!(pin);
423
424 critical_section::with(|_| unsafe {
425 T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
426 pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
427 pin.set_speed(Speed::VeryHigh);
428 });
429
430 Self { phantom: PhantomData }
431 }
432}
433
301pub(crate) unsafe fn init(config: Config) { 434pub(crate) unsafe fn init(config: Config) {
302 let (sys_clk, sw) = match config.mux { 435 let (sys_clk, sw) = match config.mux {
303 ClockSrc::MSI(range) => { 436 ClockSrc::MSI(range) => {
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index d4bd3d6b8..d6a31f17b 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -21,6 +21,7 @@ use crate::time::Hertz;
21#[cfg_attr(rcc_u5, path = "u5.rs")] 21#[cfg_attr(rcc_u5, path = "u5.rs")]
22#[cfg_attr(rcc_wb, path = "wb.rs")] 22#[cfg_attr(rcc_wb, path = "wb.rs")]
23#[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")] 23#[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")]
24#[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")]
24mod _version; 25mod _version;
25pub use _version::*; 26pub use _version::*;
26 27
@@ -36,7 +37,7 @@ pub struct Clocks {
36 pub apb2: Hertz, 37 pub apb2: Hertz,
37 #[cfg(not(any(rcc_c0, rcc_g0)))] 38 #[cfg(not(any(rcc_c0, rcc_g0)))]
38 pub apb2_tim: Hertz, 39 pub apb2_tim: Hertz,
39 #[cfg(any(rcc_wl5, rcc_wle, rcc_u5))] 40 #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_u5))]
40 pub apb3: Hertz, 41 pub apb3: Hertz,
41 #[cfg(any(rcc_h7, rcc_h7ab))] 42 #[cfg(any(rcc_h7, rcc_h7ab))]
42 pub apb4: Hertz, 43 pub apb4: Hertz,
@@ -44,14 +45,16 @@ pub struct Clocks {
44 // AHB 45 // AHB
45 pub ahb1: Hertz, 46 pub ahb1: Hertz,
46 #[cfg(any( 47 #[cfg(any(
47 rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, rcc_wl5, rcc_wle 48 rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb,
49 rcc_wl5, rcc_wle
48 ))] 50 ))]
49 pub ahb2: Hertz, 51 pub ahb2: Hertz,
50 #[cfg(any( 52 #[cfg(any(
51 rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5, rcc_wle 53 rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5,
54 rcc_wle
52 ))] 55 ))]
53 pub ahb3: Hertz, 56 pub ahb3: Hertz,
54 #[cfg(any(rcc_h7, rcc_h7ab))] 57 #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))]
55 pub ahb4: Hertz, 58 pub ahb4: Hertz,
56 59
57 #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] 60 #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
@@ -60,7 +63,7 @@ pub struct Clocks {
60 #[cfg(stm32f1)] 63 #[cfg(stm32f1)]
61 pub adc: Hertz, 64 pub adc: Hertz,
62 65
63 #[cfg(any(rcc_h7, rcc_h7ab))] 66 #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))]
64 pub adc: Option<Hertz>, 67 pub adc: Option<Hertz>,
65} 68}
66 69
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 1f1708873..481ea4abc 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -258,7 +258,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
258 w.set_spe(true); 258 w.set_spe(true);
259 }); 259 });
260 } 260 }
261 #[cfg(any(spi_v3, spi_v4))] 261 #[cfg(any(spi_v3, spi_v4, spi_v5))]
262 unsafe { 262 unsafe {
263 T::REGS.ifcr().write(|w| w.0 = 0xffff_ffff); 263 T::REGS.ifcr().write(|w| w.0 = 0xffff_ffff);
264 T::REGS.cfg2().modify(|w| { 264 T::REGS.cfg2().modify(|w| {
@@ -317,7 +317,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
317 }); 317 });
318 } 318 }
319 319
320 #[cfg(any(spi_v3, spi_v4))] 320 #[cfg(any(spi_v3, spi_v4, spi_v5))]
321 unsafe { 321 unsafe {
322 T::REGS.cfg2().modify(|w| { 322 T::REGS.cfg2().modify(|w| {
323 w.set_cpha(cpha); 323 w.set_cpha(cpha);
@@ -330,7 +330,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
330 pub fn get_current_config(&self) -> Config { 330 pub fn get_current_config(&self) -> Config {
331 #[cfg(any(spi_v1, spi_f1, spi_v2))] 331 #[cfg(any(spi_v1, spi_f1, spi_v2))]
332 let cfg = unsafe { T::REGS.cr1().read() }; 332 let cfg = unsafe { T::REGS.cr1().read() };
333 #[cfg(any(spi_v3, spi_v4))] 333 #[cfg(any(spi_v3, spi_v4, spi_v5))]
334 let cfg = unsafe { T::REGS.cfg2().read() }; 334 let cfg = unsafe { T::REGS.cfg2().read() };
335 let polarity = if cfg.cpol() == vals::Cpol::IDLELOW { 335 let polarity = if cfg.cpol() == vals::Cpol::IDLELOW {
336 Polarity::IdleLow 336 Polarity::IdleLow
@@ -383,7 +383,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
383 w.set_spe(true); 383 w.set_spe(true);
384 }); 384 });
385 } 385 }
386 #[cfg(any(spi_v3, spi_v4))] 386 #[cfg(any(spi_v3, spi_v4, spi_v5))]
387 unsafe { 387 unsafe {
388 T::REGS.cr1().modify(|w| { 388 T::REGS.cr1().modify(|w| {
389 w.set_csusp(true); 389 w.set_csusp(true);
@@ -429,7 +429,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
429 T::REGS.cr1().modify(|w| { 429 T::REGS.cr1().modify(|w| {
430 w.set_spe(true); 430 w.set_spe(true);
431 }); 431 });
432 #[cfg(any(spi_v3, spi_v4))] 432 #[cfg(any(spi_v3, spi_v4, spi_v5))]
433 T::REGS.cr1().modify(|w| { 433 T::REGS.cr1().modify(|w| {
434 w.set_cstart(true); 434 w.set_cstart(true);
435 }); 435 });
@@ -459,7 +459,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
459 } 459 }
460 460
461 // SPIv3 clears rxfifo on SPE=0 461 // SPIv3 clears rxfifo on SPE=0
462 #[cfg(not(any(spi_v3, spi_v4)))] 462 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
463 flush_rx_fifo(T::REGS); 463 flush_rx_fifo(T::REGS);
464 464
465 set_rxdmaen(T::REGS, true); 465 set_rxdmaen(T::REGS, true);
@@ -481,7 +481,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
481 T::REGS.cr1().modify(|w| { 481 T::REGS.cr1().modify(|w| {
482 w.set_spe(true); 482 w.set_spe(true);
483 }); 483 });
484 #[cfg(any(spi_v3, spi_v4))] 484 #[cfg(any(spi_v3, spi_v4, spi_v5))]
485 T::REGS.cr1().modify(|w| { 485 T::REGS.cr1().modify(|w| {
486 w.set_cstart(true); 486 w.set_cstart(true);
487 }); 487 });
@@ -514,7 +514,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
514 } 514 }
515 515
516 // SPIv3 clears rxfifo on SPE=0 516 // SPIv3 clears rxfifo on SPE=0
517 #[cfg(not(any(spi_v3, spi_v4)))] 517 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
518 flush_rx_fifo(T::REGS); 518 flush_rx_fifo(T::REGS);
519 519
520 set_rxdmaen(T::REGS, true); 520 set_rxdmaen(T::REGS, true);
@@ -534,7 +534,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
534 T::REGS.cr1().modify(|w| { 534 T::REGS.cr1().modify(|w| {
535 w.set_spe(true); 535 w.set_spe(true);
536 }); 536 });
537 #[cfg(any(spi_v3, spi_v4))] 537 #[cfg(any(spi_v3, spi_v4, spi_v5))]
538 T::REGS.cr1().modify(|w| { 538 T::REGS.cr1().modify(|w| {
539 w.set_cstart(true); 539 w.set_cstart(true);
540 }); 540 });
@@ -619,9 +619,9 @@ impl<'d, T: Instance, Tx, Rx> Drop for Spi<'d, T, Tx, Rx> {
619 } 619 }
620} 620}
621 621
622#[cfg(not(any(spi_v3, spi_v4)))] 622#[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
623use vals::Br; 623use vals::Br;
624#[cfg(any(spi_v3, spi_v4))] 624#[cfg(any(spi_v3, spi_v4, spi_v5))]
625use vals::Mbr as Br; 625use vals::Mbr as Br;
626 626
627fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { 627fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br {
@@ -647,17 +647,17 @@ trait RegsExt {
647 647
648impl RegsExt for Regs { 648impl RegsExt for Regs {
649 fn tx_ptr<W>(&self) -> *mut W { 649 fn tx_ptr<W>(&self) -> *mut W {
650 #[cfg(not(any(spi_v3, spi_v4)))] 650 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
651 let dr = self.dr(); 651 let dr = self.dr();
652 #[cfg(any(spi_v3, spi_v4))] 652 #[cfg(any(spi_v3, spi_v4, spi_v5))]
653 let dr = self.txdr(); 653 let dr = self.txdr();
654 dr.ptr() as *mut W 654 dr.ptr() as *mut W
655 } 655 }
656 656
657 fn rx_ptr<W>(&self) -> *mut W { 657 fn rx_ptr<W>(&self) -> *mut W {
658 #[cfg(not(any(spi_v3, spi_v4)))] 658 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
659 let dr = self.dr(); 659 let dr = self.dr();
660 #[cfg(any(spi_v3, spi_v4))] 660 #[cfg(any(spi_v3, spi_v4, spi_v5))]
661 let dr = self.rxdr(); 661 let dr = self.rxdr();
662 dr.ptr() as *mut W 662 dr.ptr() as *mut W
663 } 663 }
@@ -667,22 +667,22 @@ fn check_error_flags(sr: regs::Sr) -> Result<(), Error> {
667 if sr.ovr() { 667 if sr.ovr() {
668 return Err(Error::Overrun); 668 return Err(Error::Overrun);
669 } 669 }
670 #[cfg(not(any(spi_f1, spi_v3, spi_v4)))] 670 #[cfg(not(any(spi_f1, spi_v3, spi_v4, spi_v5)))]
671 if sr.fre() { 671 if sr.fre() {
672 return Err(Error::Framing); 672 return Err(Error::Framing);
673 } 673 }
674 #[cfg(any(spi_v3, spi_v4))] 674 #[cfg(any(spi_v3, spi_v4, spi_v5))]
675 if sr.tifre() { 675 if sr.tifre() {
676 return Err(Error::Framing); 676 return Err(Error::Framing);
677 } 677 }
678 if sr.modf() { 678 if sr.modf() {
679 return Err(Error::ModeFault); 679 return Err(Error::ModeFault);
680 } 680 }
681 #[cfg(not(any(spi_v3, spi_v4)))] 681 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
682 if sr.crcerr() { 682 if sr.crcerr() {
683 return Err(Error::Crc); 683 return Err(Error::Crc);
684 } 684 }
685 #[cfg(any(spi_v3, spi_v4))] 685 #[cfg(any(spi_v3, spi_v4, spi_v5))]
686 if sr.crce() { 686 if sr.crce() {
687 return Err(Error::Crc); 687 return Err(Error::Crc);
688 } 688 }
@@ -696,11 +696,11 @@ fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> {
696 696
697 check_error_flags(sr)?; 697 check_error_flags(sr)?;
698 698
699 #[cfg(not(any(spi_v3, spi_v4)))] 699 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
700 if sr.txe() { 700 if sr.txe() {
701 return Ok(()); 701 return Ok(());
702 } 702 }
703 #[cfg(any(spi_v3, spi_v4))] 703 #[cfg(any(spi_v3, spi_v4, spi_v5))]
704 if sr.txp() { 704 if sr.txp() {
705 return Ok(()); 705 return Ok(());
706 } 706 }
@@ -713,11 +713,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
713 713
714 check_error_flags(sr)?; 714 check_error_flags(sr)?;
715 715
716 #[cfg(not(any(spi_v3, spi_v4)))] 716 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
717 if sr.rxne() { 717 if sr.rxne() {
718 return Ok(()); 718 return Ok(());
719 } 719 }
720 #[cfg(any(spi_v3, spi_v4))] 720 #[cfg(any(spi_v3, spi_v4, spi_v5))]
721 if sr.rxp() { 721 if sr.rxp() {
722 return Ok(()); 722 return Ok(());
723 } 723 }
@@ -726,11 +726,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
726 726
727fn flush_rx_fifo(regs: Regs) { 727fn flush_rx_fifo(regs: Regs) {
728 unsafe { 728 unsafe {
729 #[cfg(not(any(spi_v3, spi_v4)))] 729 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
730 while regs.sr().read().rxne() { 730 while regs.sr().read().rxne() {
731 let _ = regs.dr().read(); 731 let _ = regs.dr().read();
732 } 732 }
733 #[cfg(any(spi_v3, spi_v4))] 733 #[cfg(any(spi_v3, spi_v4, spi_v5))]
734 while regs.sr().read().rxp() { 734 while regs.sr().read().rxp() {
735 let _ = regs.rxdr().read(); 735 let _ = regs.rxdr().read();
736 } 736 }
@@ -739,11 +739,11 @@ fn flush_rx_fifo(regs: Regs) {
739 739
740fn set_txdmaen(regs: Regs, val: bool) { 740fn set_txdmaen(regs: Regs, val: bool) {
741 unsafe { 741 unsafe {
742 #[cfg(not(any(spi_v3, spi_v4)))] 742 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
743 regs.cr2().modify(|reg| { 743 regs.cr2().modify(|reg| {
744 reg.set_txdmaen(val); 744 reg.set_txdmaen(val);
745 }); 745 });
746 #[cfg(any(spi_v3, spi_v4))] 746 #[cfg(any(spi_v3, spi_v4, spi_v5))]
747 regs.cfg1().modify(|reg| { 747 regs.cfg1().modify(|reg| {
748 reg.set_txdmaen(val); 748 reg.set_txdmaen(val);
749 }); 749 });
@@ -752,11 +752,11 @@ fn set_txdmaen(regs: Regs, val: bool) {
752 752
753fn set_rxdmaen(regs: Regs, val: bool) { 753fn set_rxdmaen(regs: Regs, val: bool) {
754 unsafe { 754 unsafe {
755 #[cfg(not(any(spi_v3, spi_v4)))] 755 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
756 regs.cr2().modify(|reg| { 756 regs.cr2().modify(|reg| {
757 reg.set_rxdmaen(val); 757 reg.set_rxdmaen(val);
758 }); 758 });
759 #[cfg(any(spi_v3, spi_v4))] 759 #[cfg(any(spi_v3, spi_v4, spi_v5))]
760 regs.cfg1().modify(|reg| { 760 regs.cfg1().modify(|reg| {
761 reg.set_rxdmaen(val); 761 reg.set_rxdmaen(val);
762 }); 762 });
@@ -768,9 +768,9 @@ fn finish_dma(regs: Regs) {
768 #[cfg(spi_v2)] 768 #[cfg(spi_v2)]
769 while regs.sr().read().ftlvl() > 0 {} 769 while regs.sr().read().ftlvl() > 0 {}
770 770
771 #[cfg(any(spi_v3, spi_v4))] 771 #[cfg(any(spi_v3, spi_v4, spi_v5))]
772 while !regs.sr().read().txc() {} 772 while !regs.sr().read().txc() {}
773 #[cfg(not(any(spi_v3, spi_v4)))] 773 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
774 while regs.sr().read().bsy() {} 774 while regs.sr().read().bsy() {}
775 775
776 // Disable the spi peripheral 776 // Disable the spi peripheral
@@ -780,12 +780,12 @@ fn finish_dma(regs: Regs) {
780 780
781 // The peripheral automatically disables the DMA stream on completion without error, 781 // The peripheral automatically disables the DMA stream on completion without error,
782 // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. 782 // but it does not clear the RXDMAEN/TXDMAEN flag in CR2.
783 #[cfg(not(any(spi_v3, spi_v4)))] 783 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
784 regs.cr2().modify(|reg| { 784 regs.cr2().modify(|reg| {
785 reg.set_txdmaen(false); 785 reg.set_txdmaen(false);
786 reg.set_rxdmaen(false); 786 reg.set_rxdmaen(false);
787 }); 787 });
788 #[cfg(any(spi_v3, spi_v4))] 788 #[cfg(any(spi_v3, spi_v4, spi_v5))]
789 regs.cfg1().modify(|reg| { 789 regs.cfg1().modify(|reg| {
790 reg.set_txdmaen(false); 790 reg.set_txdmaen(false);
791 reg.set_rxdmaen(false); 791 reg.set_rxdmaen(false);
@@ -799,7 +799,7 @@ fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
799 unsafe { 799 unsafe {
800 ptr::write_volatile(regs.tx_ptr(), tx_word); 800 ptr::write_volatile(regs.tx_ptr(), tx_word);
801 801
802 #[cfg(any(spi_v3, spi_v4))] 802 #[cfg(any(spi_v3, spi_v4, spi_v5))]
803 regs.cr1().modify(|reg| reg.set_cstart(true)); 803 regs.cr1().modify(|reg| reg.set_cstart(true));
804 } 804 }
805 805
@@ -970,7 +970,7 @@ pub(crate) mod sealed {
970 } 970 }
971 } 971 }
972 972
973 #[cfg(any(spi_v3, spi_v4))] 973 #[cfg(any(spi_v3, spi_v4, spi_v5))]
974 pub fn dsize(&self) -> u8 { 974 pub fn dsize(&self) -> u8 {
975 match self { 975 match self {
976 WordSize::EightBit => 0b0111, 976 WordSize::EightBit => 0b0111,
@@ -978,7 +978,7 @@ pub(crate) mod sealed {
978 } 978 }
979 } 979 }
980 980
981 #[cfg(any(spi_v3, spi_v4))] 981 #[cfg(any(spi_v3, spi_v4, spi_v5))]
982 pub fn _frxth(&self) -> vals::Fthlv { 982 pub fn _frxth(&self) -> vals::Fthlv {
983 match self { 983 match self {
984 WordSize::EightBit => vals::Fthlv::ONEFRAME, 984 WordSize::EightBit => vals::Fthlv::ONEFRAME,
diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs
index 975517a48..f08abe331 100644
--- a/embassy-stm32/src/time.rs
+++ b/embassy-stm32/src/time.rs
@@ -1,7 +1,9 @@
1//! Time units 1//! Time units
2 2
3use core::ops::{Div, Mul};
4
3/// Hertz 5/// Hertz
4#[derive(PartialEq, PartialOrd, Clone, Copy, Debug, Eq)] 6#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))] 7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6pub struct Hertz(pub u32); 8pub struct Hertz(pub u32);
7 9
@@ -33,3 +35,45 @@ pub fn khz(kilohertz: u32) -> Hertz {
33pub fn mhz(megahertz: u32) -> Hertz { 35pub fn mhz(megahertz: u32) -> Hertz {
34 Hertz::mhz(megahertz) 36 Hertz::mhz(megahertz)
35} 37}
38
39impl Mul<u32> for Hertz {
40 type Output = Hertz;
41 fn mul(self, rhs: u32) -> Self::Output {
42 Hertz(self.0 * rhs)
43 }
44}
45
46impl Div<u32> for Hertz {
47 type Output = Hertz;
48 fn div(self, rhs: u32) -> Self::Output {
49 Hertz(self.0 / rhs)
50 }
51}
52
53impl Mul<u16> for Hertz {
54 type Output = Hertz;
55 fn mul(self, rhs: u16) -> Self::Output {
56 self * (rhs as u32)
57 }
58}
59
60impl Div<u16> for Hertz {
61 type Output = Hertz;
62 fn div(self, rhs: u16) -> Self::Output {
63 self / (rhs as u32)
64 }
65}
66
67impl Mul<u8> for Hertz {
68 type Output = Hertz;
69 fn mul(self, rhs: u8) -> Self::Output {
70 self * (rhs as u32)
71 }
72}
73
74impl Div<u8> for Hertz {
75 type Output = Hertz;
76 fn div(self, rhs: u8) -> Self::Output {
77 self / (rhs as u32)
78 }
79}
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs
index cd7d72f91..3e23e7ca1 100644
--- a/embassy-stm32/src/usart/buffered.rs
+++ b/embassy-stm32/src/usart/buffered.rs
@@ -1,55 +1,51 @@
1use core::cell::RefCell;
2use core::future::poll_fn; 1use core::future::poll_fn;
3use core::sync::atomic::{compiler_fence, Ordering}; 2use core::slice;
4use core::task::Poll; 3use core::task::Poll;
5 4
6use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; 5use embassy_cortex_m::interrupt::Interrupt;
7use embassy_hal_common::ring_buffer::RingBuffer; 6use embassy_hal_common::atomic_ring_buffer::RingBuffer;
8use embassy_sync::waitqueue::WakerRegistration; 7use embassy_sync::waitqueue::AtomicWaker;
9 8
10use super::*; 9use super::*;
11 10
12pub struct State<'d, T: BasicInstance>(StateStorage<StateInner<'d, T>>); 11pub struct State {
13impl<'d, T: BasicInstance> State<'d, T> { 12 rx_waker: AtomicWaker,
14 pub const fn new() -> Self { 13 rx_buf: RingBuffer,
15 Self(StateStorage::new())
16 }
17}
18
19struct StateInner<'d, T: BasicInstance> {
20 phantom: PhantomData<&'d mut T>,
21
22 rx_waker: WakerRegistration,
23 rx: RingBuffer<'d>,
24 14
25 tx_waker: WakerRegistration, 15 tx_waker: AtomicWaker,
26 tx: RingBuffer<'d>, 16 tx_buf: RingBuffer,
27} 17}
28 18
29unsafe impl<'d, T: BasicInstance> Send for StateInner<'d, T> {} 19impl State {
30unsafe impl<'d, T: BasicInstance> Sync for StateInner<'d, T> {} 20 pub const fn new() -> Self {
21 Self {
22 rx_buf: RingBuffer::new(),
23 tx_buf: RingBuffer::new(),
24 rx_waker: AtomicWaker::new(),
25 tx_waker: AtomicWaker::new(),
26 }
27 }
28}
31 29
32pub struct BufferedUart<'d, T: BasicInstance> { 30pub struct BufferedUart<'d, T: BasicInstance> {
33 inner: RefCell<PeripheralMutex<'d, StateInner<'d, T>>>, 31 rx: BufferedUartRx<'d, T>,
32 tx: BufferedUartTx<'d, T>,
34} 33}
35 34
36pub struct BufferedUartTx<'u, 'd, T: BasicInstance> { 35pub struct BufferedUartTx<'d, T: BasicInstance> {
37 inner: &'u BufferedUart<'d, T>, 36 phantom: PhantomData<&'d mut T>,
38} 37}
39 38
40pub struct BufferedUartRx<'u, 'd, T: BasicInstance> { 39pub struct BufferedUartRx<'d, T: BasicInstance> {
41 inner: &'u BufferedUart<'d, T>, 40 phantom: PhantomData<&'d mut T>,
42} 41}
43 42
44impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {}
45
46impl<'d, T: BasicInstance> BufferedUart<'d, T> { 43impl<'d, T: BasicInstance> BufferedUart<'d, T> {
47 pub fn new( 44 pub fn new(
48 state: &'d mut State<'d, T>,
49 peri: impl Peripheral<P = T> + 'd, 45 peri: impl Peripheral<P = T> + 'd,
46 irq: impl Peripheral<P = T::Interrupt> + 'd,
50 rx: impl Peripheral<P = impl RxPin<T>> + 'd, 47 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
51 tx: impl Peripheral<P = impl TxPin<T>> + 'd, 48 tx: impl Peripheral<P = impl TxPin<T>> + 'd,
52 irq: impl Peripheral<P = T::Interrupt> + 'd,
53 tx_buffer: &'d mut [u8], 49 tx_buffer: &'d mut [u8],
54 rx_buffer: &'d mut [u8], 50 rx_buffer: &'d mut [u8],
55 config: Config, 51 config: Config,
@@ -57,15 +53,14 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
57 T::enable(); 53 T::enable();
58 T::reset(); 54 T::reset();
59 55
60 Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) 56 Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config)
61 } 57 }
62 58
63 pub fn new_with_rtscts( 59 pub fn new_with_rtscts(
64 state: &'d mut State<'d, T>,
65 peri: impl Peripheral<P = T> + 'd, 60 peri: impl Peripheral<P = T> + 'd,
61 irq: impl Peripheral<P = T::Interrupt> + 'd,
66 rx: impl Peripheral<P = impl RxPin<T>> + 'd, 62 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
67 tx: impl Peripheral<P = impl TxPin<T>> + 'd, 63 tx: impl Peripheral<P = impl TxPin<T>> + 'd,
68 irq: impl Peripheral<P = T::Interrupt> + 'd,
69 rts: impl Peripheral<P = impl RtsPin<T>> + 'd, 64 rts: impl Peripheral<P = impl RtsPin<T>> + 'd,
70 cts: impl Peripheral<P = impl CtsPin<T>> + 'd, 65 cts: impl Peripheral<P = impl CtsPin<T>> + 'd,
71 tx_buffer: &'d mut [u8], 66 tx_buffer: &'d mut [u8],
@@ -86,16 +81,15 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
86 }); 81 });
87 } 82 }
88 83
89 Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) 84 Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config)
90 } 85 }
91 86
92 #[cfg(not(usart_v1))] 87 #[cfg(not(usart_v1))]
93 pub fn new_with_de( 88 pub fn new_with_de(
94 state: &'d mut State<'d, T>,
95 peri: impl Peripheral<P = T> + 'd, 89 peri: impl Peripheral<P = T> + 'd,
90 irq: impl Peripheral<P = T::Interrupt> + 'd,
96 rx: impl Peripheral<P = impl RxPin<T>> + 'd, 91 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
97 tx: impl Peripheral<P = impl TxPin<T>> + 'd, 92 tx: impl Peripheral<P = impl TxPin<T>> + 'd,
98 irq: impl Peripheral<P = T::Interrupt> + 'd,
99 de: impl Peripheral<P = impl DePin<T>> + 'd, 93 de: impl Peripheral<P = impl DePin<T>> + 'd,
100 tx_buffer: &'d mut [u8], 94 tx_buffer: &'d mut [u8],
101 rx_buffer: &'d mut [u8], 95 rx_buffer: &'d mut [u8],
@@ -113,23 +107,27 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
113 }); 107 });
114 } 108 }
115 109
116 Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) 110 Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config)
117 } 111 }
118 112
119 fn new_inner( 113 fn new_inner(
120 state: &'d mut State<'d, T>,
121 _peri: impl Peripheral<P = T> + 'd, 114 _peri: impl Peripheral<P = T> + 'd,
115 irq: impl Peripheral<P = T::Interrupt> + 'd,
122 rx: impl Peripheral<P = impl RxPin<T>> + 'd, 116 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
123 tx: impl Peripheral<P = impl TxPin<T>> + 'd, 117 tx: impl Peripheral<P = impl TxPin<T>> + 'd,
124 irq: impl Peripheral<P = T::Interrupt> + 'd,
125 tx_buffer: &'d mut [u8], 118 tx_buffer: &'d mut [u8],
126 rx_buffer: &'d mut [u8], 119 rx_buffer: &'d mut [u8],
127 config: Config, 120 config: Config,
128 ) -> BufferedUart<'d, T> { 121 ) -> BufferedUart<'d, T> {
129 into_ref!(_peri, rx, tx, irq); 122 into_ref!(_peri, rx, tx, irq);
130 123
131 let r = T::regs(); 124 let state = T::buffered_state();
125 let len = tx_buffer.len();
126 unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
127 let len = rx_buffer.len();
128 unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) };
132 129
130 let r = T::regs();
133 unsafe { 131 unsafe {
134 rx.set_as_af(rx.af_num(), AFType::Input); 132 rx.set_as_af(rx.af_num(), AFType::Input);
135 tx.set_as_af(tx.af_num(), AFType::OutputPushPull); 133 tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
@@ -147,273 +145,259 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
147 }); 145 });
148 } 146 }
149 147
150 Self { 148 irq.set_handler(on_interrupt::<T>);
151 inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner { 149 irq.unpend();
152 phantom: PhantomData, 150 irq.enable();
153 tx: RingBuffer::new(tx_buffer),
154 tx_waker: WakerRegistration::new(),
155 151
156 rx: RingBuffer::new(rx_buffer), 152 Self {
157 rx_waker: WakerRegistration::new(), 153 rx: BufferedUartRx { phantom: PhantomData },
158 })), 154 tx: BufferedUartTx { phantom: PhantomData },
159 } 155 }
160 } 156 }
161 157
162 pub fn split<'u>(&'u mut self) -> (BufferedUartRx<'u, 'd, T>, BufferedUartTx<'u, 'd, T>) { 158 pub fn split(self) -> (BufferedUartTx<'d, T>, BufferedUartRx<'d, T>) {
163 (BufferedUartRx { inner: self }, BufferedUartTx { inner: self }) 159 (self.tx, self.rx)
164 } 160 }
161}
165 162
166 async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result<usize, Error> { 163impl<'d, T: BasicInstance> BufferedUartRx<'d, T> {
164 async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
167 poll_fn(move |cx| { 165 poll_fn(move |cx| {
168 let mut do_pend = false; 166 let state = T::buffered_state();
169 let mut inner = self.inner.borrow_mut(); 167 let mut rx_reader = unsafe { state.rx_buf.reader() };
170 let res = inner.with(|state| { 168 let data = rx_reader.pop_slice();
171 compiler_fence(Ordering::SeqCst);
172
173 // We have data ready in buffer? Return it.
174 let data = state.rx.pop_buf();
175 if !data.is_empty() {
176 let len = data.len().min(buf.len());
177 buf[..len].copy_from_slice(&data[..len]);
178
179 if state.rx.is_full() {
180 do_pend = true;
181 }
182 state.rx.pop(len);
183
184 return Poll::Ready(Ok(len));
185 }
186 169
187 state.rx_waker.register(cx.waker()); 170 if !data.is_empty() {
188 Poll::Pending 171 let len = data.len().min(buf.len());
189 }); 172 buf[..len].copy_from_slice(&data[..len]);
173
174 let do_pend = state.rx_buf.is_full();
175 rx_reader.pop_done(len);
190 176
191 if do_pend { 177 if do_pend {
192 inner.pend(); 178 unsafe { T::Interrupt::steal().pend() };
179 }
180
181 return Poll::Ready(Ok(len));
193 } 182 }
194 183
195 res 184 state.rx_waker.register(cx.waker());
185 Poll::Pending
196 }) 186 })
197 .await 187 .await
198 } 188 }
199 189
200 fn inner_blocking_read(&self, buf: &mut [u8]) -> Result<usize, Error> { 190 fn blocking_read(&self, buf: &mut [u8]) -> Result<usize, Error> {
201 loop { 191 loop {
202 let mut do_pend = false; 192 let state = T::buffered_state();
203 let mut inner = self.inner.borrow_mut(); 193 let mut rx_reader = unsafe { state.rx_buf.reader() };
204 let n = inner.with(|state| { 194 let data = rx_reader.pop_slice();
205 compiler_fence(Ordering::SeqCst);
206
207 // We have data ready in buffer? Return it.
208 let data = state.rx.pop_buf();
209 if !data.is_empty() {
210 let len = data.len().min(buf.len());
211 buf[..len].copy_from_slice(&data[..len]);
212
213 if state.rx.is_full() {
214 do_pend = true;
215 }
216 state.rx.pop(len);
217
218 return len;
219 }
220 195
221 0 196 if !data.is_empty() {
222 }); 197 let len = data.len().min(buf.len());
198 buf[..len].copy_from_slice(&data[..len]);
199
200 let do_pend = state.rx_buf.is_full();
201 rx_reader.pop_done(len);
202
203 if do_pend {
204 unsafe { T::Interrupt::steal().pend() };
205 }
223 206
224 if do_pend { 207 return Ok(len);
225 inner.pend();
226 } 208 }
209 }
210 }
227 211
228 if n > 0 { 212 async fn fill_buf(&self) -> Result<&[u8], Error> {
229 return Ok(n); 213 poll_fn(move |cx| {
214 let state = T::buffered_state();
215 let mut rx_reader = unsafe { state.rx_buf.reader() };
216 let (p, n) = rx_reader.pop_buf();
217 if n == 0 {
218 state.rx_waker.register(cx.waker());
219 return Poll::Pending;
230 } 220 }
221
222 let buf = unsafe { slice::from_raw_parts(p, n) };
223 Poll::Ready(Ok(buf))
224 })
225 .await
226 }
227
228 fn consume(&self, amt: usize) {
229 let state = T::buffered_state();
230 let mut rx_reader = unsafe { state.rx_buf.reader() };
231 let full = state.rx_buf.is_full();
232 rx_reader.pop_done(amt);
233 if full {
234 unsafe { T::Interrupt::steal().pend() };
231 } 235 }
232 } 236 }
237}
233 238
234 async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, Error> { 239impl<'d, T: BasicInstance> BufferedUartTx<'d, T> {
240 async fn write(&self, buf: &[u8]) -> Result<usize, Error> {
235 poll_fn(move |cx| { 241 poll_fn(move |cx| {
236 let mut inner = self.inner.borrow_mut(); 242 let state = T::buffered_state();
237 let (poll, empty) = inner.with(|state| { 243 let empty = state.tx_buf.is_empty();
238 let empty = state.tx.is_empty(); 244
239 let tx_buf = state.tx.push_buf(); 245 let mut tx_writer = unsafe { state.tx_buf.writer() };
240 if tx_buf.is_empty() { 246 let data = tx_writer.push_slice();
241 state.tx_waker.register(cx.waker()); 247 if data.is_empty() {
242 return (Poll::Pending, empty); 248 state.tx_waker.register(cx.waker());
243 } 249 return Poll::Pending;
250 }
244 251
245 let n = core::cmp::min(tx_buf.len(), buf.len()); 252 let n = data.len().min(buf.len());
246 tx_buf[..n].copy_from_slice(&buf[..n]); 253 data[..n].copy_from_slice(&buf[..n]);
247 state.tx.push(n); 254 tx_writer.push_done(n);
248 255
249 (Poll::Ready(Ok(n)), empty)
250 });
251 if empty { 256 if empty {
252 inner.pend(); 257 unsafe { T::Interrupt::steal() }.pend();
253 } 258 }
254 poll 259
260 Poll::Ready(Ok(n))
255 }) 261 })
256 .await 262 .await
257 } 263 }
258 264
259 async fn inner_flush<'a>(&'a self) -> Result<(), Error> { 265 async fn flush(&self) -> Result<(), Error> {
260 poll_fn(move |cx| { 266 poll_fn(move |cx| {
261 self.inner.borrow_mut().with(|state| { 267 let state = T::buffered_state();
262 if !state.tx.is_empty() { 268 if !state.tx_buf.is_empty() {
263 state.tx_waker.register(cx.waker()); 269 state.tx_waker.register(cx.waker());
264 return Poll::Pending; 270 return Poll::Pending;
265 } 271 }
266 272
267 Poll::Ready(Ok(())) 273 Poll::Ready(Ok(()))
268 })
269 }) 274 })
270 .await 275 .await
271 } 276 }
272 277
273 fn inner_blocking_write(&self, buf: &[u8]) -> Result<usize, Error> { 278 fn blocking_write(&self, buf: &[u8]) -> Result<usize, Error> {
274 loop { 279 loop {
275 let mut inner = self.inner.borrow_mut(); 280 let state = T::buffered_state();
276 let (n, empty) = inner.with(|state| { 281 let empty = state.tx_buf.is_empty();
277 let empty = state.tx.is_empty(); 282
278 let tx_buf = state.tx.push_buf(); 283 let mut tx_writer = unsafe { state.tx_buf.writer() };
279 if tx_buf.is_empty() { 284 let data = tx_writer.push_slice();
280 return (0, empty); 285 if !data.is_empty() {
286 let n = data.len().min(buf.len());
287 data[..n].copy_from_slice(&buf[..n]);
288 tx_writer.push_done(n);
289
290 if empty {
291 unsafe { T::Interrupt::steal() }.pend();
281 } 292 }
282 293
283 let n = core::cmp::min(tx_buf.len(), buf.len());
284 tx_buf[..n].copy_from_slice(&buf[..n]);
285 state.tx.push(n);
286
287 (n, empty)
288 });
289 if empty {
290 inner.pend();
291 }
292 if n != 0 {
293 return Ok(n); 294 return Ok(n);
294 } 295 }
295 } 296 }
296 } 297 }
297 298
298 fn inner_blocking_flush(&self) -> Result<(), Error> { 299 fn blocking_flush(&self) -> Result<(), Error> {
299 loop { 300 loop {
300 if !self.inner.borrow_mut().with(|state| state.tx.is_empty()) { 301 let state = T::buffered_state();
302 if state.tx_buf.is_empty() {
301 return Ok(()); 303 return Ok(());
302 } 304 }
303 } 305 }
304 } 306 }
307}
305 308
306 async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> { 309impl<'d, T: BasicInstance> Drop for BufferedUartRx<'d, T> {
307 poll_fn(move |cx| { 310 fn drop(&mut self) {
308 self.inner.borrow_mut().with(|state| { 311 let state = T::buffered_state();
309 compiler_fence(Ordering::SeqCst); 312 unsafe {
310 313 state.rx_buf.deinit();
311 // We have data ready in buffer? Return it.
312 let buf = state.rx.pop_buf();
313 if !buf.is_empty() {
314 let buf: &[u8] = buf;
315 // Safety: buffer lives as long as uart
316 let buf: &[u8] = unsafe { core::mem::transmute(buf) };
317 return Poll::Ready(Ok(buf));
318 }
319
320 state.rx_waker.register(cx.waker());
321 Poll::<Result<&[u8], Error>>::Pending
322 })
323 })
324 .await
325 }
326 314
327 fn inner_consume(&self, amt: usize) { 315 // TX is inactive if the the buffer is not available.
328 let mut inner = self.inner.borrow_mut(); 316 // We can now unregister the interrupt handler
329 let signal = inner.with(|state| { 317 if state.tx_buf.len() == 0 {
330 let full = state.rx.is_full(); 318 T::Interrupt::steal().disable();
331 state.rx.pop(amt); 319 }
332 full
333 });
334 if signal {
335 inner.pend();
336 } 320 }
337 } 321 }
338} 322}
339 323
340impl<'d, T: BasicInstance> StateInner<'d, T> 324impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> {
341where 325 fn drop(&mut self) {
342 Self: 'd, 326 let state = T::buffered_state();
343{
344 fn on_rx(&mut self) {
345 let r = T::regs();
346 unsafe { 327 unsafe {
347 let sr = sr(r).read(); 328 state.tx_buf.deinit();
348 clear_interrupt_flags(r, sr);
349 329
350 // This read also clears the error and idle interrupt flags on v1. 330 // RX is inactive if the the buffer is not available.
351 let b = rdr(r).read_volatile(); 331 // We can now unregister the interrupt handler
332 if state.rx_buf.len() == 0 {
333 T::Interrupt::steal().disable();
334 }
335 }
336 }
337}
352 338
353 if sr.rxne() { 339unsafe fn on_interrupt<T: BasicInstance>(_: *mut ()) {
354 if sr.pe() { 340 let r = T::regs();
355 warn!("Parity error"); 341 let state = T::buffered_state();
356 }
357 if sr.fe() {
358 warn!("Framing error");
359 }
360 if sr.ne() {
361 warn!("Noise error");
362 }
363 if sr.ore() {
364 warn!("Overrun error");
365 }
366 342
367 let buf = self.rx.push_buf(); 343 // RX
368 if !buf.is_empty() { 344 unsafe {
369 buf[0] = b; 345 let sr = sr(r).read();
370 self.rx.push(1); 346 clear_interrupt_flags(r, sr);
371 } else {
372 warn!("RX buffer full, discard received byte");
373 }
374 347
375 if self.rx.is_full() { 348 if sr.rxne() {
376 self.rx_waker.wake(); 349 if sr.pe() {
377 } 350 warn!("Parity error");
351 }
352 if sr.fe() {
353 warn!("Framing error");
354 }
355 if sr.ne() {
356 warn!("Noise error");
357 }
358 if sr.ore() {
359 warn!("Overrun error");
378 } 360 }
379 361
380 if sr.idle() { 362 let mut rx_writer = state.rx_buf.writer();
381 self.rx_waker.wake(); 363 let buf = rx_writer.push_slice();
382 }; 364 if !buf.is_empty() {
383 } 365 // This read also clears the error and idle interrupt flags on v1.
384 } 366 buf[0] = rdr(r).read_volatile();
367 rx_writer.push_done(1);
368 } else {
369 // FIXME: Should we disable any further RX interrupts when the buffer becomes full.
370 }
385 371
386 fn on_tx(&mut self) { 372 if state.rx_buf.is_full() {
387 let r = T::regs(); 373 state.rx_waker.wake();
388 unsafe {
389 if sr(r).read().txe() {
390 let buf = self.tx.pop_buf();
391 if !buf.is_empty() {
392 r.cr1().modify(|w| {
393 w.set_txeie(true);
394 });
395 tdr(r).write_volatile(buf[0].into());
396 self.tx.pop(1);
397 self.tx_waker.wake();
398 } else {
399 // Disable interrupt until we have something to transmit again
400 r.cr1().modify(|w| {
401 w.set_txeie(false);
402 });
403 }
404 } 374 }
405 } 375 }
406 }
407}
408 376
409impl<'d, T: BasicInstance> PeripheralState for StateInner<'d, T> 377 if sr.idle() {
410where 378 state.rx_waker.wake();
411 Self: 'd, 379 };
412{ 380 }
413 type Interrupt = T::Interrupt; 381
414 fn on_interrupt(&mut self) { 382 // TX
415 self.on_rx(); 383 unsafe {
416 self.on_tx(); 384 if sr(r).read().txe() {
385 let mut tx_reader = state.tx_buf.reader();
386 let buf = tx_reader.pop_slice();
387 if !buf.is_empty() {
388 r.cr1().modify(|w| {
389 w.set_txeie(true);
390 });
391 tdr(r).write_volatile(buf[0].into());
392 tx_reader.pop_done(1);
393 state.tx_waker.wake();
394 } else {
395 // Disable interrupt until we have something to transmit again
396 r.cr1().modify(|w| {
397 w.set_txeie(false);
398 });
399 }
400 }
417 } 401 }
418} 402}
419 403
@@ -427,94 +411,284 @@ impl<'d, T: BasicInstance> embedded_io::Io for BufferedUart<'d, T> {
427 type Error = Error; 411 type Error = Error;
428} 412}
429 413
430impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartRx<'u, 'd, T> { 414impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartRx<'d, T> {
431 type Error = Error; 415 type Error = Error;
432} 416}
433 417
434impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> { 418impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartTx<'d, T> {
435 type Error = Error; 419 type Error = Error;
436} 420}
437 421
438impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { 422impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> {
439 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 423 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
440 self.inner_read(buf).await 424 self.rx.read(buf).await
441 } 425 }
442} 426}
443 427
444impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { 428impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'d, T> {
445 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 429 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
446 self.inner.inner_read(buf).await 430 Self::read(self, buf).await
447 } 431 }
448} 432}
449 433
450impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { 434impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> {
451 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 435 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
452 self.inner_fill_buf().await 436 self.rx.fill_buf().await
453 } 437 }
454 438
455 fn consume(&mut self, amt: usize) { 439 fn consume(&mut self, amt: usize) {
456 self.inner_consume(amt) 440 self.rx.consume(amt)
457 } 441 }
458} 442}
459 443
460impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> { 444impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> {
461 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 445 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
462 self.inner.inner_fill_buf().await 446 Self::fill_buf(self).await
463 } 447 }
464 448
465 fn consume(&mut self, amt: usize) { 449 fn consume(&mut self, amt: usize) {
466 self.inner.inner_consume(amt) 450 Self::consume(self, amt)
467 } 451 }
468} 452}
469 453
470impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { 454impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> {
471 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 455 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
472 self.inner_write(buf).await 456 self.tx.write(buf).await
473 } 457 }
474 458
475 async fn flush(&mut self) -> Result<(), Self::Error> { 459 async fn flush(&mut self) -> Result<(), Self::Error> {
476 self.inner_flush().await 460 self.tx.flush().await
477 } 461 }
478} 462}
479 463
480impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { 464impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'d, T> {
481 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 465 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
482 self.inner.inner_write(buf).await 466 Self::write(self, buf).await
483 } 467 }
484 468
485 async fn flush(&mut self) -> Result<(), Self::Error> { 469 async fn flush(&mut self) -> Result<(), Self::Error> {
486 self.inner.inner_flush().await 470 Self::flush(self).await
487 } 471 }
488} 472}
489 473
490impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> { 474impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> {
491 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 475 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
492 self.inner_blocking_read(buf) 476 self.rx.blocking_read(buf)
493 } 477 }
494} 478}
495 479
496impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'u, 'd, T> { 480impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'d, T> {
497 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 481 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
498 self.inner.inner_blocking_read(buf) 482 self.blocking_read(buf)
499 } 483 }
500} 484}
501 485
502impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> { 486impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> {
503 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 487 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
504 self.inner_blocking_write(buf) 488 self.tx.blocking_write(buf)
505 } 489 }
506 490
507 fn flush(&mut self) -> Result<(), Self::Error> { 491 fn flush(&mut self) -> Result<(), Self::Error> {
508 self.inner_blocking_flush() 492 self.tx.blocking_flush()
509 } 493 }
510} 494}
511 495
512impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'u, 'd, T> { 496impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'d, T> {
513 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 497 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
514 self.inner.inner_blocking_write(buf) 498 Self::blocking_write(self, buf)
515 } 499 }
516 500
517 fn flush(&mut self) -> Result<(), Self::Error> { 501 fn flush(&mut self) -> Result<(), Self::Error> {
518 self.inner.inner_blocking_flush() 502 Self::blocking_flush(self)
503 }
504}
505
506mod eh02 {
507 use super::*;
508
509 impl<'d, T: BasicInstance> embedded_hal_02::serial::Read<u8> for BufferedUartRx<'d, T> {
510 type Error = Error;
511
512 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
513 let r = T::regs();
514 unsafe {
515 let sr = sr(r).read();
516 if sr.pe() {
517 rdr(r).read_volatile();
518 Err(nb::Error::Other(Error::Parity))
519 } else if sr.fe() {
520 rdr(r).read_volatile();
521 Err(nb::Error::Other(Error::Framing))
522 } else if sr.ne() {
523 rdr(r).read_volatile();
524 Err(nb::Error::Other(Error::Noise))
525 } else if sr.ore() {
526 rdr(r).read_volatile();
527 Err(nb::Error::Other(Error::Overrun))
528 } else if sr.rxne() {
529 Ok(rdr(r).read_volatile())
530 } else {
531 Err(nb::Error::WouldBlock)
532 }
533 }
534 }
535 }
536
537 impl<'d, T: BasicInstance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUartTx<'d, T> {
538 type Error = Error;
539
540 fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {
541 while !buffer.is_empty() {
542 match self.blocking_write(buffer) {
543 Ok(0) => panic!("zero-length write."),
544 Ok(n) => buffer = &buffer[n..],
545 Err(e) => return Err(e),
546 }
547 }
548 Ok(())
549 }
550
551 fn bflush(&mut self) -> Result<(), Self::Error> {
552 self.blocking_flush()
553 }
554 }
555
556 impl<'d, T: BasicInstance> embedded_hal_02::serial::Read<u8> for BufferedUart<'d, T> {
557 type Error = Error;
558
559 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
560 embedded_hal_02::serial::Read::read(&mut self.rx)
561 }
562 }
563
564 impl<'d, T: BasicInstance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUart<'d, T> {
565 type Error = Error;
566
567 fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {
568 while !buffer.is_empty() {
569 match self.tx.blocking_write(buffer) {
570 Ok(0) => panic!("zero-length write."),
571 Ok(n) => buffer = &buffer[n..],
572 Err(e) => return Err(e),
573 }
574 }
575 Ok(())
576 }
577
578 fn bflush(&mut self) -> Result<(), Self::Error> {
579 self.tx.blocking_flush()
580 }
581 }
582}
583
584#[cfg(feature = "unstable-traits")]
585mod eh1 {
586 use super::*;
587
588 impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> {
589 type Error = Error;
590 }
591
592 impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> {
593 type Error = Error;
594 }
595
596 impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> {
597 type Error = Error;
598 }
599
600 impl<'d, T: BasicInstance> embedded_hal_nb::serial::Read for BufferedUartRx<'d, T> {
601 fn read(&mut self) -> nb::Result<u8, Self::Error> {
602 embedded_hal_02::serial::Read::read(self)
603 }
604 }
605
606 impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> {
607 fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
608 self.blocking_write(buffer).map(drop)
609 }
610
611 fn flush(&mut self) -> Result<(), Self::Error> {
612 self.blocking_flush()
613 }
614 }
615
616 impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> {
617 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
618 self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)
619 }
620
621 fn flush(&mut self) -> nb::Result<(), Self::Error> {
622 self.blocking_flush().map_err(nb::Error::Other)
623 }
624 }
625
626 impl<'d, T: BasicInstance> embedded_hal_nb::serial::Read for BufferedUart<'d, T> {
627 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
628 embedded_hal_02::serial::Read::read(&mut self.rx)
629 }
630 }
631
632 impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUart<'d, T> {
633 fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
634 self.tx.blocking_write(buffer).map(drop)
635 }
636
637 fn flush(&mut self) -> Result<(), Self::Error> {
638 self.tx.blocking_flush()
639 }
640 }
641
642 impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> {
643 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
644 self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)
645 }
646
647 fn flush(&mut self) -> nb::Result<(), Self::Error> {
648 self.tx.blocking_flush().map_err(nb::Error::Other)
649 }
650 }
651}
652
653#[cfg(all(
654 feature = "unstable-traits",
655 feature = "nightly",
656 feature = "_todo_embedded_hal_serial"
657))]
658mod eha {
659 use core::future::Future;
660
661 use super::*;
662
663 impl<'d, T: BasicInstance> embedded_hal_async::serial::Write for BufferedUartTx<'d, T> {
664 async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
665 Self::write(buf)
666 }
667
668 async fn flush(&mut self) -> Result<(), Self::Error> {
669 Self::flush()
670 }
671 }
672
673 impl<'d, T: BasicInstance> embedded_hal_async::serial::Read for BufferedUartRx<'d, T> {
674 async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
675 Self::read(buf)
676 }
677 }
678
679 impl<'d, T: BasicInstance> embedded_hal_async::serial::Write for BufferedUart<'d, T> {
680 async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
681 self.tx.write(buf)
682 }
683
684 async fn flush(&mut self) -> Result<(), Self::Error> {
685 self.tx.flush()
686 }
687 }
688
689 impl<'d, T: BasicInstance> embedded_hal_async::serial::Read for BufferedUart<'d, T> {
690 async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
691 self.rx.read(buf)
692 }
519 } 693 }
520} 694}
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index f80323e37..a42eede18 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -1112,6 +1112,9 @@ pub(crate) mod sealed {
1112 1112
1113 fn regs() -> Regs; 1113 fn regs() -> Regs;
1114 fn state() -> &'static State; 1114 fn state() -> &'static State;
1115
1116 #[cfg(feature = "nightly")]
1117 fn buffered_state() -> &'static buffered::State;
1115 } 1118 }
1116 1119
1117 pub trait FullInstance: BasicInstance { 1120 pub trait FullInstance: BasicInstance {
@@ -1147,6 +1150,12 @@ macro_rules! impl_lpuart {
1147 static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new(); 1150 static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new();
1148 &STATE 1151 &STATE
1149 } 1152 }
1153
1154 #[cfg(feature = "nightly")]
1155 fn buffered_state() -> &'static buffered::State {
1156 static STATE: buffered::State = buffered::State::new();
1157 &STATE
1158 }
1150 } 1159 }
1151 1160
1152 impl BasicInstance for peripherals::$inst {} 1161 impl BasicInstance for peripherals::$inst {}
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs
index 0355c5f14..ad68eaba2 100644
--- a/embassy-stm32/src/usb/usb.rs
+++ b/embassy-stm32/src/usb/usb.rs
@@ -12,22 +12,29 @@ use embassy_usb_driver as driver;
12use embassy_usb_driver::{ 12use embassy_usb_driver::{
13 Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, 13 Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported,
14}; 14};
15use pac::common::{Reg, RW};
16use pac::usb::vals::{EpType, Stat};
17 15
18use super::{DmPin, DpPin, Instance}; 16use super::{DmPin, DpPin, Instance};
19use crate::gpio::sealed::AFType; 17use crate::gpio::sealed::AFType;
20use crate::interrupt::InterruptExt; 18use crate::interrupt::InterruptExt;
21use crate::pac::usb::regs; 19use crate::pac::usb::regs;
20use crate::pac::usb::vals::{EpType, Stat};
21use crate::pac::USBRAM;
22use crate::rcc::sealed::RccPeripheral; 22use crate::rcc::sealed::RccPeripheral;
23use crate::{pac, Peripheral}; 23use crate::Peripheral;
24 24
25const EP_COUNT: usize = 8; 25const EP_COUNT: usize = 8;
26 26
27#[cfg(any(usb_v1_x1, usb_v1_x2))] 27#[cfg(any(usbram_16x1_512, usbram_16x2_512))]
28const EP_MEMORY_SIZE: usize = 512; 28const USBRAM_SIZE: usize = 512;
29#[cfg(not(any(usb_v1_x1, usb_v1_x2)))] 29#[cfg(usbram_16x2_1024)]
30const EP_MEMORY_SIZE: usize = 1024; 30const USBRAM_SIZE: usize = 1024;
31#[cfg(usbram_32_2048)]
32const USBRAM_SIZE: usize = 2048;
33
34#[cfg(not(usbram_32_2048))]
35const USBRAM_ALIGN: usize = 2;
36#[cfg(usbram_32_2048)]
37const USBRAM_ALIGN: usize = 4;
31 38
32const NEW_AW: AtomicWaker = AtomicWaker::new(); 39const NEW_AW: AtomicWaker = AtomicWaker::new();
33static BUS_WAKER: AtomicWaker = NEW_AW; 40static BUS_WAKER: AtomicWaker = NEW_AW;
@@ -57,25 +64,60 @@ fn invariant(mut r: regs::Epr) -> regs::Epr {
57 r 64 r
58} 65}
59 66
67fn align_len_up(len: u16) -> u16 {
68 ((len as usize + USBRAM_ALIGN - 1) / USBRAM_ALIGN * USBRAM_ALIGN) as u16
69}
70
60// Returns (actual_len, len_bits) 71// Returns (actual_len, len_bits)
61fn calc_out_len(len: u16) -> (u16, u16) { 72fn calc_out_len(len: u16) -> (u16, u16) {
62 match len { 73 match len {
63 2..=62 => ((len + 1) / 2 * 2, ((len + 1) / 2) << 10), 74 // NOTE: this could be 2..=62 with 16bit USBRAM, but not with 32bit. Limit it to 60 for simplicity.
64 63..=480 => ((len + 31) / 32 * 32, (((len + 31) / 32 - 1) << 10) | 0x8000), 75 2..=60 => (align_len_up(len), align_len_up(len) / 2 << 10),
76 61..=1024 => ((len + 31) / 32 * 32, (((len + 31) / 32 - 1) << 10) | 0x8000),
65 _ => panic!("invalid OUT length {}", len), 77 _ => panic!("invalid OUT length {}", len),
66 } 78 }
67} 79}
68fn ep_in_addr<T: Instance>(index: usize) -> Reg<u16, RW> { 80
69 T::regs().ep_mem(index * 4 + 0) 81#[cfg(not(usbram_32_2048))]
70} 82mod btable {
71fn ep_in_len<T: Instance>(index: usize) -> Reg<u16, RW> { 83 use super::*;
72 T::regs().ep_mem(index * 4 + 1) 84
73} 85 pub(super) unsafe fn write_in<T: Instance>(index: usize, addr: u16) {
74fn ep_out_addr<T: Instance>(index: usize) -> Reg<u16, RW> { 86 USBRAM.mem(index * 4 + 0).write_value(addr);
75 T::regs().ep_mem(index * 4 + 2) 87 }
88
89 pub(super) unsafe fn write_in_len<T: Instance>(index: usize, _addr: u16, len: u16) {
90 USBRAM.mem(index * 4 + 1).write_value(len);
91 }
92
93 pub(super) unsafe fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
94 USBRAM.mem(index * 4 + 2).write_value(addr);
95 USBRAM.mem(index * 4 + 3).write_value(max_len_bits);
96 }
97
98 pub(super) unsafe fn read_out_len<T: Instance>(index: usize) -> u16 {
99 USBRAM.mem(index * 4 + 3).read()
100 }
76} 101}
77fn ep_out_len<T: Instance>(index: usize) -> Reg<u16, RW> { 102#[cfg(usbram_32_2048)]
78 T::regs().ep_mem(index * 4 + 3) 103mod btable {
104 use super::*;
105
106 pub(super) unsafe fn write_in<T: Instance>(_index: usize, _addr: u16) {}
107
108 pub(super) unsafe fn write_in_len<T: Instance>(index: usize, addr: u16, len: u16) {
109 USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16));
110 }
111
112 pub(super) unsafe fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
113 USBRAM
114 .mem(index * 2 + 1)
115 .write_value((addr as u32) | ((max_len_bits as u32) << 16));
116 }
117
118 pub(super) unsafe fn read_out_len<T: Instance>(index: usize) -> u16 {
119 (USBRAM.mem(index * 2 + 1).read() >> 16) as u16
120 }
79} 121}
80 122
81struct EndpointBuffer<T: Instance> { 123struct EndpointBuffer<T: Instance> {
@@ -87,23 +129,25 @@ struct EndpointBuffer<T: Instance> {
87impl<T: Instance> EndpointBuffer<T> { 129impl<T: Instance> EndpointBuffer<T> {
88 fn read(&mut self, buf: &mut [u8]) { 130 fn read(&mut self, buf: &mut [u8]) {
89 assert!(buf.len() <= self.len as usize); 131 assert!(buf.len() <= self.len as usize);
90 for i in 0..((buf.len() + 1) / 2) { 132 for i in 0..(buf.len() + USBRAM_ALIGN - 1) / USBRAM_ALIGN {
91 let val = unsafe { T::regs().ep_mem(self.addr as usize / 2 + i).read() }; 133 let val = unsafe { USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).read() };
92 buf[i * 2] = val as u8; 134 let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN);
93 if i * 2 + 1 < buf.len() { 135 buf[i * USBRAM_ALIGN..][..n].copy_from_slice(&val.to_le_bytes()[..n]);
94 buf[i * 2 + 1] = (val >> 8) as u8;
95 }
96 } 136 }
97 } 137 }
98 138
99 fn write(&mut self, buf: &[u8]) { 139 fn write(&mut self, buf: &[u8]) {
100 assert!(buf.len() <= self.len as usize); 140 assert!(buf.len() <= self.len as usize);
101 for i in 0..((buf.len() + 1) / 2) { 141 for i in 0..(buf.len() + USBRAM_ALIGN - 1) / USBRAM_ALIGN {
102 let mut val = buf[i * 2] as u16; 142 let mut val = [0u8; USBRAM_ALIGN];
103 if i * 2 + 1 < buf.len() { 143 let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN);
104 val |= (buf[i * 2 + 1] as u16) << 8; 144 val[..n].copy_from_slice(&buf[i * USBRAM_ALIGN..][..n]);
105 } 145
106 unsafe { T::regs().ep_mem(self.addr as usize / 2 + i).write_value(val) }; 146 #[cfg(not(usbram_32_2048))]
147 let val = u16::from_le_bytes(val);
148 #[cfg(usbram_32_2048)]
149 let val = u32::from_le_bytes(val);
150 unsafe { USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val) };
107 } 151 }
108 } 152 }
109} 153}
@@ -139,8 +183,12 @@ impl<'d, T: Instance> Driver<'d, T> {
139 #[cfg(stm32l5)] 183 #[cfg(stm32l5)]
140 unsafe { 184 unsafe {
141 crate::peripherals::PWR::enable(); 185 crate::peripherals::PWR::enable();
186 crate::pac::PWR.cr2().modify(|w| w.set_usv(true));
187 }
142 188
143 pac::PWR.cr2().modify(|w| w.set_usv(true)); 189 #[cfg(pwr_h5)]
190 unsafe {
191 crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true))
144 } 192 }
145 193
146 unsafe { 194 unsafe {
@@ -256,8 +304,9 @@ impl<'d, T: Instance> Driver<'d, T> {
256 } 304 }
257 305
258 fn alloc_ep_mem(&mut self, len: u16) -> u16 { 306 fn alloc_ep_mem(&mut self, len: u16) -> u16 {
307 assert!(len as usize % USBRAM_ALIGN == 0);
259 let addr = self.ep_mem_free; 308 let addr = self.ep_mem_free;
260 if addr + len > EP_MEMORY_SIZE as _ { 309 if addr + len > USBRAM_SIZE as _ {
261 panic!("Endpoint memory full"); 310 panic!("Endpoint memory full");
262 } 311 }
263 self.ep_mem_free += len; 312 self.ep_mem_free += len;
@@ -306,10 +355,7 @@ impl<'d, T: Instance> Driver<'d, T> {
306 let addr = self.alloc_ep_mem(len); 355 let addr = self.alloc_ep_mem(len);
307 356
308 trace!(" len_bits = {:04x}", len_bits); 357 trace!(" len_bits = {:04x}", len_bits);
309 unsafe { 358 unsafe { btable::write_out::<T>(index, addr, len_bits) }
310 ep_out_addr::<T>(index).write_value(addr);
311 ep_out_len::<T>(index).write_value(len_bits);
312 }
313 359
314 EndpointBuffer { 360 EndpointBuffer {
315 addr, 361 addr,
@@ -321,13 +367,11 @@ impl<'d, T: Instance> Driver<'d, T> {
321 assert!(!ep.used_in); 367 assert!(!ep.used_in);
322 ep.used_in = true; 368 ep.used_in = true;
323 369
324 let len = (max_packet_size + 1) / 2 * 2; 370 let len = align_len_up(max_packet_size);
325 let addr = self.alloc_ep_mem(len); 371 let addr = self.alloc_ep_mem(len);
326 372
327 unsafe { 373 // ep_in_len is written when actually TXing packets.
328 ep_in_addr::<T>(index).write_value(addr); 374 unsafe { btable::write_in::<T>(index, addr) }
329 // ep_in_len is written when actually TXing packets.
330 }
331 375
332 EndpointBuffer { 376 EndpointBuffer {
333 addr, 377 addr,
@@ -398,7 +442,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
398 w.set_ctrm(true); 442 w.set_ctrm(true);
399 }); 443 });
400 444
401 #[cfg(usb_v3)] 445 #[cfg(any(usb_v3, usb_v4))]
402 regs.bcdr().write(|w| w.set_dppu(true)) 446 regs.bcdr().write(|w| w.set_dppu(true))
403 } 447 }
404 448
@@ -633,12 +677,12 @@ impl<'d, T: Instance, D> Endpoint<'d, T, D> {
633 fn write_data(&mut self, buf: &[u8]) { 677 fn write_data(&mut self, buf: &[u8]) {
634 let index = self.info.addr.index(); 678 let index = self.info.addr.index();
635 self.buf.write(buf); 679 self.buf.write(buf);
636 unsafe { ep_in_len::<T>(index).write_value(buf.len() as _) }; 680 unsafe { btable::write_in_len::<T>(index, self.buf.addr, buf.len() as _) }
637 } 681 }
638 682
639 fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> { 683 fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
640 let index = self.info.addr.index(); 684 let index = self.info.addr.index();
641 let rx_len = unsafe { ep_out_len::<T>(index).read() as usize } & 0x3FF; 685 let rx_len = unsafe { btable::read_out_len::<T>(index) as usize } & 0x3FF;
642 trace!("READ DONE, rx_len = {}", rx_len); 686 trace!("READ DONE, rx_len = {}", rx_len);
643 if rx_len > buf.len() { 687 if rx_len > buf.len() {
644 return Err(EndpointError::BufferOverflow); 688 return Err(EndpointError::BufferOverflow);
diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs
index 84fef78cb..193e0df0d 100644
--- a/embassy-stm32/src/usb_otg/mod.rs
+++ b/embassy-stm32/src/usb_otg/mod.rs
@@ -89,6 +89,9 @@ foreach_interrupt!(
89 } else if #[cfg(stm32h7)] { 89 } else if #[cfg(stm32h7)] {
90 const FIFO_DEPTH_WORDS: u16 = 1024; 90 const FIFO_DEPTH_WORDS: u16 = 1024;
91 const ENDPOINT_COUNT: usize = 9; 91 const ENDPOINT_COUNT: usize = 9;
92 } else if #[cfg(stm32u5)] {
93 const FIFO_DEPTH_WORDS: u16 = 320;
94 const ENDPOINT_COUNT: usize = 6;
92 } else { 95 } else {
93 compile_error!("USB_OTG_FS peripheral is not supported by this chip."); 96 compile_error!("USB_OTG_FS peripheral is not supported by this chip.");
94 } 97 }
@@ -137,6 +140,9 @@ foreach_interrupt!(
137 ))] { 140 ))] {
138 const FIFO_DEPTH_WORDS: u16 = 1024; 141 const FIFO_DEPTH_WORDS: u16 = 1024;
139 const ENDPOINT_COUNT: usize = 9; 142 const ENDPOINT_COUNT: usize = 9;
143 } else if #[cfg(stm32u5)] {
144 const FIFO_DEPTH_WORDS: u16 = 1024;
145 const ENDPOINT_COUNT: usize = 9;
140 } else { 146 } else {
141 compile_error!("USB_OTG_HS peripheral is not supported by this chip."); 147 compile_error!("USB_OTG_HS peripheral is not supported by this chip.");
142 } 148 }
diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml
index 7b5d3ce48..e4871e718 100644
--- a/embassy-sync/Cargo.toml
+++ b/embassy-sync/Cargo.toml
@@ -25,6 +25,7 @@ features = ["nightly"]
25[features] 25[features]
26nightly = ["embedded-io/async"] 26nightly = ["embedded-io/async"]
27std = [] 27std = []
28turbowakers = []
28 29
29[dependencies] 30[dependencies]
30defmt = { version = "0.3", optional = true } 31defmt = { version = "0.3", optional = true }
diff --git a/embassy-sync/src/waitqueue/atomic_waker.rs b/embassy-sync/src/waitqueue/atomic_waker.rs
new file mode 100644
index 000000000..63fe04a6e
--- /dev/null
+++ b/embassy-sync/src/waitqueue/atomic_waker.rs
@@ -0,0 +1,41 @@
1use core::cell::Cell;
2use core::task::Waker;
3
4use crate::blocking_mutex::raw::CriticalSectionRawMutex;
5use crate::blocking_mutex::Mutex;
6
7/// Utility struct to register and wake a waker.
8pub struct AtomicWaker {
9 waker: Mutex<CriticalSectionRawMutex, Cell<Option<Waker>>>,
10}
11
12impl AtomicWaker {
13 /// Create a new `AtomicWaker`.
14 pub const fn new() -> Self {
15 Self {
16 waker: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
17 }
18 }
19
20 /// Register a waker. Overwrites the previous waker, if any.
21 pub fn register(&self, w: &Waker) {
22 critical_section::with(|cs| {
23 let cell = self.waker.borrow(cs);
24 cell.set(match cell.replace(None) {
25 Some(w2) if (w2.will_wake(w)) => Some(w2),
26 _ => Some(w.clone()),
27 })
28 })
29 }
30
31 /// Wake the registered waker, if any.
32 pub fn wake(&self) {
33 critical_section::with(|cs| {
34 let cell = self.waker.borrow(cs);
35 if let Some(w) = cell.replace(None) {
36 w.wake_by_ref();
37 cell.set(Some(w));
38 }
39 })
40 }
41}
diff --git a/embassy-sync/src/waitqueue/atomic_waker_turbo.rs b/embassy-sync/src/waitqueue/atomic_waker_turbo.rs
new file mode 100644
index 000000000..5c6a96ec8
--- /dev/null
+++ b/embassy-sync/src/waitqueue/atomic_waker_turbo.rs
@@ -0,0 +1,30 @@
1use core::ptr;
2use core::ptr::NonNull;
3use core::sync::atomic::{AtomicPtr, Ordering};
4use core::task::Waker;
5
6/// Utility struct to register and wake a waker.
7pub struct AtomicWaker {
8 waker: AtomicPtr<()>,
9}
10
11impl AtomicWaker {
12 /// Create a new `AtomicWaker`.
13 pub const fn new() -> Self {
14 Self {
15 waker: AtomicPtr::new(ptr::null_mut()),
16 }
17 }
18
19 /// Register a waker. Overwrites the previous waker, if any.
20 pub fn register(&self, w: &Waker) {
21 self.waker.store(w.as_turbo_ptr().as_ptr() as _, Ordering::Release);
22 }
23
24 /// Wake the registered waker, if any.
25 pub fn wake(&self) {
26 if let Some(ptr) = NonNull::new(self.waker.load(Ordering::Acquire)) {
27 unsafe { Waker::from_turbo_ptr(ptr) }.wake();
28 }
29 }
30}
diff --git a/embassy-sync/src/waitqueue/mod.rs b/embassy-sync/src/waitqueue/mod.rs
index 6661a6b61..6b0b0c64e 100644
--- a/embassy-sync/src/waitqueue/mod.rs
+++ b/embassy-sync/src/waitqueue/mod.rs
@@ -1,7 +1,11 @@
1//! Async low-level wait queues 1//! Async low-level wait queues
2 2
3mod waker; 3#[cfg_attr(feature = "turbowakers", path = "atomic_waker_turbo.rs")]
4pub use waker::*; 4mod atomic_waker;
5pub use atomic_waker::*;
6
7mod waker_registration;
8pub use waker_registration::*;
5 9
6mod multi_waker; 10mod multi_waker;
7pub use multi_waker::*; 11pub use multi_waker::*;
diff --git a/embassy-sync/src/waitqueue/waker.rs b/embassy-sync/src/waitqueue/waker_registration.rs
index 9ce94a089..9b666e7c4 100644
--- a/embassy-sync/src/waitqueue/waker.rs
+++ b/embassy-sync/src/waitqueue/waker_registration.rs
@@ -1,10 +1,6 @@
1use core::cell::Cell;
2use core::mem; 1use core::mem;
3use core::task::Waker; 2use core::task::Waker;
4 3
5use crate::blocking_mutex::raw::CriticalSectionRawMutex;
6use crate::blocking_mutex::Mutex;
7
8/// Utility struct to register and wake a waker. 4/// Utility struct to register and wake a waker.
9#[derive(Debug, Default)] 5#[derive(Debug, Default)]
10pub struct WakerRegistration { 6pub struct WakerRegistration {
@@ -54,39 +50,3 @@ impl WakerRegistration {
54 self.waker.is_some() 50 self.waker.is_some()
55 } 51 }
56} 52}
57
58/// Utility struct to register and wake a waker.
59pub struct AtomicWaker {
60 waker: Mutex<CriticalSectionRawMutex, Cell<Option<Waker>>>,
61}
62
63impl AtomicWaker {
64 /// Create a new `AtomicWaker`.
65 pub const fn new() -> Self {
66 Self {
67 waker: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
68 }
69 }
70
71 /// Register a waker. Overwrites the previous waker, if any.
72 pub fn register(&self, w: &Waker) {
73 critical_section::with(|cs| {
74 let cell = self.waker.borrow(cs);
75 cell.set(match cell.replace(None) {
76 Some(w2) if (w2.will_wake(w)) => Some(w2),
77 _ => Some(w.clone()),
78 })
79 })
80 }
81
82 /// Wake the registered waker, if any.
83 pub fn wake(&self) {
84 critical_section::with(|cs| {
85 let cell = self.waker.borrow(cs);
86 if let Some(w) = cell.replace(None) {
87 w.wake_by_ref();
88 cell.set(Some(w));
89 }
90 })
91 }
92}
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml
index 5b14814a1..38d31f1c4 100644
--- a/embassy-time/Cargo.toml
+++ b/embassy-time/Cargo.toml
@@ -152,8 +152,8 @@ defmt = { version = "0.3", optional = true }
152log = { version = "0.4.14", optional = true } 152log = { version = "0.4.14", optional = true }
153 153
154embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } 154embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" }
155embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} 155embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
156embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} 156embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
157 157
158futures-util = { version = "0.3.17", default-features = false } 158futures-util = { version = "0.3.17", default-features = false }
159embassy-sync = { version = "0.1", path = "../embassy-sync" } 159embassy-sync = { version = "0.1", path = "../embassy-sync" }
diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs
index 0ca176abd..cf1918724 100644
--- a/embassy-time/src/delay.rs
+++ b/embassy-time/src/delay.rs
@@ -19,14 +19,12 @@ mod eh1 {
19 use super::*; 19 use super::*;
20 20
21 impl embedded_hal_1::delay::DelayUs for Delay { 21 impl embedded_hal_1::delay::DelayUs for Delay {
22 type Error = core::convert::Infallible; 22 fn delay_us(&mut self, us: u32) {
23 23 block_for(Duration::from_micros(us as u64))
24 fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> {
25 Ok(block_for(Duration::from_micros(us as u64)))
26 } 24 }
27 25
28 fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { 26 fn delay_ms(&mut self, ms: u32) {
29 Ok(block_for(Duration::from_millis(ms as u64))) 27 block_for(Duration::from_millis(ms as u64))
30 } 28 }
31 } 29 }
32} 30}
@@ -37,14 +35,12 @@ mod eha {
37 use crate::Timer; 35 use crate::Timer;
38 36
39 impl embedded_hal_async::delay::DelayUs for Delay { 37 impl embedded_hal_async::delay::DelayUs for Delay {
40 type Error = core::convert::Infallible; 38 async fn delay_us(&mut self, micros: u32) {
41 39 Timer::after(Duration::from_micros(micros as _)).await
42 async fn delay_us(&mut self, micros: u32) -> Result<(), Self::Error> {
43 Ok(Timer::after(Duration::from_micros(micros as _)).await)
44 } 40 }
45 41
46 async fn delay_ms(&mut self, millis: u32) -> Result<(), Self::Error> { 42 async fn delay_ms(&mut self, millis: u32) {
47 Ok(Timer::after(Duration::from_millis(millis as _)).await) 43 Timer::after(Duration::from_millis(millis as _)).await
48 } 44 }
49 } 45 }
50} 46}
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml
index 888993255..e75c73cbd 100644
--- a/examples/boot/application/nrf/Cargo.toml
+++ b/examples/boot/application/nrf/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] }
11embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } 11embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] }
12embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" } 12embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" }
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml
index 8d826790b..8de2d2ebd 100644
--- a/examples/boot/application/rp/Cargo.toml
+++ b/examples/boot/application/rp/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] }
11embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] } 11embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] }
12embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp" } 12embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp" }
diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml
index aa279fb76..083607de5 100644
--- a/examples/boot/application/stm32f3/Cargo.toml
+++ b/examples/boot/application/stm32f3/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml
index 1ec0643a6..74f508515 100644
--- a/examples/boot/application/stm32f7/Cargo.toml
+++ b/examples/boot/application/stm32f7/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml
index a4eefe2a5..898b9a47e 100644
--- a/examples/boot/application/stm32h7/Cargo.toml
+++ b/examples/boot/application/stm32h7/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml
index 36eada29b..e142c8481 100644
--- a/examples/boot/application/stm32l0/Cargo.toml
+++ b/examples/boot/application/stm32l0/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml
index 67efda748..f0e92e1ac 100644
--- a/examples/boot/application/stm32l1/Cargo.toml
+++ b/examples/boot/application/stm32l1/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml
index 4b2e02dd2..87689e9a9 100644
--- a/examples/boot/application/stm32l4/Cargo.toml
+++ b/examples/boot/application/stm32l4/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml
index fecbfc51d..a6708bf51 100644
--- a/examples/boot/application/stm32wl/Cargo.toml
+++ b/examples/boot/application/stm32wl/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs
index aca3b857a..8818a23b8 100644
--- a/examples/boot/bootloader/nrf/src/main.rs
+++ b/examples/boot/bootloader/nrf/src/main.rs
@@ -27,9 +27,11 @@ fn main() -> ! {
27 wdt_config.run_during_sleep = true; 27 wdt_config.run_during_sleep = true;
28 wdt_config.run_during_debug_halt = false; 28 wdt_config.run_during_debug_halt = false;
29 29
30 let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( 30 let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::new(WatchdogFlash::start(
31 WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config), 31 Nvmc::new(p.NVMC),
32 ))); 32 p.WDT,
33 wdt_config,
34 ))));
33 unsafe { bl.load(start) } 35 unsafe { bl.load(start) }
34} 36}
35 37
diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs
index fb7f0522b..8129591fa 100644
--- a/examples/boot/bootloader/rp/src/main.rs
+++ b/examples/boot/bootloader/rp/src/main.rs
@@ -5,7 +5,6 @@ use cortex_m_rt::{entry, exception};
5#[cfg(feature = "defmt")] 5#[cfg(feature = "defmt")]
6use defmt_rtt as _; 6use defmt_rtt as _;
7use embassy_boot_rp::*; 7use embassy_boot_rp::*;
8use embassy_rp::flash::ERASE_SIZE;
9use embassy_time::Duration; 8use embassy_time::Duration;
10 9
11const FLASH_SIZE: usize = 2 * 1024 * 1024; 10const FLASH_SIZE: usize = 2 * 1024 * 1024;
@@ -24,7 +23,7 @@ fn main() -> ! {
24 23
25 let mut bl: BootLoader = BootLoader::default(); 24 let mut bl: BootLoader = BootLoader::default();
26 let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); 25 let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8));
27 let mut flash = BootFlash::<_, ERASE_SIZE>::new(flash); 26 let mut flash = BootFlash::new(flash);
28 let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); 27 let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash));
29 core::mem::drop(flash); 28 core::mem::drop(flash);
30 29
diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs
index 4b17cd799..49c21920b 100644
--- a/examples/boot/bootloader/stm32/src/main.rs
+++ b/examples/boot/bootloader/stm32/src/main.rs
@@ -5,7 +5,7 @@ use cortex_m_rt::{entry, exception};
5#[cfg(feature = "defmt")] 5#[cfg(feature = "defmt")]
6use defmt_rtt as _; 6use defmt_rtt as _;
7use embassy_boot_stm32::*; 7use embassy_boot_stm32::*;
8use embassy_stm32::flash::{Flash, ERASE_SIZE, ERASE_VALUE, WRITE_SIZE}; 8use embassy_stm32::flash::Flash;
9 9
10#[entry] 10#[entry]
11fn main() -> ! { 11fn main() -> ! {
@@ -19,9 +19,10 @@ fn main() -> ! {
19 } 19 }
20 */ 20 */
21 21
22 let mut bl: BootLoader<ERASE_SIZE, WRITE_SIZE> = BootLoader::default(); 22 let mut bl: BootLoader<2048> = BootLoader::default();
23 let flash = Flash::new(p.FLASH); 23 let flash = Flash::new(p.FLASH);
24 let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash); 24 let layout = flash.into_regions();
25 let mut flash = BootFlash::new(layout.bank1_region);
25 let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); 26 let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash));
26 core::mem::drop(flash); 27 core::mem::drop(flash);
27 unsafe { bl.load(start) } 28 unsafe { bl.load(start) }
diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml
index d8c24dfad..7910b372a 100644
--- a/examples/nrf-rtos-trace/Cargo.toml
+++ b/examples/nrf-rtos-trace/Cargo.toml
@@ -17,7 +17,7 @@ log = [
17 17
18[dependencies] 18[dependencies]
19embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } 19embassy-sync = { version = "0.1.0", path = "../../embassy-sync" }
20embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features=["rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } 20embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] }
21embassy-time = { version = "0.1.0", path = "../../embassy-time" } 21embassy-time = { version = "0.1.0", path = "../../embassy-time" }
22embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } 22embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
23 23
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index cc88d92c7..3ece24066 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -12,7 +12,7 @@ nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/night
12[dependencies] 12[dependencies]
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
14embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 14embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
15embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 15embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
16embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 16embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
17embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 17embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
18embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } 18embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml
index e88ddf2f7..4134db46f 100644
--- a/examples/nrf5340/Cargo.toml
+++ b/examples/nrf5340/Cargo.toml
@@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [ 9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [
10 "defmt", 10 "defmt",
11] } 11] }
12embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = [ 12embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread",
13 "nightly", 13 "nightly",
14 "defmt", 14 "defmt",
15 "integrated-timers", 15 "integrated-timers",
diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml
index d1c8c1c5a..2ee6fcb00 100644
--- a/examples/rp/.cargo/config.toml
+++ b/examples/rp/.cargo/config.toml
@@ -1,5 +1,5 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2runner = "probe-run --chip RP2040" 2runner = "probe-rs-cli run --chip RP2040"
3 3
4[build] 4[build]
5target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ 5target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 1e8870ed7..63d0ac82a 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -6,8 +6,9 @@ license = "MIT OR Apache-2.0"
6 6
7 7
8[dependencies] 8[dependencies]
9embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 11embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } 13embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] }
13embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
@@ -30,8 +31,8 @@ display-interface = "0.4.1"
30byte-slice-cast = { version = "1.2.0", default-features = false } 31byte-slice-cast = { version = "1.2.0", default-features = false }
31smart-leds = "0.3.0" 32smart-leds = "0.3.0"
32 33
33embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } 34embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
34embedded-hal-async = "0.2.0-alpha.0" 35embedded-hal-async = "0.2.0-alpha.1"
35embedded-io = { version = "0.4.0", features = ["async", "defmt"] } 36embedded-io = { version = "0.4.0", features = ["async", "defmt"] }
36embedded-storage = { version = "0.3" } 37embedded-storage = { version = "0.3" }
37static_cell = "1.0.0" 38static_cell = "1.0.0"
diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs
index 778cad3fa..85a19ce07 100644
--- a/examples/rp/src/bin/spi_display.rs
+++ b/examples/rp/src/bin/spi_display.rs
@@ -5,10 +5,13 @@
5use core::cell::RefCell; 5use core::cell::RefCell;
6 6
7use defmt::*; 7use defmt::*;
8use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
8use embassy_executor::Spawner; 9use embassy_executor::Spawner;
9use embassy_rp::gpio::{Level, Output}; 10use embassy_rp::gpio::{Level, Output};
10use embassy_rp::spi; 11use embassy_rp::spi;
11use embassy_rp::spi::{Blocking, Spi}; 12use embassy_rp::spi::{Blocking, Spi};
13use embassy_sync::blocking_mutex::raw::NoopRawMutex;
14use embassy_sync::blocking_mutex::Mutex;
12use embassy_time::Delay; 15use embassy_time::Delay;
13use embedded_graphics::image::{Image, ImageRawLE}; 16use embedded_graphics::image::{Image, ImageRawLE};
14use embedded_graphics::mono_font::ascii::FONT_10X20; 17use embedded_graphics::mono_font::ascii::FONT_10X20;
@@ -21,10 +24,9 @@ use st7789::{Orientation, ST7789};
21use {defmt_rtt as _, panic_probe as _}; 24use {defmt_rtt as _, panic_probe as _};
22 25
23use crate::my_display_interface::SPIDeviceInterface; 26use crate::my_display_interface::SPIDeviceInterface;
24use crate::shared_spi::SpiDeviceWithCs;
25use crate::touch::Touch; 27use crate::touch::Touch;
26 28
27//const DISPLAY_FREQ: u32 = 64_000_000; 29const DISPLAY_FREQ: u32 = 64_000_000;
28const TOUCH_FREQ: u32 = 200_000; 30const TOUCH_FREQ: u32 = 200_000;
29 31
30#[embassy_executor::main] 32#[embassy_executor::main]
@@ -43,16 +45,20 @@ async fn main(_spawner: Spawner) {
43 //let touch_irq = p.PIN_17; 45 //let touch_irq = p.PIN_17;
44 46
45 // create SPI 47 // create SPI
46 let mut config = spi::Config::default(); 48 let mut display_config = spi::Config::default();
47 config.frequency = TOUCH_FREQ; // use the lowest freq 49 display_config.frequency = DISPLAY_FREQ;
48 config.phase = spi::Phase::CaptureOnSecondTransition; 50 display_config.phase = spi::Phase::CaptureOnSecondTransition;
49 config.polarity = spi::Polarity::IdleHigh; 51 display_config.polarity = spi::Polarity::IdleHigh;
52 let mut touch_config = spi::Config::default();
53 touch_config.frequency = TOUCH_FREQ;
54 touch_config.phase = spi::Phase::CaptureOnSecondTransition;
55 touch_config.polarity = spi::Polarity::IdleHigh;
50 56
51 let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, config); 57 let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone());
52 let spi_bus = RefCell::new(spi); 58 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));
53 59
54 let display_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(display_cs, Level::High)); 60 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);
55 let touch_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(touch_cs, Level::High)); 61 let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config);
56 62
57 let mut touch = Touch::new(touch_spi); 63 let mut touch = Touch::new(touch_spi);
58 64
@@ -104,85 +110,9 @@ async fn main(_spawner: Spawner) {
104 } 110 }
105} 111}
106 112
107mod shared_spi {
108 use core::cell::RefCell;
109 use core::fmt::Debug;
110
111 use embedded_hal_1::digital::OutputPin;
112 use embedded_hal_1::spi;
113 use embedded_hal_1::spi::SpiDevice;
114
115 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
116 pub enum SpiDeviceWithCsError<BUS, CS> {
117 #[allow(unused)] // will probably use in the future when adding a flush() to SpiBus
118 Spi(BUS),
119 Cs(CS),
120 }
121
122 impl<BUS, CS> spi::Error for SpiDeviceWithCsError<BUS, CS>
123 where
124 BUS: spi::Error + Debug,
125 CS: Debug,
126 {
127 fn kind(&self) -> spi::ErrorKind {
128 match self {
129 Self::Spi(e) => e.kind(),
130 Self::Cs(_) => spi::ErrorKind::Other,
131 }
132 }
133 }
134
135 pub struct SpiDeviceWithCs<'a, BUS, CS> {
136 bus: &'a RefCell<BUS>,
137 cs: CS,
138 }
139
140 impl<'a, BUS, CS> SpiDeviceWithCs<'a, BUS, CS> {
141 pub fn new(bus: &'a RefCell<BUS>, cs: CS) -> Self {
142 Self { bus, cs }
143 }
144 }
145
146 impl<'a, BUS, CS> spi::ErrorType for SpiDeviceWithCs<'a, BUS, CS>
147 where
148 BUS: spi::ErrorType,
149 CS: OutputPin,
150 {
151 type Error = SpiDeviceWithCsError<BUS::Error, CS::Error>;
152 }
153
154 impl<'a, BUS, CS> SpiDevice for SpiDeviceWithCs<'a, BUS, CS>
155 where
156 BUS: spi::SpiBusFlush,
157 CS: OutputPin,
158 {
159 type Bus = BUS;
160
161 fn transaction<R>(
162 &mut self,
163 f: impl FnOnce(&mut Self::Bus) -> Result<R, BUS::Error>,
164 ) -> Result<R, Self::Error> {
165 let mut bus = self.bus.borrow_mut();
166 self.cs.set_low().map_err(SpiDeviceWithCsError::Cs)?;
167
168 let f_res = f(&mut bus);
169
170 // On failure, it's important to still flush and deassert CS.
171 let flush_res = bus.flush();
172 let cs_res = self.cs.set_high();
173
174 let f_res = f_res.map_err(SpiDeviceWithCsError::Spi)?;
175 flush_res.map_err(SpiDeviceWithCsError::Spi)?;
176 cs_res.map_err(SpiDeviceWithCsError::Cs)?;
177
178 Ok(f_res)
179 }
180 }
181}
182
183/// Driver for the XPT2046 resistive touchscreen sensor 113/// Driver for the XPT2046 resistive touchscreen sensor
184mod touch { 114mod touch {
185 use embedded_hal_1::spi::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; 115 use embedded_hal_1::spi::{Operation, SpiDevice};
186 116
187 struct Calibration { 117 struct Calibration {
188 x1: i32, 118 x1: i32,
@@ -209,7 +139,6 @@ mod touch {
209 impl<SPI> Touch<SPI> 139 impl<SPI> Touch<SPI>
210 where 140 where
211 SPI: SpiDevice, 141 SPI: SpiDevice,
212 SPI::Bus: SpiBus,
213 { 142 {
214 pub fn new(spi: SPI) -> Self { 143 pub fn new(spi: SPI) -> Self {
215 Self { spi } 144 Self { spi }
@@ -219,13 +148,12 @@ mod touch {
219 let mut x = [0; 2]; 148 let mut x = [0; 2];
220 let mut y = [0; 2]; 149 let mut y = [0; 2];
221 self.spi 150 self.spi
222 .transaction(|bus| { 151 .transaction(&mut [
223 bus.write(&[0x90])?; 152 Operation::Write(&[0x90]),
224 bus.read(&mut x)?; 153 Operation::Read(&mut x),
225 bus.write(&[0xd0])?; 154 Operation::Write(&[0xd0]),
226 bus.read(&mut y)?; 155 Operation::Read(&mut y),
227 Ok(()) 156 ])
228 })
229 .unwrap(); 157 .unwrap();
230 158
231 let x = (u16::from_be_bytes(x) >> 3) as i32; 159 let x = (u16::from_be_bytes(x) >> 3) as i32;
@@ -247,7 +175,7 @@ mod touch {
247mod my_display_interface { 175mod my_display_interface {
248 use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; 176 use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand};
249 use embedded_hal_1::digital::OutputPin; 177 use embedded_hal_1::digital::OutputPin;
250 use embedded_hal_1::spi::{SpiBusWrite, SpiDevice}; 178 use embedded_hal_1::spi::SpiDeviceWrite;
251 179
252 /// SPI display interface. 180 /// SPI display interface.
253 /// 181 ///
@@ -259,8 +187,7 @@ mod my_display_interface {
259 187
260 impl<SPI, DC> SPIDeviceInterface<SPI, DC> 188 impl<SPI, DC> SPIDeviceInterface<SPI, DC>
261 where 189 where
262 SPI: SpiDevice, 190 SPI: SpiDeviceWrite,
263 SPI::Bus: SpiBusWrite,
264 DC: OutputPin, 191 DC: OutputPin,
265 { 192 {
266 /// Create new SPI interface for communciation with a display driver 193 /// Create new SPI interface for communciation with a display driver
@@ -271,42 +198,27 @@ mod my_display_interface {
271 198
272 impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC> 199 impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC>
273 where 200 where
274 SPI: SpiDevice, 201 SPI: SpiDeviceWrite,
275 SPI::Bus: SpiBusWrite,
276 DC: OutputPin, 202 DC: OutputPin,
277 { 203 {
278 fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { 204 fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> {
279 let r = self.spi.transaction(|bus| { 205 // 1 = data, 0 = command
280 // 1 = data, 0 = command 206 self.dc.set_low().map_err(|_| DisplayError::DCError)?;
281 if let Err(_) = self.dc.set_low() {
282 return Ok(Err(DisplayError::DCError));
283 }
284
285 // Send words over SPI
286 send_u8(bus, cmds)?;
287 207
288 Ok(Ok(())) 208 send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?;
289 }); 209 Ok(())
290 r.map_err(|_| DisplayError::BusWriteError)?
291 } 210 }
292 211
293 fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { 212 fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> {
294 let r = self.spi.transaction(|bus| { 213 // 1 = data, 0 = command
295 // 1 = data, 0 = command 214 self.dc.set_high().map_err(|_| DisplayError::DCError)?;
296 if let Err(_) = self.dc.set_high() {
297 return Ok(Err(DisplayError::DCError));
298 }
299
300 // Send words over SPI
301 send_u8(bus, buf)?;
302 215
303 Ok(Ok(())) 216 send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?;
304 }); 217 Ok(())
305 r.map_err(|_| DisplayError::BusWriteError)?
306 } 218 }
307 } 219 }
308 220
309 fn send_u8<T: SpiBusWrite>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { 221 fn send_u8<T: SpiDeviceWrite>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> {
310 match words { 222 match words {
311 DataFormat::U8(slice) => spi.write(slice), 223 DataFormat::U8(slice) => spi.write(slice),
312 DataFormat::U16(slice) => { 224 DataFormat::U16(slice) => {
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml
index 8087df09a..ff08e378c 100644
--- a/examples/std/Cargo.toml
+++ b/examples/std/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] }
11embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } 11embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] }
12embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } 12embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" }
diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml
index 0095a680c..3b1d888f6 100644
--- a/examples/stm32c0/Cargo.toml
+++ b/examples/stm32c0/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] }
12 12
diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml
index 89d99b6d3..5c82c5579 100644
--- a/examples/stm32f0/Cargo.toml
+++ b/examples/stm32f0/Cargo.toml
@@ -13,7 +13,7 @@ defmt = "0.3"
13defmt-rtt = "0.4" 13defmt-rtt = "0.4"
14panic-probe = "0.3" 14panic-probe = "0.3"
15embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 15embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
16embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 16embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
17embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 17embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
18embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } 18embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] }
19static_cell = "1.0" 19static_cell = "1.0"
diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs
new file mode 100644
index 000000000..8ed9f98f8
--- /dev/null
+++ b/examples/stm32f0/src/bin/adc.rs
@@ -0,0 +1,35 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::adc::{Adc, SampleTime};
8use embassy_time::{Delay, Duration, Timer};
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 let p = embassy_stm32::init(Default::default());
14 info!("Hello World!");
15
16 let mut adc = Adc::new(p.ADC, &mut Delay);
17 adc.set_sample_time(SampleTime::Cycles71_5);
18 let mut pin = p.PA1;
19
20 let mut vrefint = adc.enable_vref(&mut Delay);
21 let vrefint_sample = adc.read_internal(&mut vrefint);
22 let convert_to_millivolts = |sample| {
23 // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf
24 // 6.3.4 Embedded reference voltage
25 const VREFINT_MV: u32 = 1230; // mV
26
27 (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16
28 };
29
30 loop {
31 let v = adc.read(&mut pin);
32 info!("--> {} - {} mV", v, convert_to_millivolts(v));
33 Timer::after(Duration::from_millis(100)).await;
34 }
35}
diff --git a/examples/stm32f0/src/bin/multiprio.rs b/examples/stm32f0/src/bin/multiprio.rs
index e0dc8c989..430a805fc 100644
--- a/examples/stm32f0/src/bin/multiprio.rs
+++ b/examples/stm32f0/src/bin/multiprio.rs
@@ -62,7 +62,7 @@ use core::mem;
62use cortex_m::peripheral::NVIC; 62use cortex_m::peripheral::NVIC;
63use cortex_m_rt::entry; 63use cortex_m_rt::entry;
64use defmt::*; 64use defmt::*;
65use embassy_stm32::executor::{Executor, InterruptExecutor}; 65use embassy_executor::{Executor, InterruptExecutor};
66use embassy_stm32::interrupt; 66use embassy_stm32::interrupt;
67use embassy_stm32::pac::Interrupt; 67use embassy_stm32::pac::Interrupt;
68use embassy_time::{Duration, Instant, Timer}; 68use embassy_time::{Duration, Instant, Timer};
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml
index 53f369b3a..99f37cdda 100644
--- a/examples/stm32f1/Cargo.toml
+++ b/examples/stm32f1/Cargo.toml
@@ -6,9 +6,9 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
14 14
diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml
index afaf9a0c9..ffb232310 100644
--- a/examples/stm32f2/Cargo.toml
+++ b/examples/stm32f2/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
12 12
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml
index 69ebef786..38f11201d 100644
--- a/examples/stm32f3/Cargo.toml
+++ b/examples/stm32f3/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs
index baa7484d0..e40ad4fc0 100644
--- a/examples/stm32f3/src/bin/flash.rs
+++ b/examples/stm32f3/src/bin/flash.rs
@@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
15 15
16 const ADDR: u32 = 0x26000; 16 const ADDR: u32 = 0x26000;
17 17
18 let mut f = Flash::new(p.FLASH); 18 let mut f = Flash::new(p.FLASH).into_regions().bank1_region;
19 19
20 info!("Reading..."); 20 info!("Reading...");
21 let mut buf = [0u8; 8]; 21 let mut buf = [0u8; 8];
diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs
index 77df51ac7..5d010f799 100644
--- a/examples/stm32f3/src/bin/multiprio.rs
+++ b/examples/stm32f3/src/bin/multiprio.rs
@@ -62,7 +62,7 @@ use core::mem;
62use cortex_m::peripheral::NVIC; 62use cortex_m::peripheral::NVIC;
63use cortex_m_rt::entry; 63use cortex_m_rt::entry;
64use defmt::*; 64use defmt::*;
65use embassy_stm32::executor::{Executor, InterruptExecutor}; 65use embassy_executor::{Executor, InterruptExecutor};
66use embassy_stm32::interrupt; 66use embassy_stm32::interrupt;
67use embassy_stm32::pac::Interrupt; 67use embassy_stm32::pac::Interrupt;
68use embassy_time::{Duration, Instant, Timer}; 68use embassy_time::{Duration, Instant, Timer};
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index 7a7bab5bb..d967d8501 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs
index 7ea068a42..bd3a7c95e 100644
--- a/examples/stm32f4/src/bin/flash.rs
+++ b/examples/stm32f4/src/bin/flash.rs
@@ -5,7 +5,6 @@
5use defmt::{info, unwrap}; 5use defmt::{info, unwrap};
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::flash::Flash; 7use embassy_stm32::flash::Flash;
8use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
9use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
10 9
11#[embassy_executor::main] 10#[embassy_executor::main]
@@ -13,6 +12,8 @@ async fn main(_spawner: Spawner) {
13 let p = embassy_stm32::init(Default::default()); 12 let p = embassy_stm32::init(Default::default());
14 info!("Hello Flash!"); 13 info!("Hello Flash!");
15 14
15 // Once can also call `into_regions()` to get access to NorFlash implementations
16 // for each of the unique characteristics.
16 let mut f = Flash::new(p.FLASH); 17 let mut f = Flash::new(p.FLASH);
17 18
18 // Sector 5 19 // Sector 5
@@ -30,19 +31,19 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) {
30 31
31 info!("Reading..."); 32 info!("Reading...");
32 let mut buf = [0u8; 32]; 33 let mut buf = [0u8; 32];
33 unwrap!(f.read(offset, &mut buf)); 34 unwrap!(f.blocking_read(offset, &mut buf));
34 info!("Read: {=[u8]:x}", buf); 35 info!("Read: {=[u8]:x}", buf);
35 36
36 info!("Erasing..."); 37 info!("Erasing...");
37 unwrap!(f.erase(offset, offset + size)); 38 unwrap!(f.blocking_erase(offset, offset + size));
38 39
39 info!("Reading..."); 40 info!("Reading...");
40 let mut buf = [0u8; 32]; 41 let mut buf = [0u8; 32];
41 unwrap!(f.read(offset, &mut buf)); 42 unwrap!(f.blocking_read(offset, &mut buf));
42 info!("Read after erase: {=[u8]:x}", buf); 43 info!("Read after erase: {=[u8]:x}", buf);
43 44
44 info!("Writing..."); 45 info!("Writing...");
45 unwrap!(f.write( 46 unwrap!(f.blocking_write(
46 offset, 47 offset,
47 &[ 48 &[
48 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 49 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
@@ -52,7 +53,7 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) {
52 53
53 info!("Reading..."); 54 info!("Reading...");
54 let mut buf = [0u8; 32]; 55 let mut buf = [0u8; 32];
55 unwrap!(f.read(offset, &mut buf)); 56 unwrap!(f.blocking_read(offset, &mut buf));
56 info!("Read: {=[u8]:x}", buf); 57 info!("Read: {=[u8]:x}", buf);
57 assert_eq!( 58 assert_eq!(
58 &buf[..], 59 &buf[..],
diff --git a/examples/stm32f4/src/bin/mco.rs b/examples/stm32f4/src/bin/mco.rs
new file mode 100644
index 000000000..2b9ceebc3
--- /dev/null
+++ b/examples/stm32f4/src/bin/mco.rs
@@ -0,0 +1,30 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::rcc::{Mco, Mco1Source, Mco2Source, McoClock};
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let p = embassy_stm32::init(Default::default());
15 info!("Hello World!");
16
17 let _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::DIV1);
18 let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::Pll, McoClock::DIV4);
19 let mut led = Output::new(p.PB7, Level::High, Speed::Low);
20
21 loop {
22 info!("high");
23 led.set_high();
24 Timer::after(Duration::from_millis(300)).await;
25
26 info!("low");
27 led.set_low();
28 Timer::after(Duration::from_millis(300)).await;
29 }
30}
diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs
index 77df51ac7..5d010f799 100644
--- a/examples/stm32f4/src/bin/multiprio.rs
+++ b/examples/stm32f4/src/bin/multiprio.rs
@@ -62,7 +62,7 @@ use core::mem;
62use cortex_m::peripheral::NVIC; 62use cortex_m::peripheral::NVIC;
63use cortex_m_rt::entry; 63use cortex_m_rt::entry;
64use defmt::*; 64use defmt::*;
65use embassy_stm32::executor::{Executor, InterruptExecutor}; 65use embassy_executor::{Executor, InterruptExecutor};
66use embassy_stm32::interrupt; 66use embassy_stm32::interrupt;
67use embassy_stm32::pac::Interrupt; 67use embassy_stm32::pac::Interrupt;
68use embassy_time::{Duration, Instant, Timer}; 68use embassy_time::{Duration, Instant, Timer};
diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs
new file mode 100644
index 000000000..6e17f3fd3
--- /dev/null
+++ b/examples/stm32f4/src/bin/pwm_complementary.rs
@@ -0,0 +1,77 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::pwm::complementary_pwm::{Ckd, ComplementaryPwm, ComplementaryPwmPin};
8use embassy_stm32::pwm::simple_pwm::PwmPin;
9use embassy_stm32::pwm::Channel;
10use embassy_stm32::time::khz;
11use embassy_time::{Duration, Timer};
12use {defmt_rtt as _, panic_probe as _};
13
14#[embassy_executor::main]
15async fn main(_spawner: Spawner) {
16 let p = embassy_stm32::init(Default::default());
17 info!("Hello World!");
18
19 let ch1 = PwmPin::new_ch1(p.PE9);
20 let ch1n = ComplementaryPwmPin::new_ch1(p.PA7);
21 let mut pwm = ComplementaryPwm::new(
22 p.TIM1,
23 Some(ch1),
24 Some(ch1n),
25 None,
26 None,
27 None,
28 None,
29 None,
30 None,
31 khz(10),
32 );
33
34 /*
35 Dead-time = T_clk * T_dts * T_dtg
36
37 T_dts:
38 This bit-field indicates the division ratio between the timer clock (CK_INT) frequency and the
39 dead-time and sampling clock (tDTS)used by the dead-time generators and the digital filters
40 (ETR, TIx),
41 00: tDTS=tCK_INT
42 01: tDTS=2*tCK_INT
43 10: tDTS=4*tCK_INT
44
45 T_dtg:
46 This bit-field defines the duration of the dead-time inserted between the complementary
47 outputs. DT correspond to this duration.
48 DTG[7:5]=0xx => DT=DTG[7:0]x tdtg with tdtg=tDTS.
49 DTG[7:5]=10x => DT=(64+DTG[5:0])xtdtg with Tdtg=2xtDTS.
50 DTG[7:5]=110 => DT=(32+DTG[4:0])xtdtg with Tdtg=8xtDTS.
51 DTG[7:5]=111 => DT=(32+DTG[4:0])xtdtg with Tdtg=16xtDTS.
52 Example if TDTS=125ns (8MHz), dead-time possible values are:
53 0 to 15875 ns by 125 ns steps,
54 16 us to 31750 ns by 250 ns steps,
55 32 us to 63us by 1 us steps,
56 64 us to 126 us by 2 us steps
57 */
58 pwm.set_dead_time_clock_division(Ckd::DIV1);
59 pwm.set_dead_time_value(0);
60
61 let max = pwm.get_max_duty();
62 pwm.enable(Channel::Ch1);
63
64 info!("PWM initialized");
65 info!("PWM max duty {}", max);
66
67 loop {
68 pwm.set_duty(Channel::Ch1, 0);
69 Timer::after(Duration::from_millis(300)).await;
70 pwm.set_duty(Channel::Ch1, max / 4);
71 Timer::after(Duration::from_millis(300)).await;
72 pwm.set_duty(Channel::Ch1, max / 2);
73 Timer::after(Duration::from_millis(300)).await;
74 pwm.set_duty(Channel::Ch1, max - 1);
75 Timer::after(Duration::from_millis(300)).await;
76 }
77}
diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs
index dd171fe13..a93f8baeb 100644
--- a/examples/stm32f4/src/bin/usart_buffered.rs
+++ b/examples/stm32f4/src/bin/usart_buffered.rs
@@ -5,7 +5,7 @@
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::interrupt; 7use embassy_stm32::interrupt;
8use embassy_stm32::usart::{BufferedUart, Config, State}; 8use embassy_stm32::usart::{BufferedUart, Config};
9use embedded_io::asynch::BufRead; 9use embedded_io::asynch::BufRead;
10use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
11 11
@@ -16,20 +16,10 @@ async fn main(_spawner: Spawner) {
16 16
17 let config = Config::default(); 17 let config = Config::default();
18 18
19 let mut state = State::new();
20 let irq = interrupt::take!(USART3); 19 let irq = interrupt::take!(USART3);
21 let mut tx_buf = [0u8; 32]; 20 let mut tx_buf = [0u8; 32];
22 let mut rx_buf = [0u8; 32]; 21 let mut rx_buf = [0u8; 32];
23 let mut buf_usart = BufferedUart::new( 22 let mut buf_usart = BufferedUart::new(p.USART3, irq, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, config);
24 &mut state,
25 p.USART3,
26 p.PD9,
27 p.PD8,
28 irq,
29 &mut tx_buf,
30 &mut rx_buf,
31 config,
32 );
33 23
34 loop { 24 loop {
35 let buf = buf_usart.fill_buf().await.unwrap(); 25 let buf = buf_usart.fill_buf().await.unwrap();
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index ea4cbd808..74e7bf53d 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] }
12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } 12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs
index 4a7bca1fa..aabfe8557 100644
--- a/examples/stm32f7/src/bin/flash.rs
+++ b/examples/stm32f7/src/bin/flash.rs
@@ -14,12 +14,12 @@ async fn main(_spawner: Spawner) {
14 let p = embassy_stm32::init(Default::default()); 14 let p = embassy_stm32::init(Default::default());
15 info!("Hello Flash!"); 15 info!("Hello Flash!");
16 16
17 const ADDR: u32 = 0x8_0000; 17 const ADDR: u32 = 0x8_0000; // This is the offset into the third region, the absolute address is 4x32K + 128K + 0x8_0000.
18 18
19 // wait a bit before accessing the flash 19 // wait a bit before accessing the flash
20 Timer::after(Duration::from_millis(300)).await; 20 Timer::after(Duration::from_millis(300)).await;
21 21
22 let mut f = Flash::new(p.FLASH); 22 let mut f = Flash::new(p.FLASH).into_regions().bank1_region3;
23 23
24 info!("Reading..."); 24 info!("Reading...");
25 let mut buf = [0u8; 32]; 25 let mut buf = [0u8; 32];
diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml
index e7273c9fc..03bdbcea3 100644
--- a/examples/stm32g0/Cargo.toml
+++ b/examples/stm32g0/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] }
12 12
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml
index 8a57a8ef0..4e4150350 100644
--- a/examples/stm32g4/Cargo.toml
+++ b/examples/stm32g4/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] }
12embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } 12embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" }
diff --git a/examples/stm32h5/.cargo/config.toml b/examples/stm32h5/.cargo/config.toml
new file mode 100644
index 000000000..c8b864b6c
--- /dev/null
+++ b/examples/stm32h5/.cargo/config.toml
@@ -0,0 +1,8 @@
1[target.thumbv8m.main-none-eabihf]
2runner = 'probe-rs-cli run --chip STM32H563ZITx'
3
4[build]
5target = "thumbv8m.main-none-eabihf"
6
7[env]
8DEFMT_LOG = "trace"
diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml
new file mode 100644
index 000000000..b77d376ca
--- /dev/null
+++ b/examples/stm32h5/Cargo.toml
@@ -0,0 +1,71 @@
1[package]
2edition = "2021"
3name = "embassy-stm32h5-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6
7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] }
13embedded-io = { version = "0.4.0", features = ["async"] }
14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
15
16defmt = "0.3"
17defmt-rtt = "0.4"
18
19cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
20cortex-m-rt = "0.7.0"
21embedded-hal = "0.2.6"
22embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
23embedded-hal-async = { version = "=0.2.0-alpha.1" }
24embedded-nal-async = "0.4.0"
25panic-probe = { version = "0.3", features = ["print-defmt"] }
26futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
27heapless = { version = "0.7.5", default-features = false }
28rand_core = "0.6.3"
29critical-section = "1.1"
30micromath = "2.0.0"
31stm32-fmc = "0.2.4"
32embedded-storage = "0.3.0"
33static_cell = "1.0"
34
35# cargo build/run
36[profile.dev]
37codegen-units = 1
38debug = 2
39debug-assertions = true # <-
40incremental = false
41opt-level = 3 # <-
42overflow-checks = true # <-
43
44# cargo test
45[profile.test]
46codegen-units = 1
47debug = 2
48debug-assertions = true # <-
49incremental = false
50opt-level = 3 # <-
51overflow-checks = true # <-
52
53# cargo build/run --release
54[profile.release]
55codegen-units = 1
56debug = 2
57debug-assertions = false # <-
58incremental = false
59lto = 'fat'
60opt-level = 3 # <-
61overflow-checks = false # <-
62
63# cargo test --release
64[profile.bench]
65codegen-units = 1
66debug = 2
67debug-assertions = false # <-
68incremental = false
69lto = 'fat'
70opt-level = 3 # <-
71overflow-checks = false # <-
diff --git a/examples/stm32h5/build.rs b/examples/stm32h5/build.rs
new file mode 100644
index 000000000..8cd32d7ed
--- /dev/null
+++ b/examples/stm32h5/build.rs
@@ -0,0 +1,5 @@
1fn main() {
2 println!("cargo:rustc-link-arg-bins=--nmagic");
3 println!("cargo:rustc-link-arg-bins=-Tlink.x");
4 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
5}
diff --git a/examples/stm32h5/memory.x b/examples/stm32h5/memory.x
new file mode 100644
index 000000000..456061509
--- /dev/null
+++ b/examples/stm32h5/memory.x
@@ -0,0 +1,5 @@
1MEMORY
2{
3 FLASH : ORIGIN = 0x08000000, LENGTH = 0x200000
4 RAM : ORIGIN = 0x20000000, LENGTH = 0x50000
5}
diff --git a/examples/stm32h5/src/bin/blinky.rs b/examples/stm32h5/src/bin/blinky.rs
new file mode 100644
index 000000000..f9bf90d2e
--- /dev/null
+++ b/examples/stm32h5/src/bin/blinky.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_time::{Duration, Timer};
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 let p = embassy_stm32::init(Default::default());
14 info!("Hello World!");
15
16 let mut led = Output::new(p.PB0, Level::High, Speed::Low);
17
18 loop {
19 info!("high");
20 led.set_high();
21 Timer::after(Duration::from_millis(500)).await;
22
23 info!("low");
24 led.set_low();
25 Timer::after(Duration::from_millis(500)).await;
26 }
27}
diff --git a/examples/stm32h5/src/bin/button_exti.rs b/examples/stm32h5/src/bin/button_exti.rs
new file mode 100644
index 000000000..dfe587d41
--- /dev/null
+++ b/examples/stm32h5/src/bin/button_exti.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::exti::ExtiInput;
8use embassy_stm32::gpio::{Input, Pull};
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 let p = embassy_stm32::init(Default::default());
14 info!("Hello World!");
15
16 let button = Input::new(p.PC13, Pull::Down);
17 let mut button = ExtiInput::new(button, p.EXTI13);
18
19 info!("Press the USER button...");
20
21 loop {
22 button.wait_for_rising_edge().await;
23 info!("Pressed!");
24 button.wait_for_falling_edge().await;
25 info!("Released!");
26 }
27}
diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs
new file mode 100644
index 000000000..6d650da9e
--- /dev/null
+++ b/examples/stm32h5/src/bin/eth.rs
@@ -0,0 +1,133 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_net::tcp::TcpSocket;
8use embassy_net::{Ipv4Address, Stack, StackResources};
9use embassy_stm32::eth::generic_smi::GenericSMI;
10use embassy_stm32::eth::{Ethernet, PacketQueue};
11use embassy_stm32::peripherals::ETH;
12use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale};
13use embassy_stm32::rng::Rng;
14use embassy_stm32::time::Hertz;
15use embassy_stm32::{interrupt, Config};
16use embassy_time::{Duration, Timer};
17use embedded_io::asynch::Write;
18use rand_core::RngCore;
19use static_cell::StaticCell;
20use {defmt_rtt as _, panic_probe as _};
21
22macro_rules! singleton {
23 ($val:expr) => {{
24 type T = impl Sized;
25 static STATIC_CELL: StaticCell<T> = StaticCell::new();
26 let (x,) = STATIC_CELL.init(($val,));
27 x
28 }};
29}
30
31type Device = Ethernet<'static, ETH, GenericSMI>;
32
33#[embassy_executor::task]
34async fn net_task(stack: &'static Stack<Device>) -> ! {
35 stack.run().await
36}
37
38#[embassy_executor::main]
39async fn main(spawner: Spawner) -> ! {
40 let mut config = Config::default();
41 config.rcc.hsi = None;
42 config.rcc.hsi48 = true; // needed for rng
43 config.rcc.hse = Some(Hse {
44 freq: Hertz(8_000_000),
45 mode: HseMode::BypassDigital,
46 });
47 config.rcc.pll1 = Some(Pll {
48 source: PllSource::Hse,
49 prediv: 2,
50 mul: 125,
51 divp: Some(2),
52 divq: Some(2),
53 divr: None,
54 });
55 config.rcc.ahb_pre = AHBPrescaler::NotDivided;
56 config.rcc.apb1_pre = APBPrescaler::NotDivided;
57 config.rcc.apb2_pre = APBPrescaler::NotDivided;
58 config.rcc.apb3_pre = APBPrescaler::NotDivided;
59 config.rcc.sys = Sysclk::Pll1P;
60 config.rcc.voltage_scale = VoltageScale::Scale0;
61 let p = embassy_stm32::init(config);
62 info!("Hello World!");
63
64 // Generate random seed.
65 let mut rng = Rng::new(p.RNG);
66 let mut seed = [0; 8];
67 rng.fill_bytes(&mut seed);
68 let seed = u64::from_le_bytes(seed);
69
70 let eth_int = interrupt::take!(ETH);
71 let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
72
73 let device = Ethernet::new(
74 singleton!(PacketQueue::<4, 4>::new()),
75 p.ETH,
76 eth_int,
77 p.PA1,
78 p.PA2,
79 p.PC1,
80 p.PA7,
81 p.PC4,
82 p.PC5,
83 p.PG13,
84 p.PB15,
85 p.PG11,
86 GenericSMI,
87 mac_addr,
88 0,
89 );
90
91 let config = embassy_net::Config::Dhcp(Default::default());
92 //let config = embassy_net::Config::Static(embassy_net::StaticConfig {
93 // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
94 // dns_servers: Vec::new(),
95 // gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
96 //});
97
98 // Init network stack
99 let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed));
100
101 // Launch network task
102 unwrap!(spawner.spawn(net_task(&stack)));
103
104 info!("Network task initialized");
105
106 // Then we can use it!
107 let mut rx_buffer = [0; 1024];
108 let mut tx_buffer = [0; 1024];
109
110 loop {
111 let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer);
112
113 socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
114
115 let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000);
116 info!("connecting...");
117 let r = socket.connect(remote_endpoint).await;
118 if let Err(e) = r {
119 info!("connect error: {:?}", e);
120 Timer::after(Duration::from_secs(3)).await;
121 continue;
122 }
123 info!("connected!");
124 loop {
125 let r = socket.write_all(b"Hello\n").await;
126 if let Err(e) = r {
127 info!("write error: {:?}", e);
128 continue;
129 }
130 Timer::after(Duration::from_secs(1)).await;
131 }
132 }
133}
diff --git a/examples/stm32h5/src/bin/i2c.rs b/examples/stm32h5/src/bin/i2c.rs
new file mode 100644
index 000000000..6cbf58bbc
--- /dev/null
+++ b/examples/stm32h5/src/bin/i2c.rs
@@ -0,0 +1,44 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::i2c::{Error, I2c, TimeoutI2c};
8use embassy_stm32::interrupt;
9use embassy_stm32::time::Hertz;
10use embassy_time::Duration;
11use {defmt_rtt as _, panic_probe as _};
12
13const ADDRESS: u8 = 0x5F;
14const WHOAMI: u8 = 0x0F;
15
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) {
18 info!("Hello world!");
19 let p = embassy_stm32::init(Default::default());
20
21 let irq = interrupt::take!(I2C2_EV);
22 let mut i2c = I2c::new(
23 p.I2C2,
24 p.PB10,
25 p.PB11,
26 irq,
27 p.GPDMA1_CH4,
28 p.GPDMA1_CH5,
29 Hertz(100_000),
30 Default::default(),
31 );
32
33 // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long.
34 // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay.
35 let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000));
36
37 let mut data = [0u8; 1];
38
39 match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) {
40 Ok(()) => info!("Whoami: {}", data[0]),
41 Err(Error::Timeout) => error!("Operation timed out"),
42 Err(e) => error!("I2c Error: {:?}", e),
43 }
44}
diff --git a/examples/stm32h5/src/bin/rng.rs b/examples/stm32h5/src/bin/rng.rs
new file mode 100644
index 000000000..af9be0b62
--- /dev/null
+++ b/examples/stm32h5/src/bin/rng.rs
@@ -0,0 +1,20 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::rng::Rng;
8use {defmt_rtt as _, panic_probe as _};
9
10#[embassy_executor::main]
11async fn main(_spawner: Spawner) {
12 let p = embassy_stm32::init(Default::default());
13 info!("Hello World!");
14
15 let mut rng = Rng::new(p.RNG);
16
17 let mut buf = [0u8; 16];
18 unwrap!(rng.async_fill_bytes(&mut buf).await);
19 info!("random bytes: {:02x}", buf);
20}
diff --git a/examples/stm32h5/src/bin/usart.rs b/examples/stm32h5/src/bin/usart.rs
new file mode 100644
index 000000000..405f18ec7
--- /dev/null
+++ b/examples/stm32h5/src/bin/usart.rs
@@ -0,0 +1,43 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use cortex_m_rt::entry;
6use defmt::*;
7use embassy_executor::Executor;
8use embassy_stm32::dma::NoDma;
9use embassy_stm32::interrupt;
10use embassy_stm32::usart::{Config, Uart};
11use static_cell::StaticCell;
12use {defmt_rtt as _, panic_probe as _};
13
14#[embassy_executor::task]
15async fn main_task() {
16 let p = embassy_stm32::init(Default::default());
17
18 let config = Config::default();
19 let irq = interrupt::take!(UART7);
20 let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, NoDma, NoDma, config);
21
22 unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n"));
23 info!("wrote Hello, starting echo");
24
25 let mut buf = [0u8; 1];
26 loop {
27 unwrap!(usart.blocking_read(&mut buf));
28 unwrap!(usart.blocking_write(&buf));
29 }
30}
31
32static EXECUTOR: StaticCell<Executor> = StaticCell::new();
33
34#[entry]
35fn main() -> ! {
36 info!("Hello World!");
37
38 let executor = EXECUTOR.init(Executor::new());
39
40 executor.run(|spawner| {
41 unwrap!(spawner.spawn(main_task()));
42 })
43}
diff --git a/examples/stm32h5/src/bin/usart_dma.rs b/examples/stm32h5/src/bin/usart_dma.rs
new file mode 100644
index 000000000..43d791aae
--- /dev/null
+++ b/examples/stm32h5/src/bin/usart_dma.rs
@@ -0,0 +1,46 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use core::fmt::Write;
6
7use cortex_m_rt::entry;
8use defmt::*;
9use embassy_executor::Executor;
10use embassy_stm32::dma::NoDma;
11use embassy_stm32::interrupt;
12use embassy_stm32::usart::{Config, Uart};
13use heapless::String;
14use static_cell::StaticCell;
15use {defmt_rtt as _, panic_probe as _};
16
17#[embassy_executor::task]
18async fn main_task() {
19 let p = embassy_stm32::init(Default::default());
20
21 let config = Config::default();
22 let irq = interrupt::take!(UART7);
23 let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.GPDMA1_CH0, NoDma, config);
24
25 for n in 0u32.. {
26 let mut s: String<128> = String::new();
27 core::write!(&mut s, "Hello DMA World {}!\r\n", n).unwrap();
28
29 usart.write(s.as_bytes()).await.ok();
30
31 info!("wrote DMA");
32 }
33}
34
35static EXECUTOR: StaticCell<Executor> = StaticCell::new();
36
37#[entry]
38fn main() -> ! {
39 info!("Hello World!");
40
41 let executor = EXECUTOR.init(Executor::new());
42
43 executor.run(|spawner| {
44 unwrap!(spawner.spawn(main_task()));
45 })
46}
diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs
new file mode 100644
index 000000000..16a499582
--- /dev/null
+++ b/examples/stm32h5/src/bin/usart_split.rs
@@ -0,0 +1,58 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::dma::NoDma;
8use embassy_stm32::interrupt;
9use embassy_stm32::peripherals::{GPDMA1_CH1, UART7};
10use embassy_stm32::usart::{Config, Uart, UartRx};
11use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
12use embassy_sync::channel::Channel;
13use {defmt_rtt as _, panic_probe as _};
14
15#[embassy_executor::task]
16async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) {
17 unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n"));
18 info!("wrote Hello, starting echo");
19
20 let mut buf = [0u8; 1];
21 loop {
22 unwrap!(usart.blocking_read(&mut buf));
23 unwrap!(usart.blocking_write(&buf));
24 }
25}
26
27static CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new();
28
29#[embassy_executor::main]
30async fn main(spawner: Spawner) -> ! {
31 let p = embassy_stm32::init(Default::default());
32 info!("Hello World!");
33
34 let config = Config::default();
35 let irq = interrupt::take!(UART7);
36 let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.GPDMA1_CH0, p.GPDMA1_CH1, config);
37 unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n"));
38
39 let (mut tx, rx) = usart.split();
40
41 unwrap!(spawner.spawn(reader(rx)));
42
43 loop {
44 let buf = CHANNEL.recv().await;
45 info!("writing...");
46 unwrap!(tx.write(&buf).await);
47 }
48}
49
50#[embassy_executor::task]
51async fn reader(mut rx: UartRx<'static, UART7, GPDMA1_CH1>) {
52 let mut buf = [0; 8];
53 loop {
54 info!("reading...");
55 unwrap!(rx.read(&mut buf).await);
56 CHANNEL.send(buf).await;
57 }
58}
diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs
new file mode 100644
index 000000000..6af269c1d
--- /dev/null
+++ b/examples/stm32h5/src/bin/usb_serial.rs
@@ -0,0 +1,128 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{panic, *};
6use embassy_executor::Spawner;
7use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale};
8use embassy_stm32::time::Hertz;
9use embassy_stm32::usb::{Driver, Instance};
10use embassy_stm32::{interrupt, pac, Config};
11use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
12use embassy_usb::driver::EndpointError;
13use embassy_usb::Builder;
14use futures::future::join;
15use {defmt_rtt as _, panic_probe as _};
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 let mut config = Config::default();
20 config.rcc.hsi = None;
21 config.rcc.hsi48 = true; // needed for usb
22 config.rcc.hse = Some(Hse {
23 freq: Hertz(8_000_000),
24 mode: HseMode::BypassDigital,
25 });
26 config.rcc.pll1 = Some(Pll {
27 source: PllSource::Hse,
28 prediv: 2,
29 mul: 125,
30 divp: Some(2), // 250mhz
31 divq: None,
32 divr: None,
33 });
34 config.rcc.ahb_pre = AHBPrescaler::Div2;
35 config.rcc.apb1_pre = APBPrescaler::Div4;
36 config.rcc.apb2_pre = APBPrescaler::Div2;
37 config.rcc.apb3_pre = APBPrescaler::Div4;
38 config.rcc.sys = Sysclk::Pll1P;
39 config.rcc.voltage_scale = VoltageScale::Scale0;
40 let p = embassy_stm32::init(config);
41
42 info!("Hello World!");
43
44 unsafe {
45 pac::RCC.ccipr4().write(|w| {
46 w.set_usbsel(pac::rcc::vals::Usbsel::HSI48);
47 });
48 }
49
50 // Create the driver, from the HAL.
51 let irq = interrupt::take!(USB_DRD_FS);
52 let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
53
54 // Create embassy-usb Config
55 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
56 config.manufacturer = Some("Embassy");
57 config.product = Some("USB-serial example");
58 config.serial_number = Some("12345678");
59
60 // Required for windows compatiblity.
61 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
62 config.device_class = 0xEF;
63 config.device_sub_class = 0x02;
64 config.device_protocol = 0x01;
65 config.composite_with_iads = true;
66
67 // Create embassy-usb DeviceBuilder using the driver and config.
68 // It needs some buffers for building the descriptors.
69 let mut device_descriptor = [0; 256];
70 let mut config_descriptor = [0; 256];
71 let mut bos_descriptor = [0; 256];
72 let mut control_buf = [0; 64];
73
74 let mut state = State::new();
75
76 let mut builder = Builder::new(
77 driver,
78 config,
79 &mut device_descriptor,
80 &mut config_descriptor,
81 &mut bos_descriptor,
82 &mut control_buf,
83 );
84
85 // Create classes on the builder.
86 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
87
88 // Build the builder.
89 let mut usb = builder.build();
90
91 // Run the USB device.
92 let usb_fut = usb.run();
93
94 // Do stuff with the class!
95 let echo_fut = async {
96 loop {
97 class.wait_connection().await;
98 info!("Connected");
99 let _ = echo(&mut class).await;
100 info!("Disconnected");
101 }
102 };
103
104 // Run everything concurrently.
105 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
106 join(usb_fut, echo_fut).await;
107}
108
109struct Disconnected {}
110
111impl From<EndpointError> for Disconnected {
112 fn from(val: EndpointError) -> Self {
113 match val {
114 EndpointError::BufferOverflow => panic!("Buffer overflow"),
115 EndpointError::Disabled => Disconnected {},
116 }
117 }
118}
119
120async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
121 let mut buf = [0; 64];
122 loop {
123 let n = class.read_packet(&mut buf).await?;
124 let data = &buf[..n];
125 info!("data: {:x}", data);
126 class.write_packet(data).await?;
127 }
128}
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index a04134789..154f5a987 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } 12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] }
@@ -19,8 +19,8 @@ defmt-rtt = "0.4"
19cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 19cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
20cortex-m-rt = "0.7.0" 20cortex-m-rt = "0.7.0"
21embedded-hal = "0.2.6" 21embedded-hal = "0.2.6"
22embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } 22embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
23embedded-hal-async = { version = "=0.2.0-alpha.0" } 23embedded-hal-async = { version = "=0.2.0-alpha.1" }
24embedded-nal-async = "0.4.0" 24embedded-nal-async = "0.4.0"
25panic-probe = { version = "0.3", features = ["print-defmt"] } 25panic-probe = { version = "0.3", features = ["print-defmt"] }
26futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 26futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs
index ee86bdbf6..7ee9838c9 100644
--- a/examples/stm32h7/src/bin/flash.rs
+++ b/examples/stm32h7/src/bin/flash.rs
@@ -14,12 +14,12 @@ async fn main(_spawner: Spawner) {
14 let p = embassy_stm32::init(Default::default()); 14 let p = embassy_stm32::init(Default::default());
15 info!("Hello Flash!"); 15 info!("Hello Flash!");
16 16
17 const ADDR: u32 = 0x08_0000; 17 const ADDR: u32 = 0; // This is the offset into bank 2, the absolute address is 0x8_0000
18 18
19 // wait a bit before accessing the flash 19 // wait a bit before accessing the flash
20 Timer::after(Duration::from_millis(300)).await; 20 Timer::after(Duration::from_millis(300)).await;
21 21
22 let mut f = Flash::new(p.FLASH); 22 let mut f = Flash::new(p.FLASH).into_regions().bank2_region;
23 23
24 info!("Reading..."); 24 info!("Reading...");
25 let mut buf = [0u8; 32]; 25 let mut buf = [0u8; 32];
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index 86933a629..413d5c18f 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -10,7 +10,7 @@ nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan",
10 10
11[dependencies] 11[dependencies]
12embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 12embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
13embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 13embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
14embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 14embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
15embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } 15embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
16embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} 16embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true}
diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs
index ffe4fb10b..337425028 100644
--- a/examples/stm32l0/src/bin/flash.rs
+++ b/examples/stm32l0/src/bin/flash.rs
@@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
15 15
16 const ADDR: u32 = 0x26000; 16 const ADDR: u32 = 0x26000;
17 17
18 let mut f = Flash::new(p.FLASH); 18 let mut f = Flash::new(p.FLASH).into_regions().bank1_region;
19 19
20 info!("Reading..."); 20 info!("Reading...");
21 let mut buf = [0u8; 8]; 21 let mut buf = [0u8; 8];
diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs
index 8e84cd092..465347004 100644
--- a/examples/stm32l0/src/bin/usart_irq.rs
+++ b/examples/stm32l0/src/bin/usart_irq.rs
@@ -5,7 +5,7 @@
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::interrupt; 7use embassy_stm32::interrupt;
8use embassy_stm32::usart::{BufferedUart, Config, State}; 8use embassy_stm32::usart::{BufferedUart, Config};
9use embedded_io::asynch::{Read, Write}; 9use embedded_io::asynch::{Read, Write};
10use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
11 11
@@ -20,20 +20,8 @@ async fn main(_spawner: Spawner) {
20 let mut config = Config::default(); 20 let mut config = Config::default();
21 config.baudrate = 9600; 21 config.baudrate = 9600;
22 22
23 let mut state = State::new();
24 let irq = interrupt::take!(USART2); 23 let irq = interrupt::take!(USART2);
25 let mut usart = unsafe { 24 let mut usart = unsafe { BufferedUart::new(p.USART2, irq, p.PA3, p.PA2, &mut TX_BUFFER, &mut RX_BUFFER, config) };
26 BufferedUart::new(
27 &mut state,
28 p.USART2,
29 p.PA3,
30 p.PA2,
31 irq,
32 &mut TX_BUFFER,
33 &mut RX_BUFFER,
34 config,
35 )
36 };
37 25
38 usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); 26 usart.write_all(b"Hello Embassy World!\r\n").await.unwrap();
39 info!("wrote Hello, starting echo"); 27 info!("wrote Hello, starting echo");
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml
index 6e3b2103c..cd9508d57 100644
--- a/examples/stm32l1/Cargo.toml
+++ b/examples/stm32l1/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] }
12 12
diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs
index 476ed51a4..38feb0d76 100644
--- a/examples/stm32l1/src/bin/flash.rs
+++ b/examples/stm32l1/src/bin/flash.rs
@@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
15 15
16 const ADDR: u32 = 0x26000; 16 const ADDR: u32 = 0x26000;
17 17
18 let mut f = Flash::new(p.FLASH); 18 let mut f = Flash::new(p.FLASH).into_regions().bank1_region;
19 19
20 info!("Reading..."); 20 info!("Reading...");
21 let mut buf = [0u8; 8]; 21 let mut buf = [0u8; 8];
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml
index 644c90b1a..fa39df6db 100644
--- a/examples/stm32l4/Cargo.toml
+++ b/examples/stm32l4/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } 11embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
12embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } 12embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] }
@@ -18,8 +18,8 @@ defmt-rtt = "0.4"
18cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 18cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
19cortex-m-rt = "0.7.0" 19cortex-m-rt = "0.7.0"
20embedded-hal = "0.2.6" 20embedded-hal = "0.2.6"
21embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } 21embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
22embedded-hal-async = { version = "=0.2.0-alpha.0" } 22embedded-hal-async = { version = "=0.2.0-alpha.1" }
23panic-probe = { version = "0.3", features = ["print-defmt"] } 23panic-probe = { version = "0.3", features = ["print-defmt"] }
24futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 24futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
25heapless = { version = "0.7.5", default-features = false } 25heapless = { version = "0.7.5", default-features = false }
diff --git a/examples/stm32l4/src/bin/mco.rs b/examples/stm32l4/src/bin/mco.rs
new file mode 100644
index 000000000..dea0c66e0
--- /dev/null
+++ b/examples/stm32l4/src/bin/mco.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::rcc::{Mco, Mco1Source, McoClock};
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let p = embassy_stm32::init(Default::default());
15 info!("Hello World!");
16
17 let _mco = Mco::new(p.MCO, p.PA8, Mco1Source::Hsi16, McoClock::DIV1);
18
19 let mut led = Output::new(p.PB14, Level::High, Speed::Low);
20
21 loop {
22 led.set_high();
23 Timer::after(Duration::from_millis(300)).await;
24 led.set_low();
25 Timer::after(Duration::from_millis(300)).await;
26 }
27}
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml
index f880328dc..1c662b9da 100644
--- a/examples/stm32l5/Cargo.toml
+++ b/examples/stm32l5/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml
index 2b02eda92..ebef0a4f7 100644
--- a/examples/stm32u5/Cargo.toml
+++ b/examples/stm32u5/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml
index e27b4527c..ddf9729e6 100644
--- a/examples/stm32wb/Cargo.toml
+++ b/examples/stm32wb/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] }
12 12
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml
index 690481bbf..0d2194ea2 100644
--- a/examples/stm32wl/Cargo.toml
+++ b/examples/stm32wl/Cargo.toml
@@ -6,9 +6,9 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] }
12embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } 12embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] }
13 13
14lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } 14lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] }
diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs
index 2a8880624..e6bc2865c 100644
--- a/examples/stm32wl/src/bin/flash.rs
+++ b/examples/stm32wl/src/bin/flash.rs
@@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) {
15 15
16 const ADDR: u32 = 0x36000; 16 const ADDR: u32 = 0x36000;
17 17
18 let mut f = Flash::new(p.FLASH); 18 let mut f = Flash::new(p.FLASH).into_regions().bank1_region;
19 19
20 info!("Reading..."); 20 info!("Reading...");
21 let mut buf = [0u8; 8]; 21 let mut buf = [0u8; 8];
diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml
index e0e799a34..430d0b4c7 100644
--- a/examples/wasm/Cargo.toml
+++ b/examples/wasm/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
9 9
10[dependencies] 10[dependencies]
11embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } 11embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] }
12embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "wasm", "nightly", "integrated-timers"] } 12embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] }
13embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } 13embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] }
14 14
15wasm-logger = "0.2.0" 15wasm-logger = "0.2.0"
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index da75fa53a..9785cd9eb 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,7 +1,7 @@
1# Before upgrading check that everything is available on all tier1 targets here: 1# Before upgrading check that everything is available on all tier1 targets here:
2# https://rust-lang.github.io/rustup-components-history 2# https://rust-lang.github.io/rustup-components-history
3[toolchain] 3[toolchain]
4channel = "nightly-2023-02-07" 4channel = "nightly-2023-04-02"
5components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] 5components = [ "rust-src", "rustfmt", "llvm-tools-preview" ]
6targets = [ 6targets = [
7 "thumbv7em-none-eabi", 7 "thumbv7em-none-eabi",
@@ -9,5 +9,6 @@ targets = [
9 "thumbv6m-none-eabi", 9 "thumbv6m-none-eabi",
10 "thumbv7em-none-eabihf", 10 "thumbv7em-none-eabihf",
11 "thumbv8m.main-none-eabihf", 11 "thumbv8m.main-none-eabihf",
12 "riscv32imac-unknown-none-elf",
12 "wasm32-unknown-unknown", 13 "wasm32-unknown-unknown",
13] 14]
diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml
index 2a4e8cf41..912749e5d 100644
--- a/tests/nrf/Cargo.toml
+++ b/tests/nrf/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } 9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] }
10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } 10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] }
11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } 12embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
13embedded-io = { version = "0.4.0", features = ["async"] } 13embedded-io = { version = "0.4.0", features = ["async"] }
diff --git a/tests/nrf/src/bin/timer.rs b/tests/nrf/src/bin/timer.rs
new file mode 100644
index 000000000..9b9b5fb28
--- /dev/null
+++ b/tests/nrf/src/bin/timer.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{assert, info};
6use embassy_executor::Spawner;
7use embassy_time::{Duration, Instant, Timer};
8use {defmt_rtt as _, panic_probe as _};
9
10#[embassy_executor::main]
11async fn main(_spawner: Spawner) {
12 let _p = embassy_nrf::init(Default::default());
13 info!("Hello World!");
14
15 let start = Instant::now();
16 Timer::after(Duration::from_millis(100)).await;
17 let end = Instant::now();
18 let ms = (end - start).as_millis();
19 info!("slept for {} ms", ms);
20 assert!(ms >= 99);
21 assert!(ms < 110);
22
23 info!("Test OK");
24 cortex_m::asm::bkpt();
25}
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
index 572a9ce88..463a370fe 100644
--- a/tests/rp/Cargo.toml
+++ b/tests/rp/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] }
11embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } 11embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] }
12embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 12embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@@ -17,8 +17,8 @@ defmt-rtt = "0.4"
17cortex-m = { version = "0.7.6" } 17cortex-m = { version = "0.7.6" }
18cortex-m-rt = "0.7.0" 18cortex-m-rt = "0.7.0"
19embedded-hal = "0.2.6" 19embedded-hal = "0.2.6"
20embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } 20embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
21embedded-hal-async = { version = "=0.2.0-alpha.0" } 21embedded-hal-async = { version = "=0.2.0-alpha.1" }
22panic-probe = { version = "0.3.0", features = ["print-defmt"] } 22panic-probe = { version = "0.3.0", features = ["print-defmt"] }
23futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 23futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
24embedded-io = { version = "0.4.0", features = ["async"] } 24embedded-io = { version = "0.4.0", features = ["async"] }
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index 08a775eae..bd181f235 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -11,11 +11,12 @@ stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo
11stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo 11stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo
12stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"] # Nucleo 12stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"] # Nucleo
13stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo 13stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo
14stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo
14stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board 15stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board
15 16
16[dependencies] 17[dependencies]
17embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 18embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
18embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 19embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
19embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } 20embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] }
20embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] } 21embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] }
21 22
@@ -25,8 +26,8 @@ defmt-rtt = "0.4"
25cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 26cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
26cortex-m-rt = "0.7.0" 27cortex-m-rt = "0.7.0"
27embedded-hal = "0.2.6" 28embedded-hal = "0.2.6"
28embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } 29embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
29embedded-hal-async = { version = "=0.2.0-alpha.0" } 30embedded-hal-async = { version = "=0.2.0-alpha.1" }
30panic-probe = { version = "0.3.0", features = ["print-defmt"] } 31panic-probe = { version = "0.3.0", features = ["print-defmt"] }
31 32
32[profile.dev] 33[profile.dev]
diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs
index 18fd85d44..6a36df8cc 100644
--- a/tests/stm32/src/bin/gpio.rs
+++ b/tests/stm32/src/bin/gpio.rs
@@ -30,6 +30,8 @@ async fn main(_spawner: Spawner) {
30 let (mut a, mut b) = (p.PB6, p.PB7); 30 let (mut a, mut b) = (p.PB6, p.PB7);
31 #[cfg(feature = "stm32u585ai")] 31 #[cfg(feature = "stm32u585ai")]
32 let (mut a, mut b) = (p.PD9, p.PD8); 32 let (mut a, mut b) = (p.PD9, p.PD8);
33 #[cfg(feature = "stm32h563zi")]
34 let (mut a, mut b) = (p.PB6, p.PB7);
33 35
34 // Test initial output 36 // Test initial output
35 { 37 {
diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs
index 1c5dc87c0..bf8098b1b 100644
--- a/tests/stm32/src/bin/spi.rs
+++ b/tests/stm32/src/bin/spi.rs
@@ -17,22 +17,25 @@ async fn main(_spawner: Spawner) {
17 info!("Hello World!"); 17 info!("Hello World!");
18 18
19 #[cfg(feature = "stm32f103c8")] 19 #[cfg(feature = "stm32f103c8")]
20 let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); 20 let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6);
21 #[cfg(feature = "stm32f429zi")] 21 #[cfg(feature = "stm32f429zi")]
22 let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); 22 let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6);
23 #[cfg(feature = "stm32h755zi")] 23 #[cfg(feature = "stm32h755zi")]
24 let (sck, mosi, miso) = (p.PA5, p.PB5, p.PA6); 24 let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PB5, p.PA6);
25 #[cfg(feature = "stm32g491re")] 25 #[cfg(feature = "stm32g491re")]
26 let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); 26 let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6);
27 #[cfg(feature = "stm32g071rb")] 27 #[cfg(feature = "stm32g071rb")]
28 let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); 28 let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6);
29 #[cfg(feature = "stm32wb55rg")] 29 #[cfg(feature = "stm32wb55rg")]
30 let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); 30 let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6);
31 #[cfg(feature = "stm32u585ai")] 31 #[cfg(feature = "stm32u585ai")]
32 let (sck, mosi, miso) = (p.PE13, p.PE15, p.PE14); 32 let (spi, sck, mosi, miso) = (p.SPI1, p.PE13, p.PE15, p.PE14);
33 #[cfg(feature = "stm32h563zi")]
34 let (spi, sck, mosi, miso) = (p.SPI4, p.PE12, p.PE14, p.PE13);
33 35
36 info!("asdfa;");
34 let mut spi = Spi::new( 37 let mut spi = Spi::new(
35 p.SPI1, 38 spi,
36 sck, // Arduino D13 39 sck, // Arduino D13
37 mosi, // Arduino D11 40 mosi, // Arduino D11
38 miso, // Arduino D12 41 miso, // Arduino D12
diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs
index cb2152e0b..b3dad8132 100644
--- a/tests/stm32/src/bin/spi_dma.rs
+++ b/tests/stm32/src/bin/spi_dma.rs
@@ -16,22 +16,24 @@ async fn main(_spawner: Spawner) {
16 info!("Hello World!"); 16 info!("Hello World!");
17 17
18 #[cfg(feature = "stm32f103c8")] 18 #[cfg(feature = "stm32f103c8")]
19 let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2); 19 let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2);
20 #[cfg(feature = "stm32f429zi")] 20 #[cfg(feature = "stm32f429zi")]
21 let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA2_CH3, p.DMA2_CH2); 21 let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA2_CH3, p.DMA2_CH2);
22 #[cfg(feature = "stm32h755zi")] 22 #[cfg(feature = "stm32h755zi")]
23 let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PB5, p.PA6, p.DMA1_CH0, p.DMA1_CH1); 23 let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PB5, p.PA6, p.DMA1_CH0, p.DMA1_CH1);
24 #[cfg(feature = "stm32g491re")] 24 #[cfg(feature = "stm32g491re")]
25 let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); 25 let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2);
26 #[cfg(feature = "stm32g071rb")] 26 #[cfg(feature = "stm32g071rb")]
27 let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); 27 let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2);
28 #[cfg(feature = "stm32wb55rg")] 28 #[cfg(feature = "stm32wb55rg")]
29 let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); 29 let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2);
30 #[cfg(feature = "stm32u585ai")] 30 #[cfg(feature = "stm32u585ai")]
31 let (sck, mosi, miso, tx_dma, rx_dma) = (p.PE13, p.PE15, p.PE14, p.GPDMA1_CH0, p.GPDMA1_CH1); 31 let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PE13, p.PE15, p.PE14, p.GPDMA1_CH0, p.GPDMA1_CH1);
32 #[cfg(feature = "stm32h563zi")]
33 let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI4, p.PE12, p.PE14, p.PE13, p.GPDMA1_CH0, p.GPDMA1_CH1);
32 34
33 let mut spi = Spi::new( 35 let mut spi = Spi::new(
34 p.SPI1, 36 spi,
35 sck, // Arduino D13 37 sck, // Arduino D13
36 mosi, // Arduino D11 38 mosi, // Arduino D11
37 miso, // Arduino D12 39 miso, // Arduino D12
diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs
index af55867f2..52409567c 100644
--- a/tests/stm32/src/bin/usart.rs
+++ b/tests/stm32/src/bin/usart.rs
@@ -32,6 +32,8 @@ async fn main(_spawner: Spawner) {
32 let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); 32 let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1));
33 #[cfg(feature = "stm32u585ai")] 33 #[cfg(feature = "stm32u585ai")]
34 let (tx, rx, usart, irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); 34 let (tx, rx, usart, irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3));
35 #[cfg(feature = "stm32h563zi")]
36 let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.LPUART1, interrupt::take!(LPUART1));
35 37
36 let config = Config::default(); 38 let config = Config::default();
37 let mut usart = Uart::new(usart, rx, tx, irq, NoDma, NoDma, config); 39 let mut usart = Uart::new(usart, rx, tx, irq, NoDma, NoDma, config);
diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs
index d12605a9a..3f70791c1 100644
--- a/tests/stm32/src/bin/usart_dma.rs
+++ b/tests/stm32/src/bin/usart_dma.rs
@@ -62,6 +62,15 @@ async fn main(_spawner: Spawner) {
62 p.GPDMA1_CH0, 62 p.GPDMA1_CH0,
63 p.GPDMA1_CH1, 63 p.GPDMA1_CH1,
64 ); 64 );
65 #[cfg(feature = "stm32h563zi")]
66 let (tx, rx, usart, irq, tx_dma, rx_dma) = (
67 p.PB6,
68 p.PB7,
69 p.LPUART1,
70 interrupt::take!(LPUART1),
71 p.GPDMA1_CH0,
72 p.GPDMA1_CH1,
73 );
65 74
66 let config = Config::default(); 75 let config = Config::default();
67 let mut usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); 76 let mut usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config);